Litestore · Developer docs
API

MCP

Connect a shopper's AI assistant or your own assistant to the store over MCP.

AI assistants can connect to the store over MCP. You use the shopper endpoint when a customer's assistant should browse and fill a cart, and the operator endpoint when your own assistant should answer questions about the business.

Search previewyour-store.com › products › linen-shirtLinen Shirt, Relaxed Fit | Your StoreA breathable linen shirt with a relaxed fit, made for warm days. Free shipping over $50.
Drafted by AI
Your assistant proposes a change, and nothing happens until you approve the card in the admin.

Endpoints

Litestore has two MCP route handlers, and each one authenticates its caller differently. Shoppers and store owners need different things from an assistant, and the owner's tool list would be wrong for an anonymous shopper's assistant.

EndpointWho uses itAuthWhat can it change?
/api/mcpShoppers' assistantsNone, but rate limited by IPIt fills a cart, and a person checks out
/api/mcp/adminThe operator's assistantA Bearer ServiceApiKey with the admin:mcp scopeIt only proposes changes, and a person approves them in the admin

Both endpoints speak JSON-RPC 2.0 over POST. They use stateless MCP streamable-http with protocol version 2025-06-18, and both answer the same four methods: initialize, tools/list, tools/call and ping.

Shopper endpoint

app/api/mcp/route.ts offers eight tools. A GET on the same URL returns the transport details and the tool names. /.well-known/ucp advertises the endpoint, so agents can find it without being told where it is.

ToolWhat the assistant uses it for
get_store_infoReads basic facts about the store.
search_productsSearches the catalog.
get_product_detailsReads one product.
create_cartStarts a cart.
add_to_cartAdds an item to the cart.
view_cartReads the cart.
get_checkout_urlGets a link the shopper opens to check out.
get_order_statusLooks up an order.
Terminal — try it
curl -X POST https://yourstore.com/api/mcp \
  -H 'content-type: application/json' \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'

Shared storefront code

Each tool is a thin wrapper around code the storefront already uses: searchProducts, the PDP queries, the cart functions in modules/checkout/ (resolveCart, addCartLine) and the guest order lookup. An agent therefore receives the same data as a browser, including the channel's catalog, live stock and current sale prices, because it runs the same code.

Visibility works the same way. get_product_details applies the storefront's own visibility rule and returns "Product not found" for any product that isn't Published or that is hidden.

Payment

The carts are real Cart rows, keyed by a randomUUID session id. The agent builds the same cart the storefront would build, but it can't pay for it.

get_checkout_url returns a /cart/claim?session=… link. The link opens the cart in the shopper's browser, and checkout runs there with all the normal rules. Nothing in this route charges anyone.

Refusals

When the store rejects something, such as an out-of-stock item, a variant that isn't eligible or input that fails validation, the tool returns a result with isError: true. It doesn't return a JSON-RPC fault.

The model reads these two differently. A JSON-RPC fault tells it the call broke. A tool result with isError: true tells it the store said no and why, so the model can act on the reason and try something else.

Operator endpoint

app/api/mcp/admin/route.ts gives an outside client, such as Claude Desktop, access to agentToolRegistry. The assistant inside the admin uses the same registry. Ask your assistant about revenue and cohorts, which orders need shipping, which stock is low or which drafts are waiting, and the store answers from live data.

Every call acts as the admin who created the key. Each ServiceApiKey records its creator in createdById, and on every request the route reads that user fresh: current role, permission overrides and ban state, rebuilt through the one standard permission builder. A key whose creator is gone, banned, no longer an admin or without ai:view is refused, and proposals land in that admin's own approval queue. The key doesn't store the permissions, so they can't drift away from the account. Every tools/call is written to the activity log with the tool, a hash of its arguments, the outcome, the duration, the key and the user.

API key

In the admin, open Settings → Developer and create an API key with the Operator MCP (admin:mcp) scope. Keys start with lsk_. Litestore stores them as a SHA-256 hash and shows each key exactly once.

Client setup

Point any MCP client at the endpoint and send the key as a bearer token:

Claude Desktop — mcp config
{
  "mcpServers": {
    "my-store": {
      "url": "https://yourstore.com/api/mcp/admin",
      "headers": { "Authorization": "Bearer lsk_..." }
    }
  }
}

A GET without a key returns 401 with these same instructions, which is a quick way to check the URL before you set up the client.

Requests

tools/list returns every available tool. Litestore converts each tool's zod schema to JSON Schema, and each description states the tool's mode:

(read-only)
(PROPOSES a change: you approve it on the Litestore admin home within 5 minutes before it runs)

Read tools answer right away. Tools in any other mode stop at callAgentTool, which records the proposal as an AgentRun row (tool and parameters stored on the server) and returns status: "needs_approval" with the pending action and the row's proposalId. The approval card in the admin is rendered from that row.

Admin approval

The tool's run only fires from approveAgentProposal in modules/assistant/admin/approval-actions.ts, after you click Approve. It re-checks the capability, your permission and the stored parameters, then claims the row with a compare-and-set (proposed → executed), so each proposal runs at most once, and only the stored parameters run. A proposal expires after five minutes.

The model has no confirm flag it can set. A boolean the model controls can never prove that a person approved. The server stores the proposal and a person has to approve that row by its id in the admin. Otherwise the model could simply claim approval and skip the step.

Approval gate

The operator endpoint doesn't re-implement any of these rules. The in-admin chat loop and this route both call callAgentTool in modules/assistant/agent-tool-kit.ts, so exactly one piece of code determines who may use a tool and what happens when a tool changes something.

RuleWhat Litestore does
ai_max_autonomy (Settings → AI)Applies the store-wide ceiling in both places. A tool above the ceiling is left out of tools/list entirely. It defaults to read, so a fresh store shows only read tools over MCP.
PermissionsDerives permissions from the key's creator again on every call. If the acting user lacks permission for a tool, the tool returns forbidden.
Protected domainsLets tools in protected domains only propose changes, because of how Litestore builds them.

Litestore checks that last rule when it builds the tools, not when they are called. EXECUTE_FORBIDDEN lists the protected permissions:

financial processing
the danger zone
refunds:edit
refunds:delete
collabs:edit

If any tool declares mode: "execute" for one of these, building the tool map throws an error. Litestore can't register a tool that would let an assistant move money without a person, so code review doesn't need a separate check for it.

Security notes

Treat an admin:mcp key like an admin password

Whoever holds the key can ask it business questions. Revoke the key in the same panel where you created it. Revoking takes effect on the next call, because validateApiKey reads isActive from the database every time.

Next steps

On this page