> ## Documentation Index
> Fetch the complete documentation index at: https://spreecommerce.org/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# MCP Server Custom Tools

> Add your own tools to the MCP server, or expose a workflow your extension already ships.

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 registered is offered to the admin MCP server and to any other adapter reading the same registry.

The catalog is a registry in `spree_core`, so an extension or a host application adds to it without depending on the MCP gem. Two kinds of thing to add.

**A back-office operation you already have as a workflow.** Name its `Spree::Dependencies` key and the permission that should gate it:

```ruby server/config/initializers/spree.rb theme={"theme":"night-owl"}
Spree.admin_agent_tools.expose_workflows(supplier_sync_workflow: 'write_purchasing')
```

That is the whole change. The tool's name comes from the workflow's own key (`suppliers.sync` becomes `suppliers_sync`), its description from the class's leading comment, and its parameters from `perform`'s signature and the YARD `@param` lines above it — so the tool and the workflow cannot drift apart. Two rules the derivation relies on: every parameter needs an `@param` with a type, and a parameter naming a record must be typed as a model the agent can already read.

**Something that is not a workflow** — a lookup against another system, a report of your own. Subclass `Spree::AgentTool` and register the class by name:

```ruby app/tools/my_shop/agent_tools/supplier_stock.rb theme={"theme":"night-owl"}
module MyShop
  module AgentTools
    # Asks the supplier's API what they hold, so a merchant can ask before
    # committing to a purchase order.
    class SupplierStock < Spree::AgentTool
      tool_name 'supplier_stock'
      description 'Current stock at a supplier, by SKU.'
      permission 'read_purchasing'

      param :sku, description: 'The SKU to look up', required: true

      def call(sku:)
        stock = MyShop::SupplierApi.stock_for(sku, store: context.store)
        return { error: "No supplier carries #{sku}." } if stock.nil?

        { summary: "#{sku}: #{stock} available", sku: sku, available: stock }
      end
    end
  end
end
```

```ruby server/config/initializers/spree.rb theme={"theme":"night-owl"}
Spree.admin_agent_tools << 'MyShop::AgentTools::SupplierStock'
```

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.

## Which registry

There is one registry per surface, named the way the authentication strategies are:

```ruby theme={"theme":"night-owl"}
Spree.admin_agent_tools   # the back office
Spree.store_agent_tools   # the storefront (no catalog yet)
```

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:

```ruby theme={"theme":"night-owl"}
class SupplierStock < Spree::AgentTool
  audience :admin   # the default; state it when a tool is storefront-facing
end
```

## See also

<CardGroup cols={2}>
  <Card title="Tool reference" icon="wrench" href="/docs/developer/mcp/tools">
    The tools that ship, and the shape yours should follow.
  </Card>

  <Card title="Workflows" icon="diagram-project" href="/docs/developer/customization/workflows">
    What `expose_workflows` is exposing.
  </Card>
</CardGroup>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.