Charming Seal

Charming Seal·Blog

Letting an agent run the shop.

An MCP server puts eight tools between a language model and a shop that holds real money. What that is like to run, which decisions in the design turned out to carry the weight, and the part where you should be careful.

26 July 2026Four-minute read

A page and a price are not the same risk

Most AI in commerce writes something. It drafts product copy, suggests a layout, builds you a landing page. If it is wrong you read it and ask again. The cost of the mistake is your afternoon.

An agent holding an API key into a live shop is a different instrument. It sets the price itself. The next buyer to open your checkout pays the number the model chose, and the charge that follows is a real charge on a real card, settled into your Stripe account before anyone reviews the wording. There is no draft state sitting between the tool call and the money.

That difference is the whole reason the tool set is small.

Eight tools, and no ninth

Charming Seal ships exactly eight MCP tools. Four read. Four write. Nothing else in the shop is reachable at all.

ToolWhat it can change
list_productsNothing. Reads the live, active catalogue.
get_productNothing. Reads one product, in any state, by id.
create_productAdds a product to the catalogue.
update_productPrice, name, description and state.
delete_productSets a product inactive. The record stays.
list_ordersNothing. Reads recent orders, filterable by status.
get_settingsNothing. Reads shop name, theme and configuration.
update_settingsThe shop's own settings, including its public name and theme.

Note what is absent. Nothing in that list issues a refund. Nothing exports the order data. Both of those exist in the admin, behind a human session, and neither was handed to the agent.

A tool set is a permission grant written as an API. Every entry you add is one more thing a confused model can do to a business unsupervised, so the test for each candidate was whether an agent doing it unattended is a thing anybody actually wants. Refunds failed that test. Reading the orders passed it easily.

The decisions that matter once money is involved

Three details in the design do more work than the tool list does.

Prices go in as dollars and come back as both. You send {"price":"29.99"}, and the response hands back the dollar string alongside amount_cents as an integer. That redundancy is there because a familiar failure in payments code is one side of a wire thinking in cents while the other thinks in dollars, and the failure is silent. Nothing errors. A product is listed at a hundred times its price, or a hundredth, and it stays that way until a human notices. Returning both figures means the model, or the person reading the transcript afterwards, can see the disagreement at a glance.

list_products shows only live and active items. A product can be draft, live, hidden or archived. When an agent asks what the shop sells, it gets what buyers can actually buy. Drafts and archived items are still reachable, through get_product, by id, deliberately. An agent tidying up the catalogue cannot sweep an unfinished product into its context and then helpfully publish it.

Delete is a soft delete. delete_product sets active=false. The row stays, orders that reference it stay coherent, and undoing the mistake is a flag rather than a restore from backup. An agent can take something off the shelf. It cannot take it out of the history.

One key, shown once

A key is made in the shop's own settings, under API Keys, and shown to you exactly once. It is scoped to a single shop. That scoping was tested rather than assumed: the security audit turned up a case where one key could read and reprice another shop's products, and it was closed with a before-and-after test.

The local server wants the address of your instance and that key, and nothing else.

CHARMING_SEAL_URL=https://your-domain.com \
CHARMING_SEAL_API_KEY=csk_live_YOUR_KEY \
npx -y charming-seal-mcp

Then run the self-test before you trust it with anything.

npx -y charming-seal-mcp --self-test

That flag exists because the alternative is quiet. A misconfigured MCP server still lists its tools in the client. The model calls one, something ambiguous comes back, and the model narrates around it plausibly enough that you do not question the answer. The self-test verifies the connection to the instance you pointed it at, so a wrong URL or a revoked key fails in your terminal rather than halfway through a conversation about pricing.

The part to be careful about

update_product can reprice your catalogue. That is the tool working correctly on an instruction it read more broadly than you meant it. "Round our prices" and "discount the slow movers" are both sentences a person says easily and a model can execute across every product you own.

So approve actions rather than granting a standing yes to the whole tool set. Read the arguments on the writes, particularly anything carrying a price. Point it at a spare instance first and give it real work to do there. And keep the key in a state where revoking it is a small decision, because a key you are reluctant to revoke has quietly stopped being a control.

Handled that way it earns its place. Asking a shop what sold this week and getting an answer, or putting a product on sale from a name and a price, beats clicking through an admin. The tool set is small so that the useful version of this and the alarming version stay a long way apart.

The same key opens the REST API the tools are built on, and there is a hosted endpoint at your-domain.com/api/mcp if you would rather not run the local server. Per-client setup is written up in the MCP guide.