@postman/postman-mcp-server 2.8.2 → 2.8.3-beta.2

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
@@ -283,7 +283,7 @@ This configuration uses the remote server (`https://mcp.postman.com`), which aut
283
283
 
284
284
  ### Install in Antigravity
285
285
 
286
- 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.
286
+ To install the MCP server in Antigravity, click **Manage MCP servers > View raw config**. Then, copy the following JSON config into the `mcp_config.json` file.
287
287
 
288
288
  This configuration uses the remote server (`https://mcp.postman.com`), which authenticates automatically with OAuth.
289
289
 
package/dist/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@postman/postman-mcp-server",
3
- "version": "2.8.2",
3
+ "version": "2.8.3-beta.2",
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",
package/dist/src/index.js CHANGED
@@ -2,7 +2,7 @@
2
2
  import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
3
3
  import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
4
4
  import { ErrorCode, isInitializeRequest, McpError, } from '@modelcontextprotocol/sdk/types.js';
5
- import { readdir } from 'node:fs/promises';
5
+ import { readdir, readFile } from 'node:fs/promises';
6
6
  import { join, dirname } from 'node:path';
7
7
  import { fileURLToPath } from 'node:url';
8
8
  import dotenv from 'dotenv';
@@ -131,7 +131,24 @@ async function run() {
131
131
  const minimalTools = allGeneratedTools.filter((t) => enabledResources.minimal.includes(t.method));
132
132
  const codeTools = allGeneratedTools.filter((t) => enabledResources.code.includes(t.method));
133
133
  const tools = useCode ? codeTools : useFull ? fullTools : minimalTools;
134
- const server = new McpServer({ name: SERVER_NAME, version: APP_VERSION });
134
+ const __filename = fileURLToPath(import.meta.url);
135
+ const __dirname = dirname(__filename);
136
+ let instructionsContent;
137
+ try {
138
+ const resourcesDir = join(__dirname, './resources');
139
+ instructionsContent = await readFile(join(resourcesDir, 'Instructions.md'), 'utf-8');
140
+ log('info', 'Loaded Instructions.md resource');
141
+ }
142
+ catch (error) {
143
+ log('warn', 'Failed to load Instructions.md resource', {
144
+ error: String(error?.message || error),
145
+ });
146
+ }
147
+ const server = new McpServer({ name: SERVER_NAME, version: APP_VERSION }, instructionsContent
148
+ ? {
149
+ instructions: 'Read the instructions resource completely for detailed usage instructions before answering any API-related questions.',
150
+ }
151
+ : {});
135
152
  server.onerror = (error) => {
136
153
  const msg = String(error?.message || error);
137
154
  logBoth(server, 'error', `MCP server error: ${msg}`, { error: msg });
@@ -145,8 +162,6 @@ async function run() {
145
162
  serverType: useCode ? 'code' : useFull ? 'full' : 'minimal',
146
163
  availableTools: tools.map((t) => t.method),
147
164
  };
148
- const __filename = fileURLToPath(import.meta.url);
149
- const __dirname = dirname(__filename);
150
165
  const viewsDir = join(__dirname, './views');
151
166
  const renderTemplate = createTemplateRenderer(viewsDir);
152
167
  const errorsDir = join(__dirname, './views/errors');
@@ -218,6 +233,12 @@ async function run() {
218
233
  }
219
234
  });
220
235
  }
236
+ if (instructionsContent) {
237
+ server.registerResource('instructions', 'postman://instructions', { description: 'Instructions for using the Postman MCP server', mimeType: 'text/markdown' }, async (uri) => ({
238
+ contents: [{ uri: uri.href, mimeType: 'text/markdown', text: instructionsContent }],
239
+ }));
240
+ log('info', 'Registered resource: instructions');
241
+ }
221
242
  log('info', 'Starting stdio transport');
222
243
  const transport = new StdioServerTransport();
223
244
  transport.onmessage = (message) => {
@@ -0,0 +1,114 @@
1
+ # Postman MCP Server — Instructions
2
+
3
+ ## What is Postman?
4
+
5
+ Postman is a comprehensive API platform used by developers and teams to design, build, test, document, and share APIs. It provides tools for making HTTP requests, organizing API endpoints into collections, managing environments and variables, creating mock servers, setting up monitors, and publishing API documentation.
6
+
7
+ ## What is this MCP server?
8
+
9
+ This is a Model Context Protocol (MCP) server that exposes Postman's API as a set of tools you can invoke. It lets you interact with your Postman workspace programmatically — managing collections, environments, APIs, mocks, monitors, and more — all through structured tool calls.
10
+
11
+ ## Postman elements
12
+
13
+ Postman organises API work around a set of core elements. Understanding these is key to using the MCP server effectively.
14
+
15
+ - **Workspace** — A shared space that groups related collections, environments, APIs, mocks, and monitors. Every element belongs to a workspace. Workspaces can be personal, team, partner, or public.
16
+ - **Collection** — An ordered set of API requests grouped into folders. Collections are the primary unit for organising, documenting, testing, and running API calls. Each collection can contain folders, requests, responses (saved examples), pre-request scripts, and tests.
17
+ - **Folder** — A logical grouping of requests inside a collection. Folders can be nested to create a hierarchy (e.g. by resource or feature).
18
+ - **Request** — A single API call definition inside a collection. It includes the HTTP method, URL, headers, query parameters, body, authorization settings, and optional pre-request/test scripts.
19
+ - **Response (saved example)** — A snapshot of a request/response pair saved inside a request. Saved examples are used by mock servers to return realistic responses and serve as inline documentation.
20
+ - **Environment** — A set of key-value variables scoped to a particular context (e.g. development, staging, production). Variables can be of type `default` or `secret`. Environments are applied to requests at runtime so you can switch contexts without editing URLs or headers.
21
+ - **API (Spec)** — A versioned API definition (OpenAPI, GraphQL SDL, etc.) managed inside Postman. An API can be linked to collections, environments, mocks, monitors, and documentation.
22
+ - **Mock server** — A simulated endpoint that returns responses based on the saved examples in a collection. Useful for front-end development and testing before the real API is ready.
23
+ - **Monitor** — A scheduled runner that executes a collection on a set interval and reports results. Monitors are used for uptime checks, integration tests, and alerting.
24
+ - **Tags** — Labels attached to collections or workspaces for categorisation and search. Tags make it easier to find related elements across the organisation.
25
+
26
+ ## Private API Network
27
+ The Private API Network is an organisation-wide catalogue of approved APIs, collections, and workspaces. Teams publish elements to the Private API Network so others can discover and reuse them.
28
+
29
+ ## Public API Network (Postman API Network)
30
+ The Public API Network is a collection of APIs and collections published by Postman and third-party developers. It's a public repository of APIs and collections that anyone can use.
31
+
32
+ ## Finding APIs
33
+
34
+ These instructions apply any time the user wants to find, discover, or locate an API — whether internal or public.
35
+
36
+ **Do NOT rely on your own knowledge of available tools or APIs to answer questions like "is there an API for X?" or "find me an API that does Y."**
37
+ Always search first using this workflow
38
+
39
+ **Follow the search workflow whenever the user:**
40
+ - Asks to **find**, **discover**, **locate**, or **look up** an API
41
+ - Asks **"is there an API for..."** or **"can I do X via an API?"**
42
+ - Asks about capabilities that may or may not exist
43
+ - Is unsure what API to use for a task
44
+
45
+ > **Anti-pattern to avoid:**
46
+ > User asks "Is there a bulk API for workspaces?" → You scan the tool list, don't see one, and say "No such API exists."
47
+ > Correct behavior: Follow the search workflow below, search the network, and only then draw conclusions.
48
+
49
+ ---
50
+
51
+ ### Decision Framework: Private vs Public Network
52
+
53
+ **Default to the Private API Network** for most requests. Only use the Public API Network when the user explicitly signals public discovery intent.
54
+
55
+ #### Use `searchPostmanElementsInPrivateNetwork` (Default) When:
56
+
57
+ - The user asks to "find an API", "search for a service", or "look up an endpoint" without specifying public
58
+ - The user mentions internal services (e.g., "find the notification service", "search for our payment API", "look up the auth microservice")
59
+ - The user wants to integrate with a team or company API
60
+ - The user says "find a trusted API for X" or "what APIs do we have for Y"
61
+ - The user references internal infrastructure, microservices, or team-shared resources
62
+ - The request is ambiguous — **always prefer Private Network first**
63
+
64
+ #### Use `searchPostmanElementsInPublicNetwork` ONLY When:
65
+
66
+ - The user explicitly mentions a well-known public API by name (e.g., "find the Stripe API", "search for the GitHub API", "look up Twilio")
67
+ - The user explicitly says "public API", "public network", or "Postman public network"
68
+ - The user wants to explore third-party or open-source APIs (e.g., "find an open source weather API")
69
+ - The context makes it unambiguous that the user is looking for an external, publicly available API
70
+
71
+ **When in doubt, search the Private API Network first.** If no relevant results are found, then try the Public API Network and let the user know you're expanding the search.
72
+
73
+ ---
74
+
75
+ ### Search Workflow
76
+
77
+ #### Step 1: Determine Search Target
78
+
79
+ Based on the user's request, decide which network to search using the decision framework above.
80
+
81
+ #### Step 2: Execute the Search
82
+
83
+ **For Private API Network (default):**
84
+
85
+ `searchPostmanElementsInPrivateNetwork(q, entityType)`
86
+ - Use `entityType: "collections"` to find API collections (recommended starting point)
87
+ - Use `entityType: "requests"` to find specific API requests
88
+ - Craft a concise, relevant search query from the user's intent (e.g., "notification", "payment", "authentication")
89
+
90
+ **For Public API Network:**
91
+
92
+ `searchPostmanElementsInPublicNetwork(q, entityType)`
93
+ - Use `entityType: "collections"` to find API collections (recommended starting point)
94
+ - Use `entityType: "requests"` to find specific API requests
95
+ - Use the API name or domain as the query (e.g., "stripe", "github", "twilio")
96
+
97
+ #### Step 3: Present Results
98
+
99
+ - Summarize the search results clearly to the user
100
+ - Highlight the most relevant matches based on the user's intent
101
+ - If multiple results are found, briefly describe each and ask the user which one to explore
102
+ - If no results are found in the Private Network, suggest searching the Public Network
103
+
104
+ #### Step 4: Explore the Selected API
105
+
106
+ Once the user selects a collection:
107
+
108
+ 1. `getCollection(collectionId)` — Fetch collection details with the recursive itemRefs index
109
+ 2. `getCollectionRequest(requestId, collectionId, uid: true, populate: false)` — Explore individual requests
110
+ 3. `getCollectionFolder(folderId, collectionId, uid: true, populate: false)` — Explore folders
111
+
112
+ **IMPORTANT:** After establishing a collection, do NOT call `searchPostmanElementsInPublicNetwork` or `searchPostmanElementsInPrivateNetwork` again to find requests within it. Use `getCollectionRequest` and `getCollectionFolder` to navigate the collection's contents.
113
+
114
+ - **Never conclude that an API doesn't exist based solely on the MCP tool list.** The tool list covers tools for *using* Postman, not the full universe of APIs your organization may have published. Always search before concluding.
@@ -9,7 +9,7 @@ export const parameters = z.object({
9
9
  .object({
10
10
  info: z
11
11
  .object({
12
- name: z.string().describe("The collection's name."),
12
+ name: z.string().min(1).describe("The collection's name. Must not be empty."),
13
13
  description: z.string().describe("The collection's description.").optional(),
14
14
  schema: z
15
15
  .literal('https://schema.getpostman.com/json/collection/v2.1.0/collection.json')
@@ -15,7 +15,7 @@ export const parameters = z.object({
15
15
  .optional(),
16
16
  description: z.string().nullable().describe("The request's description.").optional(),
17
17
  method: z
18
- .enum([
18
+ .preprocess((v) => (typeof v === 'string' ? v.toUpperCase() : v), z.enum([
19
19
  'GET',
20
20
  'PUT',
21
21
  'POST',
@@ -31,7 +31,7 @@ export const parameters = z.object({
31
31
  'UNLOCK',
32
32
  'PROPFIND',
33
33
  'VIEW',
34
- ])
34
+ ]))
35
35
  .describe("The request's HTTP method.")
36
36
  .optional(),
37
37
  url: z.string().nullable().describe("The request's URL.").optional(),
@@ -13,7 +13,7 @@ export const parameters = z.object({
13
13
  description: z.string().nullable().describe("The response's description.").optional(),
14
14
  url: z.string().nullable().describe("The associated request's URL.").optional(),
15
15
  method: z
16
- .enum([
16
+ .preprocess((v) => (typeof v === 'string' ? v.toUpperCase() : v), z.enum([
17
17
  'GET',
18
18
  'PUT',
19
19
  'POST',
@@ -29,7 +29,7 @@ export const parameters = z.object({
29
29
  'UNLOCK',
30
30
  'PROPFIND',
31
31
  'VIEW',
32
- ])
32
+ ]))
33
33
  .describe("The request's HTTP method.")
34
34
  .optional(),
35
35
  headers: z
@@ -7,7 +7,7 @@ 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
9
  type: z
10
- .enum([
10
+ .preprocess((v) => (typeof v === 'string' ? v.toUpperCase() : v), z.enum([
11
11
  'OPENAPI:2.0',
12
12
  'OPENAPI:3.0',
13
13
  'OPENAPI:3.1',
@@ -15,7 +15,7 @@ export const parameters = z.object({
15
15
  'PROTOBUF:2',
16
16
  'PROTOBUF:3',
17
17
  'GRAPHQL',
18
- ])
18
+ ]))
19
19
  .describe("The specification's type."),
20
20
  files: z
21
21
  .array(z.union([
@@ -25,7 +25,7 @@ export const parameters = z.object({
25
25
  .describe("The file's path. Accepts .json, .yaml, .proto and .graphql file types."),
26
26
  content: z.string().describe("The file's stringified contents."),
27
27
  type: z
28
- .enum(['DEFAULT', 'ROOT'])
28
+ .preprocess((v) => (typeof v === 'string' ? v.toUpperCase() : v), z.enum(['DEFAULT', 'ROOT']))
29
29
  .describe('The type of file. This property is required when creating multi-file specifications:\n- `ROOT` — The file containing the full OpenAPI structure. This serves as the entry point for the API spec and references other (`DEFAULT`) spec files. Multi-file specs can only have one root file.\n- `DEFAULT` — A file referenced by the `ROOT` file.\n'),
30
30
  }),
31
31
  z.object({
@@ -19,8 +19,8 @@ export const parameters = z.object({
19
19
  .default('Space'),
20
20
  parametersResolution: z
21
21
  .string()
22
- .describe('Generated collections use examples for parameter generation by default. Any existing collections generated using the schema parameter generation will continue to sync using their existing strategy.')
23
- .default('Schema'),
22
+ .describe('Determines how parameter values are generated in the collection. Must be set to "Example" the "Schema" value is no longer supported by the Postman API and will result in an error. Always use "Example" to generate parameters from example values in the spec.')
23
+ .default('Example'),
24
24
  folderStrategy: z
25
25
  .enum(['Paths', 'Tags'])
26
26
  .describe("Whether to create folders based on the specification's `paths` or `tags` properties.")
@@ -50,8 +50,7 @@ export const parameters = z.object({
50
50
  .describe("If true, creates subfolders in the generated collection based on the order of the endpoints' tags.")
51
51
  .default(false),
52
52
  })
53
- .describe("The advanced creation options and their values. For more details, see Postman's [OpenAPI to Postman Collection Converter OPTIONS documentation](https://github.com/postmanlabs/openapi-to-postman/blob/develop/OPTIONS.md). These properties are case-sensitive.")
54
- .default({ enableOptionalParameters: true, folderStrategy: 'Paths' }),
53
+ .describe("The advanced creation options and their values. For more details, see Postman's [OpenAPI to Postman Collection Converter OPTIONS documentation](https://github.com/postmanlabs/openapi-to-postman/blob/develop/OPTIONS.md). These properties are case-sensitive."),
55
54
  });
56
55
  export const annotations = {
57
56
  title: 'Creates a collection from the given API specification.',
@@ -8,7 +8,9 @@ export const parameters = z.object({
8
8
  elementType: z.literal('spec').describe('The `spec` value.'),
9
9
  name: z.string().describe("The API specification's name."),
10
10
  type: z.literal('OPENAPI:3.0').describe("The specification's type."),
11
- format: z.enum(['JSON', 'YAML']).describe('The format of the API specification.'),
11
+ format: z
12
+ .preprocess((v) => (typeof v === 'string' ? v.toUpperCase() : v), z.enum(['JSON', 'YAML']))
13
+ .describe('The format of the API specification.'),
12
14
  });
13
15
  export const annotations = {
14
16
  title: 'Generates an API specification for the given collection. The response contains a polling link to the task status.',
@@ -1,7 +1,7 @@
1
1
  import { z } from 'zod';
2
2
  import { asMcpError, McpError } from './utils/toolHelpers.js';
3
3
  export const method = 'getTaggedEntities';
4
- export const description = 'Gets Postman elements (entities) by a given tag. Tags enable you to organize and search [workspaces](https://learning.postman.com/docs/collaborating-in-postman/using-workspaces/managing-workspaces/#tagging-a-workspace), [APIs](https://learning.postman.com/docs/designing-and-developing-your-api/managing-apis/#tagging-apis), and [collections](https://learning.postman.com/docs/collections/using-collections/#tagging-a-collection) that contain shared tags.\n\n**Note:**\n\nTagging is available on Postman [**Enterprise** plans](https://www.postman.com/pricing/).\n';
4
+ export const description = '**Requires an Enterprise plan.** Tagging is only available on Postman Enterprise plans. This tool returns a 404 error on Free, Basic, and Professional accounts.\n\nGets Postman elements (entities) by a given tag. Tags enable you to organize and search workspaces, APIs, and collections that contain shared tags.\n';
5
5
  export const parameters = z.object({
6
6
  slug: z
7
7
  .string()
@@ -29,7 +29,7 @@ export const parameters = z.object({
29
29
  .optional(),
30
30
  });
31
31
  export const annotations = {
32
- title: 'Gets Postman elements (entities) by a given tag. Tags enable you to organize and search [workspaces](https://learning.postman.com/docs/collaborating-in-postman/using-workspaces/managing-workspaces/#tagging-a-workspace), [APIs](https://learning.postman.com/docs/designing-and-developing-your-api/managing-apis/#tagging-apis), and [collections](https://learning.postman.com/docs/collections/using-collections/#tagging-a-collection) that contain shared tags.',
32
+ title: '**Requires an Enterprise plan.** Tagging is only available on Postman Enterprise plans. This tool returns a 404 error on Free, Basic, and Professional accounts.',
33
33
  readOnlyHint: true,
34
34
  destructiveHint: false,
35
35
  idempotentHint: true,
@@ -2,7 +2,7 @@ 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 = 'patchCollection';
5
- export const description = 'Updates specific collection information, such as its name, events, or its variables. For more information, see the [Postman Collection Format documentation](https://schema.postman.com/collection/json/v2.1.0/draft-07/docs/index.html).\n';
5
+ export const description = 'Updates specific collection information, such as its name, events, or its variables. For more information, see the [Postman Collection Format documentation](https://schema.postman.com/collection/json/v2.1.0/draft-07/docs/index.html).\n\n**Important usage notes:**\n\n- **Sequential calls only.** Do NOT call \\`patchCollection\\` in parallel with other \\`patchCollection\\` calls for the same collection — concurrent PATCH requests conflict with each other and cause cancellation errors. Always wait for one call to complete before making another.\n- **Partial updates.** Only include the fields you want to change. Omit all other fields entirely; unspecified fields are left unchanged.\n- **Variables (\\`collection.variable\\`).** When updating variables, provide only the fields you intend to set on each variable object (\\`key\\`, \\`value\\`, \\`description\\`). Omit \\`id\\` and \\`disabled\\` unless you explicitly need to change them — including extra fields can cause validation errors.\n';
6
6
  export const parameters = z.object({
7
7
  collectionId: z
8
8
  .string()
@@ -32,7 +32,7 @@ export const parameters = z.object({
32
32
  disabled: z
33
33
  .boolean()
34
34
  .describe("If true, the variable is not enabled. Doesn't apply to path parameter variables.")
35
- .default(false),
35
+ .optional(),
36
36
  })
37
37
  .describe('Information about the variable.'))
38
38
  .describe("A list of the collection's [variables](https://learning.postman.com/docs/sending-requests/variables/variables/). Make certain not to include sensitive information in variables.")
@@ -9,7 +9,7 @@ export const parameters = z.object({
9
9
  name: z.string().describe("The request's name.").optional(),
10
10
  description: z.string().nullable().describe("The request's description.").optional(),
11
11
  method: z
12
- .enum([
12
+ .preprocess((v) => (typeof v === 'string' ? v.toUpperCase() : v), z.enum([
13
13
  'GET',
14
14
  'PUT',
15
15
  'POST',
@@ -25,7 +25,7 @@ export const parameters = z.object({
25
25
  'UNLOCK',
26
26
  'PROPFIND',
27
27
  'VIEW',
28
- ])
28
+ ]))
29
29
  .describe("The request's HTTP method.")
30
30
  .optional(),
31
31
  url: z.string().nullable().describe("The request's URL.").optional(),
@@ -10,7 +10,7 @@ export const parameters = z.object({
10
10
  description: z.string().nullable().describe("The response's description.").optional(),
11
11
  url: z.string().nullable().describe("The associated request's URL.").optional(),
12
12
  method: z
13
- .enum([
13
+ .preprocess((v) => (typeof v === 'string' ? v.toUpperCase() : v), z.enum([
14
14
  'GET',
15
15
  'PUT',
16
16
  'POST',
@@ -26,7 +26,7 @@ export const parameters = z.object({
26
26
  'UNLOCK',
27
27
  'PROPFIND',
28
28
  'VIEW',
29
- ])
29
+ ]))
30
30
  .describe("The request's HTTP method.")
31
31
  .optional(),
32
32
  headers: z
@@ -8,7 +8,7 @@ export const parameters = z.object({
8
8
  filePath: z.string().describe('The path to the file.'),
9
9
  name: z.string().describe("The file's name.").optional(),
10
10
  type: z
11
- .enum(['DEFAULT', 'ROOT'])
11
+ .preprocess((v) => (typeof v === 'string' ? v.toUpperCase() : v), z.enum(['DEFAULT', 'ROOT']))
12
12
  .describe('The type of file:\n- `ROOT` — The file containing the full OpenAPI structure. This serves as the entry point for the API spec and references other (`DEFAULT`) spec files. Multi-file specs can only have one root file.\n- `DEFAULT` — A file referenced by the `ROOT` file.\n')
13
13
  .optional(),
14
14
  content: z.string().describe("The specification's stringified contents.").optional(),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@postman/postman-mcp-server",
3
- "version": "2.8.2",
3
+ "version": "2.8.3-beta.2",
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",