Spendrein Docs

Connect an AI assistant (MCP)

Connect Claude, ChatGPT, Cursor, and other MCP clients to Spendrein — issue a credential, the tool reference, worked examples, security, rate limits, and errors.

Spendrein speaks Model Context Protocol (MCP) over HTTP, so any MCP-compatible assistant — Claude, ChatGPT, Cursor, Claude Code, VS Code, Windsurf — can read your audits, look up vendor benchmarks, and (on a paid plan) trigger cancellations and generate negotiation scripts directly from chat. There's no SDK, no background process, and nothing to install on your machine: Spendrein runs as a remote MCP server at a single endpoint.

The assistant is the JSON-RPC client; https://spendrein.com/api/mcp is the JSON-RPC server. Every request is authenticated, rate-limited, dispatched to a tool, and logged — in that order.

One credential, two minutes

Setup is a one-time job: issue a credential in Settings, paste it into your assistant's connector config alongside the endpoint, and ask a question. The rest of this page is reference you'll rarely need to re-read.

What an assistant can do

ToolWhat it doesPlan
audit.queryReturns your most recent completed audit, or a specific audit by ID — with the parsed subscription list, monthly cost, AI confidence, and recommendation.Free + paid
benchmark.lookupReturns Spendrein's typical-spend range for a named vendor, so the assistant can tell you whether you're above or below market.Free + paid
subscription.cancelQueues Spendrein's managed cancellation flow for a specific subscription.Paid only
subscription.negotiateGenerates a negotiation script (three talking points plus a full script) and saves it to your negotiation history.Paid only

The two write tools require an active paid plan. If a free-tier account's assistant calls them, the tool returns a clear upgrade message rather than executing anything.

Issue a credential

  1. Open Settings → AI assistant integration.
  2. Click Issue credential.
  3. The credential appears in a one-time dialog. Copy it immediately — Spendrein only stores a one-way SHA-256 hash, so the cleartext can't be recovered or re-displayed if you lose it.

Each account has one active credential at a time. Issuing a new one automatically revokes the previous one. The string starts with spr_mcp_ followed by 32 random bytes (base64url). Treat it like a password — anyone holding it can read your audits and, on a paid plan, trigger cancellations.

Header-only authentication

The endpoint accepts the credential only in the Authorization header. URL-parameter tokens (?token=…) are rejected with a 401, to keep credentials out of server logs and browser history.

Connect your assistant

The two values are universal everywhere: the endpoint is https://spendrein.com/api/mcp and the auth header is Authorization: Bearer spr_mcp_…. Everything else is client-specific.

Claude / Claude Desktop. Add Spendrein as a remote connector under Settings → Connectors → Add custom remote MCP. Use the URL above and the Bearer credential. (The claude_desktop_config.json file is for local servers — Spendrein is remote, so use the connector flow.)

ChatGPT. Settings → Connectors → New connector → Custom MCP server. Paste the URL, choose Bearer token, and enter your credential.

Cursor — add an HTTP MCP server in ~/.cursor/mcp.json:

{
  "mcpServers": {
    "spendrein": {
      "type": "http",
      "url": "https://spendrein.com/api/mcp",
      "headers": {
        "Authorization": "Bearer spr_mcp_REPLACE_WITH_YOUR_CREDENTIAL"
      }
    }
  }
}

Claude Code — one command:

claude mcp add spendrein \
  --transport http \
  --header "Authorization: Bearer spr_mcp_REPLACE_WITH_YOUR_CREDENTIAL" \
  https://spendrein.com/api/mcp

VS Code — add an HTTP server in .vscode/mcp.json and set the Authorization header (a promptString input keeps the credential out of the committed file). Windsurf — add a remote HTTP MCP in Cascade's settings or ~/.codeium/windsurf/mcp_config.json with the same URL and header.

Any other client — point any client that supports remote HTTP MCP with custom headers at the endpoint above. The wire protocol is JSON-RPC 2.0; implement initialize, tools/list, and tools/call.

To verify the connection, a tools/list request should return all four tools:

curl -s https://spendrein.com/api/mcp \
  -H "Authorization: Bearer $SPR_MCP_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "jsonrpc": "2.0", "id": 1, "method": "tools/list" }' \
  | jq '.result.tools[].name'

# expected:
# "audit.query"
# "benchmark.lookup"
# "subscription.cancel"
# "subscription.negotiate"

Tool reference

All inputs and outputs are validated against strict schemas — extra properties are rejected.

audit.query · free + paid

Returns the most recent completed audit for your workspace, or a specific one by audit_id. Pass no arguments for the latest. The response carries an audit summary (id, created_at, totals, currency, subscription_count) and a subscriptions[] list (vendor, category, monthly cost, billing cycle, status, AI confidence, and recommendation reasoning).

{
  "jsonrpc": "2.0",
  "id": 2,
  "method": "tools/call",
  "params": { "name": "audit.query", "arguments": {} }
}

benchmark.lookup · free + paid

Returns Spendrein's typical-spend range for a vendor — price_low, price_typical, price_high. The input is free-form; Notion, Notion Labs Inc., and NOTION all normalize to the same record. When Spendrein has no data for the vendor, benchmark is null and the response is still HTTP 200, not an error.

{
  "jsonrpc": "2.0",
  "id": 3,
  "method": "tools/call",
  "params": {
    "name": "benchmark.lookup",
    "arguments": { "vendor_name": "Notion" }
  }
}

subscription.cancel · paid only

Queues Spendrein's managed cancellation flow for a specific subscription. Pass subscription_id (preferred — guarantees an unambiguous match; always available from a prior audit.query). vendor_name is a fallback: if more than one subscription matches, the tool returns a candidates[] list and you must re-call with the chosen subscription_id. Spendrein never auto-substitutes a similar-looking vendor. Re-calls are idempotent — they return the existing queued cancellation.

{
  "jsonrpc": "2.0",
  "id": 4,
  "method": "tools/call",
  "params": {
    "name": "subscription.cancel",
    "arguments": { "subscription_id": "8d2e..." }
  }
}

subscription.negotiate · paid only

Generates a negotiation script for a subscription — three talking points plus a complete script — and persists it to your negotiation history. The script is never auto-sent. Requires subscription_id; pass an optional target_price (the monthly price you want). With no target, the tool returns no_target_price — call benchmark.lookup first to get a sensible figure.

{
  "jsonrpc": "2.0",
  "id": 5,
  "method": "tools/call",
  "params": {
    "name": "subscription.negotiate",
    "arguments": {
      "subscription_id": "8d2e...",
      "target_price": 12,
      "context_hint": "Team shrinking from 12 to 6 seats."
    }
  }
}

Worked examples

The wording isn't load-bearing, but using the tool verbs ("query", "benchmark", "cancel", "negotiate") helps the assistant pick the right tool on the first try.

Find waste in the latest audit.

"Pull my latest Spendrein audit. Group the subscriptions by AI recommendation (cancel, downgrade, keep) and show the monthly total per group."

The assistant calls audit.query with no arguments, buckets the subscriptions by recommendation, sums the monthly cost per bucket, and renders a table — no follow-up tool call needed.

Decide whether you're overpaying.

"Look up market benchmarks for my three most expensive subscriptions. Tell me which ones are above the typical-spend range."

The assistant calls audit.query, sorts by monthly cost, takes the top three, calls benchmark.lookup for each vendor, and flags anything above price_high as overpaying.

Security

  • Credential storage. Only a SHA-256 hash is stored — never the cleartext. A database compromise yields no usable credentials.
  • Audit log of every call. Each tools/call is logged to tool_call_events (tool name, outcome, rejection reason, duration, remaining rate-limit budget) — but never the input or output payloads. The log is visible in Settings; the CSV export carries a # checksum sha256=… line and always covers at least 90 days, so an empty-but-checksummed file proves "no events in this window" for compliance.
  • Payload privacy. Tool responses never include raw bank statements, transaction descriptions, payment instruments, customer email addresses, Stripe IDs, or workspace IDs.
  • Instant revocation. Revoke from Settings; the next request — even one already in flight — gets a 401. Re-issuing produces a fresh credential with no shared state.

Your AI provider's data policy

When your assistant calls a Spendrein tool, the request and response transit through your AI provider (Anthropic, OpenAI, etc.). Each provider sets its own policy on whether assistant-tool conversations train future models — Spendrein can't control that. Review your provider's settings before connecting.

Rate limits

Limits are enforced per credential, per tool class, on a rolling one-minute window. Rejected calls still consume budget, which closes the "trigger errors to starve the limiter" loophole.

ClassLimitTools
Read tools60 / minuteaudit.query, benchmark.lookup
Write tools10 / minutesubscription.cancel, subscription.negotiate
Credential lifecycle5 / hour / accountIssuance + revocation

Rate-limited calls return HTTP 429 with a Retry-After header in seconds. Honor it — most clients (Claude, Cursor, Claude Code) already do.

Error reference

Errors return a JSON-RPC envelope with a stable error discriminator and a human-readable message.

ErrorHTTPWhat happened
invalid_credential401Bearer header missing, malformed, or didn't match an active credential. Re-issue and update your client config.
invalid_input400Input failed schema validation (missing field, extra property, wrong type). The message names the offending field.
tier_gate403A free-tier account hit a paid-only tool (subscription.cancel / subscription.negotiate). Upgrade to unblock.
rate_limit429Per-credential, per-tool-class budget exhausted. The Retry-After header carries the wait in seconds.
audit_not_found404audit.query got an audit_id that doesn't exist or isn't in your workspace.
subscription_not_found404A cancel/negotiate call got a subscription_id outside your workspace.
subscription_not_found_by_name404subscription.cancel got a vendor_name with multiple matches. The response includes a candidates[] array — re-call with the chosen subscription_id.
no_target_price422subscription.negotiate needs a target_price. Call benchmark.lookup first.
tool_error500Unexpected internal error. Surface a friendly message and offer to retry.

Fail-closed under outage

If the rate-limit ledger is unreachable, the MCP server fails closed (a 503) rather than waving requests through — attackers can't bypass limits during an outage. Retry shortly; contact support if it persists.

On this page