Skip to main content
The catalog is a registry in spree_core, so an extension or a host application adds to it without depending on the MCP gem. Anything in it is offered to the admin MCP server and to any other adapter reading the same registry.

Your admin endpoints are already tools

Every documented Admin API action is an agent tool: cancel, approve, send, capture, anything that is not a plain create, update or destroy (those go through create_resource, update_resource and delete_resource). The exceptions are an action its spec opts out, a custom read its spec does not opt in, and the actions of a controller whose records carry credentials (API keys, webhooks, gift cards). Nothing registers a tool. Each is built from the endpoint’s OpenAPI operation: So adding an endpoint and documenting it is the whole change. A call runs through the endpoint, with its authentication, scope check, record-level permissions and workflow, so an agent gets exactly what an API client gets. Write the summary and description for someone choosing the operation: they are what the model reads. Core reads its own admin.yaml. Your application, and each extension, generates the same document from its own rswag specs at openapi/admin.yaml, and Spree merges every one into the document the tools read. Spree’s own API reference and SDKs are built from core’s document only; an app or extension that publishes docs or a typed client builds them from its own file.

Documenting your application’s endpoints

A new project from create-spree-app is set up already: server/spec/swagger_helper.rb and the contract spec come with it. In an older project, the generator adds them:
It writes the swagger helper and the contract spec below. It also writes a skipped skeleton rswag spec for every admin action your controllers serve that is not documented yet, so what is left is each operation’s description and the records its response is tested against. Run it again after adding endpoints; it only writes what is missing. Write each spec the way the example below does, then generate the document and commit it:

Documenting your extension’s endpoints

An extension runs the same generator and task from its own directory, against its dummy app:
The task comes with spree/testing_support/extension_rake, which your Rakefile already requires. A gemspec that ships git ls-files includes the committed openapi/admin.yaml already. One that lists its files needs openapi/*.yaml added:
my_extension.gemspec
An extension with Store API endpoints documents them too, in openapi/store.yaml: pass store_title: to Spree::Api::TestingSupport::Openapi.configure in its spec/swagger_helper.rb, and name the document in those specs with openapi_spec: 'openapi/store.yaml'. A package that ships a client for the extension generates its client and types from these documents with spree sdk generate.

Writing the specs

Document each admin endpoint the way core does:
spec/integration/admin/supplier_syncs_spec.rb
admin_operation sets what every admin operation repeats: the content types, the API key and bearer token headers, the scope note in the description, and a signed-in admin and secret key to call it with. A response block overrides either credential with its own let. request_body_for documents the body from the controller’s request_params declaration, so the tool’s arguments, the API reference and the SDK types all come from the one declaration that also permits the request.

Keeping an action out

An action that should not be offered to agents says so in its spec, with the reason. Irreversible erasure, platform jobs and wizard steps qualify; “not done yet” does not.
A read is not a tool unless its spec opts it in, because most custom reads are type lists, which describe_resource already covers, or file downloads:

The contract spec

spec/agent_tools_spec.rb, which the generator writes, fails when a new admin action lands undocumented, an opt-out lands without its reason, or two actions claim one tool name. Keep it in your suite.

Something that is not an endpoint

A lookup against another system, a report of your own: subclass Spree::AgentTool and register the class by name.
app/tools/my_shop/agent_tools/supplier_stock.rb
server/config/initializers/spree.rb
Four things worth knowing when you write one:
  • Registration is by name, not by class, so your edits to a description or a parameter take effect on the next request rather than needing a restart.
  • context is who is asking: context.store is always the caller’s store, and context.principal is the admin or the API key — pass it to anything that records who acted.
  • A refusal is { error: '…' }, not an exception. The model reads that text and corrects itself, so write it as an instruction rather than a stack trace.
  • Keep the result small. It lands in the model’s context. Return a summary line plus the few fields that answer the question, and let the caller ask for more.
A tool is offered only to a credential holding its permission, so a key that cannot read purchasing never learns supplier_stock exists.
Spree.admin_agent_tools.expose_workflows is deprecated and has no effect: an exposed workflow behind a documented endpoint is already that endpoint’s tool, and one whose endpoint is not in any OpenAPI document is not offered. It is removed in 7.0. Tool names and parameters from 6.0.0 that changed keep answering through 6.0.x, and a call that uses one says what to send instead.

Which registry

There is one registry per surface, named the way the authentication strategies are:
A tool declares the surface it belongs to, defaulting to :admin, so registering one in the wrong place raises rather than being discovered when a storefront visitor is offered the back office:

See also

Tool reference

The tools that ship, and the shape yours should follow.

Admin API

The operations the action tools are built from.