MCP server
Point Claude Code, Cursor or any MCP client at your Fictura app — it reads your live paywalls, funnel, experiments, errors and docs, then writes integration code against your real setup instead of guessing.
An AI coding agent that has never seen your dashboard will invent a placement name, hardcode a price, and leave YOUR_API_KEY in the file. Connect it to this server and it reads the real thing first.
Everything here is read-only. An agent can see your revenue and your error stacks; it cannot ship a paywall, start an experiment, or change a single setting. Writing stays where a human can watch it happen.
Get a key
Dashboard → Setup → Coding agents (MCP) → create a key. It is shown once, stored only as a hash, and revoking it takes effect on the next request.
The key names the app, so there is no app id anywhere in the setup — and no way for one key to read another app's data.
Connect your client
The server is hosted at https://api.fictura.co/mcp and speaks Streamable HTTP. Most clients connect to that URL directly.
Claude Code
claude mcp add --transport http fictura https://api.fictura.co/mcp \
--header "Authorization: Bearer fk_mcp_YOUR_KEY"Cursor
~/.cursor/mcp.json:
{
"mcpServers": {
"fictura": {
"url": "https://api.fictura.co/mcp",
"headers": { "Authorization": "Bearer fk_mcp_YOUR_KEY" }
}
}
}VS Code
.vscode/mcp.json:
{
"servers": {
"fictura": {
"type": "http",
"url": "https://api.fictura.co/mcp",
"headers": { "Authorization": "Bearer fk_mcp_YOUR_KEY" }
}
}
}Windsurf
{
"mcpServers": {
"fictura": {
"serverUrl": "https://api.fictura.co/mcp",
"headers": { "Authorization": "Bearer fk_mcp_YOUR_KEY" }
}
}
}Claude Desktop
Claude Desktop speaks stdio only, so it needs the @fictura/mcp bridge. In claude_desktop_config.json:
{
"mcpServers": {
"fictura": {
"command": "npx",
"args": ["-y", "@fictura/mcp"],
"env": { "FICTURA_MCP_KEY": "fk_mcp_YOUR_KEY" }
}
}
}The bridge defines no tools of its own — it only translates stdio to HTTP, so it never falls behind the server. FICTURA_API_BASE points it somewhere other than production.
Documentation works without a key
search_docs, read_doc and list_docs answer whether or not you have authenticated, so an agent can learn how Fictura works before anyone has signed up. Connect with no Authorization header at all to get exactly those three.
The docs an agent reads are the same pages you are reading, regenerated on every change to the product — so it is never working from a stale copy of the API.
What the agent can read
| Tool | What it answers |
|---|---|
search_docs · read_doc · list_docs | The documentation. No key required. |
get_app_setup | API base, publishable SDK key, RevenueCat webhook URL, bundle id. The one to call before generating any integration code. |
get_setup_status | Setup checklist, every feature toggle, connected integrations. |
get_analytics | Overview, conversion funnel, or cohort compare. |
get_attribution_report | Attribution summary, KPIs by source/campaign/link/day, or raw installs. |
list_users · get_user | The user roster, and one user's stitched identity, events and churn state. |
check_entitlement | Whether a given user is subscribed, unified across Stripe, RevenueCat, App Store and Play. |
get_tracking_plan | Every event the app sends, with volume, last-seen and kill-switch state. |
list_surfaces | Paywalls, onboarding flows, what is live per placement, and the valid placement names. |
get_surface | One paywall or onboarding config, with version history. |
list_templates | The built-in paywall and onboarding starter templates. |
list_experiments · get_experiment | Experiments with full Bayesian results. |
get_acquisition_report | ROAS, raw ad spend, or tracking links. |
list_campaigns | Live campaigns from a connected ad platform. |
list_creators | Creator records and their 28-day funnel. |
get_link_infrastructure | Deep-link setup: Universal Links / App Links association, branded domains, web-to-app banner, ESP. |
get_push_config | Registered-device counts per cohort and whether OneSignal is configured. |
list_store_products | App Store Connect subscriptions, optionally with Apple's price ladder. |
list_error_issues · get_error_issue | The error feed, and one issue with its stack traces. |
There is also an integrate_fictura_sdk prompt that walks an agent through a correct first integration.
What it deliberately cannot see
Push tokens and individual recipients (they identify people, and counts answer the same question), your OneSignal REST key, integration credentials, and your Supabase token. App Builder projects are out of scope for now — that is Fictura's own agent, and pointing a second one at its working tree solves nothing.
Why this beats pasting docs into the chat
Three integration mistakes are near-universal, and all three are things only your account knows:
- Placement names. A
<GrowthPaywall placement="...">naming a placement that does not exist renders nothing and reports no error.list_surfacesreturns the names that are actually configured, alongside the ones your app has been observed sending — which is how you spot a typo you already shipped. - Deep links. Universal Links and App Links both fail silently: a malformed team id produces an association file that parses, associates nothing, and shows no error anywhere, so links keep opening the browser.
get_link_infrastructurereturnsios_readyandandroid_readyrather than making an agent guess. - Prices. Paywall configs never contain price strings — prices resolve at render from the store. An agent that has read
get_surfacesees that; one working from memory writes a literal and risks a rejected build.
Troubleshooting
401 Invalid or revoked Fictura MCP key — the key was sent but does not work. Mint a new one under Setup. A key that is merely absent is not an error; you get the documentation tools instead.
406 Not Acceptable — the client sent no Accept header, or only one of the two required values. Streamable HTTP needs Accept: application/json, text/event-stream. Every supported client does this for you; it usually shows up when testing with curl.
405 Method Not Allowed — the endpoint accepts POST only. There is no session to resume and no server-initiated stream, so GET and DELETE have nothing to do.
An MCP key rejected by the Links API — that is deliberate. fk_mcp_… keys authenticate /mcp and nothing else; the Links API takes fk_live_…. Neither surface accepts the other's key, which is what keeps MCP access read-only. See Authentication.