FirearmCart runs a Model Context Protocol server that lets an AI client browse, edit, screenshot and publish your store’s Liquid themes. It is a sibling of the v1 REST API, not part of it: same host, same bearer tokens, different protocol.
POST https://api.firearmcart.com/mcp/themesThe server advertises itself as FirearmCart Theme Dev and exposes 33 tools, 3 resources and 2 prompts. Nothing here is reachable over /v1 — there is no REST equivalent for theme files.
| Property | Value |
|---|---|
| Endpoint | https://api.firearmcart.com/mcp/themes |
| Method | POST only |
| Transport | Streamable HTTP (JSON-RPC 2.0) |
| Auth | Authorization: Bearer <token> — the same store token the REST API uses |
| Abilities | themes:read and/or themes:write |
| Rate limit | 240 requests/minute per store, in its own bucket |
| Protocol versions | 2025-11-25 (default), 2025-06-18, 2025-03-26, 2024-11-05 |
Transport
The endpoint speaks Streamable HTTP, the MCP transport where every client message is an HTTP POST carrying a JSON-RPC envelope.
Three things about this deployment are worth knowing before you configure a client:
POSTis the only usable method.GETandDELETEon the endpoint are registered, but they answer405withAllow: POSTand nothing else. There is no server-initiated SSE channel to listen on, so a client that insists on opening aGETstream before it will talk to you cannot connect.- Those
405s are unauthenticated. The auth, ability and throttle middleware are attached to thePOSTroute only. AGETwithout a token returns405, never401— so a probe that expects a401to confirm the endpoint exists will read the wrong signal. - Responses are
application/json. The server repliesContent-Type: application/jsonfor ordinary requests and only switches totext/event-streamwhen a tool streams its output. SendAccept: application/json, text/event-streamas the MCP spec requires; the server reorders that header internally soapplication/jsonwins content negotiation.
A session id is returned in an MCP-Session-Id response header on initialize. The server does not persist sessions and does not reject later requests that omit the header.
Get a token
The MCP server uses the same tokens as the REST API. Mint one at Profile → API Tokens (/user/api-tokens) on https://app.firearmcart.com, after switching to the store you want the token to act on.
Tick themes:read, themes:write, or both. Neither ability appears in the pre-ticked read-only default, so you have to select them deliberately. See Authentication for the token model and the token format.
Your store must also pass the API access gate — the plan must include API & Webhooks, or the store must hold the API access add-on. The gate runs on MCP requests exactly as it runs on /v1. See Authentication.
Connecting a client
The raw contract
If you are wiring this up by hand, or your client’s config format is not covered below, this is everything the server needs:
| URL | https://api.firearmcart.com/mcp/themes |
| Method | POST |
Authorization |
Bearer <your-token> |
Content-Type |
application/json |
Accept |
application/json, text/event-stream |
There is no OAuth flow on this server. It does not publish authorization-server metadata, and an unauthenticated request answers with WWW-Authenticate: Bearer realm="mcp", error="invalid_token" rather than a discovery URL. A client that only knows how to obtain a token by OAuth cannot bootstrap itself here — paste a token in.
Claude Code
claude mcp add --transport http fc-themes https://api.firearmcart.com/mcp/themes \
--header "Authorization: Bearer $FIREARMCART_TOKEN"Clients that take a JSON server list
Most desktop and IDE MCP clients keep a JSON file of servers, and most of them accept a remote HTTP server in roughly this shape:
{
"mcpServers": {
"fc-themes": {
"type": "http",
"url": "https://api.firearmcart.com/mcp/themes",
"headers": {
"Authorization": "Bearer YOUR_TOKEN_HERE"
}
}
}
}Note: The exact key names differ between clients — some spell the transport
"transport"rather than"type", some nest headers differently, and some do not support remote servers at all without a local bridge. We cannot promise a schema we do not control. Check your client’s own documentation and map it onto the raw contract above; the only values that come from FirearmCart are the URL and theAuthorizationheader.
Verify with curl
The handshake is plain JSON-RPC, so you can prove a token works before touching a client config:
curl -sS https://api.firearmcart.com/mcp/themes \
-H "Authorization: Bearer $FIREARMCART_TOKEN" \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-d '{
"jsonrpc": "2.0",
"id": 1,
"method": "initialize",
"params": {
"protocolVersion": "2025-11-25",
"capabilities": {},
"clientInfo": { "name": "curl", "version": "1.0" }
}
}'A working token returns a result whose serverInfo.name is FirearmCart Theme Dev and whose instructions field carries the server’s full authoring brief. Swap "method": "initialize" for "method": "tools/list" to see the tools your token can actually reach — which is not always all 33. That is the next section.
Which ability connects, which ability gates
This distinction is the one thing most likely to confuse you, so it is worth being precise.
Connecting requires any one of the two theme abilities, not both. themes:read
alone is enough to open a session; themes:write alone is too.
Practically:
- A token holding only
themes:readconnects successfully. It is not a second-class connection; the handshake, resources and prompts all work. - A token holding only
themes:writealso connects. - A token holding neither is rejected at the route with
403 {"message": "Invalid ability provided."}.
Write access is then enforced per tool, in two layers:
- The 15 write tools hide themselves. Each one’s
shouldRegister()checks the token forthemes:write. A read-only token’stools/listreturns 18 tools, not 33 — the write tools are absent from the listing rather than present-and-failing. If you are debugging a “the agent says that tool doesn’t exist” report, check the token’s abilities first. - The tools re-check on call. Reaching a write tool anyway returns an error result:
This token lacks themes:write. Create a token with Themes: Write ability.
One tool sits slightly off this grid. screenshot-theme renders a preview and uploads the image, so it is not annotated read-only the way the other 17 read tools are — but it is not write-gated either, and a themes:read token can call it. Screenshotting is how an agent verifies its own work, so a read-only token can still run the full inspect-and-report loop.
Team scoping
Everything here inherits the REST API’s identity model: the authenticated principal is a store, not a person. Read Authentication if you have not.
Every tool resolves the store from the token and constrains its query to that store’s rows before doing anything else. There is no store argument on any tool and no way to reach another store’s themes:
- Theme lookups match on
team_idplus the id or UUID you passed. A theme that exists but belongs elsewhere is indistinguishable from one that does not exist:Theme not found for this team. Call list-themes first. - Blog articles have no store column of their own, so article tools resolve the blog first and reach articles only through it. The scoping cannot be bypassed by passing an article id directly.
- A token that authenticates but does not resolve to a store is rejected by the tool itself with an unauthenticated error, telling you to provide a store API token.
If you run several stores, you need one token per store, and one configured MCP server per store.
Rate limits
The MCP server has its own bucket: 240 requests per minute, keyed by store. Agent sessions make many small tool calls, so it is four times the /v1 allowance.
| Limit | Keyed by | |
|---|---|---|
| Authenticated | 240/minute | Store |
| Unauthenticated | 10/minute | Client IP |
It does not draw on, and is not drawn on by, the /v1 budget of 60/minute. Exhausting one leaves the other untouched. See Rate limits.
Note that the unit is requests, not tool calls. A client that batches several JSON-RPC messages into one POST spends one slot.
Failures that are not JSON-RPC
Tool-level problems come back as ordinary MCP error results, and your client will render them. Middleware-level rejections happen before the MCP server runs at all, so they arrive as plain HTTP with a framework error body — which most clients surface as a bare connection failure.
| Status | Body | Cause |
|---|---|---|
401 |
{"message": "Unauthenticated."} |
Missing, mistyped or revoked token. Also carries WWW-Authenticate: Bearer realm="mcp", error="invalid_token". |
403 |
{"error": "forbidden", "message": "API access is not enabled for this team."} |
The store’s plan has no API access and it holds no add-on. |
403 |
{"error": "subscription_expired", "message": "Your subscription has expired. Please renew to continue using the API."} |
Subscription suspended after a failed payment, or past its cancellation date. |
403 |
{"message": "Invalid ability provided."} |
The token holds neither themes:read nor themes:write. |
405 |
(empty, with Allow: POST) |
The request used GET or DELETE. |
429 |
Standard throttle response, with Retry-After |
240/minute exceeded. |
If a client reports “server disconnected” or “failed to initialize” with no further detail, replay the handshake with the curl command above — the HTTP status tells you which row you are in.
How the server expects to be used
The initialize response ships a long instructions block that your agent receives automatically. Its shape, condensed:
list-themes, then never build against the active theme. The active theme is the live storefront. Use an inactive library copy, or callduplicate-themeto make one. File-write tools refuse the active theme unless you passallow_active: true.- Read the
liquid-guidelinesresource before writing any Liquid. This is not optional advice; it is the platform-specific rulebook, and the failure modes it covers are silent ones. See Resources & prompts. - Inventory before authoring.
list-theme-filesandget-section-schemafirst — configuring an existing section throughtemplates/*.jsonbeats writing a new one. - Save, then look. Writes are validated (Liquid, JSON and schema) and versioned;
screenshot-themeon desktop, then a tablet and mobile pass. - Revert cheaply.
list-file-versionsandrestore-file-versionwhen a change made things worse. - Publish last.
publish-themewhen the merchant is ready; the previously active theme is deactivated automatically.
The brief also covers what a theme reads but does not own. Menus, policies and plugin sections are store data with their own tools. Collection filters are a theme setting: which specification groups appear as filters on collection pages is trimmed with list-collection-filters and set-collection-filters, never by editing the facets snippets or hiding filter groups with CSS.
The full tool surface is on Tools, and the reference material the server hands your agent is on Resources & prompts.
Related documentation
- Tools — all 33 tools, their arguments and their guardrails
- Resources & prompts — the 3 resources and 2 prompts the server ships
- Authentication — the store-scoped token model and the API access gate
- Rate limits — how the MCP bucket sits alongside the
/v1buckets - Themes — the merchant-facing theme editor these tools write into

