@postman/postman-mcp-server 2.5.3 → 2.6.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
@@ -39,11 +39,18 @@ The Postman MCP Server supports the EU region for remote and local servers:
39
39
  * [**VS Code**](#install-in-visual-studio-code)
40
40
  * [**Cursor**](#install-in-cursor)
41
41
  * [**Claude Code**](#install-in-claude-code)
42
+ * [**Codex**](#install-in-codex)
43
+ * [**Windsurf**](#install-in-windsurf)
44
+ * [**Antigravity**](#install-in-antigravity)
45
+ * [**GitHub Copilot CLI**](#install-in-github-copilot-cli)
42
46
  * [**Local server**](#local-server)
43
47
  * [**VS Code**](#install-in-visual-studio-code-1)
44
48
  * [**Cursor**](#install-in-cursor-1)
45
49
  * [**Claude**](#claude-integration)
46
50
  * [**Claude Code**](#install-in-claude-code-1)
51
+ * [**Windsurf**](#install-in-windsurf-1)
52
+ * [**Antigravity**](#install-in-antigravity-1)
53
+ * [**GitHub Copilot CLI**](#install-in-github-copilot-cli-1)
47
54
  * [**Gemini CLI**](#use-as-a-gemini-cli-extension)
48
55
  * [**Docker**](#install-in-docker)
49
56
  * [**Questions and support**](#questions-and-support)
@@ -129,6 +136,150 @@ For **Full** mode:
129
136
  claude mcp add --transport http postman https://mcp.postman.com/mcp --header "Authorization: Bearer <POSTMAN_API_KEY>"
130
137
  ```
131
138
 
139
+ ### Install in Codex
140
+
141
+ To install the MCP server in Codex, run the following command in your terminal:
142
+
143
+ For **Minimal** mode:
144
+
145
+ ```bash
146
+ codex mcp add postman --env POSTMAN_API_KEY=<POSTMAN_API_KEY> -- npx @postman/postman-mcp-server --minimal
147
+ ```
148
+
149
+ For **Code** mode:
150
+
151
+ ```bash
152
+ codex mcp add postman --env POSTMAN_API_KEY=<POSTMAN_API_KEY> -- npx @postman/postman-mcp-server --code
153
+ ```
154
+
155
+ For **Full** mode:
156
+
157
+ ```bash
158
+ codex mcp add postman --env POSTMAN_API_KEY=<POSTMAN_API_KEY> -- npx @postman/postman-mcp-server --full
159
+ ```
160
+
161
+ ### Install in Windsurf
162
+
163
+ To install the MCP server in Windsurf, copy the following JSON config into the `.codeium/windsurf/mcp_config.json` file:
164
+
165
+ ```json
166
+ {
167
+ "mcpServers": {
168
+ "postman-api": {
169
+ "args": [
170
+ "mcp-remote",
171
+ "https://mcp.postman.com/mcp",
172
+ "--header",
173
+ "Authorization: Bearer XXX"
174
+ ],
175
+ "command": "npx",
176
+ "disabled": false,
177
+ "disabledTools": [],
178
+ "env": {}
179
+ },
180
+ "postman-api-code": {
181
+ "args": [
182
+ "mcp-remote",
183
+ "https://mcp.postman.com/code",
184
+ "--header",
185
+ "Authorization: Bearer XXX"
186
+ ],
187
+ "command": "npx",
188
+ "disabled": false,
189
+ "disabledTools": [],
190
+ "env": {}
191
+ },
192
+ "postman-api-minimal": {
193
+ "args": [
194
+ "mcp-remote",
195
+ "https://mcp.postman.com/minimal",
196
+ "--header",
197
+ "Authorization: Bearer XXX"
198
+ ],
199
+ "command": "npx",
200
+ "disabled": false,
201
+ "disabledTools": [],
202
+ "env": {}
203
+ }
204
+ }
205
+ }
206
+ ```
207
+
208
+ ### Install in Antigravity
209
+
210
+ To install the MCP server in Antigravity, click **Manage MCP servers > View raw config**. Then, copy the following JSON config into the `.codeium/windsurf/mcp_config.json` file:
211
+
212
+ ```json
213
+ {
214
+ "mcpServers": {
215
+ "postman-api": {
216
+ "args": [
217
+ "mcp-remote",
218
+ "https://mcp.postman.com/mcp",
219
+ "--header",
220
+ "Authorization: Bearer XXX"
221
+ ],
222
+ "command": "npx",
223
+ "disabled": false,
224
+ "disabledTools": [],
225
+ "env": {}
226
+ },
227
+ "postman-api-code": {
228
+ "args": [
229
+ "mcp-remote",
230
+ "https://mcp.postman.com/code",
231
+ "--header",
232
+ "Authorization: Bearer XXX"
233
+ ],
234
+ "command": "npx",
235
+ "disabled": false,
236
+ "disabledTools": [],
237
+ "env": {}
238
+ },
239
+ "postman-api-minimal": {
240
+ "args": [
241
+ "mcp-remote",
242
+ "https://mcp.postman.com/minimal",
243
+ "--header",
244
+ "Authorization: Bearer XXX"
245
+ ],
246
+ "command": "npx",
247
+ "disabled": false,
248
+ "disabledTools": [],
249
+ "env": {}
250
+ }
251
+ }
252
+ }
253
+ ```
254
+
255
+ ### Install in GitHub Copilot CLI
256
+
257
+ Use the Copilot CLI to interactively add the MCP server:
258
+
259
+ ```bash
260
+ /mcp add
261
+ ```
262
+
263
+ Alternatively, create or edit the configuration file `~/.copilot/mcp-config.json` and add:
264
+
265
+ ```json
266
+ {
267
+ "mcpServers": {
268
+ "postman-api-http-server": {
269
+ "type": "http",
270
+ "url": "https://mcp.postman.com/minimal",
271
+ "headers": {
272
+ "Authorization": "Bearer YOUR_API_KEY"
273
+ }
274
+ }
275
+ }
276
+ }
277
+ ```
278
+
279
+ By default, this uses **Minimal** mode. To access **Full** mode, change the `url` value to `https://mcp.postman.com/mcp`. To access **Code** mode, change the value to `https://mcp.postman.com/code`.
280
+
281
+ For more information, see the [Copilot CLI documentation](https://docs.github.com/en/copilot/concepts/agents/about-copilot-cli).
282
+
132
283
  ---
133
284
 
134
285
  ## Local server
@@ -147,7 +298,6 @@ The local server supports the following tool configurations:
147
298
 
148
299
  **Note:** Use the `--region` flag to specify the Postman API region (`us` or `eu`), or set the `POSTMAN_API_BASE_URL` environment variable directly. By default, the server uses the `us` option.
149
300
 
150
-
151
301
  ### Install in Visual Studio Code
152
302
 
153
303
  [![Install with Node in VS Code](https://img.shields.io/badge/VS_Code-Install_Server-0098FF?style=flat-square&logo=visualstudiocode&logoColor=white)](https://insiders.vscode.dev/redirect/mcp/install?name=postman-api-mcp&inputs=%5B%7B%22id%22%3A%22postman-api-key%22%2C%22type%22%3A%22promptString%22%2C%22description%22%3A%22Enter%20your%20Postman%20API%20key%22%7D%5D&config=%7B%22command%22%3A%22npx%22%2C%22args%22%3A%5B%22%40postman%2Fpostman-mcp-server%22%2C%22--full%22%5D%2C%22env%22%3A%7B%22POSTMAN_API_KEY%22%3A%22%24%7Binput%3Apostman-api-key%7D%22%7D%7D)
@@ -227,6 +377,87 @@ For **Full** mode:
227
377
  claude mcp add postman --env POSTMAN_API_KEY=YOUR_KEY -- npx @postman/postman-mcp-server@latest --full
228
378
  ```
229
379
 
380
+ ### Install in Windsurf
381
+
382
+ To manually install the MCP server in Windsurf, do the following:
383
+
384
+ 1. Click **Open MCP Marketplace** in Windsurf.
385
+ 1. Type "Postman" in the search text box to filter the marketplace results.
386
+ 1. Click **Install**.
387
+ 1. When prompted, enter a valid Postman API key.
388
+ 1. Select the tools that you want to enable, or click **All Tools** to select all available tools.
389
+ 1. Turn on **Enabled** to enable the Postman MCP server.
390
+
391
+ #### Manual installation
392
+
393
+ Copy the following JSON config into the `.codeium/windsurf/mcp_config.json` file:
394
+
395
+ ```json
396
+ {
397
+ "mcpServers": {
398
+ "postman_mcp_server_stdio": {
399
+ "args": [
400
+ "@postman/postman-mcp-server"
401
+ ],
402
+ "command": "npx",
403
+ "disabled": false,
404
+ "disabledTools": [],
405
+ "env": {
406
+ "POSTMAN_API_KEY": "XXXX"
407
+ }
408
+ }
409
+ }
410
+ }
411
+ ```
412
+
413
+ ### Install in Antigravity
414
+
415
+ To install the MCP server in Antigravity, click **Manage MCP servers > View raw config**. Then, copy the following JSON config into the `.codeium/windsurf/mcp_config.json` file:
416
+
417
+ ```json
418
+ {
419
+ "mcpServers": {
420
+ "postman_mcp_server_stdio": {
421
+ "args": [
422
+ "@postman/postman-mcp-server"
423
+ ],
424
+ "command": "npx",
425
+ "disabled": false,
426
+ "disabledTools": [],
427
+ "env": {
428
+ "POSTMAN_API_KEY": "XXXX"
429
+ }
430
+ }
431
+ }
432
+ }
433
+ ```
434
+
435
+ ### Install in GitHub Copilot CLI
436
+
437
+ Use the Copilot CLI to interactively add the MCP server:
438
+
439
+ ```bash
440
+ /mcp add
441
+ ```
442
+
443
+ Alternatively, create or edit the configuration file `~/.copilot/mcp-config.json` and add:
444
+
445
+ ```json
446
+ {
447
+ "mcpServers": {
448
+ "postman-api-mcp": {
449
+ "command": "npx",
450
+ "args": ["@postman/postman-mcp-server"],
451
+ "env": {
452
+ "POSTMAN_API_KEY": "YOUR_API_KEY"
453
+ }
454
+ }
455
+ }
456
+ }
457
+ ```
458
+
459
+ For more information, see the [Copilot CLI documentation](https://docs.github.com/en/copilot/concepts/agents/about-copilot-cli).
460
+
230
461
  ### Use as a Gemini CLI extension
231
462
 
232
463
  To install the MCP server as a Gemini CLI extension, run the following command in your terminal:
package/dist/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@postman/postman-mcp-server",
3
- "version": "2.5.3",
3
+ "version": "2.6.0",
4
4
  "description": "A simple MCP server to operate on the Postman API",
5
5
  "mcpName": "com.postman/postman-mcp-server",
6
6
  "main": "dist/src/index.js",
@@ -1,5 +1,6 @@
1
1
  import { McpError, ErrorCode } from '@modelcontextprotocol/sdk/types.js';
2
2
  import { USER_AGENT } from '../constants.js';
3
+ import { env } from '../env.js';
3
4
  export var ContentType;
4
5
  (function (ContentType) {
5
6
  ContentType["Json"] = "application/json";
@@ -10,7 +11,7 @@ export class PostmanAPIClient {
10
11
  apiKey;
11
12
  serverContext;
12
13
  static instance = null;
13
- constructor(apiKey, baseUrl = process.env.POSTMAN_API_BASE_URL || 'https://api.postman.com', serverContext) {
14
+ constructor(apiKey, baseUrl = env.POSTMAN_API_BASE_URL, serverContext) {
14
15
  this.apiKey = apiKey;
15
16
  this.baseUrl = baseUrl;
16
17
  this.serverContext = serverContext;
@@ -40,7 +41,7 @@ export class PostmanAPIClient {
40
41
  return this.request(endpoint, { ...options, method: 'DELETE' });
41
42
  }
42
43
  async request(endpoint, options) {
43
- const currentApiKey = this.apiKey || process.env.POSTMAN_API_KEY;
44
+ const currentApiKey = this.apiKey || env.POSTMAN_API_KEY;
44
45
  if (!currentApiKey) {
45
46
  throw new Error('API key is required for requests. Provide it via constructor parameter or set POSTMAN_API_KEY environment variable.');
46
47
  }
@@ -155,6 +155,7 @@ const minimal = [
155
155
  'getStatusOfAnAsyncApiTask',
156
156
  'runCollection',
157
157
  'getEnabledTools',
158
+ 'updateCollectionRequest',
158
159
  ];
159
160
  const code = [
160
161
  'getCodeGenerationInstructions',
@@ -0,0 +1,12 @@
1
+ import { z } from 'zod';
2
+ const envSchema = z.object({
3
+ POSTMAN_API_KEY: z.string(),
4
+ POSTMAN_API_BASE_URL: z.string().url().default('https://api.postman.com'),
5
+ GIT_BRANCH: z.string().default('main'),
6
+ });
7
+ const parsedEnv = envSchema.safeParse(process.env);
8
+ if (!parsedEnv.success) {
9
+ console.error('Invalid environment variables for Postman MCP server:', parsedEnv.error.format());
10
+ process.exit(1);
11
+ }
12
+ export const env = parsedEnv.data;
package/dist/src/index.js CHANGED
@@ -1,14 +1,15 @@
1
1
  #!/usr/bin/env node
2
- import dotenv from 'dotenv';
3
2
  import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
4
3
  import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
5
4
  import { ErrorCode, isInitializeRequest, McpError, } from '@modelcontextprotocol/sdk/types.js';
6
5
  import { readdir } from 'node:fs/promises';
7
6
  import { join, dirname } from 'node:path';
8
7
  import { fileURLToPath } from 'node:url';
8
+ import dotenv from 'dotenv';
9
9
  import { enabledResources } from './enabledResources.js';
10
10
  import { PostmanAPIClient } from './clients/postman.js';
11
11
  import { SERVER_NAME, APP_VERSION } from './constants.js';
12
+ import { env } from './env.js';
12
13
  const SUPPORTED_REGIONS = {
13
14
  us: 'https://api.postman.com',
14
15
  eu: 'https://api.eu.postman.com',
@@ -20,7 +21,7 @@ function setRegionEnvironment(region) {
20
21
  if (!isValidRegion(region)) {
21
22
  throw new Error(`Invalid region: ${region}. Supported regions: us, eu`);
22
23
  }
23
- process.env.POSTMAN_API_BASE_URL = SUPPORTED_REGIONS[region];
24
+ env.POSTMAN_API_BASE_URL = SUPPORTED_REGIONS[region];
24
25
  }
25
26
  function log(level, message, context) {
26
27
  const timestamp = new Date().toISOString();
@@ -42,17 +43,17 @@ function logBoth(server, level, message, context) {
42
43
  async function loadAllTools() {
43
44
  const __filename = fileURLToPath(import.meta.url);
44
45
  const __dirname = dirname(__filename);
45
- const toolsDir = join(__dirname, 'tools');
46
+ const generatedToolsDir = join(__dirname, './tools');
47
+ const isWindows = process.platform === 'win32';
48
+ const tools = [];
46
49
  try {
47
- log('info', 'Loading tools from directory', { toolsDir });
48
- const files = await readdir(toolsDir);
50
+ log('info', 'Loading tools from directory', { toolsDir: generatedToolsDir });
51
+ const files = await readdir(generatedToolsDir);
49
52
  const toolFiles = files.filter((file) => file.endsWith('.js'));
50
53
  log('debug', 'Discovered tool files', { count: toolFiles.length });
51
- const tools = [];
52
54
  for (const file of toolFiles) {
53
55
  try {
54
- const toolPath = join(toolsDir, file);
55
- const isWindows = process.platform === 'win32';
56
+ const toolPath = join(generatedToolsDir, file);
56
57
  const toolModule = await import(isWindows ? `file://${toolPath}` : toolPath);
57
58
  if (toolModule.method &&
58
59
  toolModule.description &&
@@ -72,16 +73,15 @@ async function loadAllTools() {
72
73
  });
73
74
  }
74
75
  }
75
- log('info', 'Tool loading completed', { totalLoaded: tools.length });
76
- return tools;
77
76
  }
78
77
  catch (error) {
79
78
  log('error', 'Failed to read tools directory', {
80
- toolsDir,
79
+ toolsDir: generatedToolsDir,
81
80
  error: String(error?.message || error),
82
81
  });
83
- return [];
84
82
  }
83
+ log('info', 'Tool loading completed', { totalLoaded: tools.length });
84
+ return tools;
85
85
  }
86
86
  const dotEnvOutput = dotenv.config({ quiet: true });
87
87
  if (dotEnvOutput.error) {
@@ -105,7 +105,7 @@ async function run() {
105
105
  setRegionEnvironment(region);
106
106
  log('info', `Using region: ${region}`, {
107
107
  region,
108
- baseUrl: process.env.POSTMAN_API_BASE_URL,
108
+ baseUrl: env.POSTMAN_API_BASE_URL,
109
109
  });
110
110
  }
111
111
  else {
@@ -114,7 +114,7 @@ async function run() {
114
114
  process.exit(1);
115
115
  }
116
116
  }
117
- const apiKey = process.env.POSTMAN_API_KEY;
117
+ const apiKey = env.POSTMAN_API_KEY;
118
118
  if (!apiKey) {
119
119
  log('error', 'POSTMAN_API_KEY environment variable is required for STDIO mode');
120
120
  process.exit(1);
@@ -9,9 +9,176 @@ export const parameters = z.object({
9
9
  .string()
10
10
  .describe('The folder ID in which to create the request. By default, the system will create the request at the collection level.')
11
11
  .optional(),
12
- name: z
13
- .string()
14
- .describe("The request's name. It is recommended that you pass the `name` property in the request body. If you do not, the system uses a null value. As a result, this creates a request with a blank name.")
12
+ name: z.string().describe('Name of the request').optional(),
13
+ description: z.string().nullable().optional(),
14
+ method: z
15
+ .enum([
16
+ 'GET',
17
+ 'PUT',
18
+ 'POST',
19
+ 'PATCH',
20
+ 'DELETE',
21
+ 'COPY',
22
+ 'HEAD',
23
+ 'OPTIONS',
24
+ 'LINK',
25
+ 'UNLINK',
26
+ 'PURGE',
27
+ 'LOCK',
28
+ 'UNLOCK',
29
+ 'PROPFIND',
30
+ 'VIEW',
31
+ ])
32
+ .nullable()
33
+ .optional(),
34
+ url: z.string().nullable().optional(),
35
+ headerData: z
36
+ .array(z.object({
37
+ key: z.string().optional(),
38
+ value: z.string().optional(),
39
+ description: z.string().nullable().optional(),
40
+ }))
41
+ .optional(),
42
+ queryParams: z
43
+ .array(z.object({
44
+ key: z.string().optional(),
45
+ value: z.string().optional(),
46
+ description: z.string().nullable().optional(),
47
+ enabled: z.boolean().optional(),
48
+ }))
49
+ .optional(),
50
+ dataMode: z.enum(['raw', 'urlencoded', 'formdata', 'binary', 'graphql']).nullable().optional(),
51
+ data: z
52
+ .array(z.object({
53
+ key: z.string().optional(),
54
+ value: z.string().optional(),
55
+ description: z.string().nullable().optional(),
56
+ enabled: z.boolean().optional(),
57
+ type: z.enum(['text', 'file']).optional(),
58
+ uuid: z.string().optional(),
59
+ }))
60
+ .nullable()
61
+ .optional(),
62
+ rawModeData: z.string().nullable().optional(),
63
+ graphqlModeData: z
64
+ .object({ query: z.string().optional(), variables: z.string().optional() })
65
+ .nullable()
66
+ .optional(),
67
+ dataOptions: z
68
+ .object({
69
+ raw: z.record(z.string(), z.unknown()).optional(),
70
+ urlencoded: z.record(z.string(), z.unknown()).optional(),
71
+ params: z.record(z.string(), z.unknown()).optional(),
72
+ binary: z.record(z.string(), z.unknown()).optional(),
73
+ graphql: z.record(z.string(), z.unknown()).optional(),
74
+ })
75
+ .nullable()
76
+ .optional(),
77
+ auth: z
78
+ .object({
79
+ type: z
80
+ .enum([
81
+ 'noauth',
82
+ 'basic',
83
+ 'bearer',
84
+ 'apikey',
85
+ 'digest',
86
+ 'oauth1',
87
+ 'oauth2',
88
+ 'hawk',
89
+ 'awsv4',
90
+ 'ntlm',
91
+ 'edgegrid',
92
+ 'jwt',
93
+ 'asap',
94
+ ])
95
+ .optional(),
96
+ apikey: z
97
+ .array(z.object({
98
+ key: z.string().optional(),
99
+ value: z.unknown().optional(),
100
+ type: z.enum(['string', 'boolean', 'number', 'array', 'object', 'any']).optional(),
101
+ }))
102
+ .optional(),
103
+ bearer: z
104
+ .array(z.object({
105
+ key: z.string().optional(),
106
+ value: z.unknown().optional(),
107
+ type: z.enum(['string', 'boolean', 'number', 'array', 'object', 'any']).optional(),
108
+ }))
109
+ .optional(),
110
+ basic: z
111
+ .array(z.object({
112
+ key: z.string().optional(),
113
+ value: z.unknown().optional(),
114
+ type: z.enum(['string', 'boolean', 'number', 'array', 'object', 'any']).optional(),
115
+ }))
116
+ .optional(),
117
+ digest: z
118
+ .array(z.object({
119
+ key: z.string().optional(),
120
+ value: z.unknown().optional(),
121
+ type: z.enum(['string', 'boolean', 'number', 'array', 'object', 'any']).optional(),
122
+ }))
123
+ .optional(),
124
+ oauth1: z
125
+ .array(z.object({
126
+ key: z.string().optional(),
127
+ value: z.unknown().optional(),
128
+ type: z.enum(['string', 'boolean', 'number', 'array', 'object', 'any']).optional(),
129
+ }))
130
+ .optional(),
131
+ oauth2: z
132
+ .array(z.object({
133
+ key: z.string().optional(),
134
+ value: z.unknown().optional(),
135
+ type: z.enum(['string', 'boolean', 'number', 'array', 'object', 'any']).optional(),
136
+ }))
137
+ .optional(),
138
+ hawk: z
139
+ .array(z.object({
140
+ key: z.string().optional(),
141
+ value: z.unknown().optional(),
142
+ type: z.enum(['string', 'boolean', 'number', 'array', 'object', 'any']).optional(),
143
+ }))
144
+ .optional(),
145
+ awsv4: z
146
+ .array(z.object({
147
+ key: z.string().optional(),
148
+ value: z.unknown().optional(),
149
+ type: z.enum(['string', 'boolean', 'number', 'array', 'object', 'any']).optional(),
150
+ }))
151
+ .optional(),
152
+ ntlm: z
153
+ .array(z.object({
154
+ key: z.string().optional(),
155
+ value: z.unknown().optional(),
156
+ type: z.enum(['string', 'boolean', 'number', 'array', 'object', 'any']).optional(),
157
+ }))
158
+ .optional(),
159
+ edgegrid: z
160
+ .array(z.object({
161
+ key: z.string().optional(),
162
+ value: z.unknown().optional(),
163
+ type: z.enum(['string', 'boolean', 'number', 'array', 'object', 'any']).optional(),
164
+ }))
165
+ .optional(),
166
+ jwt: z
167
+ .array(z.object({
168
+ key: z.string().optional(),
169
+ value: z.unknown().optional(),
170
+ type: z.enum(['string', 'boolean', 'number', 'array', 'object', 'any']).optional(),
171
+ }))
172
+ .optional(),
173
+ asap: z
174
+ .array(z.object({
175
+ key: z.string().optional(),
176
+ value: z.unknown().optional(),
177
+ type: z.enum(['string', 'boolean', 'number', 'array', 'object', 'any']).optional(),
178
+ }))
179
+ .optional(),
180
+ })
181
+ .nullable()
15
182
  .optional(),
16
183
  });
17
184
  export const annotations = {
@@ -30,6 +197,28 @@ export async function handler(args, extra) {
30
197
  const bodyPayload = {};
31
198
  if (args.name !== undefined)
32
199
  bodyPayload.name = args.name;
200
+ if (args.description !== undefined)
201
+ bodyPayload.description = args.description;
202
+ if (args.method !== undefined)
203
+ bodyPayload.method = args.method;
204
+ if (args.url !== undefined)
205
+ bodyPayload.url = args.url;
206
+ if (args.headerData !== undefined)
207
+ bodyPayload.headerData = args.headerData;
208
+ if (args.queryParams !== undefined)
209
+ bodyPayload.queryParams = args.queryParams;
210
+ if (args.dataMode !== undefined)
211
+ bodyPayload.dataMode = args.dataMode;
212
+ if (args.data !== undefined)
213
+ bodyPayload.data = args.data;
214
+ if (args.rawModeData !== undefined)
215
+ bodyPayload.rawModeData = args.rawModeData;
216
+ if (args.graphqlModeData !== undefined)
217
+ bodyPayload.graphqlModeData = args.graphqlModeData;
218
+ if (args.dataOptions !== undefined)
219
+ bodyPayload.dataOptions = args.dataOptions;
220
+ if (args.auth !== undefined)
221
+ bodyPayload.auth = args.auth;
33
222
  const options = {
34
223
  body: JSON.stringify(bodyPayload),
35
224
  contentType: ContentType.Json,
@@ -2,11 +2,13 @@ import { z } from 'zod';
2
2
  import { ContentType } from '../clients/postman.js';
3
3
  import { asMcpError, McpError } from './utils/toolHelpers.js';
4
4
  export const method = 'createSpec';
5
- export const description = "Creates an API specification in Postman's [Spec Hub](https://learning.postman.com/docs/design-apis/specifications/overview/). Specifications can be single or multi-file.\n\n**Note:**\n- Postman supports OpenAPI 3.0 and AsyncAPI 2.0 specifications.\n- If the file path contains a \\`/\\` (forward slash) character, then a folder is created. For example, if the path is the \\`components/schemas.json\\` value, then a \\`components\\` folder is created with the \\`schemas.json\\` file inside.\n- Multi-file specifications can only have one root file.\n- Files cannot exceed a maximum of 10 MB in size.\n";
5
+ export const description = "Creates an API specification in Postman's [Spec Hub](https://learning.postman.com/docs/design-apis/specifications/overview/). Specifications can be single or multi-file.\n\n**Note:**\n- Postman supports OpenAPI 2.0, OpenAPI 3.0, OpenAPI 3.1, and AsyncAPI 2.0 specifications.\n- If the file path contains a \\`/\\` (forward slash) character, then a folder is created. For example, if the path is the \\`components/schemas.json\\` value, then a \\`components\\` folder is created with the \\`schemas.json\\` file inside.\n- Multi-file specifications can only have one root file.\n- Files cannot exceed a maximum of 10 MB in size.\n";
6
6
  export const parameters = z.object({
7
7
  workspaceId: z.string().describe("The workspace's ID."),
8
8
  name: z.string().describe("The specification's name."),
9
- type: z.enum(['OPENAPI:3.0', 'ASYNCAPI:2.0']).describe("The specification's type."),
9
+ type: z
10
+ .enum(['OPENAPI:2.0', 'OPENAPI:3.0', 'OPENAPI:3.1', 'ASYNCAPI:2.0'])
11
+ .describe("The specification's type."),
10
12
  files: z
11
13
  .array(z.union([
12
14
  z.object({
@@ -24,7 +26,7 @@ export const parameters = z.object({
24
26
  .describe("A list of the specification's files and their contents."),
25
27
  });
26
28
  export const annotations = {
27
- title: "Creates an API specification in Postman's [Spec Hub](https://learning.postman.com/docs/design-apis/specifications/overview/). Specifications can be single or multi-file.\n\n**Note:**\n- Postman supports OpenAPI 3.0 and AsyncAPI 2.0 specifications.\n- If the file path contains a \\`/\\` (forward slash) character, then a folder is created. For example, if the path is the \\`components/schemas.json\\` value, then a \\`components\\` folder is created with the \\`schemas.json\\` file inside.\n- Multi-file specifications can only have one root file.\n- Files cannot exceed a maximum of 10 MB in size.\n",
29
+ title: "Creates an API specification in Postman's [Spec Hub](https://learning.postman.com/docs/design-apis/specifications/overview/). Specifications can be single or multi-file.\n\n**Note:**\n- Postman supports OpenAPI 2.0, OpenAPI 3.0, OpenAPI 3.1, and AsyncAPI 2.0 specifications.\n- If the file path contains a \\`/\\` (forward slash) character, then a folder is created. For example, if the path is the \\`components/schemas.json\\` value, then a \\`components\\` folder is created with the \\`schemas.json\\` file inside.\n- Multi-file specifications can only have one root file.\n- Files cannot exceed a maximum of 10 MB in size.\n",
28
30
  readOnlyHint: false,
29
31
  destructiveHint: false,
30
32
  idempotentHint: false,
@@ -22,6 +22,7 @@ export const parameters = z.object({
22
22
  limit: z
23
23
  .number()
24
24
  .int()
25
+ .lte(25)
25
26
  .describe('The maximum number of rows to return in the response, up to a maximum value of 25. Any value greater than 25 returns a 400 Bad Request response.')
26
27
  .default(25),
27
28
  });
@@ -1,7 +1,7 @@
1
1
  import { z } from 'zod';
2
2
  import { asMcpError, McpError } from './utils/toolHelpers.js';
3
3
  export const method = 'getWorkspace';
4
- export const description = "Gets information about a workspace.\n\n**Note:**\n\nThis endpoint's response contains the \\`visibility\\` field. [Visibility](https://learning.postman.com/docs/collaborating-in-postman/using-workspaces/managing-workspaces/#changing-workspace-visibility) determines who can access the workspace:\n- \\`personal\\` — Only you can access the workspace.\n- \\`team\\` — All team members can access the workspace.\n- \\`private\\` — Only invited team members can access the workspace ([**Professional** and **Enterprise** plans only](https://www.postman.com/pricing)).\n- \\`public\\` — Everyone can access the workspace.\n- \\`partner\\` — Only invited team members and [partners](https://learning.postman.com/docs/collaborating-in-postman/using-workspaces/partner-workspaces/) can access the workspace ([**Professional** and **Enterprise** plans only](https://www.postman.com/pricing)).\n\n### Important\n\nWe have deprecated the \\`name\\` and \\`uid\\` responses in the following array of objects:\n- \\`collections\\`\n- \\`environments\\`\n- \\`mocks\\`\n- \\`monitors\\`\n- \\`apis\\`\n";
4
+ export const description = "Gets information about a workspace.\n\n**Note:**\n\nThis endpoint's response contains the \\`visibility\\` field. [Visibility](https://learning.postman.com/docs/collaborating-in-postman/using-workspaces/managing-workspaces/#changing-workspace-visibility) determines who can access the workspace:\n- \\`personal\\` — Only you can access the workspace.\n- \\`team\\` — All team members can access the workspace.\n- \\`private\\` — Only invited team members can access the workspace ([**Professional** and **Enterprise** plans only](https://www.postman.com/pricing)).\n- \\`public\\` — Everyone can access the workspace.\n- \\`partner\\` — Only invited team members and [partners](https://learning.postman.com/docs/collaborating-in-postman/using-workspaces/partner-workspaces/) can access the workspace ([**Professional** and **Enterprise** plans only](https://www.postman.com/pricing)).\n";
5
5
  export const parameters = z.object({
6
6
  workspaceId: z.string().describe("The workspace's ID."),
7
7
  include: z
@@ -10,7 +10,7 @@ export const parameters = z.object({
10
10
  .optional(),
11
11
  });
12
12
  export const annotations = {
13
- title: "Gets information about a workspace.\n\n**Note:**\n\nThis endpoint's response contains the \\`visibility\\` field. [Visibility](https://learning.postman.com/docs/collaborating-in-postman/using-workspaces/managing-workspaces/#changing-workspace-visibility) determines who can access the workspace:\n- \\`personal\\` — Only you can access the workspace.\n- \\`team\\` — All team members can access the workspace.\n- \\`private\\` — Only invited team members can access the workspace ([**Professional** and **Enterprise** plans only](https://www.postman.com/pricing)).\n- \\`public\\` — Everyone can access the workspace.\n- \\`partner\\` — Only invited team members and [partners](https://learning.postman.com/docs/collaborating-in-postman/using-workspaces/partner-workspaces/) can access the workspace ([**Professional** and **Enterprise** plans only](https://www.postman.com/pricing)).\n\n### Important\n\nWe have deprecated the \\`name\\` and \\`uid\\` responses in the following array of objects:\n- \\`collections\\`\n- \\`environments\\`\n- \\`mocks\\`\n- \\`monitors\\`\n- \\`apis\\`\n",
13
+ title: "Gets information about a workspace.\n\n**Note:**\n\nThis endpoint's response contains the \\`visibility\\` field. [Visibility](https://learning.postman.com/docs/collaborating-in-postman/using-workspaces/managing-workspaces/#changing-workspace-visibility) determines who can access the workspace:\n- \\`personal\\` — Only you can access the workspace.\n- \\`team\\` — All team members can access the workspace.\n- \\`private\\` — Only invited team members can access the workspace ([**Professional** and **Enterprise** plans only](https://www.postman.com/pricing)).\n- \\`public\\` — Everyone can access the workspace.\n- \\`partner\\` — Only invited team members and [partners](https://learning.postman.com/docs/collaborating-in-postman/using-workspaces/partner-workspaces/) can access the workspace ([**Professional** and **Enterprise** plans only](https://www.postman.com/pricing)).\n",
14
14
  readOnlyHint: true,
15
15
  destructiveHint: false,
16
16
  idempotentHint: true,
@@ -1,4 +1,5 @@
1
1
  import { RUNNER_ACCEPT_HEADER } from '../../constants.js';
2
+ import { env } from '../../env.js';
2
3
  import { v4 as uuidv4 } from 'uuid';
3
4
  export function buildTelemetryPayload(collectionId, collectionName, result) {
4
5
  const durationMs = result.durationMs;
@@ -98,7 +99,7 @@ export function buildTelemetryPayload(collectionId, collectionName, result) {
98
99
  startedAt: result.startTime,
99
100
  createdAt: result.endTime,
100
101
  branchSource: 'local',
101
- branch: process.env.GIT_BRANCH || 'main',
102
+ branch: env.GIT_BRANCH,
102
103
  },
103
104
  runOverview: {
104
105
  collectionName: collectionName,
@@ -2,11 +2,11 @@ import { z } from 'zod';
2
2
  import { ContentType } from '../clients/postman.js';
3
3
  import { asMcpError, McpError } from './utils/toolHelpers.js';
4
4
  export const method = 'updateCollectionRequest';
5
- export const description = 'Updates a request in a collection. For a complete list of properties, refer to the **Request** entry in the [Postman Collection Format documentation](https://schema.postman.com/collection/json/v2.1.0/draft-07/docs/index.html).\n\n**Note:**\n\n- You must pass a collection ID (\\`12ece9e1-2abf-4edc-8e34-de66e74114d2\\`), not a collection(\\`12345678-12ece9e1-2abf-4edc-8e34-de66e74114d2\\`), in this endpoint.\n- This endpoint does not support changing the folder of a request.\n';
5
+ export const description = 'Updates a request in a collection. For a complete list of properties, refer to the **Request** entry in the [Postman Collection Format documentation](https://schema.postman.com/collection/json/v2.1.0/draft-07/docs/index.html).\n\n**Note:**\n\n- You must pass a collection ID (\\`12ece9e1-2abf-4edc-8e34-de66e74114d2\\`), not a collection(\\`12345678-12ece9e1-2abf-4edc-8e34-de66e74114d2\\`), in this endpoint.\n- This endpoint does not support changing the folder of a request.\n- This endpoint acts like a PATCH method. It only updates the values that you pass in the request body.';
6
6
  export const parameters = z.object({
7
7
  requestId: z.string().describe("The request's ID."),
8
8
  collectionId: z.string().describe("The collection's ID."),
9
- name: z.string().describe("The request's name.").optional(),
9
+ name: z.string().describe('Name of the request. Only provided fields are updated.').optional(),
10
10
  method: z
11
11
  .enum([
12
12
  'GET',
@@ -25,11 +25,162 @@ export const parameters = z.object({
25
25
  'PROPFIND',
26
26
  'VIEW',
27
27
  ])
28
+ .nullable()
28
29
  .describe("The request's method.")
29
30
  .optional(),
31
+ description: z.string().nullable().optional(),
32
+ url: z.string().nullable().optional(),
33
+ headerData: z
34
+ .array(z.object({
35
+ key: z.string().optional(),
36
+ value: z.string().optional(),
37
+ description: z.string().nullable().optional(),
38
+ }))
39
+ .optional(),
40
+ queryParams: z
41
+ .array(z.object({
42
+ key: z.string().optional(),
43
+ value: z.string().optional(),
44
+ description: z.string().nullable().optional(),
45
+ enabled: z.boolean().optional(),
46
+ }))
47
+ .optional(),
48
+ dataMode: z.enum(['raw', 'urlencoded', 'formdata', 'binary', 'graphql']).nullable().optional(),
49
+ data: z
50
+ .array(z.object({
51
+ key: z.string().optional(),
52
+ value: z.string().optional(),
53
+ description: z.string().nullable().optional(),
54
+ enabled: z.boolean().optional(),
55
+ type: z.enum(['text', 'file']).optional(),
56
+ uuid: z.string().optional(),
57
+ }))
58
+ .nullable()
59
+ .optional(),
60
+ rawModeData: z.string().nullable().optional(),
61
+ graphqlModeData: z
62
+ .object({ query: z.string().optional(), variables: z.string().optional() })
63
+ .nullable()
64
+ .optional(),
65
+ dataOptions: z
66
+ .object({
67
+ raw: z.record(z.string(), z.unknown()).optional(),
68
+ urlencoded: z.record(z.string(), z.unknown()).optional(),
69
+ params: z.record(z.string(), z.unknown()).optional(),
70
+ binary: z.record(z.string(), z.unknown()).optional(),
71
+ graphql: z.record(z.string(), z.unknown()).optional(),
72
+ })
73
+ .nullable()
74
+ .optional(),
75
+ auth: z
76
+ .object({
77
+ type: z
78
+ .enum([
79
+ 'noauth',
80
+ 'basic',
81
+ 'bearer',
82
+ 'apikey',
83
+ 'digest',
84
+ 'oauth1',
85
+ 'oauth2',
86
+ 'hawk',
87
+ 'awsv4',
88
+ 'ntlm',
89
+ 'edgegrid',
90
+ 'jwt',
91
+ 'asap',
92
+ ])
93
+ .optional(),
94
+ apikey: z
95
+ .array(z.object({
96
+ key: z.string().optional(),
97
+ value: z.unknown().optional(),
98
+ type: z.enum(['string', 'boolean', 'number', 'array', 'object', 'any']).optional(),
99
+ }))
100
+ .optional(),
101
+ bearer: z
102
+ .array(z.object({
103
+ key: z.string().optional(),
104
+ value: z.unknown().optional(),
105
+ type: z.enum(['string', 'boolean', 'number', 'array', 'object', 'any']).optional(),
106
+ }))
107
+ .optional(),
108
+ basic: z
109
+ .array(z.object({
110
+ key: z.string().optional(),
111
+ value: z.unknown().optional(),
112
+ type: z.enum(['string', 'boolean', 'number', 'array', 'object', 'any']).optional(),
113
+ }))
114
+ .optional(),
115
+ digest: z
116
+ .array(z.object({
117
+ key: z.string().optional(),
118
+ value: z.unknown().optional(),
119
+ type: z.enum(['string', 'boolean', 'number', 'array', 'object', 'any']).optional(),
120
+ }))
121
+ .optional(),
122
+ oauth1: z
123
+ .array(z.object({
124
+ key: z.string().optional(),
125
+ value: z.unknown().optional(),
126
+ type: z.enum(['string', 'boolean', 'number', 'array', 'object', 'any']).optional(),
127
+ }))
128
+ .optional(),
129
+ oauth2: z
130
+ .array(z.object({
131
+ key: z.string().optional(),
132
+ value: z.unknown().optional(),
133
+ type: z.enum(['string', 'boolean', 'number', 'array', 'object', 'any']).optional(),
134
+ }))
135
+ .optional(),
136
+ hawk: z
137
+ .array(z.object({
138
+ key: z.string().optional(),
139
+ value: z.unknown().optional(),
140
+ type: z.enum(['string', 'boolean', 'number', 'array', 'object', 'any']).optional(),
141
+ }))
142
+ .optional(),
143
+ awsv4: z
144
+ .array(z.object({
145
+ key: z.string().optional(),
146
+ value: z.unknown().optional(),
147
+ type: z.enum(['string', 'boolean', 'number', 'array', 'object', 'any']).optional(),
148
+ }))
149
+ .optional(),
150
+ ntlm: z
151
+ .array(z.object({
152
+ key: z.string().optional(),
153
+ value: z.unknown().optional(),
154
+ type: z.enum(['string', 'boolean', 'number', 'array', 'object', 'any']).optional(),
155
+ }))
156
+ .optional(),
157
+ edgegrid: z
158
+ .array(z.object({
159
+ key: z.string().optional(),
160
+ value: z.unknown().optional(),
161
+ type: z.enum(['string', 'boolean', 'number', 'array', 'object', 'any']).optional(),
162
+ }))
163
+ .optional(),
164
+ jwt: z
165
+ .array(z.object({
166
+ key: z.string().optional(),
167
+ value: z.unknown().optional(),
168
+ type: z.enum(['string', 'boolean', 'number', 'array', 'object', 'any']).optional(),
169
+ }))
170
+ .optional(),
171
+ asap: z
172
+ .array(z.object({
173
+ key: z.string().optional(),
174
+ value: z.unknown().optional(),
175
+ type: z.enum(['string', 'boolean', 'number', 'array', 'object', 'any']).optional(),
176
+ }))
177
+ .optional(),
178
+ })
179
+ .nullable()
180
+ .optional(),
30
181
  });
31
182
  export const annotations = {
32
- title: 'Updates a request in a collection. For a complete list of properties, refer to the **Request** entry in the [Postman Collection Format documentation](https://schema.postman.com/collection/json/v2.1.0/draft-07/docs/index.html).\n\n**Note:**\n\n- You must pass a collection ID (\\`12ece9e1-2abf-4edc-8e34-de66e74114d2\\`), not a collection(\\`12345678-12ece9e1-2abf-4edc-8e34-de66e74114d2\\`), in this endpoint.\n- This endpoint does not support changing the folder of a request.\n',
183
+ title: 'Updates a request in a collection. For a complete list of properties, refer to the **Request** entry in the [Postman Collection Format documentation](https://schema.postman.com/collection/json/v2.1.0/draft-07/docs/index.html).\n\n**Note:**\n\n- You must pass a collection ID (\\`12ece9e1-2abf-4edc-8e34-de66e74114d2\\`), not a collection(\\`12345678-12ece9e1-2abf-4edc-8e34-de66e74114d2\\`), in this endpoint.\n- This endpoint does not support changing the folder of a request.\n- This endpoint acts like a PATCH method. It only updates the values that you pass in the request body.',
33
184
  readOnlyHint: false,
34
185
  destructiveHint: false,
35
186
  idempotentHint: true,
@@ -44,6 +195,26 @@ export async function handler(args, extra) {
44
195
  bodyPayload.name = args.name;
45
196
  if (args.method !== undefined)
46
197
  bodyPayload.method = args.method;
198
+ if (args.description !== undefined)
199
+ bodyPayload.description = args.description;
200
+ if (args.url !== undefined)
201
+ bodyPayload.url = args.url;
202
+ if (args.headerData !== undefined)
203
+ bodyPayload.headerData = args.headerData;
204
+ if (args.queryParams !== undefined)
205
+ bodyPayload.queryParams = args.queryParams;
206
+ if (args.dataMode !== undefined)
207
+ bodyPayload.dataMode = args.dataMode;
208
+ if (args.data !== undefined)
209
+ bodyPayload.data = args.data;
210
+ if (args.rawModeData !== undefined)
211
+ bodyPayload.rawModeData = args.rawModeData;
212
+ if (args.graphqlModeData !== undefined)
213
+ bodyPayload.graphqlModeData = args.graphqlModeData;
214
+ if (args.dataOptions !== undefined)
215
+ bodyPayload.dataOptions = args.dataOptions;
216
+ if (args.auth !== undefined)
217
+ bodyPayload.auth = args.auth;
47
218
  const options = {
48
219
  body: JSON.stringify(bodyPayload),
49
220
  contentType: ContentType.Json,
@@ -15,6 +15,9 @@ export const parameters = z.object({
15
15
  .describe('If true, the mock server is set private. By default, mock servers are public and can receive requests from anyone and anywhere.')
16
16
  .default(false),
17
17
  versionTag: z.string().describe("The API's version tag ID.").optional(),
18
+ collection: z
19
+ .string()
20
+ .describe("The associated collection's unique ID. This is a mandatory parameter."),
18
21
  config: z
19
22
  .object({
20
23
  serverResponseId: z
@@ -25,9 +28,6 @@ export const parameters = z.object({
25
28
  })
26
29
  .describe("The mock server's configuration settings.")
27
30
  .optional(),
28
- collection: z
29
- .string()
30
- .describe("The associated collection's unique ID. This is a mandatory parameter."),
31
31
  })
32
32
  .optional(),
33
33
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@postman/postman-mcp-server",
3
- "version": "2.5.3",
3
+ "version": "2.6.0",
4
4
  "description": "A simple MCP server to operate on the Postman API",
5
5
  "mcpName": "com.postman/postman-mcp-server",
6
6
  "main": "dist/src/index.js",
@@ -1,101 +0,0 @@
1
- import { z } from 'zod';
2
- import { asMcpError, McpError } from './utils/toolHelpers.js';
3
- export const method = 'getCollectionMap';
4
- export const description = `Get a Postman collection map with metadata and a complete recursive index of all folders and requests. Response includes collection metadata and description. Response includes itemRefs property (name and id only) instead of the full item array. After calling, present the collection summary and ask the user where they\'d like to explore next, calling getCollectionFolder and/or getCollectionRequest tools in parallel to get more data quickly.
5
- Once you've called this tool, DO NOT call searchPostmanElements to find items in or related to this collection. Instead, use the map in itemRefs.
6
- Only use searchPostmanElements to find the collection where a request may be. Then, stay in the collection and don't use the search.
7
- When using the getCollectionRequest tool to look up request data, omit the populate parameter to avoid getting all response examples
8
- back at once (can be very large). Instead, use the response ids from the return value and call getCollectionResponse for each one.
9
- Prepend the collection's ownerId to the front of each response id when passing it to getCollectionResponse. This is the first part of the collection uid.
10
- Infer the response schema from that information and remember it. Omit the raw response examples from the conversation going forward.`;
11
- export const parameters = z.object({
12
- collectionId: z
13
- .string()
14
- .describe('The collection ID must be in the form <OWNER_ID>-<UUID> (e.g. 12345-33823532ab9e41c9b6fd12d0fd459b8b).'),
15
- access_key: z
16
- .string()
17
- .describe("A collection's read-only access key. Using this query parameter does not require an API key to call the endpoint.")
18
- .optional(),
19
- });
20
- export const annotations = {
21
- title: 'Get Postman Collection Map',
22
- readOnlyHint: true,
23
- destructiveHint: false,
24
- idempotentHint: true,
25
- };
26
- function buildItemRefs(items) {
27
- if (!Array.isArray(items) || items.length === 0) {
28
- return undefined;
29
- }
30
- return items.map((item) => {
31
- const itemId = item.uid || item.id || '';
32
- const itemRef = {
33
- name: item.name || '',
34
- id: itemId,
35
- };
36
- if (item.item && Array.isArray(item.item)) {
37
- const nestedRefs = buildItemRefs(item.item);
38
- if (nestedRefs) {
39
- itemRef.itemRefs = nestedRefs;
40
- }
41
- }
42
- return itemRef;
43
- });
44
- }
45
- export async function handler(args, extra) {
46
- try {
47
- const endpoint = `/collections/${args.collectionId}`;
48
- const query = new URLSearchParams();
49
- if (args.access_key !== undefined)
50
- query.set('access_key', String(args.access_key));
51
- const url = query.toString() ? `${endpoint}?${query.toString()}` : endpoint;
52
- const options = {
53
- headers: extra.headers,
54
- };
55
- const result = await extra.client.get(url, options);
56
- if (typeof result === 'string') {
57
- return {
58
- content: [
59
- {
60
- type: 'text',
61
- text: result,
62
- },
63
- ],
64
- };
65
- }
66
- const response = result;
67
- if (response.collection) {
68
- const { item, ...collectionWithoutItems } = response.collection;
69
- const itemRefs = buildItemRefs(item);
70
- const processedResponse = {
71
- ...response,
72
- collection: {
73
- ...collectionWithoutItems,
74
- ...(itemRefs && { itemRefs }),
75
- },
76
- };
77
- return {
78
- content: [
79
- {
80
- type: 'text',
81
- text: JSON.stringify(processedResponse, null, 2),
82
- },
83
- ],
84
- };
85
- }
86
- return {
87
- content: [
88
- {
89
- type: 'text',
90
- text: JSON.stringify(result, null, 2),
91
- },
92
- ],
93
- };
94
- }
95
- catch (e) {
96
- if (e instanceof McpError) {
97
- throw e;
98
- }
99
- throw asMcpError(e);
100
- }
101
- }