# Self-hosted Commerce MCP You can run the self-hosted variant of Commerce MCP locally, extend it, or use it as part of agent frameworks. For more information about the tools, see [Commerce MCP server tools](/dev-tooling/mcp/commerce-mcp.md#available-tools). You decide which [API Client](/api/getting-started/create-api-client.md) Commerce MCP uses and configure which tools the MCP server can use. For a hosted, centrally managed alternative with guaranteed enforcement of your configuration, see [Managed MCP Servers](/api/managed-mcp-servers-overview.md). This page describes how to [set up your self-hosted Commerce MCP server in Visual Studio Code](/dev-tooling/mcp/self-hosted-commerce-mcp.md#get-started) and configure its [transport protocol](/dev-tooling/mcp/self-hosted-commerce-mcp.md#transport-protocol). You can also control [data transformation](/dev-tooling/mcp/self-hosted-commerce-mcp.md#data-transformation) and enable [logging](/dev-tooling/mcp/self-hosted-commerce-mcp.md#logging) for debugging. Find [best practices](/dev-tooling/mcp/self-hosted-commerce-mcp.md#best-practices) and options for integrating Commerce MCP in [agent frameworks](/dev-tooling/mcp/self-hosted-commerce-mcp.md#commerce-agent) and for extending it with [custom tools](/dev-tooling/mcp/self-hosted-commerce-mcp.md#custom-tools). ## Placeholder values This page uses the following placeholders in examples. Replace these placeholders with your own values. If you do not have an API Client, follow the [Get your API Client](/api/getting-started/create-api-client.md) guide. | Placeholder | Replace with | From | | --- | --- | --- | | `{PROJECT_KEY}` | project\_key | Your API Client | | `{CLIENT_ID}` | client\_id | Your API Client | | `{CLIENT_SECRET}` | secret | Your API Client | | `{ACCESS_TOKEN}` | access\_token | An existing access token (when using `auth_token` authentication type) | | `{AUTH_URL}` | Your Authorization URL | [Request an access token using the internal OAuth 2.0 service](/api/authorization.md#request-an-access-token-using-the-internal-oauth-20-service) | | `{API_URL}` | Your API URL | [Hosts](/api/general-concepts.md#hosts) | ## Get started This section describes how to set up and use self-hosted Commerce MCP with Visual Studio Code in stdio mode. You need Node.js 18 or later installed on your machine. You also need an API Client with the appropriate scopes for the tools you want to use. For this getting started guide, use an Admin Client with manage and view scopes enabled to see all available tools. Have the API Client credentials ready before you proceed. Replace the [placeholder values](/dev-tooling/mcp/self-hosted-commerce-mcp.md#placeholder-values) with your own values to access your Project. ### Add self-hosted Commerce MCP server to Visual Studio Code Add the following entry to the `mcp.json` file in your VS Code configuration: ```json title="Add self-hosted Commerce MCP server to VS Code" { "servers": { "commerce-mcp": { "type": "stdio", "command": "npx", "args": [ "-y", "@commercetools/commerce-mcp@latest", "--tools=all", "--isAdmin=true", "--dynamicToolLoadingThreshold=450", "--authType=client_credentials", "--clientId={CLIENT_ID}", "--clientSecret={CLIENT_SECRET}", "--authUrl={AUTH_URL}", "--projectKey={PROJECT_KEY}", "--apiUrl={API_URL}" ] } } } ``` The `--dynamicToolLoadingThreshold=450` value is intentionally set above the total number of available tools, so all tools are loaded and [dynamic tool loading](/dev-tooling/mcp/self-hosted-commerce-mcp.md#dynamic-tool-loading) is effectively disabled. A higher value injects more tools upfront, which increases context and token usage. Replace the placeholders with your own values. You can start the server directly from the configuration file. If the status does not change to `Running`, open the **Output** panel in VS Code to see any error messages. If the server is running, you should see that self-hosted Commerce MCP discovered several [tools](/dev-tooling/mcp/commerce-mcp.md#available-tools). ### Use self-hosted Commerce MCP's tools with VS Code Chat You can use the Commerce MCP tools in your VS Code chat using Agent mode. Ask questions like: - What is the project key? - Which countries are configured in my project? You can also create resources in your Project, for example: - Create a new inventory supply channel for Italy. Verify in the Merchant Center that the channel has been created. ### Next steps If you have completed this guide to this point, you have successfully set up self-hosted Commerce MCP running on your machine. You can explore more advanced configurations in the following sections. For example, you can set up self-hosted Commerce MCP to run as a [Streamable HTTP](/dev-tooling/mcp/self-hosted-commerce-mcp.md#streamable-http) server for remote access. In the [Best practices](/dev-tooling/mcp/self-hosted-commerce-mcp.md#best-practices) section, you can find recommendations on how to configure only the tools you want to enable for an agent. The [Arguments and environment variables](/dev-tooling/mcp/self-hosted-commerce-mcp.md#arguments-and-environment-variables) section explains the parameters you provided in the MCP server configuration. ## Reference ### Executable package You can run self-hosted Commerce MCP directly using `npx` without installing it globally: ```bash title="Run self-hosted Commerce MCP with npx" npx -y @commercetools/commerce-mcp {list of arguments} ``` Find a list of the available arguments and environment variables that control the MCP server in the following section. ### Arguments and environment variables You can provide the following arguments as command-line parameters or as environment variables when invoking self-hosted Commerce MCP. The two examples below show each form, and every row in the argument table applies to both forms. ```bash title="Use command line argument" highlightLines="3" npx -y @commercetools/commerce-mcp --tools=all --dynamicToolLoadingThreshold=50 --clientId={CLIENT_ID} --clientSecret={CLIENT_SECRET} --projectKey={PROJECT_KEY} --authUrl={AUTH_URL} --apiUrl={API_URL} ``` ```bash title="Use environment variable" highlightLines="1" export DYNAMIC_TOOL_LOADING_THRESHOLD=50 npx -y @commercetools/commerce-mcp --tools=all --clientId={CLIENT_ID} --clientSecret={CLIENT_SECRET} --projectKey={PROJECT_KEY} --authUrl={AUTH_URL} --apiUrl={API_URL} ``` | Command-line argument | Environment variable | Required | Description or set value | | --- | --- | --- | --- | | `tools` | `TOOLS` | Required | The [tools](/dev-tooling/mcp/commerce-mcp.md#available-tools) to use. To include multiple tools, use a comma-separated list without spaces. | | `authType` | `AUTH_TYPE` | Required | The [authentication type](/dev-tooling/mcp/self-hosted-commerce-mcp.md#authentication-types) to use. | | `projectKey` | `PROJECT_KEY` | Required | The `{PROJECT_KEY}` of the Project you want to use. | | `authUrl` | `AUTH_URL` | Required | The `{AUTH_URL}` of your Project's authorization service. | | `apiUrl` | `API_URL` | Required | The `{API_URL}` of your Project's API service. | | `accessToken` | `ACCESS_TOKEN` | Required, if `authType` is `auth_token`. | The access token to use for [token authentication](/dev-tooling/mcp/self-hosted-commerce-mcp.md#authentication-types). | | `clientId` | `CLIENT_ID` | Required, if `authType` is `client_credentials`. | The `{CLIENT_ID}` to use for [client credentials authentication](/dev-tooling/mcp/self-hosted-commerce-mcp.md#authentication-types). | | `clientSecret` | `CLIENT_SECRET` | Required, if `authType` is `client_credentials`. | The `{CLIENT_SECRET}` to use for client credentials authentication. | | `remote` | `REMOTE` | Optional | Specifies the [transport protocol](/dev-tooling/mcp/self-hosted-commerce-mcp.md#transport-protocol) between MCP server and client. If `true`, Streamable HTTP is used, stdio if `false`. | | `stateless` | `STATELESS` | Required, if `remote` is `true`. | If `true`, the server is stateless. | | `port` | `PORT` | Optional | The port number of the MCP server in remote mode. Defaults to port `8080`. | | `isAdmin` | `IS_ADMIN` | Optional | If the server should be run with administrator privileges. Must be `true` if `tools` is set to `all`. | | `dynamicToolLoadingThreshold` | `DYNAMIC_TOOL_LOADING_THRESHOLD` | Optional | The number of tools to inject. If the value exceeds the number of available tools, all available tools are injected. For more information, see [dynamic tool loading](/dev-tooling/mcp/self-hosted-commerce-mcp.md#dynamic-tool-loading). | | `toolOutputFormat` | `TOOL_OUTPUT_FORMAT` | Optional | The output format of the MCP server. Can be `json` or `tabular`. If not defined, `tabular` is used. See [data transformation](/dev-tooling/mcp/self-hosted-commerce-mcp.md#data-transformation). | | `logging` | `LOGGING` | Optional | If `true`, the server runs in [logging](/dev-tooling/mcp/self-hosted-commerce-mcp.md#logging) mode. | | `customerId` | `CUSTOMER_ID` | Optional | The `customerId` to use in Customer-specific queries. | | `cartId` | `CART_ID` | Optional | The `cartId` to use in Cart-specific queries. | | `storeKey` | `STORE_KEY` | Optional | The `storeKey` to use in Store-specific queries. | | `businessUnitKey` | `BUSINESS_UNIT_KEY` | Optional | The `businessUnitKey` to use in Business Unit-specific queries. | #### Dynamic tool loading To optimize performance, the self-hosted Commerce MCP uses an efficient loading strategy for the enabled tools called dynamic tool loading. It limits the number of tools loaded at once and only loads additional tools as needed. This strategy reduces context usage when working with large numbers of tools. The self-hosted Commerce MCP applies dynamic tool loading automatically when the number of enabled tools exceeds a configurable threshold. The default threshold is 30. To change it, use one of the following options: - the command-line argument `--dynamicToolLoadingThreshold=50`, or - the environment variable `DYNAMIC_TOOL_LOADING_THRESHOLD=50`. When dynamic tool loading is active, the server does not expose every enabled tool at once. Instead, it provides the following meta-tools that let the agent discover and load the business tools it needs: | Tool | Description | | --- | --- | | `list_available_tools` | Lists the available tools for a given resource type. | | `inject_tools` | Loads a list of tools into the active context so the agent can call them. | | `execute_tool` | Executes a tool by its method name and arguments. | The agent calls `list_available_tools` to discover the operations for a resource, then uses `inject_tools` or `execute_tool` to load or run them. ### Authentication types The self-hosted Commerce MCP supports two types of authentication: - [Client credentials authentication](/dev-tooling/mcp/self-hosted-commerce-mcp.md#client-credentials-authentication): uses the API Client ID and secret to obtain an access token. - [Auth token authentication](/dev-tooling/mcp/self-hosted-commerce-mcp.md#auth-token-authentication): uses an existing access token. Choose the authentication type that best fits your use case and provide the corresponding credentials. #### Client credentials authentication Use the `--authType=client_credentials` argument (or `type: 'client_credentials'` in TypeScript) and provide the API Client ID and secret using `--clientId={CLIENT_ID}` and `--clientSecret={CLIENT_SECRET}`. ```json title="Self-hosted Commerce MCP server configuration using client_credentials authentication" highlightLines="9-11" { "servers": { "commerce-mcp": { "type": "stdio", "command": "npx", "args": [ "-y", "@commercetools/commerce-mcp@latest", "--authType=client_credentials", "--clientId={CLIENT_ID}", "--clientSecret={CLIENT_SECRET}", "--authUrl={AUTH_URL}", "--projectKey={PROJECT_KEY}", "--apiUrl={API_URL}", "--tools=read_all" ] } } } ``` #### Auth token authentication Use the `--authType=auth_token` argument (or `type: 'auth_token'` in TypeScript) and provide the existing access token using `--accessToken={ACCESS_TOKEN}`. ```json title="Self-hosted Commerce MCP server configuration using auth_token authentication" highlightLines="9-10" { "servers": { "commerce-mcp": { "type": "stdio", "command": "npx", "args": [ "-y", "@commercetools/commerce-mcp@latest", "--authType=auth_token", "--accessToken={ACCESS_TOKEN}", "--authUrl={AUTH_URL}", "--projectKey={PROJECT_KEY}", "--apiUrl={API_URL}", "--tools=read_all" ] } } } ``` ### Transport protocol The self-hosted Commerce MCP supports two transport protocols for communication between the MCP server and the client. - [stdio](/dev-tooling/mcp/self-hosted-commerce-mcp.md#stdio): use this for development and testing locally on your machine. - [Streamable HTTP](/dev-tooling/mcp/self-hosted-commerce-mcp.md#streamable-http): use this for remote deployments where your MCP server is hosted on a remote machine or in the cloud. #### stdio With [stdio transport](https://modelcontextprotocol.io/specification/2025-06-18/basic/transports#stdio), you run the self-hosted Commerce MCP server locally. The server interacts with your client using standard input and output streams. This is the transport used in [Get started](/dev-tooling/mcp/self-hosted-commerce-mcp.md#add-self-hosted-commerce-mcp-server-to-visual-studio-code), where `"type": "stdio"` selects it in the server entry. Your exact configuration may differ depending on the software you use, but it follows the same shape shown there. #### Streamable HTTP The self-hosted Commerce MCP supports Streamable HTTP transport, allowing you to run the MCP server in a remote mode accessible over HTTP. This setup is useful for deploying the MCP server in cloud environments or on remote machines. In Streamable HTTP mode, every `/mcp` request must include a valid `Authorization` header that uses the `Bearer` scheme. If the header is missing or malformed, the server returns `401 Unauthorized` immediately. The `Bearer` token must be a valid commercetools OAuth access token and is passed through to the commercetools API as `auth_token`. To run the self-hosted Commerce MCP server in remote mode, add the following arguments to the command: - `--remote=true`: start a server with [Streamable HTTP transport](https://modelcontextprotocol.io/specification/2025-06-18/basic/transports#streamable-http). - `--stateless=true`: run the server in stateless mode, meaning no session is preserved. - `--port=8888`: specify the port the server listens on. If not specified, this defaults to `8080`. ```bash title="Set up the self-hosted Commerce MCP server with all available tools and Streamable HTTP transport" highlightLines="2-4" npx -y @commercetools/commerce-mcp --remote=true --stateless=true --port=8888 --tools=all --isAdmin=true --dynamicToolLoadingThreshold=450 --authType=client_credentials --clientId={CLIENT_ID} --clientSecret={CLIENT_SECRET} --projectKey={PROJECT_KEY} --authUrl={AUTH_URL} --apiUrl={API_URL} ``` The `clientId` and `clientSecret` parameters are only needed to start the server and are not used to authenticate `/mcp` requests. Each request must supply its own `Bearer` token. ##### Use with Claude Desktop To use a self-hosted Commerce MCP server running on port `8888`, configure Claude Desktop with the following: ```json title="Claude Desktop configuration to use a self-hosted Commerce MCP server running on port 8888" { "mcpServers": { "commercetools": { "command": "npx", "args": [ "-y", "mcp-remote", "--header", "Authorization: Bearer {ACCESS_TOKEN}", "http://localhost:8888/mcp" ] } } } ``` ### Data transformation By default, the self-hosted Commerce MCP transforms data fetched from tools and resources before sending it back to the LLM. This transformation converts JSON data into tabular format, with minor modifications to booleans and property names. The transformed data format improves the quality of the data passed to the LLM, and in most cases reduces the token count. For deeply nested data structures, the token count may increase slightly instead. The following example shows the original JSON object together with its transformed tabular format: ```json title="Original JSON output from a tool" { "customer": { "customerID": "12", "firstName": "John", "lastName": "Doe", "isActive": true, "isGoldCustomer": false } } ``` ```text title="Transformed tabular output" Customer Customer ID: 12 First Name: John Last Name: Doe Is Active: Yes Is Gold Customer: No ``` You can override this behavior by passing the `--toolOutputFormat=json` argument or setting the `TOOL_OUTPUT_FORMAT` environment variable. This setting makes self-hosted Commerce MCP bypass the data transformation and return the original JSON string instead. ### Field filtering and data redaction The self-hosted Commerce MCP supports resource-level field filtering and redaction. You can apply it in two ways: - On the built-in MCP server, by passing rules to the `--fieldFiltering` argument. - In a custom MCP, by setting the `fieldFiltering` option when creating a [Commerce Agent](/dev-tooling/mcp/self-hosted-commerce-mcp.md#commerce-agent). Both ways accept the same rule configuration, a `FieldFilteringManagerConfig`. A custom MCP can additionally replace the filtering logic entirely. #### Rule configuration A `FieldFilteringManagerConfig` defines how fields are filtered or redacted, and applies to both the `--fieldFiltering` argument and the rules-based custom MCP approach. A single [FieldFilteringRule](https://github.com/commercetools/commerce-mcp/blob/main/processors/src/field-filtering/FieldFilteringRule.ts) has the following structure: | Property | Description | | --- | --- | | `type` | String of value `"filter"` or `"redact"`. Specify `"filter"` to remove the whole key and value, or `"redact"` to replace the value with a redaction text, leaving the key for context. | | `value` | String representing the property value in which to apply the rule. | | `caseSensitive` | Boolean dictating whether to apply the rule value with case sensitivity. | A [FieldFilteringManagerConfig](https://github.com/commercetools/commerce-mcp/blob/main/processors/src/field-filtering/FieldFilteringManagerConfig.ts) groups these rules and supports the following properties: | Property | Description | | --- | --- | | `paths` | Optional array of `FieldFilteringRule` objects for explicit object paths (for example, `account.users.token`). | | `properties` | Optional array of `FieldFilteringRule` objects for property names at any object depth (for example, `token`). | | `whitelistPaths` | Optional array of `FieldFilteringRule` objects for explicit object paths to retain, overriding all other rules (for example, `account.users.passwordHint`). | | `includes` | Optional array of `FieldFilteringRule` objects; all properties containing the given substring are filtered or redacted (for example, `password`). | | `jsonRedactionText` | Optional string in which to replace redacted properties in JSON objects, default of `"[REDACTED]"`. | | `urlRedactionText` | Optional string in which to replace redacted properties in URL queries, default of `"REDACTED"`. | #### Filter fields on the built-in MCP server To apply rules to the built-in server, pass a stringified `FieldFilteringManagerConfig` JSON to the `--fieldFiltering` argument. For macOS or Linux, use the following: ```bash title="Set up the self-hosted Commerce MCP server with the fieldFiltering argument on macOS or Linux" npx -y @commercetools/commerce-mcp ... --fieldFiltering="{\"jsonRedactionText\":\"CUSTOM REDACTION TEXT\",\"includes\":[{\"value\": \"token\",\"type\": \"redact\",\"caseSensitive\":true}]}" ``` For Windows, use the following: ```bash title="Set up the self-hosted Commerce MCP server with the fieldFiltering argument on Windows" npx -y @commercetools/commerce-mcp ... --fieldFiltering='{"jsonRedactionText":"CUSTOM REDACTION TEXT","includes":[{"value":"token","type":"redact","caseSensitive":true}]}' ``` #### Filter fields in a custom MCP When building a custom MCP, pass the `fieldFiltering` option when creating a [Commerce Agent](/dev-tooling/mcp/self-hosted-commerce-mcp.md#commerce-agent). Choose one of the following approaches: - **Apply rules**: provide a `FieldFilteringManagerConfig` object, the same structure used by `--fieldFiltering`. - **Override the logic**: provide an object or class that implements the [FieldFilteringManager](https://github.com/commercetools/commerce-mcp/blob/main/processors/src/field-filtering/FieldFilteringManager.ts) interface. This replaces the default [FieldFilteringHandler](https://github.com/commercetools/commerce-mcp/blob/main/processors/src/field-filtering/FieldFilteringHandler.ts) behavior. To apply rules, provide a `FieldFilteringManagerConfig` object: ```typescript title="Apply field filtering and redaction rules" import { Configuration } from '@commercetools/commerce-agent/modelcontextprotocol'; const configuration: Configuration = { // ... context: { // ... fieldFiltering: { paths: [{caseSensitive: true, value: 'user.context.token', type: 'redact'}], properties: [{caseSensitive: false, value: 'META_DATA', type: 'filter'}], includes: [{caseSensitive: false, value: 'password', type: 'redact'}], whitelistPaths: [{caseSensitive: false, value: 'user.context.passwordHint'}], jsonRedactionText: "REDACTED_JSON", urlRedactionText: "REDACTED_QUERY" }, }, }; ``` To override the filtering logic, provide a `FieldFilteringManager` implementation: ```typescript title="Override field filtering and redaction logic" import { Configuration } from '@commercetools/commerce-agent/modelcontextprotocol'; const configuration: Configuration = { // ... context: { // ... fieldFiltering: { filterFields: (data: T): T extends number | boolean ? string | T : T => { // custom JSON filtering logic return data as any; }, filterUrlFields: (url: string): string => { // custom URL filtering logic return url; }, }, }, }; ``` ### Logging The self-hosted Commerce MCP supports logging (as of `@commercetools/commerce-mcp` version 2.3.0) to `stdout`. The server logs the mode, the `correlationId`, and the `sessionId` when running a remote server in stateful mode. You can enable logging by passing the `--logging=true` argument or setting the `LOGGING` environment variable to `true`. ```bash title="Configure the self-hosted Commerce MCP server with logging" highlightLines="12" npx -y @commercetools/commerce-mcp --tools=all --authType=client_credentials --clientId={CLIENT_ID} --clientSecret={CLIENT_SECRET} --projectKey={PROJECT_KEY} --authUrl={AUTH_URL} --apiUrl={API_URL} --remote=true --stateless=true --port=8888 --logging=true # log to stdout ``` ## Best practices ### Select specific tools For **optimal security and performance**, enable only the specific tools required for your use case. To do so, modify the `tools` argument or `TOOLS` environment variable to include only the tools you need. For example, to set up only read-only tools, use `--tools=read_all`. To specify individual tools, provide a comma-separated list, such as `--tools=read_carts,read_orders,create_products`. For a complete list of all available tools, see [Available tools](/dev-tooling/mcp/commerce-mcp.md#available-tools). ```json title="Set up the self-hosted Commerce MCP server with product-related tools only" highlightLines="8" { "commerce-mcp": { "type": "stdio", "command": "npx", "args": [ "-y", "@commercetools/commerce-mcp@latest", "--tools=create_products,read_products,update_products", "--authType=client_credentials", "--clientId={CLIENT_ID}", "--clientSecret={CLIENT_SECRET}", "--authUrl={AUTH_URL}", "--projectKey={PROJECT_KEY}", "--apiUrl={API_URL}" ] } } ``` ### Use administrator privileges only when necessary Many examples on this page use the `--isAdmin=true` argument, which provides full access to all resources, settings, and operations in your Project. This is useful during the exploration and experimentation phase, but should be **used with caution** in real-world scenarios. When administrator privileges are not necessary, omit `--isAdmin=true` so that access is restricted to only the tools specified in the `--tools` argument. ## Commerce Agent Commerce Agent allows agent frameworks to interact with your Project through function calling. It supports a subset of the commercetools API using the [TypeScript SDK](/dev-tooling/ts-sdk-getting-started.md). Commerce Agent requires Node.js 18 (or later). To get started, install `@commercetools/commerce-agent`: ```bash title="Install Commerce Agent" npm install @commercetools/commerce-agent ``` ### Usage Configure Commerce Agent with your API Client credentials. Your API Client must include scopes for each enabled action. For example, to enable `read_products`, the API Client must have the `view_products` scope. The following examples use environment variables to load the credentials, which you can [save after creating your API Client](/api/getting-started/create-api-client.md#save-the-api-client-information). The available actions are defined in `configuration.actions`. The following example configures Commerce Agent for LangChain. AI SDK by Vercel and Mastra use the same `authConfig` and `configuration` shape. ```typescript title="Configure Commerce Agent with LangChain" import { CommercetoolsCommerceAgent } from '@commercetools/commerce-agent/langchain'; const commercetoolsCommerceAgent = new CommercetoolsCommerceAgent({ authConfig: { type: 'client_credentials', clientId: process.env.CLIENT_ID!, clientSecret: process.env.CLIENT_SECRET!, projectKey: process.env.PROJECT_KEY!, authUrl: process.env.AUTH_URL!, apiUrl: process.env.API_URL!, }, configuration: { actions: { products: { read: true, create: true, update: true, }, project: { read: true, }, }, }, }); ``` #### Tools The toolkit integrates with LangChain and AI SDK by Vercel. Pass the tools to your agent executor: ```typescript title="Pass tools to an agent executor" import { AgentExecutor, createStructuredChatAgent } from 'langchain/agents'; const tools = commercetoolsCommerceAgent.getTools(); const agent = await createStructuredChatAgent({ llm, tools, prompt, }); const agentExecutor = new AgentExecutor({ agent, tools, }); ``` ### Expose your own MCP server over stdio You can also expose your own MCP server using `CommercetoolsCommerceAgent`: ```typescript title="Expose your own MCP server with stdio transport" import { CommercetoolsCommerceAgent } from '@commercetools/commerce-agent/modelcontextprotocol'; import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'; const server = new CommercetoolsCommerceAgent({ authConfig: { type: 'client_credentials', clientId: process.env.CLIENT_ID!, clientSecret: process.env.CLIENT_SECRET!, projectKey: process.env.PROJECT_KEY!, authUrl: process.env.AUTH_URL!, apiUrl: process.env.API_URL!, }, configuration: { actions: { products: { read: true, }, cart: { read: true, create: true, update: true, }, }, }, }); async function main() { const transport = new StdioServerTransport(); await server.connect(transport); console.error('My custom commercetools MCP Server running on stdio'); } main().catch((error) => { console.error('Fatal error in main():', error); process.exit(1); }); ``` ### Expose your own MCP server over Streamable HTTP (Express) Ensure you have [mcp-remote](https://github.com/geelen/mcp-remote) installed on your machine. You can run the Streamable HTTP server with Commerce Agent like an SDK and build on it. Choose one of the following two approaches. #### Option A: wrap an agent factory Provide a `server` factory function that creates a `CommercetoolsCommerceAgent` for each session, and pass it to `CommercetoolsCommerceAgentStreamable`. Use this approach when you want the agent configured per session, for example to run stateful sessions. ```typescript title="Set up a Streamable HTTP server with CommercetoolsCommerceAgent" import { CommercetoolsCommerceAgent, CommercetoolsCommerceAgentStreamable, } from '@commercetools/commerce-agent/modelcontextprotocol'; import express from 'express'; const expressApp = express(); async function agentServer(id: string) { // `id` is the session-id for`stateful` instance // that is the `stateless` option is set to false // console.log(id) -> f81d4fae-7dec-11d0-a765-00a0c91e6bf6 return CommercetoolsCommerceAgent.create({ authConfig: { type: 'client_credentials', clientId: process.env.CLIENT_ID!, clientSecret: process.env.CLIENT_SECRET!, projectKey: process.env.PROJECT_KEY!, authUrl: process.env.AUTH_URL!, apiUrl: process.env.API_URL!, }, configuration: { actions: { products: { read: true, }, cart: { read: true, create: true, update: true, }, ... }, }, }); } const serverStreamable = new CommercetoolsCommerceAgentStreamable({ stateless: false, // make the MCP server stateless/stateful streamableHttpOptions: { sessionIdGenerator: undefined, }, server: agentServer, app: expressApp, // optional express app instance }); serverStreamable.listen(8888, function () { console.log('listening on 8888'); }); ``` #### Option B: use the streamable class directly Instantiate `CommercetoolsCommerceAgentStreamable` on its own, passing `authConfig` and `configuration` inline, without a separate `CommercetoolsCommerceAgent`. ```typescript title="Set up a Streamable HTTP server with CommercetoolsCommerceAgentStreamable" import { CommercetoolsCommerceAgentStreamable } from '@commercetools/commerce-agent/modelcontextprotocol'; import express from 'express'; const expressApp = express(); const server = new CommercetoolsCommerceAgentStreamable({ authConfig: { type: 'client_credentials', clientId: process.env.CLIENT_ID!, clientSecret: process.env.CLIENT_SECRET!, projectKey: process.env.PROJECT_KEY!, authUrl: process.env.AUTH_URL!, apiUrl: process.env.API_URL!, }, configuration: { actions: { project: { read: true, }, // other tools can go here }, }, stateless: false, streamableHttpOptions: { sessionIdGenerator: undefined, }, app: expressApp, }); server.listen(8888, function () { console.log('listening on 8888'); }); ``` ### Mastra Like the `@commercetools/commerce-agent/langchain` library, the `@commercetools/commerce-agent/mastra` essentials library can execute multi-step commands or prompts. For example: ```typescript title="Run a multi-step task sequence with Mastra" require('dotenv').config(); import { Agent } from '@mastra/core/agent'; import { CommercetoolsCommerceAgent } from '@commercetools/commerce-agent/mastra'; const commercetoolsCommerceAgent = new CommercetoolsCommerceAgent({ authConfig: { type: 'client_credentials', clientId: process.env.CLIENT_ID!, clientSecret: process.env.CLIENT_SECRET!, authUrl: process.env.AUTH_URL!, projectKey: process.env.PROJECT_KEY!, apiUrl: process.env.API_URL!, }, configuration: { actions: { products: { read: true, create: true, update: true, }, 'product-type': { read: true, create: true, }, }, }, }); const agent = new Agent({ name: 'CommercetoolsAgent', instructions: 'You are a helpful agent that can manage products and product types in commercetools. Use the available tools to help users with their commerce operations.', tools: commercetoolsCommerceAgent.getTools(), }); (async () => { console.log('--- Starting Commercetools Mastra Agent Task Sequence ---'); // Task 1: List all products const task1Prompt = 'List all products in the commercetools project. This is the first step.'; console.log(`\nExecuting: ${task1Prompt}`); const result1 = await agent.step({ messages: [ { role: 'user', content: task1Prompt, }, ], }); console.log('--- Response from "List all products" ---'); console.log(result1.text); console.log( '--------------------------------------------------------------------------------' ); // Task 2: List product types const task2Prompt = `Based on the products listed above, list all product types available in the project. I need this information to select a product type for creating a product in the next step.`; console.log(`\nExecuting: ${task2Prompt}`); const result2 = await agent.step({ messages: [ ...result1.messages, { role: 'user', content: task2Prompt, }, ], }); console.log('--- Response from "List product types" (Task 2) ---'); console.log(result2.text); console.log( '--------------------------------------------------------------------------------' ); // Task 3: Create a test product const productName = `Mastra Test Product ${Math.floor(Date.now() / 1000)}`; const productKey = `MASTRA-${Math.random().toString(36).substring(2, 9).toUpperCase()}`; const productSku = `MTP-${Math.random().toString(36).substring(2, 9).toUpperCase()}`; const productSlug = productName.toLowerCase().replace(/\s+/g, '-'); const task3Prompt = `Based on the product types listed above, please create a new product with the following details: - Name: "${productName}" - Key: "${productKey}" - Slug (en): "${productSlug}" - SKU: "${productSku}" - Description: "This product was created automatically by a Mastra AI agent." - Ensure the product is published if possible during creation. Please provide the ID of this newly created product in your response.`; console.log(`\nExecuting: ${task3Prompt}`); const result3 = await agent.step({ messages: [ ...result2.messages, { role: 'user', content: task3Prompt, }, ], }); console.log('--- Response from "Create test product" (Task 3) ---'); console.log(result3.text); console.log( '--------------------------------------------------------------------------------' ); // Task 4: Update the product const updateDescription = 'This product was created and then updated automatically by a Mastra AI agent.'; const task4Prompt = `Using the ID of the product that was just created (from the previous step), please update it. Change its description to "${updateDescription}"`; console.log(`\nExecuting: ${task4Prompt}`); const result4 = await agent.step({ messages: [ ...result3.messages, { role: 'user', content: task4Prompt, }, ], }); console.log('--- Response from "Update the product" (Task 4) ---'); console.log(result4.text); console.log( '--------------------------------------------------------------------------------' ); console.log('\n--- Commercetools Mastra Agent Task Sequence Finished ---'); })().catch((error) => { console.error('An error occurred during the async execution:', error); }); ``` #### Get tools The `getTools()` method returns the list of tools available to your agent. ```typescript title="Get the available tools" const tools = commercetoolsCommerceAgent.getTools(); ``` ## Custom tools The self-managed `@commercetools/commerce-agent` library supports custom tools. The bootstrapping MCP server can pass and register a list of custom tool implementations at runtime. This is useful when the tools you need are not implemented, and gives you full control over how tools interact with the underlying LLM. ### Usage The following example registers a `Get Products in Category` tool. It returns the published Products in a Category and lets the agent optionally sort them by creation date. ```typescript title="Register custom tools at runtime" import { CommercetoolsCommerceAgent } from "@commercetools/commerce-agent/modelcontextprotocol"; import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; const server = await CommercetoolsCommerceAgent.create({ authConfig: {...}, configuration: { customTools: [ { name: "Get Products in Category", method: "get_products_in_category", description: `Returns the published Products in a Category, sorted by creation date.\n\nProvide "categoryId" to select the Category. Optionally set "sort" to "ASC" or "DESC" to order results by "createdAt". If "sort" is omitted, the newest Products are returned first.`, // Ensure the description clearly states what the tool does and which parameters it accepts. parameters: z.object({ categoryId: z .string() .describe("The ID of the Category to return Products for."), sort: z .enum(["ASC", "DESC"]) .optional() .default("DESC") .describe( 'Order the results by "createdAt". "ASC" returns the oldest Products first, "DESC" the newest.' ), }), actions: {}, execute: async ( args: { categoryId: string; sort?: "ASC" | "DESC" }, api: ApiRoot ) => { // The Query ProductProjections endpoint returns only published // Products by default, so no `staged` argument is required. const response = await api .withProjectKey({ projectKey: process.env.PROJECT_KEY! }) .productProjections() .get({ queryArgs: { where: `categories(id="${args.categoryId}")`, sort: `createdAt ${args.sort === "ASC" ? "asc" : "desc"}`, }, }) .execute(); return JSON.stringify(response.body); }, }, ... ], actions: {...}, }, }); ... ``` ## Related pages - [Area overview page with navigation](/dev-tooling.md) - [Previous page: Commerce MCP](/dev-tooling/mcp/commerce-mcp.md) - [Next page: Knowledge MCP](/dev-tooling/mcp/knowledge-mcp.md) - [Search documentation and API specs](/search.md)