@sixtyninecommerce/mcp
v0.22.0
Published
MCP server for sixtynine-commerce storefront development
Readme
@sixtyninecommerce/mcp
An MCP server for sixtynine-commerce. It gives an AI agent (Claude Code, Claude Desktop, any MCP
client) the storefront tools a developer needs while building a store, and, with an admin
credential, one tool per method of the admin SDK @sixtyninecommerce/admin.
{
"mcpServers": {
"commerce": {
"command": "npx",
"args": ["@sixtyninecommerce/mcp"],
"env": {
"STORE_CODE": "your-store-code",
"CDN_URL": "https://cdn.your-store.com"
}
}
}
}npx @sixtyninecommerce/mcp init writes the .mcp.json and a project CLAUDE.md for you.
| Variable | Required | What it is |
|---|---|---|
| STORE_CODE | yes, but for an SEO agency credential | The store (channel) code |
| CDN_URL | with STORE_CODE | The store's CDN URL |
| VENDURE_URL, CMS_URL, PLUGINS_URL | no | The platform's services; the hosted ones by default |
| ORDER_SCOPE | no | CONSUMER or TRADE, only when the store runs two storefronts |
| SIXTYNINE_COMMERCE_API_TOKEN | no | A personal API token: turns on the admin tools |
| SIXTYNINE_APP_ID, SIXTYNINE_APP_KEY_ID, SIXTYNINE_APP_PRIVATE_KEY_FILE, SIXTYNINE_APP_INSTALLATION_ID | no | An app's own credential instead of a token: all four, never with a token |
Admin tools
With a personal API token, or an app's credential, the server also offers the admin SDK as
tools. At start it reads who the credential is (auth.whoami()) and offers one tool per method
the credential may call:
- named
admin_<module>_<method>in snake case:orders.settleisadmin_orders_settle; - described by the method's own documentation, its effect and the permissions it needs;
- taking the method's parameters as its input, from the published method manifest
(
@sixtyninecommerce/admin/manifest).admin_payments_refundtakes{ input: { orderId, amount, reason? } }, the arguments ofadmin.payments.refund(input).
A method is left out when the credential lacks a permission it declares (@permission in the
manifest); nothing else hides a tool. In particular a plugin's tools (AFAS, MyParcel, the till,
SEO, …) are offered whether or not the store holds that plugin: the manifest does not say which
plugin a method belongs to, and the platform's own gate is the exact line. Without the plugin the
platform refuses the call and the tool answers that refusal as its error: the Admin API's
PLUGIN_ACCESS_DENIED, whose message names the plugin, or, on a plugins-service route (SEO
signals, product feeds), a 403 refusal without that code. The doors the platform keeps open without the
plugin (closing a till, money already in flight, switching it off, removing access) simply work.
Platform administration needs a signed-in person and is never offered; neither are streams, file
and raw byte uploads, a person's own sign-in and the in-product assistant.
storefront://admin-guide lists what this credential is offered, and storefront://capabilities
reads the store's capability manifest live.
Money and deletions ask first
A tool whose effect is money, destructive or irreversible does nothing on its first call,
and sends nothing at all. It answers an error coded CONFIRMATION_REQUIRED, with a plain
summary of what would happen and a confirmationToken. The summary lists every argument
exactly as it will be sent. A number whose documentation says it is in minor units says so, with
the currency code when the call names one beside it (amount: 5000 (minor units; currency
EUR)), else amount: 1250 (minor units): nothing is converted, looked up or guessed, so a
refund's amount (minor units of the order's currency) shows no currency, and a money field whose
documentation names no unit shows its bare number.
{
"status": "confirmation_required",
"error": { "code": "CONFIRMATION_REQUIRED", "message": "admin_payments_refund needs the person's agreement first. Nothing has been done." },
"summary": "Moves money: Hand money back to a customer, never more than the order took.\n…\n- input.amount: 1250 (minor units)\n…",
"confirmationToken": "v1.…",
"expiresAt": "2026-10-25T16:08:02.682Z"
}The agent shows the summary to the person it works for. Only when they agree does it call the
same tool again with exactly the same arguments and the token, within five minutes. That call runs
the method once, under an idempotency key derived from the token. Calling again with the same token
answers the same outcome ("replayed": true) for a day, and never does it twice. A token brought
to another tool, with other arguments, or first brought back after five minutes is refused
CONFIRMATION_INVALID, and nothing is sent. The token is signed with a secret this server process draws at start, so a restart
voids every open confirmation.
When a confirmed call ends without an answer (outcomeUnknown: a timeout, a dropped connection),
it may have happened or may still be running. The agent reads the state back first. If it asks
again, the new summary and the MCP client's own question (below) say so, and the new confirmation
runs under the SAME idempotency key. Only a method that honours that key answers the first call's
result instead; most do not, so confirming may do it twice.
Every confirmed call sends that derived key, except the few whose SDK refuses one (creating an
event subscription, rotating its secret: the answer carries a secret the platform never keeps for
replay); the confirmation alone keeps those to one run. A call that is not confirmed sends no key,
except the few whose SDK requires one per attempt (orders.createOrRecover, approving a purchase
order, a till's notes), which get a fresh one per call.
When the MCP client can ask its own user (MCP elicitation), the server asks them too before the
call runs, as well as the token, not instead of it. A "no" there is CONFIRMATION_DECLINED.
Agency mode
An SEO agency credential (minted by a platform SuperAdmin, scoped to several stores) needs no
STORE_CODE: every admin tool then takes a required storeCode, seo_agency_list_stores lists
the stores it reaches, storefront://agency/stores/{storeCode}/brief reads one store's SEO brief,
and the storefront tools are not offered. See
Agency mode.
Recorded as an AI agent
Every call the admin tools make says where it comes from (x-sixtynine-agent:
sixtyninecommerce-mcp/<version>). The platform records those calls with the actor type "AI
agent" in the store's audit trail and on the events they cause, under the credential's own id and
name. For a credential that should only ever act as an AI, mint the token from the ai-agent
template (admin.apiTokens.create({ templateKeys: ['ai-agent'], … })): it reads by default, and
what it may change is granted beside it.
Deprecated authoring tools
create_form, update_form, remove_form_field, list_form_submissions,
list_redirects_admin, create_redirect, upsert_redirect, update_redirect,
delete_redirect and import_redirects remain as deprecated aliases of the generated tools
(admin_content_forms_*, admin_seo_redirects_*) and will be removed in a later release. Each
runs its generated tool, so it is offered with the same permissions and the deletions ask first.
