# GoCommerce MCP Server: Let an AI Agent Run Your Store

> An open-source ecommerce MCP server: 19 tools that let an AI agent read and run a GoCommerce store through its own domain services, with an audit trail.

- Canonical: https://kitcommerce.store/gocommerce/mcp/
- Last updated: 2026-09-25

---

GoCommerce MCP server

## Let an AI agent run your store

GoCommerce’s `mcp` module is an open-source ecommerce MCP server, compiled into the store’s own binary. It gives an AI agent 19 tools — nine that read the store and ten that change it — and every one calls the same domain service the REST API calls. There is no SQL tool, no second copy of the rules, and a record of every change the agent makes.

- **19** tools
- **10** that change state
- **2** transports: HTTP and stdio
- **0** SQL tools

What it is

### An MCP server that is part of the store

The Model Context Protocol (MCP) is an open standard, built on JSON-RPC 2.0, through which an AI application discovers the tools a server offers and calls them.

The agent gets tools, not a database connection.

Each tool is a thin wrapper over one service method: `mark_order_paid` is the payment service’s `MarkPaid`, `cancel_order` is the order service’s `Cancel`, `update_variant_inventory` is the stock service’s `Adjust` or `SetOnHand`. So an agent cannot reach a state a person could not, and cannot skip a rule by coming in through a different door. There is one place an order becomes paid, whether a person, an application or an agent asked.

The module speaks JSON-RPC with Go’s standard library rather than an MCP SDK, so it adds no dependency to the binary. It implements MCP revision 2024-11-05 and offers tools only — no MCP resources or prompts.

It works for whoever runs the store: the credential is the store’s admin credential. It is not a way for a shopper’s agent to buy — see [what it does not do](https://kitcommerce.store/gocommerce/mcp/#limits).

The tools

### Nine that read, ten that change

Names exactly as an agent sees them in `tools/list`. Each tool names the right its REST equivalent requires. The ten that change state are marked `Mutates` in the source: withheld under `ReadOnly`, and written to the audit table whenever they are called.

*Read — 9 tools, offered in every mode, not audited*

| Tool | What it returns | Right |
| --- | --- | --- |
| `store_info` | The store’s currency and languages, the payment methods and fulfilment providers installed, and the engine version. | none |
| `store_health` | The same diagnostics `gocommerce doctor` runs — database, migrations, admin access, the event outbox, stock reservations, carts, catalogue, the API contract — each *ok*, *warn* or *fail* with a hint. | `store.operate` |
| `list_products` | Products, filtered by a search term or by status (draft, active, archived), as summaries with their variants. | `catalog.read` |
| `get_product` | One product with all its variants and stock levels. | `catalog.read` |
| `list_low_stock_variants` | Sellable variants at or below a threshold (default 5), across the whole store or at one location. | `inventory.read` |
| `list_orders` | Orders, newest first, filtered by status, payment status or email. Each summary carries the amount already refunded. | `orders.read` |
| `get_order` | One order in full, with its lines and shipments. The shopper’s order access token is blanked. | `orders.read` |
| `list_customers` | People who have ordered, newest first, with what each has spent. | `customers.read` |
| `sales_report` | Sales grouped by day, week or month, in minor units of the store’s currency, which the result names. | `reports.read` |

*Change — 10 tools marked Mutates: withheld under ReadOnly, every call audited*

| Tool | What it does | Right |
| --- | --- | --- |
| `update_variant_inventory` | Move a variant’s stock by a delta, or set it outright for a stock take, with a reason written to the stock ledger. Stock cannot go below what open orders have reserved. | `inventory.write` |
| `mark_order_paid` | Record that an order has been paid — how cash on delivery is settled. This also confirms the order for shipping. | `orders.write` |
| `cancel_order` | Cancel an order and return its stock. A shipped order cannot be cancelled; that is a return. | `orders.write` |
| `create_fulfillment` | Ship a confirmed order, in whole or in part, with a tracking number. The provider defaults to manual. | `orders.fulfill` |
| `mark_order_delivered` | Record that a shipped order reached the customer. | `orders.write` |
| `create_product` | Create a product, draft by default. Given a price and opening stock, it gets a single default variant. | `catalog.write` |
| `update_product` | Change a product’s title, description, status, vendor or tags. Only the fields sent are touched. | `catalog.write` |
| `set_variant_price` | Set one variant’s price in minor units, with an optional was-price. A decimal is refused, not rounded. | `catalog.write` |
| `create_discount` | Create a discount code: a percentage in basis points or a fixed amount, for the whole order or scoped to products, collections or categories. | `discounts.write` |
| `refund_order` | Refund an order, fully or in part. The payment method must be able to refund — cash on delivery cannot. | `orders.refund` |

Money crosses this boundary as whole minor units — 1999 is 19.99. `set_variant_price` refuses a decimal rather than truncating it, because an agent sending 19.99 where 1999 was meant would otherwise price the variant at nineteen cents without a word. A tool that fails for a domain reason — “that order is already shipped” — returns the reason as its result, so the agent can act on it. List tools return summaries; the single-record tools return the record.

In practice

### A session, as the module’s own test runs it

The module carries 20 tests of its own. One plays an agent end to end against a real PostgreSQL: a shopper places a cash-on-delivery order, and the agent takes it from there.

*TestScriptedAgentFlow, in `ext/mcp/mcp_test.go`*

| Tool called | What happens |
| --- | --- |
| `store_info` | The agent orients itself: the store reports its currency. |
| `list_orders` | It finds a confirmed cash-on-delivery order a shopper placed the ordinary way. |
| `mark_order_paid` | It records the cash as collected. |
| `create_fulfillment` | It ships the order with a tracking number. |
| `mark_order_delivered` | It marks the order delivered. The order now reads delivered and paid. |
| `update_variant_inventory` | It receives 40 units of the variant that sold. |

The test then counts the audit table: four rows, one for each of the four tools that changed something, readable through the audit route. The two reads left no trace, by design.

Access

### The admin credential, narrowed per tool

The module writes no authentication of its own. It is mounted through the engine’s admin routing, so the agent meets every check an admin route has — and one more, per tool.

- **The admin token is the credential** — The HTTP endpoint takes the store’s admin token as `Authorization: Bearer`. Without it the call is refused with 401 before the body is read.
- **Two rights for the module itself** — `agent.dispatch` to call tools, `agent.read` to read what an agent did. By default only the owner role holds them; a staff or manager session gets 403.
- **Rights checked per tool** — Each tool names the right its REST route needs — `orders.refund` for a refund, `inventory.write` for stock. A signed-in operator without it gets a JSON-RPC error naming the right, and nothing changes.
- **`ReadOnly` withholds the ten** — With `ReadOnly: true` the mutating tools are not offered at all — absent from `tools/list`, not merely refused. Every tool reports MCP’s `readOnlyHint` either way.
- **Every change is audited** — Each call to a mutating tool writes a row to `mcp_audit` — the tool, its arguments verbatim, the outcome and the time — including a call refused for want of a right. Reads are not recorded.
- **Readable in the admin** — The trail is served at `GET /api/admin/x/mcp/audit`, newest first, and shown in the admin under Settings → Agent activity.

The static admin token and stdio carry no operator, so they hold every right — as the static token does on every other admin route. For them, `ReadOnly` is the limit that applies.

Transports

### Over HTTP, or as a subprocess

One dispatcher, the same tools and the same audit either way. Which transport a binary serves is decided in its `main()`, not by the module.

#### HTTP: `POST /api/admin/x/mcp`

One JSON-RPC 2.0 message per POST, answered with one JSON reply. A failed call still comes back as HTTP 200, with the error in the body; a notification is answered 202 with no body. 401 and 403 arrive in the engine’s ordinary error envelope.

#### stdio: `mcp.ServeStdio(app, m)`

For a desktop agent that launches the store as a subprocess: one JSON-RPC message per line on stdin, replies on stdout. You call it from `main()` in place of `ListenAndServe`. There is no token on this path — whoever can start the process with the database URL gets every tool offered.

#### More tools: `Config.Tools`

Other modules, or your own code, can add tools, wired into `main()` by hand. There is no discovery, and none of the bundled modules contributes a tool today. The repository’s rule for a new tool is that it wraps a service method.

Install

### One entry in your store’s `main()`

Every GoCommerce module is a Go package you import. This one is `ext/mcp`; installing it is one entry in the modules slice, and its migration creates the audit table on the next boot.

Registering the module

```
import (
	"github.com/itswadesh/gocommerce/core"
	"github.com/itswadesh/gocommerce/ext/mcp"
)

modules := []gocommerce.Module{
	// The store as tools for an AI agent, at /api/admin/x/mcp. The admin
	// token is the agent's credential, and every change it makes is
	// recorded in an audit table.
	mcp.New(mcp.Config{ServerName: "example-store"}),
}
```

From `examples/store/main.go`. `mcp.Config` has three fields: `ServerName`, reported to the agent (default `gocommerce`); `ReadOnly`; and `Tools`. Install with `go get github.com/itswadesh/gocommerce@latest`.

Example: serving stdio as well as HTTP

```
m := mcp.New(mcp.Config{ServerName: "my-store", ReadOnly: true})

app, err := gocommerce.New(cfg, m) // cfg as in examples/store
if err != nil {
	log.Fatal(err)
}

// "my-store mcp" speaks MCP on stdin and stdout; anything else serves HTTP.
if len(os.Args) > 1 && os.Args[1] == "mcp" {
	if err := mcp.ServeStdio(app, m); err != nil {
		log.Fatal(err)
	}
	return
}
if err := app.ListenAndServe(); err != nil {
	log.Fatal(err)
}
```

An example, not from the repository — the `mcp` argument is this example’s choice, not a flag GoCommerce defines. `ServeStdio` needs the module registered first, which `gocommerce.New` does. Stdout carries the protocol, so logs belong elsewhere; GoCommerce’s default logger writes to stderr.

The one-command Docker stack builds the reference binary, `./cmd/gocommerce`, which does not install this module. To use it, build your own `main()` the way [`examples/store`](https://github.com/itswadesh/gocommerce/blob/main/examples/store/main.go) does.

Clients

### Pointing an AI client at it

The GoCommerce repository does not document a configuration for any particular client, so what follows is an example, not a tested recipe. Any MCP client that can launch a command, or send JSON-RPC over HTTP with a header, has what it needs.

Example: a desktop client launching the store over stdio

```
{
  "mcpServers": {
    "my-store": {
      "command": "/usr/local/bin/my-store",
      "args": ["mcp"],
      "env": {
        "DATABASE_URL": "postgres://localhost/mystore"
      }
    }
  }
}
```

An example, not from the GoCommerce repository. This `mcpServers` shape is the one Claude Desktop (`claude_desktop_config.json`), Cursor (`.cursor/mcp.json`) and Claude Code (`.mcp.json`) read; check your client’s own documentation for where the file lives. Pass whatever your `main()` reads from the environment.

Checking the HTTP endpoint by hand

```
curl -s https://shop.example.com/api/admin/x/mcp \
  -H "Authorization: Bearer $GOCOMMERCE_ADMIN_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'
```

The reply lists every tool on offer, each with its JSON Schema and `readOnlyHint`. An HTTP-capable MCP client takes the same URL and the same bearer header. The module answers each POST with a single JSON reply and implements revision 2024-11-05, so confirm your client accepts that before you rely on it.

Limits

### What it does not do

Stated plainly, so nobody discovers them in production.

#### No SQL tool

There is no “run this query” tool and no direct table access, and the repository’s rules for AI agents say to keep it that way. An agent asked how the store is doing gets `sales_report` and `store_health`, not a database prompt.

#### Not a shopper’s buying agent

It is the operator’s door, behind admin authentication. A shopper’s AI assistant cannot browse or buy through it. A buying agent can use the public catalogue, cart and checkout routes a storefront uses — that entry point is yours to build.

#### No ACP, UCP or AP2

GoCommerce implements none of the agentic commerce protocols — the Agentic Commerce Protocol, the Universal Commerce Protocol or the Agent Payments Protocol — so no agent can check out of a GoCommerce store through them. [More on that](https://kitcommerce.store/blog/agentic-commerce-open-stack/).

No store we know of runs GoCommerce in production yet.

The module’s tests run against a real database, among the engine’s over 1,000, but tests are not production mileage. Start an agent on `ReadOnly`, read what it asks for, and widen it when the audit trail gives you reason to.

There is no hosted version and no support contract. You run it; help comes from the [issue tracker](https://github.com/itswadesh/gocommerce/issues) and the [Discord](https://discord.gg/GgdAeccwDR).

FAQ

### Questions about the MCP server

**Is GoCommerce’s MCP server open source?**

Yes. It is the mcp module in the GoCommerce repository, under ext/mcp, MIT licensed like the rest. It is not a separate package or a hosted service: you import it into your store’s main() and it is compiled into the same binary. It speaks JSON-RPC with Go’s standard library rather than an MCP SDK, so installing it adds no dependency.

**Can an AI agent break my store through it?**

It can do what the tools on offer allow, and no more. Every tool calls the domain service its REST route calls, so the engine’s rules hold: stock cannot drop below what open orders reserve, a shipped order cannot be cancelled, cash on delivery cannot be refunded, and a price sent as a decimal is refused. What an agent can still do is make a legitimate change you did not want — cancel the wrong order, say. That is what ReadOnly and the audit trail are for.

**How do I limit what the agent can do?**

Three ways, from blunt to fine. ReadOnly: true withholds all ten tools that change anything. Over stdio there is no token, so ReadOnly is the only limit — whoever can start the process gets every tool offered. Over HTTP, the static admin token carries every right, while a panel session token is limited by that operator’s role: give a role agent.dispatch plus only the rights you want, such as catalog.read and orders.read, and the other tools are refused per call.

**Which AI clients work with it?**

Any MCP client that can launch a command and talk over stdin and stdout, or send JSON-RPC over HTTP with a bearer header. The GoCommerce repository does not document a configuration for any particular client, so the configuration on this page is a labelled example rather than a tested recipe. The module implements MCP revision 2024-11-05 and offers tools only, no resources or prompts.

**Can shoppers’ AI agents buy from my store through it?**

No. The MCP module is the operator’s door: it sits behind admin authentication and its tools run the store, not shop in it. GoCommerce also implements none of the agentic commerce protocols — ACP, UCP or AP2. A buying agent can use the same public catalogue, cart and checkout routes a storefront uses, and building that entry point is up to you.

**Does the one-command Docker stack include it?**

No. The Docker image builds the reference binary, ./cmd/gocommerce, which does not install the mcp module, so that stack has no MCP endpoint. Install the module in your own main(), as examples/store does, and build that.

### Give an agent the read tools first

Install the module with `ReadOnly` on, ask the agent what is running low and what sold last week, and read the audit before you hand it the ten tools that change things.

[Read ext/mcp on GitHub](https://github.com/itswadesh/gocommerce/tree/main/ext/mcp) · [Explore GoCommerce](https://kitcommerce.store/gocommerce/) · [Vibe-coding commerce](https://kitcommerce.store/vibe-coding-commerce/)
