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.

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.

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
ToolWhat it returnsRight
store_infoThe store’s currency and languages, the payment methods and fulfilment providers installed, and the engine version.none
store_healthThe 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_productsProducts, filtered by a search term or by status (draft, active, archived), as summaries with their variants.catalog.read
get_productOne product with all its variants and stock levels.catalog.read
list_low_stock_variantsSellable variants at or below a threshold (default 5), across the whole store or at one location.inventory.read
list_ordersOrders, newest first, filtered by status, payment status or email. Each summary carries the amount already refunded.orders.read
get_orderOne order in full, with its lines and shipments. The shopper’s order access token is blanked.orders.read
list_customersPeople who have ordered, newest first, with what each has spent.customers.read
sales_reportSales 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
ToolWhat it doesRight
update_variant_inventoryMove 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_paidRecord that an order has been paid — how cash on delivery is settled. This also confirms the order for shipping.orders.write
cancel_orderCancel an order and return its stock. A shipped order cannot be cancelled; that is a return.orders.write
create_fulfillmentShip a confirmed order, in whole or in part, with a tracking number. The provider defaults to manual.orders.fulfill
mark_order_deliveredRecord that a shipped order reached the customer.orders.write
create_productCreate a product, draft by default. Given a price and opening stock, it gets a single default variant.catalog.write
update_productChange a product’s title, description, status, vendor or tags. Only the fields sent are touched.catalog.write
set_variant_priceSet one variant’s price in minor units, with an optional was-price. A decimal is refused, not rounded.catalog.write
create_discountCreate 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_orderRefund 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 calledWhat happens
store_infoThe agent orients itself: the store reports its currency.
list_ordersIt finds a confirmed cash-on-delivery order a shopper placed the ordinary way.
mark_order_paidIt records the cash as collected.
create_fulfillmentIt ships the order with a tracking number.
mark_order_deliveredIt marks the order delivered. The order now reads delivered and paid.
update_variant_inventoryIt 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 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 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.

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 and the Discord.

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.

Chat on WhatsApp