@commercetools/commerce-mcp 4.1.0 → 4.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -12,11 +12,14 @@ pnpm run build
12
12
  # link the local package
13
13
  pnpm add link:../typescript
14
14
 
15
+ # build this package: the executable is dist/cli.js
16
+ pnpm run build
17
+
15
18
  # run the server in terminal
16
- npx ts-node src/index.ts --tools=read_products --clientId=CLIENT_ID --clientSecret=CLIENT_SECRET --authUrl=AUTH_URL --projectKey=PROJECT_KEY --apiUrl=API_URL
19
+ node dist/cli.js --tools=read_products --clientId=CLIENT_ID --clientSecret=CLIENT_SECRET --authUrl=AUTH_URL --projectKey=PROJECT_KEY --apiUrl=API_URL
17
20
 
18
21
  # test using mcptools : Install mcptools from https://github.com/f/mcptools
19
- mcp call read_products --params '{"limit": 2}' npx ts-node /<absolute-path>/commerce-agent/modelcontextprotocol/src/index.ts --tools=all \
22
+ mcp call read_products --params '{"limit": 2}' node /<absolute-path>/commerce-agent/modelcontextprotocol/dist/cli.js --tools=all \
20
23
  --projectKey="PROJECT_KEY" \
21
24
  --clientSecret="CLIENT_SECRET" \
22
25
  --clientId="CLIENT_ID" \
@@ -24,11 +27,14 @@ mcp call read_products --params '{"limit": 2}' npx ts-node /<absolute-path>/comm
24
27
  --apiUrl="API_URL"
25
28
  ```
26
29
 
30
+ `src/index.ts` exports `main()` but does not call it, so running that file
31
+ directly starts nothing. `dist/cli.js` is the entry point that does.
32
+
27
33
  **_Do not commit the linked package in package.json to the repo_**
28
34
 
29
35
  ## Testing Using Claude Desktop
30
36
 
31
- NOTE: This will not work with Claude Desktop unless you uninstall node v16 from your machine!
37
+ NOTE: This package requires Node.js 20 or newer. Make sure the `node` on Claude Desktop's PATH is a v20+ install.
32
38
 
33
39
  ```bash
34
40
  # navigate to ../typescript
@@ -38,6 +44,9 @@ pnpm run build
38
44
 
39
45
  # link the local package
40
46
  pnpm add link:../typescript
47
+
48
+ # build this package: the executable is dist/cli.js
49
+ pnpm run build
41
50
  ```
42
51
 
43
52
  Configure MCP servers in Claude Desktop
@@ -46,10 +55,9 @@ Configure MCP servers in Claude Desktop
46
55
  {
47
56
  "mcpServers": {
48
57
  "commercetools": {
49
- "command": "npx",
58
+ "command": "node",
50
59
  "args": [
51
- "ts-node",
52
- "/<absolute-path>/commerce-agent/modelcontextprotocol/src/index.ts",
60
+ "/<absolute-path>/commerce-agent/modelcontextprotocol/dist/cli.js",
53
61
  "--tools=all",
54
62
  "--projectKey=PROJECT_KEY",
55
63
  "--clientSecret=CLIENT_SECRET",
@@ -76,7 +84,7 @@ Run the following command in your terminal:
76
84
 
77
85
  ```bash
78
86
  # Start MCP Inspector and server with all tools
79
- npx @modelcontextprotocol/inspector node dist/index.js --tools=all --clientId=CLIENT_ID --clientSecret=CLIENT_SECRET --projectKey=PROJECT_KEY --authUrl=AUTH_URL --apiUrl=API_URL
87
+ npx @modelcontextprotocol/inspector node dist/cli.js --tools=all --clientId=CLIENT_ID --clientSecret=CLIENT_SECRET --projectKey=PROJECT_KEY --authUrl=AUTH_URL --apiUrl=API_URL
80
88
  ```
81
89
 
82
90
  ### Instructions
package/dist/cli.js ADDED
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Executable entry point. Kept separate from `index.ts` so that module's
4
+ * exports stay importable from tests and from other packages without running
5
+ * the server as a side effect.
6
+ */
7
+ import { handleError, main } from './index.js';
8
+ main().catch((error) => {
9
+ handleError(error);
10
+ process.exitCode = 1;
11
+ });
package/dist/index.js CHANGED
@@ -1,18 +1,11 @@
1
1
  #!/usr/bin/env node
2
- "use strict";
3
- var __importDefault = (this && this.__importDefault) || function (mod) {
4
- return (mod && mod.__esModule) ? mod : { "default": mod };
5
- };
6
- Object.defineProperty(exports, "__esModule", { value: true });
7
- exports.resolveToolsForConfiguration = exports.applyResolvedToolsToConfiguration = exports.ACCEPTED_TOOLS = void 0;
8
- exports.parseArgs = parseArgs;
9
- exports.main = main;
10
- const modelcontextprotocol_1 = require("@commercetools/commerce-agent/modelcontextprotocol");
11
- const processors_1 = require("@commercetools/processors");
12
- const stdio_js_1 = require("@modelcontextprotocol/sdk/server/stdio.js");
13
- const colors_1 = __importDefault(require("colors"));
14
- const red = colors_1.default.red;
15
- const yellow = colors_1.default.yellow;
2
+ import { ACCEPTED_TOOLS, applyResolvedToolsToConfiguration, CommercetoolsCommerceAgent, CommercetoolsCommerceAgentStreamable, DEFAULT_HOST, LOOPBACK_HOSTNAMES, normalizeBindHost, resolveToolsForConfiguration, } from '@commercetools/commerce-agent/modelcontextprotocol';
3
+ import { FieldFilteringHandler, } from '@commercetools/processors';
4
+ import { StdioServerTransport } from '@modelcontextprotocol/server/stdio';
5
+ import dotenv from 'dotenv';
6
+ import colors from 'colors';
7
+ const red = colors.red;
8
+ const yellow = colors.yellow;
16
9
  const HIDDEN_ARGS = ['customerId', 'isAdmin', 'storeKey', 'businessUnitKey'];
17
10
  const PUBLIC_ARGS = [
18
11
  'tools',
@@ -32,11 +25,8 @@ const PUBLIC_ARGS = [
32
25
  'allowedOrigins',
33
26
  ];
34
27
  const ACCEPTED_ARGS = [...PUBLIC_ARGS, ...HIDDEN_ARGS];
35
- var modelcontextprotocol_2 = require("@commercetools/commerce-agent/modelcontextprotocol");
36
- Object.defineProperty(exports, "ACCEPTED_TOOLS", { enumerable: true, get: function () { return modelcontextprotocol_2.ACCEPTED_TOOLS; } });
37
- Object.defineProperty(exports, "applyResolvedToolsToConfiguration", { enumerable: true, get: function () { return modelcontextprotocol_2.applyResolvedToolsToConfiguration; } });
38
- Object.defineProperty(exports, "resolveToolsForConfiguration", { enumerable: true, get: function () { return modelcontextprotocol_2.resolveToolsForConfiguration; } });
39
- function parseArgs(args) {
28
+ export { ACCEPTED_TOOLS, applyResolvedToolsToConfiguration, resolveToolsForConfiguration, } from '@commercetools/commerce-agent/modelcontextprotocol';
29
+ export function parseArgs(args) {
40
30
  const options = {};
41
31
  const env = {};
42
32
  args.forEach((arg) => {
@@ -130,8 +120,8 @@ function parseArgs(args) {
130
120
  if (tool == 'all' || tool == 'read_all') {
131
121
  return;
132
122
  }
133
- if (!modelcontextprotocol_1.ACCEPTED_TOOLS.includes(tool.trim())) {
134
- throw new Error(`Invalid tool: ${tool}. Accepted tools are: ${modelcontextprotocol_1.ACCEPTED_TOOLS.join(', ')}`);
123
+ if (!ACCEPTED_TOOLS.includes(tool.trim())) {
124
+ throw new Error(`Invalid tool: ${tool}. Accepted tools are: ${ACCEPTED_TOOLS.join(', ')}`);
135
125
  }
136
126
  });
137
127
  // Check for commercetools env vars
@@ -148,7 +138,7 @@ function parseArgs(args) {
148
138
  env.remote = env.remote || process.env.REMOTE == 'true';
149
139
  env.logging = env.logging || process.env.LOGGING == 'true';
150
140
  env.stateless = env.stateless || process.env.STATELESS == 'true';
151
- env.host = env.host || process.env.HOST || modelcontextprotocol_1.DEFAULT_HOST;
141
+ env.host = env.host || process.env.HOST || DEFAULT_HOST;
152
142
  env.allowedHosts =
153
143
  env.allowedHosts || splitList(process.env.ALLOWED_HOSTS) || undefined;
154
144
  env.allowedOrigins =
@@ -299,7 +289,7 @@ function splitList(value) {
299
289
  * in front of a proxy, a container hostname, ...).
300
290
  */
301
291
  function resolveAllowedHosts(env) {
302
- const hosts = new Set([...modelcontextprotocol_1.LOOPBACK_HOSTNAMES, ...(env.allowedHosts ?? [])]);
292
+ const hosts = new Set([...LOOPBACK_HOSTNAMES, ...(env.allowedHosts ?? [])]);
303
293
  const host = env.host;
304
294
  if (host && !isWildcardHost(host)) {
305
295
  hosts.add(host);
@@ -333,6 +323,26 @@ function warnIfPubliclyBound(host) {
333
323
  ` Callers still need a valid "Authorization: Bearer <token>" header, but make sure the port\n` +
334
324
  ` is only exposed to networks you trust. Use --host=127.0.0.1 to keep it local.\n`));
335
325
  }
326
+ /**
327
+ * Stateful mode is on its way out (DEVX-888). The 2026-07-28 spec removes
328
+ * protocol sessions and `Mcp-Session-Id` entirely, and the v2 handler answers
329
+ * the session operations (GET/DELETE) with 405.
330
+ *
331
+ * An audit found nothing kept across calls but the transport instance and the
332
+ * token fingerprint that binds a session to its opener, so per-request auth
333
+ * already covers what sessions were carrying. Nothing changes yet — this only
334
+ * warns, because remote currently defaults to stateful and silently flipping
335
+ * that would break clients mid-release.
336
+ */
337
+ function warnIfStateful(stateless) {
338
+ if (stateless)
339
+ return;
340
+ console.error(yellow(`\n\u26a0\ufe0f Stateful session mode is deprecated and will be removed.\n` +
341
+ ` The 2026-07-28 MCP spec has no protocol sessions, so this mode cannot carry\n` +
342
+ ` forward. Nothing is stored across calls today beyond the session's own auth\n` +
343
+ ` binding, which per-request credentials already provide.\n` +
344
+ ` Pass --stateless=true to adopt the future default now.\n`));
345
+ }
336
346
  /** True once the server actually holds an address. */
337
347
  function isBound(server) {
338
348
  const address = server?.address;
@@ -343,12 +353,12 @@ function onServerError(server, listener) {
343
353
  if (typeof on === 'function')
344
354
  on.call(server, 'error', listener);
345
355
  }
346
- function handleError(error) {
356
+ export function handleError(error) {
347
357
  console.error(red('\n🚨 Error initializing commercetools MCP server:\n'));
348
358
  console.error(yellow(` ${error.message}\n`));
349
359
  }
350
- async function main() {
351
- require('dotenv').config({ quiet: true });
360
+ export async function main() {
361
+ dotenv.config({ quiet: true });
352
362
  const { options, env } = parseArgs(process.argv.slice(2));
353
363
  // Create the CommercetoolsCommerceAgent instance
354
364
  const selectedTools = options.tools;
@@ -364,34 +374,32 @@ async function main() {
364
374
  businessUnitKey: options.businessUnitKey,
365
375
  logging: options.logging,
366
376
  fieldFiltering: options.fieldFiltering
367
- ? new processors_1.FieldFilteringHandler(options.fieldFiltering)
377
+ ? new FieldFilteringHandler(options.fieldFiltering)
368
378
  : undefined,
369
379
  },
370
380
  };
371
- const resolution = (0, modelcontextprotocol_1.resolveToolsForConfiguration)(selectedTools, Boolean(options.isAdmin));
372
- (0, modelcontextprotocol_1.applyResolvedToolsToConfiguration)(configuration, resolution);
381
+ const resolution = resolveToolsForConfiguration(selectedTools, Boolean(options.isAdmin));
382
+ applyResolvedToolsToConfiguration(configuration, resolution);
373
383
  const authConfig = createAuthConfig(env);
374
384
  // eslint-disable-next-line require-await
375
385
  async function getServer() {
376
- return modelcontextprotocol_1.CommercetoolsCommerceAgent.create({
386
+ return CommercetoolsCommerceAgent.create({
377
387
  authConfig,
378
388
  configuration,
379
389
  });
380
390
  }
381
391
  if (env.remote) {
382
- const streamServer = new modelcontextprotocol_1.CommercetoolsCommerceAgentStreamable({
392
+ warnIfStateful(Boolean(env.stateless));
393
+ const streamServer = new CommercetoolsCommerceAgentStreamable({
383
394
  authConfig,
384
395
  configuration,
385
396
  stateless: env.stateless,
386
397
  allowedHosts: resolveAllowedHosts(env),
387
398
  allowedOrigins: env.allowedOrigins,
388
- streamableHttpOptions: {
389
- sessionIdGenerator: undefined,
390
- },
391
399
  });
392
400
  const port = env.port || 8080;
393
401
  // `*` and an empty value mean "every interface"; the OS needs 0.0.0.0.
394
- const host = (0, modelcontextprotocol_1.normalizeBindHost)(env.host);
402
+ const host = normalizeBindHost(env.host);
395
403
  warnIfPubliclyBound(host);
396
404
  /**
397
405
  * Kept in a holder so the listen callback can read the server without a
@@ -421,13 +429,8 @@ async function main() {
421
429
  }
422
430
  else {
423
431
  const server = await getServer();
424
- const transport = new stdio_js_1.StdioServerTransport();
432
+ const transport = new StdioServerTransport();
425
433
  await server.connect(transport);
426
434
  console.error('MCP server is running...');
427
435
  }
428
436
  }
429
- if (require.main === module) {
430
- main().catch((error) => {
431
- handleError(error);
432
- });
433
- }
package/package.json CHANGED
@@ -1,11 +1,13 @@
1
1
  {
2
2
  "name": "@commercetools/commerce-mcp",
3
- "version": "4.1.0",
3
+ "version": "4.2.0",
4
+ "type": "module",
4
5
  "mcpName": "io.github.commercetools/commerce-mcp",
5
6
  "homepage": "https://github.com/commercetools/commerce-mcp",
6
7
  "description": "A command line tool for setting up commercetools MCP server",
7
- "bin": "dist/index.js",
8
+ "bin": "dist/cli.js",
8
9
  "files": [
10
+ "dist/cli.js",
9
11
  "dist/index.js",
10
12
  "LICENSE",
11
13
  "README.md",
@@ -13,18 +15,18 @@
13
15
  "package.json"
14
16
  ],
15
17
  "engines": {
16
- "node": ">=18"
18
+ "node": ">=20"
17
19
  },
18
20
  "publishConfig": {
19
21
  "access": "public"
20
22
  },
21
23
  "dependencies": {
22
- "@modelcontextprotocol/sdk": "^1.26.0",
24
+ "@modelcontextprotocol/server": "^2",
23
25
  "colors": "^1.4.0",
24
26
  "dotenv": "^17.2.1",
25
27
  "mcp-remote": "^0.1.18",
26
- "@commercetools/commerce-agent": "4.1.0",
27
- "@commercetools/processors": "0.0.0"
28
+ "@commercetools/commerce-agent": "4.2.0",
29
+ "@commercetools/processors": "0.1.0"
28
30
  },
29
31
  "keywords": [
30
32
  "mcp",
@@ -59,8 +61,8 @@
59
61
  "prebuild": "pnpm --filter @commercetools/processors run build && pnpm --filter @commercetools/commerce-agent run build",
60
62
  "dev": "concurrently -n BUILD,SERVER -c magenta,cyan \"pnpm dev:build\" \"pnpm dev:run\"",
61
63
  "dev:build": "tsup --env.NODE_ENV dev",
62
- "dev:run": "nodemon dist/index.js --watch",
63
- "build": "tsc && node -e \"require('fs').chmodSync('dist/index.js', '755')\"",
64
+ "dev:run": "nodemon --watch dist dist/cli.js",
65
+ "build": "tsc && node --input-type=module -e \"import {chmodSync} from 'node:fs'; chmodSync('dist/cli.js', '755')\"",
64
66
  "clean": "rm -rf dist",
65
67
  "lint": "eslint \"./**/*.ts*\"",
66
68
  "prettier": "prettier './**/*.{js,ts,md,html,css}' --write",