Browse documentation

MCP server

Gives Claude (and any MCP client) the same capabilities as the CLI — reads, onboarding, and human-relayed queue decisions. Runs over stdio; authenticates with an account API key.

Install

Cursor (1-Click)

Add to Cursor

Or configure ~/.cursor/mcp.json (global) or .cursor/mcp.json (project-scoped):

{
  "mcpServers": {
    "funnelkeeper": {
      "command": "npx",
      "args": ["-y", "--package", "funnelkeeper", "funnelkeeper-mcp"],
      "env": { "FUNNELKEEPER_API_KEY": "fk_live_…" }
    }
  }
}

Claude Code

claude mcp add funnelkeeper -e FUNNELKEEPER_API_KEY=fk_live_… -- npx -y --package funnelkeeper funnelkeeper-mcp

Other stdio MCP clients

{
  "mcpServers": {
    "funnelkeeper": {
      "command": "npx",
      "args": ["-y", "--package", "funnelkeeper", "funnelkeeper-mcp"],
      "env": { "FUNNELKEEPER_API_KEY": "fk_live_…" }
    }
  }
}

Optional: FUNNELKEEPER_API_URL to point at a non-production API.

Tools

ToolKindWhat it does
signup_accountwriteCreate a FunnelKeeper tenant from scratch (setup mode — only available when no API key is configured). Returns a working API key (persisted to ~/.config/funnelkeeper/config.json) plus a single-use claim_url the human opens to set a password and take over the account. Remaining tools become available after this succeeds.
get_claim_linkwriteRe-issue the single-use claim URL so the human can set a password and take over an agent-created account. 409 if the account is already claimed — they should use password reset instead.
get_billingreadCurrent workspace plan, trial days left, usage vs limits (projects, seats, events, AI analyses), and whether Stripe checkout is available. The workspace holds one plan; each project is free for its own 30 days and then adds one unit of subscription quantity — product_lines shows where each one stands. 402 plan_limit errors from other tools mean this plan is the ceiling — upgrade via the dashboard Billing screen.
get_accountreadThe current workspace: tenant id, display name, the authenticated person’s email and member_role. API keys are scoped to one workspace.
list_accountsreadWorkspaces the authenticated identity can see. A dashboard session returns every accepted membership; an API key returns only the key’s tenant.
list_teamreadPeople in the current workspace (owners, members, pending invites, disabled).
invite_team_memberwriteInvite a person to the current workspace. A new email gets a set-password link; an existing FunnelKeeper login gets a join link (409 only if they are already on this workspace). Returns the invite URL for the owner to share.
get_portfolioreadOne row per project: spend 7d/30d, visitors, leads, revenue, customers, CAC, LTV:CAC, gate status, pending card count.
get_productreadA single project’s portfolio row.
get_funnelreadFunnel stage × channel volumes for a project.
get_paybackreadCohort payback curves ({cohorts, cac}): cumulative revenue per customer by days since acquisition, per channel, plus per-channel CAC.
get_queuereadPending Keeper cards (proposals, insights, alerts). Cards are resolved only by a human decision.
get_healthreadPipeline health: job runs, per-source data freshness, queue backlog, unattributed revenue share.
get_connectionsreadIntegration inventory: each source’s status, config, last sync, last error.
create_productwriteCreate a project in the authenticated account.
connect_source_startwriteBegin connecting ga4, gtm, and/or google_ads via OAuth. Returns an authorization URL — give it to the human to open in their browser (never send them into the FunnelKeeper dashboard), then poll connect_source_status with the returned state. Prefer kinds so one consent click covers GA4+GTM together. For a zero-click path when you have Google admin, use connect_service_account instead.
connect_service_accountwriteBegin connecting GA4 and/or GTM via a per-account service account — no OAuth consent screen. Returns sa_email plus the exact Google API calls (accessBindings.create / user_permissions.create) to grant it. If you have Admin on the client’s property/container, make those calls yourself; otherwise give the email to the human to paste into GA4/GTM Admin. Then call verify_service_account. Google Ads is not on this path.
verify_service_accountwriteFinish a service-account GA4/GTM connection. Without ids: lists the properties/containers now visible to the service account (same options shape as connect_source_status). With ids: a live read proves the grant landed, the connection goes active, and a backfill starts.
connect_source_statusreadPoll an in-progress Google connection. pending → keep waiting; complete → results plus entity options (properties/containers) for connect_source_select; error → what went wrong.
connect_source_selectwriteFinish a Google connection by choosing which GA4 property / GTM container / Ads customer to use, from the options returned by connect_source_status.
set_semrush_keywriteConnect SEMrush by storing the customer’s own API key (encrypted at rest). No test call is made — SEMrush calls burn the customer’s units.
connect_claritywriteConnect Microsoft Clarity by storing the customer’s Data Export API token (encrypted) and project id. The daily job samples session recordings (playback URLs, never video) and one URL-grain insights snapshot. Token comes from Clarity → Settings → Data Export.
connect_posthogwriteConnect PostHog by storing the customer’s personal API key (encrypted), project id and host. The key needs scope query:read for funnel events, plus session_recording:read for replay. Host defaults to https://us.posthog.com — pass https://eu.posthog.com for EU cloud or their own origin when self-hosted. After connecting, set which PostHog events feed which stages with set_posthog_event_maps.
connect_opinlywriteConnect Opinly by storing the customer’s API key (encrypted) and optional company id. The daily job snapshots AI-search (GEO) visibility, site-audit health, tracked keyword ranks and competitor gaps. Key comes from Opinly → Settings → Developers (sk-…). Pass company_id when the key can see more than one company.
connect_bingwriteConnect Bing Webmaster by storing the customer’s API key (encrypted) and site URL. The daily job reads query and page stats. Key comes from Bing Webmaster → Settings → API Access.
connect_ahrefswriteConnect Ahrefs by storing the customer’s API v3 token (encrypted) and target domain. Snapshots run weekly — units deplete. Never test-called.
connect_meta_adswriteConnect Meta Ads by storing a user/system-user token (encrypted) and optional ad account id. Adult-adjacent projects are refused. Omit ad_account_id to discover accounts, then call again with one. For 1-click OAuth, use connect_meta_start.
set_meta_ads_campaign_filterwriteRestrict Meta Ads ingest to campaigns whose names start with one of the given prefixes. Empty array clears the filter (all campaigns). Needed when one Meta ad account holds campaigns for more than one product — otherwise foreign spend lands on this project’s CAC.
connect_meta_startwriteBegin connecting Meta Ads and/or Meta Social via OAuth. Returns an authorization URL for the user to open, then poll connect_meta_status with the returned state.
connect_meta_statusreadPoll an in-progress Meta connection. pending → keep waiting; complete → results plus discovered ad accounts and pages; error → what went wrong.
connect_meta_selectwriteFinish a Meta OAuth connection by choosing which Meta Ad Account and/or Facebook Page to track, from the options returned by connect_meta_status.
connect_linkedin_adswriteConnect LinkedIn Ads by storing a token with r_ads + r_ads_reporting (encrypted) and optional ad account id. Omit ad_account_id to discover.
connect_meta_socialwriteConnect Meta Social (Facebook Page + linked Instagram) by storing a user token (encrypted) and optional Page id. Omit page_id to discover.
connect_x_socialwriteConnect X (Twitter) Social by storing a Bearer token (encrypted) and username. Daily public_metrics and recent tweets are ingested.
connect_stripewriteConnect Stripe by storing a Restricted or Secret API key (encrypted). Successful charges become converted/payment events plus revenue; refunds and open/lost disputes write negative ledger rows. Prefer a Restricted key with Charges, Customers, Refunds and Disputes read.
set_posthog_event_mapswriteReplace which PostHog events count as which funnel stages. Each mapped event is ingested as daily aggregate stage rows. Map each stage from ONE source — a stage fed by both PostHog and GA4 double-counts.
list_session_recordingsreadSession recording pointers from the connected replay sources (Microsoft Clarity, PostHog): playback URL, duration, entry path, and friction signals (rage/dead click, JS error). Empty until a replay source is connected and the daily sync has run. Filter by landing-page path, signal, or provider.
test_connectionwriteVerify a connected source can deliver data (cheap, side-effect-free probe).
detect_website_integrationsreadInspect a website URL or project domain to auto-detect active analytics, pixels, tag managers, CRM, payments, and tracking scripts (GA4, GTM, Google Ads, Microsoft Clarity, PostHog, FunnelKeeper tracking, Meta Pixel, TikTok, LinkedIn, Stripe, Hotjar, Segment, Plausible, Shopify, Webflow, etc.) and recommend FunnelKeeper connections.
sync_connectionwriteRun a source now instead of waiting for the sweep (hourly for databases, daily for GA4 and PostHog), and clear any backoff so a corrected connection recovers immediately. Use this straight after setting event maps or credentials — a connection that has never synced starts a staged backfill (30 days so the dashboard populates, then a year, then everything the plan allows), so the surfaces fill rather than sitting empty. Returns when the run STARTS; poll list_connections for status/last_sync_at/last_error and backfill_days, which is how deep it has read so far. SEMrush and Clarity are excluded: their quotas deplete. Opinly is included — its snapshot reads stored company data.
track_eventswriteSend first-party funnel events (server-side). Each event needs a canonical stage and a dedupe_id (retries are safe). Optional name, identity (email/external_id), attribution (utm/gclid/fbclid/referrer, plus the Google Ads ValueTrack ids when the source captured them — those place the revenue on a campaign/keyword/creative), and value (integer cents). Source namespace is api:.
get_tracking_snippetreadThe project’s publishable write key (fk_pub_…) plus copy-paste
rotate_tracking_keywriteMint a new publishable write key and revoke the previous one. The old key stops ingesting immediately — update the snippet / GTM tag. Audited.
deploy_tracking_gtmwrite, human-gatedHUMAN-GATED: publish the FunnelKeeper tracking snippet into the connected GTM container (new workspace, Custom HTML on All Pages, create version, publish live). Only call this to relay an explicit human confirm. 403 means reconnect Google (grant is still read-only). 409 if no write key or GTM is not connected.
get_spendreadDaily spend by channel and campaign for a project, with the daily cap and whether it is currently binding.
log_distributionwriteRecord a distribution event you performed for the user — a post shipped, a listing submitted, an email sent — so its traffic joins back via utm_campaign.
propose_budget_changewritebudget.propose: suggest a spend change with rationale and rollback condition. Returns pending_human — a HUMAN approves it in the app; you cannot execute it. Policy caps are enforced server-side: over-cap proposals are rejected with policy_code.
get_attributionreadRevenue by channel under first-touch AND last-touch models, with spend, customers, CAC and ROAS per channel, plus attribution coverage. Unattributed revenue is its own row — never smeared.
get_attribution_salesreadEvery sale in a window with first-touch and last-touch channel, customer (email/external id), amount, and first-touch UTM. Unattributed sales are labelled ‘unattributed’ — never smeared. Newest first.
get_ad_performancereadROI one level BELOW channel: spend, first- and last-touch revenue, customers, CAC and ROAS per campaign, ad group, keyword or creative. Cost comes from Google Ads at that grain; revenue comes from the ad ids captured on each customer’s first click. Two rows are honest sentinels and must be reported as such, never dropped or merged: ‘unattributed’ is revenue with no paid click at all, and ‘no_dimension’ is revenue from an ad that has nothing at this grain (Performance Max, Shopping and Demand Gen have no keywords). totals.spend_without_detail_cents is the matching gap on the cost side. If coverage.identities_with_dimension is low, the fix is get_ads_url_suffix.
get_ads_url_suffixreadWhether the project’s Google Ads account tags its landing URLs with the ValueTrack parameters keyword- and creative-level ROI depends on. Returns the account’s current final URL suffix, whether auto-tagging is on, which params are missing, and the exact string to add. Read-only.
set_ads_url_suffixwriteAdd FunnelKeeper’s ValueTrack parameters to the Google Ads account’s final URL suffix, keeping any the tenant already had. This CHANGES the landing URL of every ad in the account — ask the human first and only call it when they say yes. The alternative is to hand them recommended_suffix from get_ads_url_suffix to paste themselves.
get_timeseriesreadDaily spend, revenue, visits, leads and new customers for a project — zero-filled, oldest first.
get_health_reportreadScored audit of the project (0–100): site & tracking, funnel, advertising, SEO & social. Every failing check carries the action that fixes it.
get_demand_reportreadDemand-gen report for a project: search impressions/clicks, top queries with striking-distance and low-CTR flags, keyword ranks across SEMrush/Ahrefs/Opinly, AI visibility, and authority. Empty sources still return so you can see what to connect.
get_social_reportreadOrganic social report: follower totals, reach, engagement rate, per-network split (Meta vs X), and top posts. Empty sources still return so you can see what to connect.
get_growth_actionsreadRanked next incremental changes that will generate revenue or growth for this project: activation leaks, friction, wasted spend, missing conversions, weak landing pages. Each row includes estimated monthly impact and a copy-paste prompt for a coding agent. Ask this before inventing a feature. Spend changes are listed as proposals only — never execute them.
get_growth_promptreadFull prompt for one growth action, tailored to Cursor, Claude Code, Lovable, Bolt, or v0. Use the id from get_growth_actions. Hand this to the coding agent as the next task. Does not change spend.
list_landing_pagesreadLanding pages for a project: path, conversion characteristics (sessions, engagement, conversions vs the previous window), and the latest AI quality score if analysed.
add_landing_pagewriteTrack a landing page by URL so it can be scored and compared. Path is derived from the URL; duplicates 409.
get_landing_pagereadOne landing page: analysis history (newest first) and daily traffic series.
analyze_landing_pagewriteFetch the live page and score it against the weighted quality rubric via OpenRouter. On-demand only; appends a new analysis row. Takes 10–20s.
list_landing_page_filtersreadSaved exclude filters for landing pages (path prefix or wildcard patterns to hide from the pages surface).
set_landing_page_filterwriteCreate or update an exclude filter for landing pages (e.g. pattern ‘/deals’ or ‘/guides/*’).
delete_landing_page_filterwriteDelete a saved landing page exclude filter by id.
get_historyreadThe audit timeline: every change and decision — Keeper cards raised, human approvals (resolved_by), policy blocks, config edits — newest first.
get_funnel_definitionreadThe project’s funnel definition (ordered, labelled steps) plus per-stage tracking status: recent volume and which sources feed each canonical stage.
set_funnel_definitionwriteSave the project’s funnel steps. Stages must come from the canonical taxonomy (impression, visit, engaged, lead, qualified, signup, activated, converted, payment, churned); labels are free text. Audited.
get_conversionsreadThe project’s conversion definitions — primary (the money event) and secondary (milestones) — each with its stage, source, value mode and 30-day count/value.
save_conversionwriteCreate or update a conversion definition (upserts by key). tier: primary = core bottom-of-funnel goal event (e.g. ‘waitlist-joined’ or ‘car-sold’), secondary = a milestone (‘car-listed’, ‘demo-scheduled’). stage must be canonical taxonomy; source restricts to ‘mysql’, ‘postgres’, ‘mongo’, ‘ga4’ or ‘stripe’ (with event_name for one GA4 event). value_mode: ‘transaction’ reads each conversion’s own revenue row from the source (dynamic values), ‘fixed’ uses fixed_value_cents + currency, ‘none’ counts only. score_mode: ‘none’ (default), ‘grade’ (A/B/C/D categorical qualification), or ‘numeric’ (point score). target_cpa_cents: optional target cost per acquisition in cents. Audited.
delete_conversionwriteDelete a conversion definition by key. Configuration only — the underlying events are untouched. Audited.
get_conversion_reportreadPer conversion: count and value in the window vs the prior window, plus a per-channel split (event channel, else identity first touch, else ‘unattributed’).
set_ga4_event_mapswriteMap GA4 events onto canonical funnel stages (replaces the whole map). Each mapped event is ingested daily so funnel steps and ga4-bound conversions can be built from GA4 events. Map each stage from ONE source — a stage fed by both GA4 and MySQL double-counts.
connect_mysqlwriteConnect the project’s MySQL database. Tenants pass credentials (host/port/user/password/database — encrypted at rest; use a READ-ONLY user, ideally on a replica). Operators may instead pass *_env NAMES resolved on the API host. event_maps turn source tables into funnel events, identities, channels (attribution columns) and revenue (the dynamic per-row values ‘transaction’-mode conversions read).
set_mysql_event_mapswriteReplace the MySQL connection’s table→stage maps without touching credentials. Each map: table/pk/ts_col/stage, optional identity columns (stitching), attribution columns (channels), and a revenue column (dynamic per-row values). revenue.amount_col must be what THIS BUSINESS earns — a marketplace/brokerage fee or commission, not the sale price / GMV passing through. Set revenue.basis to ‘net’ (your take) or ‘gross’ (the full transaction). Saving remaps and restates every day already backfilled, with adjustment rows — originals stay, the difference is a new row. One stage per source; map a row’s revenue on exactly one stage.
suggest_mysql_mappingsreadPropose table→stage event maps from the connected MySQL schema. source=preset returns applicable common templates (marketplace/brokerage, e-commerce, lead-gen, SaaS) — marketplace prefers a fee/commission column over sale price. source=ai asks the model to infer maps from tables, columns and 3 sample rows (those rows are sent to the AI provider) and is told to pick what the business earns, not GMV. schema_summary.money_columns lists numeric candidates labelled net/gross/unclear. schema_summary.unresolved_money_tables lists tables with two or more money columns whose names do not uniquely identify the take — ask a human, do not guess. Pass tables to restrict which tables are described. Does NOT save — review the proposal then call set_mysql_event_maps.
connect_postgreswriteConnect the project’s Postgres database (tenant-only). Pass credentials (host/port/user/password/database, optional schema — encrypted at rest; use a READ-ONLY role, ideally on a replica) plus event_maps — the data-not-code mapping from source tables to funnel stages, identities, attribution channels and revenue. Each map may set schema to reach a non-public schema.
set_postgres_event_mapswriteReplace the Postgres connection’s table→stage maps without touching credentials. Same shape as MySQL event maps, plus an optional per-map schema. One stage per source; map a row’s revenue on exactly one stage.
connect_mongowriteConnect the project’s MongoDB database (tenant-only). Pass credentials with a single connection string uri (encrypted at rest — mongodb:// or mongodb+srv://; SRV, replica sets, TLS and authSource all live in the URI; use a read-only user) plus event_maps — collection-based maps from source documents to funnel stages, identities, attribution channels and revenue. Field paths may use dot notation to reach nested values.
set_mongo_event_mapswriteReplace the MongoDB connection’s collection→stage maps without touching credentials. Each map: collection/pk_field/ts_field/stage, optional identity field paths (stitching), attribution field paths (channels), and a revenue field path (dynamic per-row values). One stage per source; map a document’s revenue on exactly one stage.
list_dashboardsreadThe project’s saved dashboards (name + widget spec). The built-in Overview is not stored.
create_dashboardwriteBuild a dashboard for the user. Pass a full spec ({widgets:[…]}, schema DashboardSpec in /openapi.json — widget types: metric, timeseries, funnel, payback, queue, breakdown, ad_breakdown (level campaign|ad_group|keyword|ad × measure roas|spend|revenue_first), note; each widget may carry layout {x,y,w,h} on a 12-column × 32px-row grid, or omit it to auto-pack in order) to compose it exactly, or just a prompt to let the server’s deterministic composer draft it. Users can then rearrange everything by drag and drop.
update_dashboardwriteRename a dashboard or replace its widget spec (validated server-side). Audited.
approve_cardwrite, human-gatedHUMAN-GATED: approve a pending Keeper card. Approving a proposal can change ad spend. Only call this to relay an explicit decision the human just made — never decide for them. The server’s policy engine still enforces caps and walls.
reject_cardwrite, human-gatedHUMAN-GATED: dismiss a pending Keeper card, relaying the human’s explicit decision.
snooze_cardwrite, human-gatedHUMAN-GATED: snooze a pending Keeper card for the human.
get_scorecardreadWeekly growth scorecard for a project: a metrics × ISO-weeks matrix auto-filled from the warehouse, plus any manual cells the human typed. Weeks are completed ISO weeks (the running week is partial and misleading); set include_current to append it. History window follows the plan. Use this before inventing a weekly report.
get_scorecard_insightsreadScorecard analysis for a project: cell highlights plus typed cards (insight / query / action). Deterministic on every plan; LLM-enriched on Growth. Never proposes spend changes.
set_scorecardwriteReplace the project’s scorecard definition (sections and metrics). Auto metrics must use catalog keys. Does not write warehouse facts.
record_scorecard_valueswriteWrite manual scorecard cells (blog posts, LinkedIn followers, …). week_start is the ISO Monday (YYYY-MM-DD). Null clears a cell. Audited.
suggest_scorecardreadPropose scorecard metrics from connected sources (LLM phrasing on Indie+). Proposals only — call set_scorecard after the human accepts.
export_scorecardreadDownload the scorecard as CSV (Growth plan). For Excel use the dashboard export button.
share_scorecardwriteMint a public read-only URL for the project’s weekly scorecard (build-in-public).
chatwriteAsk the Keeper in prose (same engine as the dashboard chat). Prefer a specific tool when you already know the call. Spend changes still become proposal cards.

Input schemas

signup_account

{
  "type": "object",
  "properties": {
    "email": {
      "type": "string",
      "format": "email",
      "description": "The human owner's email. Email is the person's identity; they can later belong to multiple workspaces."
    },
    "name": {
      "type": "string",
      "minLength": 1,
      "maxLength": 80,
      "description": "Owner display name."
    }
  },
  "required": [
    "email"
  ],
  "additionalProperties": false
}
{
  "type": "object",
  "properties": {},
  "additionalProperties": false
}

get_billing

{
  "type": "object",
  "properties": {},
  "additionalProperties": false
}

get_account

{
  "type": "object",
  "properties": {},
  "additionalProperties": false
}

list_accounts

{
  "type": "object",
  "properties": {},
  "additionalProperties": false
}

list_team

{
  "type": "object",
  "properties": {},
  "additionalProperties": false
}

invite_team_member

{
  "type": "object",
  "properties": {
    "email": {
      "type": "string",
      "format": "email"
    },
    "member_role": {
      "type": "string",
      "enum": [
        "owner",
        "member"
      ],
      "default": "member"
    }
  },
  "required": [
    "email"
  ],
  "additionalProperties": false
}

get_portfolio

{
  "type": "object",
  "properties": {},
  "additionalProperties": false
}

get_product

{
  "type": "object",
  "properties": {
    "slug": {
      "type": "string",
      "description": "Project slug, e.g. 'demo-product'."
    }
  },
  "required": [
    "slug"
  ],
  "additionalProperties": false
}

get_funnel

{
  "type": "object",
  "properties": {
    "slug": {
      "type": "string",
      "description": "Project slug, e.g. 'demo-product'."
    },
    "days": {
      "type": "integer",
      "exclusiveMinimum": true,
      "minimum": 0,
      "maximum": 365,
      "default": 30
    }
  },
  "required": [
    "slug"
  ],
  "additionalProperties": false
}

get_payback

{
  "type": "object",
  "properties": {
    "slug": {
      "type": "string",
      "description": "Project slug, e.g. 'demo-product'."
    }
  },
  "required": [
    "slug"
  ],
  "additionalProperties": false
}

get_queue

{
  "type": "object",
  "properties": {},
  "additionalProperties": false
}

get_health

{
  "type": "object",
  "properties": {},
  "additionalProperties": false
}

get_connections

{
  "type": "object",
  "properties": {},
  "additionalProperties": false
}

create_product

{
  "type": "object",
  "properties": {
    "name": {
      "type": "string",
      "minLength": 1,
      "maxLength": 120
    },
    "slug": {
      "type": "string",
      "pattern": "^[a-z0-9][a-z0-9-]*[a-z0-9]$"
    },
    "currency": {
      "type": "string",
      "minLength": 3,
      "maxLength": 3,
      "default": "USD"
    },
    "domain": {
      "type": "string"
    }
  },
  "required": [
    "name",
    "slug"
  ],
  "additionalProperties": false
}

connect_source_start

{
  "type": "object",
  "properties": {
    "product_slug": {
      "type": "string",
      "description": "Project slug, e.g. 'demo-product'."
    },
    "kinds": {
      "type": "array",
      "items": {
        "type": "string",
        "enum": [
          "ga4",
          "gtm",
          "google_ads",
          "gsc"
        ]
      },
      "minItems": 1,
      "description": "One grant covers every kind in this list. Prefer this over kind. gsc requires GOOGLE_GSC_OAUTH=1."
    },
    "kind": {
      "type": "string",
      "enum": [
        "ga4",
        "gtm",
        "google_ads",
        "gsc"
      ],
      "description": "Single kind. Ignored when kinds is set."
    }
  },
  "required": [
    "product_slug"
  ],
  "additionalProperties": false
}

connect_service_account

{
  "type": "object",
  "properties": {
    "product_slug": {
      "type": "string",
      "description": "Project slug, e.g. 'demo-product'."
    },
    "kinds": {
      "type": "array",
      "items": {
        "type": "string",
        "enum": [
          "ga4",
          "gtm",
          "gsc"
        ]
      },
      "minItems": 1,
      "default": [
        "ga4",
        "gtm"
      ]
    }
  },
  "required": [
    "product_slug"
  ],
  "additionalProperties": false
}

verify_service_account

{
  "type": "object",
  "properties": {
    "product_slug": {
      "type": "string",
      "description": "Project slug, e.g. 'demo-product'."
    },
    "ga4_property_id": {
      "type": "string",
      "description": "GA4 property id (digits, or properties/123)."
    },
    "gtm_container_path": {
      "type": "string",
      "description": "GTM container path, e.g. accounts/123/containers/456."
    },
    "gsc_site_url": {
      "type": "string",
      "description": "Search Console site URL (https://example.com/ or sc-domain:example.com)."
    }
  },
  "required": [
    "product_slug"
  ],
  "additionalProperties": false
}

connect_source_status

{
  "type": "object",
  "properties": {
    "state": {
      "type": "string"
    }
  },
  "required": [
    "state"
  ],
  "additionalProperties": false
}

connect_source_select

{
  "type": "object",
  "properties": {
    "state": {
      "type": "string"
    },
    "ga4_property_id": {
      "type": "string"
    },
    "gtm_container_path": {
      "type": "string"
    },
    "ads_customer_id": {
      "type": "string"
    },
    "gsc_site_url": {
      "type": "string"
    }
  },
  "required": [
    "state"
  ],
  "additionalProperties": false
}

set_semrush_key

{
  "type": "object",
  "properties": {
    "product_slug": {
      "type": "string",
      "description": "Project slug, e.g. 'demo-product'."
    },
    "api_key": {
      "type": "string"
    },
    "domain": {
      "type": "string"
    },
    "database": {
      "type": "string",
      "minLength": 2,
      "maxLength": 2,
      "default": "us"
    }
  },
  "required": [
    "product_slug",
    "api_key",
    "domain"
  ],
  "additionalProperties": false
}

connect_clarity

{
  "type": "object",
  "properties": {
    "product_slug": {
      "type": "string",
      "description": "Project slug, e.g. 'demo-product'."
    },
    "api_token": {
      "type": "string",
      "description": "Clarity Data Export JWT from Settings → Data Export."
    },
    "project_id": {
      "type": "string",
      "description": "Clarity project id from Settings → Overview."
    }
  },
  "required": [
    "product_slug",
    "api_token",
    "project_id"
  ],
  "additionalProperties": false
}

connect_posthog

{
  "type": "object",
  "properties": {
    "product_slug": {
      "type": "string",
      "description": "Project slug, e.g. 'demo-product'."
    },
    "api_key": {
      "type": "string",
      "description": "PostHog personal API key (starts with phx_), from Settings → Personal API keys."
    },
    "project_id": {
      "type": "string",
      "description": "PostHog project id — the number in the project URL."
    },
    "host": {
      "type": "string",
      "description": "PostHog origin. Omit for US cloud."
    }
  },
  "required": [
    "product_slug",
    "api_key",
    "project_id"
  ],
  "additionalProperties": false
}

connect_opinly

{
  "type": "object",
  "properties": {
    "product_slug": {
      "type": "string",
      "description": "Project slug, e.g. 'demo-product'."
    },
    "api_key": {
      "type": "string",
      "description": "Opinly API key from Settings → Developers (starts with sk-)."
    },
    "company_id": {
      "type": "string",
      "description": "Opinly company id (comp_…). Optional when the key sees only one company."
    }
  },
  "required": [
    "product_slug",
    "api_key"
  ],
  "additionalProperties": false
}

connect_bing

{
  "type": "object",
  "properties": {
    "product_slug": {
      "type": "string",
      "description": "Project slug, e.g. 'demo-product'."
    },
    "api_key": {
      "type": "string",
      "description": "Bing Webmaster API key."
    },
    "site_url": {
      "type": "string",
      "description": "Verified site URL as it appears in Bing Webmaster."
    }
  },
  "required": [
    "product_slug",
    "api_key",
    "site_url"
  ],
  "additionalProperties": false
}

connect_ahrefs

{
  "type": "object",
  "properties": {
    "product_slug": {
      "type": "string",
      "description": "Project slug, e.g. 'demo-product'."
    },
    "api_key": {
      "type": "string",
      "description": "Ahrefs API v3 token."
    },
    "domain": {
      "type": "string",
      "description": "Domain to track, e.g. example.com."
    }
  },
  "required": [
    "product_slug",
    "api_key",
    "domain"
  ],
  "additionalProperties": false
}

connect_meta_ads

{
  "type": "object",
  "properties": {
    "product_slug": {
      "type": "string",
      "description": "Project slug, e.g. 'demo-product'."
    },
    "access_token": {
      "type": "string",
      "description": "Meta token with ads_read."
    },
    "ad_account_id": {
      "type": "string",
      "description": "Numeric ad account id, no act_ prefix."
    }
  },
  "required": [
    "product_slug",
    "access_token"
  ],
  "additionalProperties": false
}

set_meta_ads_campaign_filter

{
  "type": "object",
  "properties": {
    "product_slug": {
      "type": "string",
      "description": "Project slug, e.g. 'demo-product'."
    },
    "campaign_name_prefixes": {
      "type": "array",
      "items": {
        "type": "string"
      },
      "maxItems": 20,
      "description": "Name prefixes to keep, e.g. [\"META_\"]. Empty clears the filter."
    }
  },
  "required": [
    "product_slug",
    "campaign_name_prefixes"
  ],
  "additionalProperties": false
}

connect_meta_start

{
  "type": "object",
  "properties": {
    "product_slug": {
      "type": "string",
      "description": "Project slug, e.g. 'demo-product'."
    },
    "kinds": {
      "type": "array",
      "items": {
        "type": "string",
        "enum": [
          "meta_ads",
          "meta_social"
        ]
      },
      "minItems": 1,
      "description": "Meta kinds to connect (meta_ads, meta_social). Default is ['meta_ads']."
    }
  },
  "required": [
    "product_slug"
  ],
  "additionalProperties": false
}

connect_meta_status

{
  "type": "object",
  "properties": {
    "state": {
      "type": "string"
    }
  },
  "required": [
    "state"
  ],
  "additionalProperties": false
}

connect_meta_select

{
  "type": "object",
  "properties": {
    "state": {
      "type": "string"
    },
    "ad_account_id": {
      "type": "string",
      "description": "Numeric ad account id, no act_ prefix."
    },
    "page_id": {
      "type": "string",
      "description": "Facebook Page id."
    }
  },
  "required": [
    "state"
  ],
  "additionalProperties": false
}

connect_linkedin_ads

{
  "type": "object",
  "properties": {
    "product_slug": {
      "type": "string",
      "description": "Project slug, e.g. 'demo-product'."
    },
    "access_token": {
      "type": "string",
      "description": "LinkedIn Marketing token."
    },
    "ad_account_id": {
      "type": "string",
      "description": "Numeric sponsored account id."
    }
  },
  "required": [
    "product_slug",
    "access_token"
  ],
  "additionalProperties": false
}

connect_meta_social

{
  "type": "object",
  "properties": {
    "product_slug": {
      "type": "string",
      "description": "Project slug, e.g. 'demo-product'."
    },
    "access_token": {
      "type": "string",
      "description": "Meta token with pages_show_list and pages_read_engagement."
    },
    "page_id": {
      "type": "string",
      "description": "Facebook Page id."
    }
  },
  "required": [
    "product_slug",
    "access_token"
  ],
  "additionalProperties": false
}

connect_x_social

{
  "type": "object",
  "properties": {
    "product_slug": {
      "type": "string",
      "description": "Project slug, e.g. 'demo-product'."
    },
    "bearer_token": {
      "type": "string",
      "description": "X API v2 Bearer token."
    },
    "username": {
      "type": "string",
      "description": "X username without @."
    }
  },
  "required": [
    "product_slug",
    "bearer_token",
    "username"
  ],
  "additionalProperties": false
}

connect_stripe

{
  "type": "object",
  "properties": {
    "product_slug": {
      "type": "string",
      "description": "Project slug, e.g. 'demo-product'."
    },
    "api_key": {
      "type": "string",
      "description": "Stripe Restricted or Secret key (rk_live_… / sk_live_…, or _test_)."
    },
    "default_stage": {
      "type": "string",
      "enum": [
        "converted",
        "payment"
      ],
      "description": "Funnel stage for a successful charge. Defaults to converted."
    }
  },
  "required": [
    "product_slug",
    "api_key"
  ],
  "additionalProperties": false
}

set_posthog_event_maps

{
  "type": "object",
  "properties": {
    "product_slug": {
      "type": "string",
      "description": "Project slug, e.g. 'demo-product'."
    },
    "event_maps": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "event": {
            "type": "string",
            "description": "PostHog event name, e.g. 'signed_up'."
          },
          "stage": {
            "type": "string",
            "enum": [
              "impression",
              "visit",
              "engaged",
              "lead",
              "qualified",
              "signup",
              "activated",
              "converted",
              "payment",
              "churned"
            ]
          }
        },
        "required": [
          "event",
          "stage"
        ],
        "additionalProperties": false
      },
      "maxItems": 20
    }
  },
  "required": [
    "product_slug",
    "event_maps"
  ],
  "additionalProperties": false
}

list_session_recordings

{
  "type": "object",
  "properties": {
    "slug": {
      "type": "string",
      "description": "Project slug, e.g. 'demo-product'."
    },
    "days": {
      "type": "integer",
      "exclusiveMinimum": true,
      "minimum": 0,
      "maximum": 30,
      "default": 7
    },
    "path": {
      "type": "string",
      "description": "Landing-page path, e.g. '/pricing'."
    },
    "signal": {
      "type": "string",
      "enum": [
        "rage_click",
        "dead_click",
        "js_error"
      ]
    },
    "provider": {
      "type": "string",
      "enum": [
        "clarity",
        "posthog"
      ]
    },
    "limit": {
      "type": "integer",
      "exclusiveMinimum": true,
      "minimum": 0,
      "maximum": 250,
      "default": 50
    }
  },
  "required": [
    "slug"
  ],
  "additionalProperties": false
}

test_connection

{
  "type": "object",
  "properties": {
    "product_slug": {
      "type": "string",
      "description": "Project slug, e.g. 'demo-product'."
    },
    "kind": {
      "type": "string",
      "enum": [
        "ga4",
        "gtm",
        "google_ads",
        "gsc",
        "semrush",
        "mysql",
        "postgres",
        "mongo",
        "clarity",
        "posthog",
        "opinly",
        "bing",
        "ahrefs",
        "meta_ads",
        "linkedin_ads",
        "meta_social",
        "x_social",
        "stripe"
      ]
    }
  },
  "required": [
    "product_slug",
    "kind"
  ],
  "additionalProperties": false
}

detect_website_integrations

{
  "type": "object",
  "properties": {
    "product_slug": {
      "type": "string",
      "description": "Project slug if inspecting an existing project in FunnelKeeper (will cross-reference existing connections)."
    },
    "url": {
      "type": "string",
      "description": "Website URL to scan (e.g. 'https://motormerchants.com.au'). If omitted and product_slug is provided, uses the project's domain."
    }
  },
  "additionalProperties": false
}

sync_connection

{
  "type": "object",
  "properties": {
    "product_slug": {
      "type": "string",
      "description": "Project slug, e.g. 'demo-product'."
    },
    "kind": {
      "type": "string",
      "enum": [
        "mysql",
        "postgres",
        "mongo",
        "ga4",
        "posthog",
        "opinly",
        "gsc",
        "bing",
        "meta_ads",
        "linkedin_ads",
        "meta_social",
        "x_social",
        "stripe"
      ]
    }
  },
  "required": [
    "product_slug",
    "kind"
  ],
  "additionalProperties": false
}

track_events

{
  "type": "object",
  "properties": {
    "slug": {
      "type": "string",
      "description": "Project slug, e.g. 'demo-product'."
    },
    "events": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "stage": {
            "type": "string",
            "enum": [
              "impression",
              "visit",
              "engaged",
              "lead",
              "qualified",
              "signup",
              "activated",
              "converted",
              "payment",
              "churned"
            ]
          },
          "dedupe_id": {
            "type": "string",
            "minLength": 1,
            "maxLength": 128
          },
          "name": {
            "type": "string",
            "maxLength": 80
          },
          "occurred_at": {
            "type": "string"
          },
          "identity": {
            "type": "object",
            "properties": {
              "email": {
                "type": "string"
              },
              "external_id": {
                "type": "string"
              }
            },
            "additionalProperties": false
          },
          "attribution": {
            "type": "object",
            "properties": {
              "channel": {
                "type": "string"
              },
              "utm_source": {
                "type": "string"
              },
              "utm_medium": {
                "type": "string"
              },
              "utm_campaign": {
                "type": "string"
              },
              "gclid": {
                "type": "string"
              },
              "fbclid": {
                "type": "string"
              },
              "li_fat_id": {
                "type": "string"
              },
              "referrer": {
                "type": "string"
              },
              "campaign_id": {
                "type": "string",
                "description": "ValueTrack {campaignid}."
              },
              "ad_group_id": {
                "type": "string",
                "description": "ValueTrack {adgroupid}."
              },
              "ad_id": {
                "type": "string",
                "description": "ValueTrack {creative} — the ad id."
              },
              "target_id": {
                "type": "string",
                "description": "ValueTrack {targetid}; only the 'kwd-…' form is a keyword."
              },
              "keyword": {
                "type": "string",
                "description": "ValueTrack {keyword} or utm_term — the keyword text."
              },
              "match_type": {
                "type": "string",
                "description": "ValueTrack {matchtype}: e/p/b/a."
              }
            },
            "additionalProperties": false
          },
          "value": {
            "type": "object",
            "properties": {
              "amount_cents": {
                "type": "integer"
              },
              "currency": {
                "type": "string",
                "minLength": 3,
                "maxLength": 3
              },
              "kind": {
                "type": "string",
                "enum": [
                  "charge",
                  "usage",
                  "invoice",
                  "refund",
                  "dispute",
                  "adjustment"
                ]
              }
            },
            "required": [
              "amount_cents"
            ],
            "additionalProperties": false
          },
          "score": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "number"
              }
            ],
            "description": "Optional lead grade ('A', 'B', 'C', 'D') or numeric score (e.g. 85)."
          },
          "props": {
            "type": "object",
            "additionalProperties": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "number"
                },
                {
                  "type": "boolean"
                },
                {
                  "enum": [
                    "null"
                  ],
                  "nullable": true
                }
              ]
            }
          }
        },
        "required": [
          "stage",
          "dedupe_id"
        ],
        "additionalProperties": false
      },
      "minItems": 1,
      "maxItems": 500
    }
  },
  "required": [
    "slug",
    "events"
  ],
  "additionalProperties": false
}

get_tracking_snippet

{
  "type": "object",
  "properties": {
    "slug": {
      "type": "string",
      "description": "Project slug, e.g. 'demo-product'."
    }
  },
  "required": [
    "slug"
  ],
  "additionalProperties": false
}

rotate_tracking_key

{
  "type": "object",
  "properties": {
    "slug": {
      "type": "string",
      "description": "Project slug, e.g. 'demo-product'."
    }
  },
  "required": [
    "slug"
  ],
  "additionalProperties": false
}

deploy_tracking_gtm

{
  "type": "object",
  "properties": {
    "slug": {
      "type": "string",
      "description": "Project slug, e.g. 'demo-product'."
    }
  },
  "required": [
    "slug"
  ],
  "additionalProperties": false
}

get_spend

{
  "type": "object",
  "properties": {
    "product_slug": {
      "type": "string",
      "description": "Project slug, e.g. 'demo-product'."
    },
    "days": {
      "type": "integer",
      "exclusiveMinimum": true,
      "minimum": 0,
      "maximum": 365,
      "default": 30
    }
  },
  "required": [
    "product_slug"
  ],
  "additionalProperties": false
}

log_distribution

{
  "type": "object",
  "properties": {
    "product_slug": {
      "type": "string",
      "description": "Project slug, e.g. 'demo-product'."
    },
    "channel": {
      "type": "string",
      "description": "Channel id, e.g. social_reddit, community, email."
    },
    "kind": {
      "type": "string",
      "description": "What happened: forum_post, dm_wave, launch, social_post, email_blast."
    },
    "url": {
      "type": "string",
      "format": "uri"
    },
    "utm_campaign": {
      "type": "string"
    },
    "note": {
      "type": "string"
    }
  },
  "required": [
    "product_slug",
    "channel",
    "kind"
  ],
  "additionalProperties": false
}

propose_budget_change

{
  "type": "object",
  "properties": {
    "product_slug": {
      "type": "string",
      "description": "Project slug, e.g. 'demo-product'."
    },
    "network": {
      "type": "string",
      "enum": [
        "meta",
        "google"
      ]
    },
    "campaign_id": {
      "type": "string"
    },
    "from_cents": {
      "type": "integer",
      "minimum": 0
    },
    "to_cents": {
      "type": "integer",
      "exclusiveMinimum": true,
      "minimum": 0,
      "description": "Proposed daily cap, integer cents."
    },
    "rationale": {
      "type": "string",
      "minLength": 10,
      "maxLength": 2000,
      "description": "Why — shown to the human on the card."
    },
    "rollback_if": {
      "type": "object",
      "additionalProperties": {}
    }
  },
  "required": [
    "product_slug",
    "network",
    "to_cents",
    "rationale"
  ],
  "additionalProperties": false
}

get_attribution

{
  "type": "object",
  "properties": {
    "slug": {
      "type": "string",
      "description": "Project slug, e.g. 'demo-product'."
    },
    "days": {
      "type": "integer",
      "exclusiveMinimum": true,
      "minimum": 0,
      "maximum": 365,
      "default": 30
    }
  },
  "required": [
    "slug"
  ],
  "additionalProperties": false
}

get_attribution_sales

{
  "type": "object",
  "properties": {
    "slug": {
      "type": "string",
      "description": "Project slug, e.g. 'demo-product'."
    },
    "days": {
      "type": "integer",
      "exclusiveMinimum": true,
      "minimum": 0,
      "maximum": 365,
      "default": 30
    },
    "channel": {
      "type": "string",
      "description": "Filter by first-touch channel, including 'unattributed'."
    },
    "limit": {
      "type": "integer",
      "exclusiveMinimum": true,
      "minimum": 0,
      "maximum": 2000,
      "default": 500
    }
  },
  "required": [
    "slug"
  ],
  "additionalProperties": false
}

get_ad_performance

{
  "type": "object",
  "properties": {
    "slug": {
      "type": "string",
      "description": "Project slug, e.g. 'demo-product'."
    },
    "level": {
      "type": "string",
      "enum": [
        "campaign",
        "ad_group",
        "keyword",
        "ad"
      ],
      "default": "campaign"
    },
    "days": {
      "type": "integer",
      "exclusiveMinimum": true,
      "minimum": 0,
      "maximum": 365,
      "default": 30
    },
    "limit": {
      "type": "integer",
      "exclusiveMinimum": true,
      "minimum": 0,
      "maximum": 500,
      "default": 100
    }
  },
  "required": [
    "slug"
  ],
  "additionalProperties": false
}

get_ads_url_suffix

{
  "type": "object",
  "properties": {
    "slug": {
      "type": "string",
      "description": "Project slug, e.g. 'demo-product'."
    }
  },
  "required": [
    "slug"
  ],
  "additionalProperties": false
}

set_ads_url_suffix

{
  "type": "object",
  "properties": {
    "slug": {
      "type": "string",
      "description": "Project slug, e.g. 'demo-product'."
    }
  },
  "required": [
    "slug"
  ],
  "additionalProperties": false
}

get_timeseries

{
  "type": "object",
  "properties": {
    "slug": {
      "type": "string",
      "description": "Project slug, e.g. 'demo-product'."
    },
    "days": {
      "type": "integer",
      "exclusiveMinimum": true,
      "minimum": 0,
      "maximum": 365,
      "default": 90
    }
  },
  "required": [
    "slug"
  ],
  "additionalProperties": false
}

get_health_report

{
  "type": "object",
  "properties": {
    "slug": {
      "type": "string",
      "description": "Project slug, e.g. 'demo-product'."
    },
    "days": {
      "type": "integer",
      "exclusiveMinimum": true,
      "minimum": 0,
      "maximum": 365,
      "default": 30
    }
  },
  "required": [
    "slug"
  ],
  "additionalProperties": false
}

get_demand_report

{
  "type": "object",
  "properties": {
    "slug": {
      "type": "string",
      "description": "Project slug, e.g. 'demo-product'."
    },
    "days": {
      "type": "integer",
      "exclusiveMinimum": true,
      "minimum": 0,
      "maximum": 365,
      "default": 30
    }
  },
  "required": [
    "slug"
  ],
  "additionalProperties": false
}

get_social_report

{
  "type": "object",
  "properties": {
    "slug": {
      "type": "string",
      "description": "Project slug, e.g. 'demo-product'."
    },
    "days": {
      "type": "integer",
      "exclusiveMinimum": true,
      "minimum": 0,
      "maximum": 365,
      "default": 30
    }
  },
  "required": [
    "slug"
  ],
  "additionalProperties": false
}

get_growth_actions

{
  "type": "object",
  "properties": {
    "slug": {
      "type": "string",
      "description": "Project slug, e.g. 'demo-product'."
    },
    "days": {
      "type": "integer",
      "exclusiveMinimum": true,
      "minimum": 0,
      "maximum": 365,
      "default": 30
    },
    "tool": {
      "type": "string",
      "enum": [
        "cursor",
        "claude_code",
        "lovable",
        "bolt",
        "v0"
      ],
      "default": "cursor",
      "description": "Which coding agent the prompts should address."
    }
  },
  "required": [
    "slug"
  ],
  "additionalProperties": false
}

get_growth_prompt

{
  "type": "object",
  "properties": {
    "slug": {
      "type": "string",
      "description": "Project slug, e.g. 'demo-product'."
    },
    "id": {
      "type": "string",
      "description": "Growth action id from get_growth_actions, e.g. activation.dropoff.signup-activated."
    },
    "tool": {
      "type": "string",
      "enum": [
        "cursor",
        "claude_code",
        "lovable",
        "bolt",
        "v0"
      ],
      "default": "cursor"
    },
    "days": {
      "type": "integer",
      "exclusiveMinimum": true,
      "minimum": 0,
      "maximum": 365
    }
  },
  "required": [
    "slug",
    "id"
  ],
  "additionalProperties": false
}

list_landing_pages

{
  "type": "object",
  "properties": {
    "slug": {
      "type": "string",
      "description": "Project slug, e.g. 'demo-product'."
    },
    "days": {
      "type": "integer",
      "exclusiveMinimum": true,
      "minimum": 0,
      "maximum": 365,
      "default": 30
    }
  },
  "required": [
    "slug"
  ],
  "additionalProperties": false
}

add_landing_page

{
  "type": "object",
  "properties": {
    "slug": {
      "type": "string",
      "description": "Project slug, e.g. 'demo-product'."
    },
    "url": {
      "type": "string",
      "format": "uri"
    }
  },
  "required": [
    "slug",
    "url"
  ],
  "additionalProperties": false
}

get_landing_page

{
  "type": "object",
  "properties": {
    "slug": {
      "type": "string",
      "description": "Project slug, e.g. 'demo-product'."
    },
    "id": {
      "type": "string",
      "format": "uuid"
    },
    "days": {
      "type": "integer",
      "exclusiveMinimum": true,
      "minimum": 0,
      "maximum": 365,
      "default": 30
    }
  },
  "required": [
    "slug",
    "id"
  ],
  "additionalProperties": false
}

analyze_landing_page

{
  "type": "object",
  "properties": {
    "slug": {
      "type": "string",
      "description": "Project slug, e.g. 'demo-product'."
    },
    "id": {
      "type": "string",
      "format": "uuid"
    }
  },
  "required": [
    "slug",
    "id"
  ],
  "additionalProperties": false
}

list_landing_page_filters

{
  "type": "object",
  "properties": {
    "slug": {
      "type": "string",
      "description": "Project slug, e.g. 'demo-product'."
    }
  },
  "required": [
    "slug"
  ],
  "additionalProperties": false
}

set_landing_page_filter

{
  "type": "object",
  "properties": {
    "slug": {
      "type": "string",
      "description": "Project slug, e.g. 'demo-product'."
    },
    "pattern": {
      "type": "string",
      "minLength": 1,
      "maxLength": 500,
      "description": "Path prefix or wildcard pattern, e.g. '/deals' or '/deals/*'"
    },
    "enabled": {
      "type": "boolean",
      "default": true,
      "description": "Whether this filter is active"
    }
  },
  "required": [
    "slug",
    "pattern"
  ],
  "additionalProperties": false
}

delete_landing_page_filter

{
  "type": "object",
  "properties": {
    "slug": {
      "type": "string",
      "description": "Project slug, e.g. 'demo-product'."
    },
    "id": {
      "type": "string",
      "format": "uuid"
    }
  },
  "required": [
    "slug",
    "id"
  ],
  "additionalProperties": false
}

get_history

{
  "type": "object",
  "properties": {
    "product_slug": {
      "type": "string",
      "description": "Limit to one project."
    },
    "days": {
      "type": "integer",
      "exclusiveMinimum": true,
      "minimum": 0,
      "maximum": 365,
      "default": 90
    },
    "limit": {
      "type": "integer",
      "exclusiveMinimum": true,
      "minimum": 0,
      "maximum": 500,
      "default": 200
    }
  },
  "additionalProperties": false
}

get_funnel_definition

{
  "type": "object",
  "properties": {
    "slug": {
      "type": "string",
      "description": "Project slug, e.g. 'demo-product'."
    }
  },
  "required": [
    "slug"
  ],
  "additionalProperties": false
}

set_funnel_definition

{
  "type": "object",
  "properties": {
    "slug": {
      "type": "string",
      "description": "Project slug, e.g. 'demo-product'."
    },
    "steps": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "stage": {
            "type": "string"
          },
          "label": {
            "type": "string",
            "minLength": 1,
            "maxLength": 40
          }
        },
        "required": [
          "stage",
          "label"
        ],
        "additionalProperties": false
      },
      "minItems": 2,
      "maxItems": 10
    }
  },
  "required": [
    "slug",
    "steps"
  ],
  "additionalProperties": false
}

get_conversions

{
  "type": "object",
  "properties": {
    "slug": {
      "type": "string",
      "description": "Project slug, e.g. 'demo-product'."
    }
  },
  "required": [
    "slug"
  ],
  "additionalProperties": false
}

save_conversion

{
  "type": "object",
  "properties": {
    "product_slug": {
      "type": "string",
      "description": "Project slug, e.g. 'demo-product'."
    },
    "key": {
      "type": "string",
      "description": "Stable slug, e.g. 'car-sold' or 'waitlist-signup'."
    },
    "label": {
      "type": "string",
      "minLength": 1,
      "maxLength": 60
    },
    "tier": {
      "type": "string",
      "description": "'primary' or 'secondary'."
    },
    "stage": {
      "type": "string",
      "description": "Canonical stage (impression, visit, engaged, lead, qualified, signup, activated, converted, payment, churned)."
    },
    "source": {
      "type": "string",
      "description": "'mysql', 'postgres', 'mongo', 'ga4' or 'stripe'; omit for any source."
    },
    "event_name": {
      "type": "string",
      "description": "GA4 only: count just this event."
    },
    "value_mode": {
      "type": "string",
      "description": "'none' (default), 'fixed', or 'transaction'."
    },
    "fixed_value_cents": {
      "type": "integer"
    },
    "currency": {
      "type": "string"
    },
    "score_mode": {
      "type": "string",
      "description": "'none' (default), 'grade', or 'numeric'."
    },
    "target_cpa_cents": {
      "type": "integer",
      "exclusiveMinimum": true,
      "minimum": 0,
      "description": "Target CPA in cents."
    },
    "grade_weights": {
      "type": "object",
      "additionalProperties": {
        "type": "number"
      }
    },
    "is_active": {
      "type": "boolean"
    }
  },
  "required": [
    "product_slug",
    "key",
    "label",
    "tier",
    "stage"
  ],
  "additionalProperties": false
}

delete_conversion

{
  "type": "object",
  "properties": {
    "product_slug": {
      "type": "string",
      "description": "Project slug, e.g. 'demo-product'."
    },
    "key": {
      "type": "string"
    }
  },
  "required": [
    "product_slug",
    "key"
  ],
  "additionalProperties": false
}

get_conversion_report

{
  "type": "object",
  "properties": {
    "slug": {
      "type": "string",
      "description": "Project slug, e.g. 'demo-product'."
    },
    "days": {
      "type": "integer",
      "exclusiveMinimum": true,
      "minimum": 0,
      "maximum": 365,
      "default": 30
    }
  },
  "required": [
    "slug"
  ],
  "additionalProperties": false
}

set_ga4_event_maps

{
  "type": "object",
  "properties": {
    "product_slug": {
      "type": "string",
      "description": "Project slug, e.g. 'demo-product'."
    },
    "event_maps": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "event": {
            "type": "string",
            "description": "GA4 event name, e.g. 'generate_lead'."
          },
          "stage": {
            "type": "string",
            "description": "Canonical stage it counts as."
          }
        },
        "required": [
          "event",
          "stage"
        ],
        "additionalProperties": false
      },
      "maxItems": 20
    }
  },
  "required": [
    "product_slug",
    "event_maps"
  ],
  "additionalProperties": false
}

connect_mysql

{
  "type": "object",
  "properties": {
    "product_slug": {
      "type": "string",
      "description": "Project slug, e.g. 'demo-product'."
    },
    "credentials": {
      "type": "object",
      "properties": {
        "host": {
          "type": "string"
        },
        "port": {
          "type": "integer"
        },
        "user": {
          "type": "string",
          "description": "A READ-ONLY database user."
        },
        "password": {
          "type": "string"
        },
        "database": {
          "type": "string"
        }
      },
      "required": [
        "host",
        "user",
        "password",
        "database"
      ],
      "additionalProperties": false,
      "description": "Tenant path. Mutually exclusive with the *_env names."
    },
    "host_env": {
      "type": "string",
      "description": "Operator path: env var NAME holding the host, e.g. 'MM_MYSQL_HOST'."
    },
    "port_env": {
      "type": "string"
    },
    "user_env": {
      "type": "string"
    },
    "password_env": {
      "type": "string"
    },
    "database_env": {
      "type": "string"
    },
    "event_maps": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "table": {
            "type": "string"
          },
          "pk": {
            "type": "string"
          },
          "ts_col": {
            "type": "string"
          },
          "stage": {
            "type": "string",
            "description": "Canonical stage."
          },
          "identity": {
            "type": "object",
            "properties": {
              "email_col": {
                "type": "string"
              },
              "external_id_col": {
                "type": "string"
              }
            },
            "additionalProperties": false
          },
          "attribution": {
            "type": "object",
            "properties": {
              "channel_col": {
                "type": "string"
              },
              "utm_source_col": {
                "type": "string"
              },
              "utm_medium_col": {
                "type": "string"
              },
              "utm_campaign_col": {
                "type": "string"
              },
              "gclid_col": {
                "type": "string"
              },
              "fbclid_col": {
                "type": "string"
              },
              "li_fat_id_col": {
                "type": "string"
              },
              "referrer_col": {
                "type": "string"
              }
            },
            "additionalProperties": false
          },
          "revenue": {
            "type": "object",
            "properties": {
              "amount_col": {
                "type": "string"
              },
              "currency": {
                "type": "string"
              },
              "kind": {
                "type": "string",
                "description": "Transaction kind, default 'charge'."
              },
              "is_dollars": {
                "type": "boolean"
              }
            },
            "required": [
              "amount_col",
              "currency"
            ],
            "additionalProperties": false
          }
        },
        "required": [
          "table",
          "pk",
          "ts_col",
          "stage"
        ],
        "additionalProperties": true
      },
      "maxItems": 20
    },
    "status": {
      "type": "string",
      "description": "'active' (default) or 'pending'."
    }
  },
  "required": [
    "product_slug"
  ],
  "additionalProperties": false
}

set_mysql_event_maps

{
  "type": "object",
  "properties": {
    "product_slug": {
      "type": "string",
      "description": "Project slug, e.g. 'demo-product'."
    },
    "event_maps": {
      "type": "array",
      "items": {
        "type": "object",
        "additionalProperties": {}
      },
      "maxItems": 20,
      "description": "MysqlEventMap objects — schema in /openapi.json."
    }
  },
  "required": [
    "product_slug",
    "event_maps"
  ],
  "additionalProperties": false
}

suggest_mysql_mappings

{
  "type": "object",
  "properties": {
    "product_slug": {
      "type": "string",
      "description": "Project slug, e.g. 'demo-product'."
    },
    "source": {
      "type": "string",
      "enum": [
        "preset",
        "ai"
      ],
      "description": "'preset' for common templates; 'ai' to infer from the live schema."
    },
    "preset_id": {
      "type": "string",
      "description": "When source=preset, optionally return just this template (ecommerce | lead-gen | saas-subscription)."
    },
    "tables": {
      "type": "array",
      "items": {
        "type": "string"
      },
      "maxItems": 30,
      "description": "Optional table names to describe and send. Omit for the first 30."
    }
  },
  "required": [
    "product_slug",
    "source"
  ],
  "additionalProperties": false
}

connect_postgres

{
  "type": "object",
  "properties": {
    "product_slug": {
      "type": "string",
      "description": "Project slug, e.g. 'demo-product'."
    },
    "credentials": {
      "type": "object",
      "properties": {
        "host": {
          "type": "string"
        },
        "port": {
          "type": "integer"
        },
        "user": {
          "type": "string",
          "description": "A READ-ONLY database role."
        },
        "password": {
          "type": "string"
        },
        "database": {
          "type": "string"
        },
        "schema": {
          "type": "string",
          "description": "Default schema; per-map `schema` overrides."
        }
      },
      "required": [
        "host",
        "user",
        "password",
        "database"
      ],
      "additionalProperties": false
    },
    "event_maps": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "schema": {
            "type": "string",
            "description": "Postgres schema; omit for 'public'."
          },
          "table": {
            "type": "string"
          },
          "pk": {
            "type": "string"
          },
          "ts_col": {
            "type": "string"
          },
          "stage": {
            "type": "string",
            "description": "Canonical stage."
          },
          "identity": {
            "type": "object",
            "properties": {
              "email_col": {
                "type": "string"
              },
              "external_id_col": {
                "type": "string"
              }
            },
            "additionalProperties": false
          },
          "attribution": {
            "type": "object",
            "properties": {
              "channel_col": {
                "type": "string"
              },
              "utm_source_col": {
                "type": "string"
              },
              "utm_medium_col": {
                "type": "string"
              },
              "utm_campaign_col": {
                "type": "string"
              },
              "gclid_col": {
                "type": "string"
              },
              "fbclid_col": {
                "type": "string"
              },
              "li_fat_id_col": {
                "type": "string"
              },
              "referrer_col": {
                "type": "string"
              }
            },
            "additionalProperties": false
          },
          "revenue": {
            "type": "object",
            "properties": {
              "amount_col": {
                "type": "string"
              },
              "currency": {
                "type": "string"
              },
              "kind": {
                "type": "string",
                "description": "Transaction kind, default 'charge'."
              },
              "is_dollars": {
                "type": "boolean"
              }
            },
            "required": [
              "amount_col",
              "currency"
            ],
            "additionalProperties": false
          }
        },
        "required": [
          "table",
          "pk",
          "ts_col",
          "stage"
        ],
        "additionalProperties": true
      },
      "maxItems": 20
    },
    "status": {
      "type": "string",
      "description": "'active' (default) or 'pending'."
    }
  },
  "required": [
    "product_slug",
    "credentials"
  ],
  "additionalProperties": false
}

set_postgres_event_maps

{
  "type": "object",
  "properties": {
    "product_slug": {
      "type": "string",
      "description": "Project slug, e.g. 'demo-product'."
    },
    "event_maps": {
      "type": "array",
      "items": {
        "type": "object",
        "additionalProperties": {}
      },
      "maxItems": 20,
      "description": "PostgresEventMap objects — schema in /openapi.json."
    }
  },
  "required": [
    "product_slug",
    "event_maps"
  ],
  "additionalProperties": false
}

connect_mongo

{
  "type": "object",
  "properties": {
    "product_slug": {
      "type": "string",
      "description": "Project slug, e.g. 'demo-product'."
    },
    "credentials": {
      "type": "object",
      "properties": {
        "uri": {
          "type": "string",
          "description": "A MongoDB connection string (mongodb:// or mongodb+srv://)."
        }
      },
      "required": [
        "uri"
      ],
      "additionalProperties": false
    },
    "event_maps": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "collection": {
            "type": "string",
            "description": "The collection to read documents from."
          },
          "pk_field": {
            "type": "string",
            "description": "Field path holding the unique id."
          },
          "ts_field": {
            "type": "string",
            "description": "Field path holding the timestamp."
          },
          "stage": {
            "type": "string",
            "description": "Canonical stage."
          },
          "identity": {
            "type": "object",
            "properties": {
              "email_field": {
                "type": "string"
              },
              "external_id_field": {
                "type": "string"
              }
            },
            "additionalProperties": false
          },
          "attribution": {
            "type": "object",
            "properties": {
              "channel_field": {
                "type": "string"
              },
              "utm_source_field": {
                "type": "string"
              },
              "utm_medium_field": {
                "type": "string"
              },
              "utm_campaign_field": {
                "type": "string"
              },
              "gclid_field": {
                "type": "string"
              },
              "fbclid_field": {
                "type": "string"
              },
              "li_fat_id_field": {
                "type": "string"
              },
              "referrer_field": {
                "type": "string"
              }
            },
            "additionalProperties": false
          },
          "revenue": {
            "type": "object",
            "properties": {
              "amount_field": {
                "type": "string"
              },
              "currency": {
                "type": "string"
              },
              "kind": {
                "type": "string",
                "description": "Transaction kind, default 'charge'."
              },
              "is_dollars": {
                "type": "boolean"
              }
            },
            "required": [
              "amount_field",
              "currency"
            ],
            "additionalProperties": false
          }
        },
        "required": [
          "collection",
          "pk_field",
          "ts_field",
          "stage"
        ],
        "additionalProperties": true
      },
      "maxItems": 20
    },
    "status": {
      "type": "string",
      "description": "'active' (default) or 'pending'."
    }
  },
  "required": [
    "product_slug",
    "credentials"
  ],
  "additionalProperties": false
}

set_mongo_event_maps

{
  "type": "object",
  "properties": {
    "product_slug": {
      "type": "string",
      "description": "Project slug, e.g. 'demo-product'."
    },
    "event_maps": {
      "type": "array",
      "items": {
        "type": "object",
        "additionalProperties": {}
      },
      "maxItems": 20,
      "description": "MongoEventMap objects — schema in /openapi.json."
    }
  },
  "required": [
    "product_slug",
    "event_maps"
  ],
  "additionalProperties": false
}

list_dashboards

{
  "type": "object",
  "properties": {
    "slug": {
      "type": "string",
      "description": "Project slug, e.g. 'demo-product'."
    }
  },
  "required": [
    "slug"
  ],
  "additionalProperties": false
}

create_dashboard

{
  "type": "object",
  "properties": {
    "slug": {
      "type": "string",
      "description": "Project slug, e.g. 'demo-product'."
    },
    "name": {
      "type": "string",
      "minLength": 1,
      "maxLength": 80
    },
    "description": {
      "type": "string",
      "maxLength": 300
    },
    "spec": {
      "type": "object",
      "additionalProperties": {},
      "description": "A DashboardSpec object. Validated server-side."
    },
    "prompt": {
      "type": "string",
      "maxLength": 500,
      "description": "Plain-words description, used only when spec is omitted."
    }
  },
  "required": [
    "slug",
    "name"
  ],
  "additionalProperties": false
}

update_dashboard

{
  "type": "object",
  "properties": {
    "id": {
      "type": "string",
      "format": "uuid"
    },
    "name": {
      "type": "string",
      "minLength": 1,
      "maxLength": 80
    },
    "spec": {
      "type": "object",
      "additionalProperties": {}
    }
  },
  "required": [
    "id"
  ],
  "additionalProperties": false
}

approve_card

{
  "type": "object",
  "properties": {
    "id": {
      "type": "string",
      "format": "uuid"
    },
    "actor": {
      "type": "string",
      "minLength": 1,
      "description": "The human operator's name or email. Ask the user for it; never supply a model name or invent one. Written to the audit log as resolved_by."
    }
  },
  "required": [
    "id",
    "actor"
  ],
  "additionalProperties": false
}

reject_card

{
  "type": "object",
  "properties": {
    "id": {
      "type": "string",
      "format": "uuid"
    },
    "actor": {
      "type": "string",
      "minLength": 1,
      "description": "The human operator's name or email. Ask the user for it; never supply a model name or invent one. Written to the audit log as resolved_by."
    }
  },
  "required": [
    "id",
    "actor"
  ],
  "additionalProperties": false
}

snooze_card

{
  "type": "object",
  "properties": {
    "id": {
      "type": "string",
      "format": "uuid"
    },
    "actor": {
      "type": "string",
      "minLength": 1,
      "description": "The human operator's name or email. Ask the user for it; never supply a model name or invent one. Written to the audit log as resolved_by."
    },
    "hours": {
      "type": "integer",
      "exclusiveMinimum": true,
      "minimum": 0,
      "maximum": 720,
      "default": 24
    }
  },
  "required": [
    "id",
    "actor"
  ],
  "additionalProperties": false
}

get_scorecard

{
  "type": "object",
  "properties": {
    "slug": {
      "type": "string",
      "description": "Project slug, e.g. 'demo-product'."
    },
    "weeks": {
      "type": "integer",
      "exclusiveMinimum": true,
      "minimum": 0,
      "maximum": 104,
      "default": 12
    },
    "include_current": {
      "type": "boolean",
      "default": false
    }
  },
  "required": [
    "slug"
  ],
  "additionalProperties": false
}

get_scorecard_insights

{
  "type": "object",
  "properties": {
    "slug": {
      "type": "string",
      "description": "Project slug, e.g. 'demo-product'."
    },
    "weeks": {
      "type": "integer",
      "exclusiveMinimum": true,
      "minimum": 0,
      "maximum": 104,
      "default": 12
    }
  },
  "required": [
    "slug"
  ],
  "additionalProperties": false
}

set_scorecard

{
  "type": "object",
  "properties": {
    "slug": {
      "type": "string",
      "description": "Project slug, e.g. 'demo-product'."
    },
    "sections": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "label": {
            "type": "string"
          },
          "metrics": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "key": {
                  "type": "string"
                },
                "label": {
                  "type": "string"
                },
                "kind": {
                  "type": "string",
                  "enum": [
                    "auto",
                    "manual",
                    "derived"
                  ]
                },
                "metric_ref": {
                  "type": "string"
                },
                "formula": {
                  "type": "string"
                },
                "format": {
                  "type": "string",
                  "enum": [
                    "int",
                    "cents",
                    "percent"
                  ]
                },
                "target": {
                  "type": "number",
                  "nullable": true
                }
              },
              "required": [
                "key",
                "label",
                "kind",
                "format"
              ],
              "additionalProperties": false
            }
          }
        },
        "required": [
          "id",
          "label",
          "metrics"
        ],
        "additionalProperties": false
      }
    },
    "template": {
      "type": "string",
      "nullable": true
    }
  },
  "required": [
    "slug",
    "sections"
  ],
  "additionalProperties": false
}

record_scorecard_values

{
  "type": "object",
  "properties": {
    "slug": {
      "type": "string",
      "description": "Project slug, e.g. 'demo-product'."
    },
    "actor": {
      "type": "string",
      "minLength": 1,
      "description": "The human operator's name or email. Ask the user for it; never supply a model name or invent one. Written to the audit log as resolved_by."
    },
    "cells": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "metric_key": {
            "type": "string"
          },
          "week_start": {
            "type": "string",
            "description": "ISO week Monday, YYYY-MM-DD."
          },
          "value": {
            "type": "number",
            "nullable": true
          }
        },
        "required": [
          "metric_key",
          "week_start",
          "value"
        ],
        "additionalProperties": false
      },
      "minItems": 1
    }
  },
  "required": [
    "slug",
    "actor",
    "cells"
  ],
  "additionalProperties": false
}

suggest_scorecard

{
  "type": "object",
  "properties": {
    "slug": {
      "type": "string",
      "description": "Project slug, e.g. 'demo-product'."
    },
    "prompt": {
      "type": "string",
      "maxLength": 400
    }
  },
  "required": [
    "slug"
  ],
  "additionalProperties": false
}

export_scorecard

{
  "type": "object",
  "properties": {
    "slug": {
      "type": "string",
      "description": "Project slug, e.g. 'demo-product'."
    },
    "weeks": {
      "type": "integer",
      "exclusiveMinimum": true,
      "minimum": 0,
      "maximum": 104,
      "default": 12
    }
  },
  "required": [
    "slug"
  ],
  "additionalProperties": false
}

share_scorecard

{
  "type": "object",
  "properties": {
    "slug": {
      "type": "string",
      "description": "Project slug, e.g. 'demo-product'."
    }
  },
  "required": [
    "slug"
  ],
  "additionalProperties": false
}

chat

{
  "type": "object",
  "properties": {
    "message": {
      "type": "string",
      "minLength": 1,
      "maxLength": 8000
    },
    "product_slug": {
      "type": "string",
      "description": "Default project scope."
    }
  },
  "required": [
    "message"
  ],
  "additionalProperties": false
}

The approval rule

approve_card, reject_card, and snooze_card require actor — the human’s name or email. They exist to relay a decision the human just made, never to make one. The server records that the write came via MCP and which key made it, and its policy engine enforces spend caps and network walls regardless of the client.