You can run the self-hosted variant of Commerce MCP locally, extend it, or use it as part of agent frameworks.
Placeholder values
This page uses the following placeholders in examples. Replace these placeholders with your own values.
| 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_URL} | Your API URL | 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.
Add self-hosted Commerce MCP server to Visual Studio Code
mcp.json file in your VS Code configuration:{
"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}"
]
}
}
}
--dynamicToolLoadingThreshold=450 value is intentionally set above the total number of available tools, so all tools are loaded and 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.
Running, open the Output panel in VS Code to see any error messages.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.
Reference
Executable package
npx without installing it globally: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.
npx -y @commercetools/commerce-mcp
--tools=all
--dynamicToolLoadingThreshold=50
--clientId={CLIENT_ID}
--clientSecret={CLIENT_SECRET}
--projectKey={PROJECT_KEY}
--authUrl={AUTH_URL}
--apiUrl={API_URL}
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 to use. To include multiple tools, use a comma-separated list without spaces. |
authType | AUTH_TYPE | Required | The authentication type 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. |
clientId | CLIENT_ID | Required, if authType is client_credentials. | The {CLIENT_ID} to use for client credentials authentication. |
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 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. |
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. |
logging | LOGGING | Optional | If true, the server runs in 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
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. |
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: uses the API Client ID and secret to obtain an access token.
- 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
--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}.{
"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
--authType=auth_token argument (or type: 'auth_token' in TypeScript) and provide the existing access token using --accessToken={ACCESS_TOKEN}.{
"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: use this for development and testing locally on your machine.
- Streamable HTTP: use this for remote deployments where your MCP server is hosted on a remote machine or in the cloud.
stdio
"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.
/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.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.--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 to8080.
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}
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
8888, configure Claude Desktop with the following:{
"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:
{
"customer": {
"customerID": "12",
"firstName": "John",
"lastName": "Doe",
"isActive": true,
"isGoldCustomer": false
}
}
Customer
Customer ID: 12
First Name: John
Last Name: Doe
Is Active: Yes
Is Gold Customer: No
--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
--fieldFilteringargument. - In a custom MCP, by setting the
fieldFilteringoption when creating a Commerce Agent.
FieldFilteringManagerConfig. A custom MCP can additionally replace the filtering logic entirely.Rule configuration
FieldFilteringManagerConfig defines how fields are filtered or redacted, and applies to both the --fieldFiltering argument and the rules-based custom MCP approach.| 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. |
| 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
FieldFilteringManagerConfig JSON to the --fieldFiltering argument.For macOS or Linux, use the following:
npx -y @commercetools/commerce-mcp \
... \
--fieldFiltering="{\"jsonRedactionText\":\"CUSTOM REDACTION TEXT\",\"includes\":[{\"value\": \"token\",\"type\": \"redact\",\"caseSensitive\":true}]}"
For Windows, use the following:
npx -y @commercetools/commerce-mcp \
... \
--fieldFiltering='{"jsonRedactionText":"CUSTOM REDACTION TEXT","includes":[{"value":"token","type":"redact","caseSensitive":true}]}'
Filter fields in a custom MCP
fieldFiltering option when creating a Commerce Agent. Choose one of the following approaches:- Apply rules: provide a
FieldFilteringManagerConfigobject, the same structure used by--fieldFiltering. - Override the logic: provide an object or class that implements the FieldFilteringManager interface. This replaces the default FieldFilteringHandler behavior.
FieldFilteringManagerConfig object: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"
},
},
};
FieldFilteringManager implementation:import { Configuration } from '@commercetools/commerce-agent/modelcontextprotocol';
const configuration: Configuration = {
// ...
context: {
// ...
fieldFiltering: {
filterFields: <T>(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
@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.--logging=true argument or setting the LOGGING environment variable to true.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
tools argument or TOOLS environment variable to include only the tools you need.--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.{
"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
--isAdmin=true argument, which provides full access to all resources, settings, and operations in your Project.--isAdmin=true so that access is restricted to only the tools specified in the --tools argument.Commerce Agent
@commercetools/commerce-agent:npm install @commercetools/commerce-agent
Usage
read_products, the API Client must have the view_products scope.configuration.actions.authConfig and configuration shape.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:
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
CommercetoolsCommerceAgent: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)
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
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.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
CommercetoolsCommerceAgentStreamable on its own, passing authConfig and configuration inline, without a separate CommercetoolsCommerceAgent.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
@commercetools/commerce-agent/langchain library, the @commercetools/commerce-agent/mastra essentials library can execute multi-step commands or prompts.For example:
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
getTools() method returns the list of tools available to your agent.const tools = commercetoolsCommerceAgent.getTools();
Custom tools
@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
Get Products in Category tool. It returns the published Products in a Category and lets the agent optionally sort them by creation date.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: {...},
},
});
...