Tools

MCP server

@usethrottle/mcp connects Throttle to AI assistants that speak the Model Context Protocol — Claude Code, Claude Desktop, Cursor, and anything else with an MCP client. It reads your commerce data, diagnoses failing checkouts, searches these docs, and checks request bodies against the published contract before you write code against it.

Install

The server runs locally over stdio and authenticates with a secret sk_ API key, exactly like the CLI.

bash
# Claude Code
claude mcp add throttle -e THROTTLE_API_KEY=sk_uat_... -- npx -y @usethrottle/mcp

For Claude Desktop or any client that reads an mcpServers config:

json
{
  "mcpServers": {
    "throttle": {
      "command": "npx",
      "args": ["-y", "@usethrottle/mcp"],
      "env": { "THROTTLE_API_KEY": "sk_uat_..." }
    }
  }
}
Mint a dedicated key
The key pins the workspace, application and environment — there is no way to switch from inside a tool call. Create a key that holds only the scopes the assistant needs, and start with a non-production environment. Publishable (pk_) keys are rejected: they carry only browser-safe compute scopes and cannot read data.

Hosted / remote server

For clients that speak MCP over HTTP instead of stdio — n8n's MCP Client Tool node, for example — connect directly to https://mcp.usethrottle.dev/mcp (Streamable HTTP) instead of running @usethrottle/mcp locally. Send the same secret key as an Authorization: Bearer sk_... header — no separate provisioning, no OAuth. The tool surface and scopes are the same as the local server's read-only toolset, with two differences besides transport: the hosted server is strictly read-only, so seed_test_data is not registered there even on a non-production credential (seed from the local CLI instead), and api_get is not registered there either, because a tool that can read any path cannot declare in advance which scopes it uses.

bash
curl https://mcp.usethrottle.dev/mcp \
  -X POST \
  -H "Authorization: Bearer sk_uat_..." \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'

A session is created on your first call and reused for the rest of the connection; it is torn down after 30 minutes of inactivity or an explicit client disconnect.

Claude and ChatGPT connectors

Claude's custom-connector static_headers auth type (Bearer token, no OAuth) works with this endpoint but is currently rolled out selectively by Anthropic. ChatGPT's Developer Mode does not support API-key auth for custom connectors at all — only OAuth or no auth — so it cannot use this endpoint yet.

As of this change, the server also supports real OAuth 2.1 (authorization_code + PKCE + refresh) for both platforms' native connector/directory flows, once each platform's OAuth client is registered. This is in addition to, not instead of — the static_headers path above still works too.

customers:read and payments:read are OAuth-grantable, so search_customers, get_customer, search_payments and get_payment register on hosted/OAuth sessions too, not only on sk_ API keys.

An OAuth connection never sees a shopper's personal data, because an OAuth grant cannot hold customer_pii:read. On every response to an OAuth token, email, phone and first/last name are withheld wherever they appear (orders, subscriptions, carts, invoices, quotes, events, webhook deliveries, the audit log): null in REST responses, and absent from MCP tool results, which drop null fields. Company and the merchant's external ids (externalId, externalCustomerId) are withheld too, since a merchant may key customers by email or name. REST addresses keep city, state, postal code and country and lose the street lines. Free-form fields are withheld whole, because a name or email can sit anywhere inside them: metadata, notes, hold and comp reasons, quote comment bodies, acceptance records, client context (IP address, user agent), tax ids and a payment's raw processorResponse. File downloads and exports (orders CSV, invoice PDFs, exemption certificates) return 403 oauth_file_download_forbidden. Nothing can be looked up by a withheld value either: GET /customers/by-external/:externalId and the externalCustomerId filter on subscriptions return 403 oauth_external_id_forbidden, a customer text search answers with no matches (search_customers refuses q and lists by date instead), and search_orders matches only order ids, numbers and statuses. On top of that, each tool on an OAuth session returns a fixed list of fields chosen for its purpose, so a field the API adds later does not reach the assistant until it is added to that list. A field a buyer can type is not on any list: line-item and quote-item names, SKUs and reference ids (a quote request form takes them from the buyer), company, and every address line but the country. For the same reason get_top_products, which ranks by item name or reference id, is not offered over OAuth. API keys and the dashboard are unaffected.

On every credential, API key or OAuth, a tool result never includes the payment processor's raw record: a payment's or transaction's processorResponse, or a saved card's metadata.gr4vy_payment_method. This covers get_payment, search_payments, get_order and api_get. The REST API still returns processorResponse on payments and payment transactions, but the field is deprecated: read the payment's own fields (status, amount, capturedAmount, cardBrand, cardLastFour, errorCode) instead.

Those lists only hold if the token cannot go around them. An OAuth access token is issued for the MCP server (its aud is https://mcp.usethrottle.dev/mcp) and works only there: sent straight to the REST API, it is refused with 401 oauth_token_wrong_audience. To call REST yourself, use an API key. (Automation connectors such as Zapier are registered for the REST API instead; see scopes.) get_integration_status reports each webhook endpoint's id, events and health but not its URL, since a destination URL often carries a secret in its path.

What it can do

Tools are registered per session based on what your key is actually allowed to do, so a read-only key produces a read-only toolset rather than tools that fail when called.

  • whoami — which workspace, application and environment this key is pinned to, and whether it is production.
  • search_docs — full-text search over these docs, with canonical links.
  • describe_endpoint and validate_payload — the request shape of any endpoint, and a pre-flight check of a body against the published OpenAPI contract.
  • api_get — a GET-only escape hatch for any /api/v1 path the curated tools do not cover. It cannot write. Local server only.
  • get_order, get_customer, search_orders, search_customers, list_subscriptions, search_quotes, list_invoices, receivables_aging — commerce reads, with related records inlined where an answer usually needs them. search_orders, search_customers, list_subscriptions and search_quotes also accept createdAfter/ createdBefore (ISO 8601; both bounds are exclusive).
  • explain_checkout_failure — session state, cart, emitted events and recent webhook deliveries for one failing checkout, in a single call.
  • get_integration_status — connectors, webhook endpoints and delivery health: “am I ready to go live?”
  • search_discounts, get_discount — discount codes: value, conditions, usage limits and redemption count.
  • search_payments, get_payment — payments as a standalone search, with full transaction history on the by-id read.
  • get_invoice — a single issued invoice by id.
  • list_abandoned_carts — carts started but never finalized, with recovery status and a summary.
  • list_extension_installations — extensions installed on this application, with status and configuration.
  • get_connector_routing — payment-routing cascade, active routing connectors, and card/other decline rules.
  • get_workspace_settings, get_audit_log — workspace identity/branding/net30 defaults, and recent admin actions on this workspace.
  • list_applications — every application in this workspace you have access to, so an agent can pass one of their ids as applicationId on the read and analytics tools above to read a sibling application instead of the one this connection was authorized for.
  • get_sales_timeseries — order count and gross revenue bucketed by day, week or month over any date range, in the merchant's timezone, optionally compared to the previous period or the same range last year.
  • get_business_summary — orders/revenue, subscription MRR, receivables and quotes pipeline over a date range, compared to the prior window of equal length. period means the trailing 7/30/90 days, not a calendar period — pass from/to/timezone for calendar months.
  • get_attention_digest — open disputes, subscriptions in dunning, overdue Net-N invoices, and webhooks with degraded delivery health, all in one call.
  • get_top_products — best sellers by gross revenue or units sold over a date range, identified by external reference id when the line item carries one, by name otherwise. API keys only; not offered over OAuth.
  • get_customer_growth — signups per day, week or month, the split between first-time and returning-customer orders, and the highest-value customers in the window.
  • get_revenue_breakdown — gross revenue split by payment method, order source, or currency over a date range.
  • get_funnel_conversion — carts created, checkout sessions started and orders completed over a date range, with the drop-off between each stage.
  • get_subscription_movement — new subscriptions, cancellations and net MRR change per day, week or month.
  • get_discount_performance — redemptions, gross revenue on discounted orders, and total discount given, per code, over a date range.
  • search_events, list_webhook_deliveries, get_webhook_delivery — the event log and outbound delivery records, including the payload we sent and the response we got.
  • seed_test_data — a customer, cart, line items and a finalized draft order so a fresh integration has something to read. Refuses to run against production.

Catching field-name mistakes before they 400

Throttle's public API is strict camelCase and rejects retired aliases. Those aliases are exactly what a model tends to guess, so the server names the replacement instead of letting you find out from a 400.

javascript
// The assistant checks a body before writing integration code.
validate_payload({
  method: 'POST',
  path: '/api/v1/carts/{id}/items',
  body: { name: 'Widget', unit_price: 2500, storeId: 'store_123' }
})
// →
{
  "ok": false,
  "matched": "POST /api/v1/carts/{id}/items",
  "errors": [
    { "field": "unit_price", "problem": "retired public field name — the API rejects it",
      "suggestion": "use \"unitPrice\"" },
    { "field": "storeId", "problem": "retired public field name — the API rejects it",
      "suggestion": "use \"applicationId\"" },
    { "field": "unitPrice", "problem": "required field is missing" }
  ]
}

Writes

Nine write tools are available behind an explicit --allow-writes flag (a second --allow-live-writes gate is required for production keys), each with an idempotency key on every call. Holding a write scope means the key may write; the flag is how you say you intended it to.

  • seed_test_data — sandbox only, refused outright on a production credential, no flag needed on the local CLI. Not available on the hosted server.
  • send_test_webhook, replay_webhook_delivery — synthetic and re-sent deliveries. Safe on Throttle's side; a replay only carries real risk if the receiving endpoint isn't idempotent. send_test_webhook only accepts an event type the endpoint is subscribed to (any type when it is subscribed to all events); another type comes back as 400 validation_error naming the allowed types.
  • manage_subscription — pause or resume. A fully reversible pair; nothing about it is buyer-visible in a way that can't be undone.
  • set_subscription_payment_method — moves one subscription to another of its customer's saved cards. Nothing is charged now; the next renewal charges the new card. The customer's default card and their other subscriptions are untouched. Reversible by setting the old card back.
  • create_quote — drafts a quote. Items may carry recurring to become subscription lines (pay-in-full only; the tool refuses netN with them). Nothing is sent to the buyer until send_quote.
  • create_customer, update_customer — create a customer record or change fields on one. Only the fields you supply are touched. create_customer does not de-duplicate by email, so search first if the buyer may already exist.
  • create_checkout_link — builds a cart from line items and returns a hosted checkout URL. The link is buyer-facing and takes real payment on a production credential, which is what the --allow-live-writes gate is for.
Refunds, cancellations and sending a quote aren't available yet
refund_payment, cancel_subscription, and send_quote move real money, end a paying customer's billing, or email a real buyer — all irreversible or externally-visible. They're held for a follow-up release with a code-enforced confirmation step, not just a prompt-level warning.

Options

  • --api-key <key> — or the THROTTLE_API_KEY environment variable.
  • --api <url> — or THROTTLE_API_URL. Defaults to https://api.usethrottle.dev.
  • --allow-writes, --allow-live-writes — reserved for the write tools described above.
Two environments means two entries
A key belongs to one environment. To let an assistant see both your production and a sandbox environment, add two server entries with different keys and distinct names.