@postman/postman-mcp-server 2.5.1 → 2.5.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 +1 -0
- package/dist/package.json +1 -1
- package/dist/src/clients/postman.js +7 -2
- package/dist/src/enabledResources.js +9 -0
- package/dist/src/index.js +1 -1
- package/dist/src/tools/getCollection/getCollection.js +52 -0
- package/dist/src/tools/getCollection/getCollectionMap.js +101 -0
- package/dist/src/tools/getCollection/index.js +46 -0
- package/dist/src/tools/getCollection.js +1 -52
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -16,6 +16,7 @@ Postman also offers servers as an [npm package](https://www.npmjs.com/package/@p
|
|
|
16
16
|
|
|
17
17
|
### Use Cases
|
|
18
18
|
|
|
19
|
+
* **API Testing** - Continuously test your API using your Postman collection.
|
|
19
20
|
* **Code synchronization** - Effortlessly keep your code in sync with your [Postman Collections](https://learning.postman.com/docs/design-apis/collections/overview/) and specs.
|
|
20
21
|
* **Collection management** - Create and [tag](https://learning.postman.com/docs/collections/use-collections/collaborate-with-collections/#tag-a-collection) collections, update collection and request [documentation](https://learning.postman.com/docs/publishing-your-api/api-documentation-overview/), add [comments](https://learning.postman.com/docs/collaborating-in-postman/comments/), or perform actions across multiple collections without leaving your editor.
|
|
21
22
|
* **Workspace and environment management** - Create [workspaces](https://learning.postman.com/docs/collaborating-in-postman/using-workspaces/overview/) and [environments](https://learning.postman.com/docs/sending-requests/variables/managing-environments/), plus manage your environment variables.
|
package/dist/package.json
CHANGED
|
@@ -8,10 +8,12 @@ export var ContentType;
|
|
|
8
8
|
export class PostmanAPIClient {
|
|
9
9
|
baseUrl;
|
|
10
10
|
apiKey;
|
|
11
|
+
serverContext;
|
|
11
12
|
static instance = null;
|
|
12
|
-
constructor(apiKey, baseUrl = process.env.POSTMAN_API_BASE_URL || 'https://api.postman.com') {
|
|
13
|
+
constructor(apiKey, baseUrl = process.env.POSTMAN_API_BASE_URL || 'https://api.postman.com', serverContext) {
|
|
13
14
|
this.apiKey = apiKey;
|
|
14
15
|
this.baseUrl = baseUrl;
|
|
16
|
+
this.serverContext = serverContext;
|
|
15
17
|
}
|
|
16
18
|
static getInstance(apiKey, baseUrl) {
|
|
17
19
|
if (!PostmanAPIClient.instance) {
|
|
@@ -45,7 +47,10 @@ export class PostmanAPIClient {
|
|
|
45
47
|
const contentType = options.contentType || ContentType.Json;
|
|
46
48
|
const userAgentKey = Object.keys(options.headers ?? {}).find((key) => key.toLowerCase() === 'user-agent');
|
|
47
49
|
const userAgentValue = userAgentKey ? options.headers?.[userAgentKey] : undefined;
|
|
48
|
-
|
|
50
|
+
let userAgentHeader = userAgentValue ? `${userAgentValue}/${USER_AGENT}` : USER_AGENT;
|
|
51
|
+
if (this.serverContext?.serverType) {
|
|
52
|
+
userAgentHeader = `${userAgentHeader} (toolset: ${this.serverContext.serverType})`;
|
|
53
|
+
}
|
|
49
54
|
const disallowed = new Set([
|
|
50
55
|
'content-length',
|
|
51
56
|
'transfer-encoding',
|
|
@@ -103,6 +103,7 @@ const full = [
|
|
|
103
103
|
'getStatusOfAnAsyncApiTask',
|
|
104
104
|
'getAuthenticatedUser',
|
|
105
105
|
'getTaggedEntities',
|
|
106
|
+
'getCodeGenerationInstructions',
|
|
106
107
|
'transferCollectionFolders',
|
|
107
108
|
'transferCollectionResponses',
|
|
108
109
|
'transferCollectionResponses',
|
|
@@ -173,10 +174,18 @@ const excludedFromGeneration = [
|
|
|
173
174
|
'getEnabledTools',
|
|
174
175
|
'getCodeGenerationInstructions',
|
|
175
176
|
'getCollectionMap',
|
|
177
|
+
'getCollection',
|
|
176
178
|
];
|
|
179
|
+
const subtools = {
|
|
180
|
+
getCollection: {
|
|
181
|
+
orchestrator: 'getCollection',
|
|
182
|
+
subtools: ['getCollection', 'getCollectionMap'],
|
|
183
|
+
},
|
|
184
|
+
};
|
|
177
185
|
export const enabledResources = {
|
|
178
186
|
full,
|
|
179
187
|
minimal,
|
|
180
188
|
code,
|
|
181
189
|
excludedFromGeneration,
|
|
190
|
+
subtools,
|
|
182
191
|
};
|
package/dist/src/index.js
CHANGED
|
@@ -139,11 +139,11 @@ async function run() {
|
|
|
139
139
|
await server.close();
|
|
140
140
|
process.exit(0);
|
|
141
141
|
});
|
|
142
|
-
const client = new PostmanAPIClient(apiKey);
|
|
143
142
|
const serverContext = {
|
|
144
143
|
serverType: useCode ? 'code' : useFull ? 'full' : 'minimal',
|
|
145
144
|
availableTools: tools.map((t) => t.method),
|
|
146
145
|
};
|
|
146
|
+
const client = new PostmanAPIClient(apiKey, undefined, serverContext);
|
|
147
147
|
log('info', 'Registering tools with McpServer');
|
|
148
148
|
for (const tool of tools) {
|
|
149
149
|
server.tool(tool.method, tool.description, tool.parameters.shape, tool.annotations || {}, async (args, extra) => {
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { asMcpError, McpError } from '../utils/toolHelpers.js';
|
|
3
|
+
export const method = 'getCollection';
|
|
4
|
+
export const description = "Gets information about a collection. For a complete list of this endpoint's possible values, refer to the [Postman Collection Format documentation](https://schema.postman.com/collection/json/v2.1.0/draft-07/docs/index.html).";
|
|
5
|
+
export const parameters = z.object({
|
|
6
|
+
collectionId: z
|
|
7
|
+
.string()
|
|
8
|
+
.describe('The collection ID must be in the form <OWNER_ID>-<UUID> (e.g. 12345-33823532ab9e41c9b6fd12d0fd459b8b).'),
|
|
9
|
+
access_key: z
|
|
10
|
+
.string()
|
|
11
|
+
.describe("A collection's read-only access key. Using this query parameter does not require an API key to call the endpoint.")
|
|
12
|
+
.optional(),
|
|
13
|
+
model: z
|
|
14
|
+
.literal('minimal')
|
|
15
|
+
.describe("Return a list of only the collection's root-level request (`rootLevelRequests`) and folder (`rootLevelFolders`) IDs instead of the full collection element data.")
|
|
16
|
+
.optional(),
|
|
17
|
+
});
|
|
18
|
+
export const annotations = {
|
|
19
|
+
title: "Gets information about a collection. For a complete list of this endpoint's possible values, refer to the [Postman Collection Format documentation](https://schema.postman.com/collection/json/v2.1.0/draft-07/docs/index.html).",
|
|
20
|
+
readOnlyHint: true,
|
|
21
|
+
destructiveHint: false,
|
|
22
|
+
idempotentHint: true,
|
|
23
|
+
};
|
|
24
|
+
export async function handler(args, extra) {
|
|
25
|
+
try {
|
|
26
|
+
const endpoint = `/collections/${args.collectionId}`;
|
|
27
|
+
const query = new URLSearchParams();
|
|
28
|
+
if (args.access_key !== undefined)
|
|
29
|
+
query.set('access_key', String(args.access_key));
|
|
30
|
+
if (args.model !== undefined)
|
|
31
|
+
query.set('model', String(args.model));
|
|
32
|
+
const url = query.toString() ? `${endpoint}?${query.toString()}` : endpoint;
|
|
33
|
+
const options = {
|
|
34
|
+
headers: extra.headers,
|
|
35
|
+
};
|
|
36
|
+
const result = await extra.client.get(url, options);
|
|
37
|
+
return {
|
|
38
|
+
content: [
|
|
39
|
+
{
|
|
40
|
+
type: 'text',
|
|
41
|
+
text: `${typeof result === 'string' ? result : JSON.stringify(result, null, 2)}`,
|
|
42
|
+
},
|
|
43
|
+
],
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
catch (e) {
|
|
47
|
+
if (e instanceof McpError) {
|
|
48
|
+
throw e;
|
|
49
|
+
}
|
|
50
|
+
throw asMcpError(e);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
@@ -0,0 +1,101 @@
|
|
|
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
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { McpError, asMcpError } from '../utils/toolHelpers.js';
|
|
3
|
+
import { handler as getCollectionApiHandler, parameters as getCollectionApiParameters, } from './getCollection.js';
|
|
4
|
+
import { handler as getCollectionMapHandler } from './getCollectionMap.js';
|
|
5
|
+
export const method = 'getCollection';
|
|
6
|
+
export const description = `Get information about a collection. By default this tool returns the lightweight collection map (metadata + recursive itemRefs).
|
|
7
|
+
Use the model parameter to opt in to Postman's full API responses:
|
|
8
|
+
- model=minimal — root-level folder/request IDs only
|
|
9
|
+
- model=full — full Postman collection payload.`;
|
|
10
|
+
const baseParameters = getCollectionApiParameters.pick({
|
|
11
|
+
collectionId: true,
|
|
12
|
+
access_key: true,
|
|
13
|
+
});
|
|
14
|
+
export const parameters = baseParameters.extend({
|
|
15
|
+
model: z
|
|
16
|
+
.enum(['minimal', 'full'])
|
|
17
|
+
.describe('Optional response shape override. Omit to receive the lightweight collection map. Set to `minimal` for the Postman minimal model or `full` for the complete collection payload.')
|
|
18
|
+
.optional(),
|
|
19
|
+
});
|
|
20
|
+
export const annotations = {
|
|
21
|
+
title: 'Get Collection (map by default)',
|
|
22
|
+
readOnlyHint: true,
|
|
23
|
+
destructiveHint: false,
|
|
24
|
+
idempotentHint: true,
|
|
25
|
+
};
|
|
26
|
+
function omitModel(args) {
|
|
27
|
+
const { model: _ignored, ...rest } = args;
|
|
28
|
+
return rest;
|
|
29
|
+
}
|
|
30
|
+
export async function handler(args, extra) {
|
|
31
|
+
try {
|
|
32
|
+
if (!args.model) {
|
|
33
|
+
return await getCollectionMapHandler(omitModel(args), extra);
|
|
34
|
+
}
|
|
35
|
+
if (args.model === 'minimal') {
|
|
36
|
+
return await getCollectionApiHandler({ ...args, model: 'minimal' }, extra);
|
|
37
|
+
}
|
|
38
|
+
return await getCollectionApiHandler(omitModel(args), extra);
|
|
39
|
+
}
|
|
40
|
+
catch (e) {
|
|
41
|
+
if (e instanceof McpError) {
|
|
42
|
+
throw e;
|
|
43
|
+
}
|
|
44
|
+
throw asMcpError(e);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
@@ -1,52 +1 @@
|
|
|
1
|
-
|
|
2
|
-
import { asMcpError, McpError } from './utils/toolHelpers.js';
|
|
3
|
-
export const method = 'getCollection';
|
|
4
|
-
export const description = "Gets information about a collection. For a complete list of this endpoint's possible values, refer to the [Postman Collection Format documentation](https://schema.postman.com/collection/json/v2.1.0/draft-07/docs/index.html).";
|
|
5
|
-
export const parameters = z.object({
|
|
6
|
-
collectionId: z
|
|
7
|
-
.string()
|
|
8
|
-
.describe('The collection ID must be in the form <OWNER_ID>-<UUID> (e.g. 12345-33823532ab9e41c9b6fd12d0fd459b8b).'),
|
|
9
|
-
access_key: z
|
|
10
|
-
.string()
|
|
11
|
-
.describe("A collection's read-only access key. Using this query parameter does not require an API key to call the endpoint.")
|
|
12
|
-
.optional(),
|
|
13
|
-
model: z
|
|
14
|
-
.literal('minimal')
|
|
15
|
-
.describe("Return a list of only the collection's root-level request (`rootLevelRequests`) and folder (`rootLevelFolders`) IDs instead of the full collection element data.")
|
|
16
|
-
.optional(),
|
|
17
|
-
});
|
|
18
|
-
export const annotations = {
|
|
19
|
-
title: "Gets information about a collection. For a complete list of this endpoint's possible values, refer to the [Postman Collection Format documentation](https://schema.postman.com/collection/json/v2.1.0/draft-07/docs/index.html).",
|
|
20
|
-
readOnlyHint: true,
|
|
21
|
-
destructiveHint: false,
|
|
22
|
-
idempotentHint: true,
|
|
23
|
-
};
|
|
24
|
-
export async function handler(args, extra) {
|
|
25
|
-
try {
|
|
26
|
-
const endpoint = `/collections/${args.collectionId}`;
|
|
27
|
-
const query = new URLSearchParams();
|
|
28
|
-
if (args.access_key !== undefined)
|
|
29
|
-
query.set('access_key', String(args.access_key));
|
|
30
|
-
if (args.model !== undefined)
|
|
31
|
-
query.set('model', String(args.model));
|
|
32
|
-
const url = query.toString() ? `${endpoint}?${query.toString()}` : endpoint;
|
|
33
|
-
const options = {
|
|
34
|
-
headers: extra.headers,
|
|
35
|
-
};
|
|
36
|
-
const result = await extra.client.get(url, options);
|
|
37
|
-
return {
|
|
38
|
-
content: [
|
|
39
|
-
{
|
|
40
|
-
type: 'text',
|
|
41
|
-
text: `${typeof result === 'string' ? result : JSON.stringify(result, null, 2)}`,
|
|
42
|
-
},
|
|
43
|
-
],
|
|
44
|
-
};
|
|
45
|
-
}
|
|
46
|
-
catch (e) {
|
|
47
|
-
if (e instanceof McpError) {
|
|
48
|
-
throw e;
|
|
49
|
-
}
|
|
50
|
-
throw asMcpError(e);
|
|
51
|
-
}
|
|
52
|
-
}
|
|
1
|
+
export { method, description, parameters, annotations, handler } from './getCollection/index.js';
|