Home
AI & MCP

MCP Server

Connect Claude, Cursor, or any MCP client to the v2 API — every action exposed as a tool, generated from the same registry that drives HTTP dispatch.

STOQ ships a built-in MCP server over the v2 external API. Every registered v2 action — preorders and back-in-stock, roughly 187 in total — is exposed as one MCP tool, generated from the same class-level declarations that drive HTTP dispatch and the /help manifest. When STOQ ships a new action, it becomes an MCP tool automatically; there is nothing to update on your side.

This means an AI assistant connected to the server can do anything the API can do: list offers, change widget text, schedule campaigns, release preorder fulfillments, pull back-in-stock signup reports, and so on — scoped to your shop, under your API key.

Endpoint & auth

POST https://app.stoqapp.com/api/v2/external/mcp

The server speaks MCP over HTTP (JSON-RPC in the request body). Auth uses the same per-shop API key as the rest of the v2 surface, sent either way:

  • X-Auth-Token: <key> — same header as the REST API
  • Authorization: Bearer <key> — for MCP clients that only support Authorization headers

You can find your API key in the STOQ app: Settings → Integrations → API Key. See API Key for the walkthrough.

Warning

The key grants full read/write access to your shop's preorder and back-in-stock data. Treat it like a password — anyone (or any agent) holding it can modify live offers.

Connecting a client

Claude Code

claude mcp add --transport http stoq https://app.stoqapp.com/api/v2/external/mcp \
  --header "X-Auth-Token: <your-api-key>"

Claude Desktop

Claude Desktop launches local processes, so use the mcp-remote bridge in claude_desktop_config.json:

{
  "mcpServers": {
    "stoq": {
      "command": "npx",
      "args": [
        "-y",
        "mcp-remote",
        "https://app.stoqapp.com/api/v2/external/mcp",
        "--header",
        "X-Auth-Token:${STOQ_API_KEY}"
      ],
      "env": {
        "STOQ_API_KEY": "your-api-key"
      }
    }
  }
}

Cursor

In .cursor/mcp.json (project) or ~/.cursor/mcp.json (global):

{
  "mcpServers": {
    "stoq": {
      "url": "https://app.stoqapp.com/api/v2/external/mcp",
      "headers": {
        "X-Auth-Token": "your-api-key"
      }
    }
  }
}

Any other MCP client that supports HTTP transport with custom headers works the same way — point it at the endpoint and send the key.

Tool naming

Tool names are the action's URL path with placeholders dropped, joined by underscores, suffixed with the action name when the path doesn't already end in it:

HTTP actionMCP tool
GET /preorders/offerspreorders_offers_list
PATCH /preorders/offers/:idpreorders_offers_update
POST /preorders/offers/:id/widget/set_button_textpreorders_offers_widget_set_button_text
GET /back_in_stock/notificationsback_in_stock_notifications_list

Path placeholders (:id etc.) become required string properties on the tool's input schema — there is no URL in an MCP call. So preorders_offers_widget_set_button_text takes { "id": "<offer-id>", "text": "..." }.

Aliases drive intent matching

Each tool's description embeds the action's natural-language aliases — the phrasings merchants actually use. preorders_offers_widget_set_button_text carries "change button text", "rename button", "set button label", and so on. This is what lets a model resolve "rename the preorder button" to the right tool out of ~187 without any custom prompting.

PATCH tools carry the toggles

The same convention as the HTTP API applies: named tools carry real side effects; plain settings — including every boolean toggle — live on the capability's PATCH tool. There is no enable_badge tool; flipping the badge is preorders_offers_widget_update with { "id": "...", "badge": { "enabled": true } }. PATCH tools are deep-partial (fields you don't send are left alone), and each PATCH tool's description notes its toggleable field paths (badge.enabled, disclaimer.enabled, button.colors.enabled, ...).

Result shape

Every tool returns a JSON text payload with an explicit success flag:

// success
{ "success": true, "data": { "id": "uuid", "name": "Summer Drop", "...": "..." } }

// failure
{ "success": false, "status": "conflict", "errors": ["Offer is discarded; call restore first."] }
  • Reads return the resource in data; writes return the updated resource, or { "job_id": ... } for bulk/async work — poll the matching jobs tool (see Bulk & Async Jobs).
  • status mirrors the HTTP error classes: unauthorized, not_found, unprocessable_entity, conflict.
Warning

status: "conflict" means an invalid lifecycle transition (e.g. enabling an offer that's discarded). The error message names the action that unblocks the transition — read it and adjust the plan. Don't retry the same call blindly.

Rate limits

MCP tool calls hit the same cost-weighted rate limiter as REST calls — reads cost 1 point, writes cost 2, against the same per-token budget. See Rate Limits. Agents running multi-step workflows should expect occasional 429-equivalent failures and back off.

A worked example

Merchant asks their assistant: "Change the preorder button text on the summer drop to 'Reserve yours'."

A connected client resolves this in two tool calls:

// 1. The name "summer drop" isn't an ID — list offers to find it.
// tool: preorders_offers_list
{ }
// → { "success": true, "data": { "offers": [
//     { "id": "9c2f6a1e-…", "name": "Summer Drop", "status": "enabled", … },
//     …
//   ] } }

// 2. Aliases match "change button text" → set_button_text.
// tool: preorders_offers_widget_set_button_text
{ "id": "9c2f6a1e-…", "text": "Reserve yours" }
// → { "success": true, "data": { …updated offer… } }

The same pattern — resolve a name to an ID with a list tool, then call the intent-bearing tool — covers most merchant requests.

Note

Building an agent that can't speak MCP? The same discovery surface is available as plain HTTP: see Integrating AI agents for the skill.md + /help manifest workflow.