MCP Server

The @affitor/mcp Model Context Protocol server — let AI agents (Claude Desktop, Cursor) track clicks, leads, sales and refunds, generate per-stack integration plans, and self-verify attribution as tool calls.

@affitor/mcp is a Model Context Protocol stdio server for Affitor. It exposes Affitor's affiliate-tracking capabilities as MCP tools, so an AI agent — Claude Desktop, Cursor, or any MCP client — can report clicks, leads, sales and refunds, fetch a per-stack integration plan, and prove attribution works, all as native tool calls.

Under the hood it wraps the consolidated server client @affitor/sdk/server (the Affitor class). Authentication is your program API key, supplied via the AFFITOR_API_KEY environment variable.

:::note The @affitor/mcp and @affitor/sdk packages are Beta. The documented happy-path works; report issues on GitHub. :::


Add it to your MCP client

Add Affitor to your client's MCP server config — claude_desktop_config.json for Claude Desktop, or .cursor/mcp.json for Cursor:

{
  "mcpServers": {
    "affitor": {
      "command": "npx",
      "args": ["-y", "@affitor/mcp"],
      "env": {
        "AFFITOR_API_KEY": "your_program_key"
      }
    }
  }
}

Restart your client and the affitor_* tools become available to the agent.

Environment variables

VariableRequiredDescription
AFFITOR_API_KEYYesYour Affitor program API key (Bearer). Server-side only — never ship it to a browser.
AFFITOR_API_URLNoAPI base override. Defaults to https://api.affitor.com.

If AFFITOR_API_KEY is not set, the server prints a clear message to stderr and exits.

:::note The program API key is a server-side secret. The MCP server runs locally and reads it from your client config — it is never exposed to the browser or sent to the agent's model. :::


Tools

The server registers seven tools. Each returns the Affitor API's JSON payload as text content; a failed request (a thrown error or an { ok: false } envelope) returns an MCP error result with the message.

ToolInputsDescription
affitor_readinessforceRecheck?: booleanCheck this program's integration/onboarding readiness — returns a 5-gate verdict + blocker + next_action. Poll until integration_verified is true.
affitor_track_clickaffiliateUrl?, pageUrl?, referrerUrl?, existingClickId? (all optional strings)Report a click (usually browser-side; public, no customer needed).
affitor_track_leadcustomerExternalId?: string, clickId?: string, email?: string (one of customerExternalId / clickId required)Report a lead/signup. Binds the customer to the click so later sales attribute by customerExternalId alone.
affitor_track_salecustomerExternalId? / clickId? (one required), amount: number (cents), invoiceId: string, currency?, saleType?, isRecurring?, subscriptionId?, subscriptionInterval?Report a sale. Resolves attribution by customerExternalId (bound at lead time).
affitor_track_refundinvoiceId: string, refundAmountCents?: number, refundReason?: stringReport a refund (omit amount = full → commission reversed; partial → refunded). Idempotent by invoiceId.
affitor_get_integration_planframework, provider, mode?Return the deterministic per-stack integration plan — install, checkout-metadata snippet, trackSale snippet + where to inject it, and the verify step.
affitor_run_verification(none)Fire the synthetic click → lead → sale verification chain through the real attribution pipeline (isolated is_test rows).

Tracking tool inputs

affitor_track_lead — one of customerExternalId / clickId is required:

  • customerExternalId?: string — your own user id; binds this customer to the click.
  • clickId?: string — Affitor click id (from the affitor_click_id cookie).
  • email?: string — the lead's email address.

affitor_track_sale — one of customerExternalId / clickId is required:

  • customerExternalId?: string — your own user id; resolves attribution (no clickId needed once bound at lead time).
  • clickId?: string — Affitor click id.
  • amount: number — sale amount in integer cents (e.g. 4999 = $49.99).
  • invoiceId: string — idempotency key; dedups retries (your invoice / transaction id).
  • currency?: string — ISO currency code (default USD).
  • saleType?: "payment" | "subscription" — one-off payment or subscription.
  • isRecurring?: boolean — whether this sale recurs (subscription renewal).
  • subscriptionId?: string — provider subscription id, if applicable.
  • subscriptionInterval?: "monthly" | "quarterly" | "annual" — billing interval for a subscription sale.

affitor_track_refund:

  • invoiceId: string — the sale's idempotency key (the invoiceId you passed to affitor_track_sale).
  • refundAmountCents?: number — integer cents. Omit (or 0) = full refund → commission reversed; partial → refunded.
  • refundReason?: string — optional human-readable refund reason.

affitor_track_click — all optional:

  • affiliateUrl?: string — the affiliate/referral URL that was clicked.
  • pageUrl?: string — the landing page URL the click arrived on.
  • referrerUrl?: string — the HTTP referrer URL, if any.
  • existingClickId?: string — reuse an existing Affitor click id instead of minting a new one.

affitor_get_integration_plan

A pure tool: it reads the canonical recipe registry (@affitor/recipes) and returns the deterministic integration plan for a given stack. It never touches the network or the client — so the agent follows a fixed contract instead of guessing one.

InputValuesDescription
frameworknext-app, next-pages, fastify, express, node, unknownThe detected app framework. Determines where trackSale is injected.
providerstripe (default), polar, lemonsqueezy, paddle, unknownThe detected payment provider.
modestripe_connect (default), s2sPayment-tracking mode. stripe_connect = Connect autocaptures the sale (metadata only, no trackSale); s2s = inject trackSale in your webhook.

The plan it returns includes:

  1. install — what to add (npm i @affitor/sdk).
  2. metadata — the checkout-session attribution snippet to plant at checkout creation (always required for Stripe).
  3. sale — the trackSale snippet and exactly where to inject it. For mode: "stripe_connect" this is null — Stripe Connect records the sale server-side, so the agent injects metadata only and must not also call trackSale (the double-count guard).
  4. renewals — for Stripe s2s, a separate case 'invoice.paid' handler so subscription renewals are not silently missed.
  5. verify — the self-verify step (synthetic chain → readiness gate).

:::note Because the CLI (affitor init / affitor onboard), this MCP tool, and the public integration guides all read the same recipe registry, the integration contract can never drift between surfaces. :::


affitor_run_verification

The agent's proof step. It fires Affitor's synthetic click → lead → sale chain through the real attribution pipeline, writing isolated is_test rows that never create real commissions. Run it, then poll affitor_readiness until integration_verified: true.

The recommended agent loop:

  1. Call affitor_run_verification to fire the chain.
  2. Call affitor_readiness and check integration_verified.
  3. If not yet verified, read the blocker and its gate's next_action, self-correct, and repeat.
Rate limit — back off, don't hammer

affitor_run_verification is rate-limited to 10 runs per program per hour. On a rate_limited result, read retry_after_seconds and wait that long before retrying. A non-2xx (including a 429) returns the parsed error body with the HTTP status merged in, so the agent can read retry_after_seconds and back off rather than crash.


  • Agent Integration — how AI agents auto-install tracking from generated AGENTS.md instruction files.
  • CLI Command Referencenpx affitor onboard, the one-shot equivalent of the MCP flow.
  • SDK Reference — the @affitor/sdk package the MCP server wraps.
  • Track Sale — the full API contract behind affitor_track_sale.
Next recommended step
Prefer a single command?

npx affitor onboard runs detect → install → inject → verify in one shot — the CLI equivalent of the MCP flow.

Continue
Edit on GitHub
© 2026 Affitor