@postman/postman-mcp-server 2.4.9 → 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.
@@ -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
+ }
@@ -1,7 +1,6 @@
1
1
  import { z } from 'zod';
2
- import newman from 'newman';
3
- import { TestTracker, OutputBuilder, buildNewmanOptions } from './utils/runner.js';
4
2
  import { asMcpError, McpError } from './utils/toolHelpers.js';
3
+ import { runCollection } from './runner/index.js';
5
4
  export const method = 'runCollection';
6
5
  export const description = 'Runs a Postman collection by ID with detailed test results and execution statistics. Supports optional environment for variable substitution. Note: Advanced parameters like custom delays and other runtime options are not yet available.';
7
6
  export const parameters = z.object({
@@ -37,85 +36,12 @@ export const annotations = {
37
36
  };
38
37
  export async function handler(params, extra) {
39
38
  try {
40
- const tracker = new TestTracker();
41
- const output = new OutputBuilder();
42
- output.add(`🚀 Fetching collection with ID: ${params.collectionId}`);
43
- const response = await extra.client.get(`/collections/${params.collectionId}`);
44
- const collectionJSON = response.collection || response;
45
- output.add(`✅ Successfully fetched collection: ${collectionJSON.info?.name || 'Unknown'}\n`);
46
- let environmentJSON;
47
- if (params.environmentId) {
48
- output.add(`🌍 Fetching environment with ID: ${params.environmentId}`);
49
- const envResponse = await extra.client.get(`/environments/${params.environmentId}`);
50
- environmentJSON = envResponse.environment || envResponse;
51
- output.add(`✅ Successfully fetched environment: ${environmentJSON.name || 'Unknown'}\n`);
52
- }
53
- const newmanOptions = buildNewmanOptions(params, collectionJSON, environmentJSON);
54
- const startTime = Date.now();
55
- await new Promise((resolve, reject) => {
56
- newman
57
- .run(newmanOptions)
58
- .on('start', () => {
59
- output.add('🎯 Starting collection run...\n');
60
- })
61
- .on('assertion', (_err, args) => {
62
- if (args.assertion) {
63
- tracker.addAssertion({
64
- passed: !args.error,
65
- assertion: args.assertion,
66
- name: args.assertion,
67
- error: args.error,
68
- });
69
- }
70
- })
71
- .on('item', (_err, args) => {
72
- if (args.item) {
73
- const testResults = tracker.displayCurrentResults();
74
- if (testResults) {
75
- output.add(`\n📝 Request: ${args.item.name}`);
76
- output.add(testResults);
77
- }
78
- }
79
- })
80
- .on('done', (err, summary) => {
81
- const endTime = Date.now();
82
- const durationMs = endTime - startTime;
83
- const durationSec = (durationMs / 1000).toFixed(2);
84
- if (err) {
85
- output.add('\n❌ Run error: ' + err.message);
86
- output.add(`⏱️ Duration: ${durationSec}s`);
87
- reject(err);
88
- return;
89
- }
90
- output.add('\n=== ✅ Run completed! ===');
91
- output.add(`⏱️ Duration: ${durationSec}s`);
92
- const testStats = tracker.getTotalStats();
93
- if (testStats.total > 0) {
94
- output.add('\n📊 Overall Test Statistics:');
95
- output.add(` Total tests: ${testStats.total}`);
96
- output.add(` Passed: ${testStats.passed} ✅`);
97
- output.add(` Failed: ${testStats.failed} ❌`);
98
- output.add(` Success rate: ${((testStats.passed / testStats.total) * 100).toFixed(1)}%`);
99
- }
100
- if (summary?.run?.stats) {
101
- output.add('\n📈 Request Summary:');
102
- output.add(` Total requests: ${summary.run.stats.requests?.total || 0}`);
103
- output.add(` Failed requests: ${summary.run.stats.requests?.failed || 0}`);
104
- output.add(` Total assertions: ${summary.run.stats.assertions?.total || 0}`);
105
- output.add(` Failed assertions: ${summary.run.stats.assertions?.failed || 0}`);
106
- if (summary.run.stats.iterations) {
107
- output.add(` Total iterations: ${summary.run.stats.iterations.total || 0}`);
108
- output.add(` Failed iterations: ${summary.run.stats.iterations.failed || 0}`);
109
- }
110
- }
111
- resolve();
112
- });
113
- });
39
+ const output = await runCollection(params, extra.client);
114
40
  return {
115
41
  content: [
116
42
  {
117
43
  type: 'text',
118
- text: output.build(),
44
+ text: output,
119
45
  },
120
46
  ],
121
47
  };
@@ -0,0 +1,165 @@
1
+ import newman from 'newman';
2
+ export class OutputBuilder {
3
+ lines = [];
4
+ add(line) {
5
+ this.lines.push(line);
6
+ }
7
+ build() {
8
+ return this.lines.join('\n');
9
+ }
10
+ }
11
+ export class TestTracker {
12
+ assertions = [];
13
+ totalTests = 0;
14
+ totalPassed = 0;
15
+ totalFailed = 0;
16
+ addAssertion(assertion) {
17
+ this.assertions.push(assertion);
18
+ this.totalTests++;
19
+ if (assertion.passed) {
20
+ this.totalPassed++;
21
+ }
22
+ else {
23
+ this.totalFailed++;
24
+ }
25
+ }
26
+ displayCurrentResults() {
27
+ if (this.assertions.length === 0) {
28
+ return '';
29
+ }
30
+ const lines = [' 📊 Test Results:'];
31
+ this.assertions.forEach((assertion) => {
32
+ const status = assertion.passed ? '✓' : '✗';
33
+ const name = assertion.assertion || assertion.name || 'Unnamed test';
34
+ lines.push(` ${status} ${name}`);
35
+ if (!assertion.passed && assertion.error) {
36
+ const errorMessage = typeof assertion.error === 'string'
37
+ ? assertion.error
38
+ : assertion.error.message || 'Unknown error';
39
+ lines.push(` └─ Error: ${errorMessage}`);
40
+ }
41
+ });
42
+ const passed = this.assertions.filter((a) => a.passed).length;
43
+ const failed = this.assertions.filter((a) => !a.passed).length;
44
+ lines.push(` ────────────────────────────────────────`);
45
+ lines.push(` ${this.assertions.length} tests | ✓ ${passed} passed | ✗ ${failed} failed\n`);
46
+ this.assertions.length = 0;
47
+ return lines.join('\n');
48
+ }
49
+ getTotalStats() {
50
+ return {
51
+ total: this.totalTests,
52
+ passed: this.totalPassed,
53
+ failed: this.totalFailed,
54
+ };
55
+ }
56
+ reset() {
57
+ this.assertions.length = 0;
58
+ this.totalTests = 0;
59
+ this.totalPassed = 0;
60
+ this.totalFailed = 0;
61
+ }
62
+ }
63
+ export function buildNewmanOptions(params, collection, environment) {
64
+ return {
65
+ collection: collection,
66
+ environment: environment,
67
+ iterationCount: params.iterationCount || 1,
68
+ timeout: params.requestTimeout || 60000,
69
+ timeoutRequest: params.requestTimeout || 60000,
70
+ timeoutScript: params.scriptTimeout || 5000,
71
+ delayRequest: 1000,
72
+ ignoreRedirects: false,
73
+ insecure: false,
74
+ bail: params.stopOnFailure ? ['failure'] : false,
75
+ suppressExitCode: true,
76
+ reporters: [],
77
+ reporter: {},
78
+ color: 'off',
79
+ verbose: false,
80
+ requestAgents: {
81
+ http: undefined,
82
+ https: undefined,
83
+ },
84
+ };
85
+ }
86
+ export async function executeCollection(context) {
87
+ const tracker = new TestTracker();
88
+ const output = new OutputBuilder();
89
+ output.add(`🚀 Starting collection: ${context.collection.name}`);
90
+ if (context.environment) {
91
+ output.add(`🌍 Using environment: ${context.environment.name}\n`);
92
+ }
93
+ const newmanOptions = buildNewmanOptions(context.params, context.collection.json, context.environment?.json);
94
+ const startTime = Date.now();
95
+ const summary = await runNewman(newmanOptions, tracker, output);
96
+ const endTime = Date.now();
97
+ const durationMs = endTime - startTime;
98
+ return {
99
+ output: output.build(),
100
+ testStats: tracker.getTotalStats(),
101
+ summary,
102
+ startTime,
103
+ endTime,
104
+ durationMs,
105
+ };
106
+ }
107
+ function runNewman(options, tracker, output) {
108
+ return new Promise((resolve, reject) => {
109
+ newman
110
+ .run(options)
111
+ .on('start', () => {
112
+ output.add('🎯 Starting collection run...\n');
113
+ })
114
+ .on('assertion', (_err, args) => {
115
+ if (args.assertion) {
116
+ tracker.addAssertion({
117
+ passed: !args.error,
118
+ assertion: args.assertion,
119
+ name: args.assertion,
120
+ error: args.error,
121
+ });
122
+ }
123
+ })
124
+ .on('item', (_err, args) => {
125
+ if (args.item) {
126
+ const testResults = tracker.displayCurrentResults();
127
+ if (testResults) {
128
+ output.add(`\n📝 Request: ${args.item.name}`);
129
+ output.add(testResults);
130
+ }
131
+ }
132
+ })
133
+ .on('done', (err, summary) => {
134
+ if (err) {
135
+ output.add('\n❌ Run error: ' + err.message);
136
+ reject(err);
137
+ return;
138
+ }
139
+ output.add('\n=== ✅ Run completed! ===');
140
+ appendSummaryToOutput(output, tracker, summary);
141
+ resolve(summary);
142
+ });
143
+ });
144
+ }
145
+ function appendSummaryToOutput(output, tracker, summary) {
146
+ const testStats = tracker.getTotalStats();
147
+ if (testStats.total > 0) {
148
+ output.add('\n📊 Overall Test Statistics:');
149
+ output.add(` Total tests: ${testStats.total}`);
150
+ output.add(` Passed: ${testStats.passed} ✅`);
151
+ output.add(` Failed: ${testStats.failed} ❌`);
152
+ output.add(` Success rate: ${((testStats.passed / testStats.total) * 100).toFixed(1)}%`);
153
+ }
154
+ if (summary?.run?.stats) {
155
+ output.add('\n📈 Request Summary:');
156
+ output.add(` Total requests: ${summary.run.stats.requests?.total || 0}`);
157
+ output.add(` Failed requests: ${summary.run.stats.requests?.failed || 0}`);
158
+ output.add(` Total assertions: ${summary.run.stats.assertions?.total || 0}`);
159
+ output.add(` Failed assertions: ${summary.run.stats.assertions?.failed || 0}`);
160
+ if (summary.run.stats.iterations) {
161
+ output.add(` Total iterations: ${summary.run.stats.iterations.total || 0}`);
162
+ output.add(` Failed iterations: ${summary.run.stats.iterations.failed || 0}`);
163
+ }
164
+ }
165
+ }
@@ -0,0 +1,33 @@
1
+ import { McpError, ErrorCode } from '@modelcontextprotocol/sdk/types.js';
2
+ export async function fetchCollection(collectionId, client) {
3
+ try {
4
+ const response = await client.get(`/collections/${collectionId}`);
5
+ const collectionJSON = response.collection || response;
6
+ return {
7
+ json: collectionJSON,
8
+ name: collectionJSON.info?.name || 'Unknown',
9
+ id: collectionId,
10
+ };
11
+ }
12
+ catch (error) {
13
+ throw new McpError(ErrorCode.InternalError, `Failed to fetch collection: ${collectionId}`, {
14
+ cause: error,
15
+ });
16
+ }
17
+ }
18
+ export async function fetchEnvironment(environmentId, client) {
19
+ try {
20
+ const response = await client.get(`/environments/${environmentId}`);
21
+ const environmentJSON = response.environment || response;
22
+ return {
23
+ json: environmentJSON,
24
+ name: environmentJSON.name || 'Unknown',
25
+ id: environmentId,
26
+ };
27
+ }
28
+ catch (error) {
29
+ throw new McpError(ErrorCode.InternalError, `Failed to fetch environment: ${environmentId}`, {
30
+ cause: error,
31
+ });
32
+ }
33
+ }
@@ -0,0 +1,20 @@
1
+ import { fetchCollection, fetchEnvironment } from './fetchers.js';
2
+ import { executeCollection } from './executor.js';
3
+ import { parseToTelemetry, formatUserOutput } from './parsers.js';
4
+ import { reportTelemetryAsync } from './telemetry.js';
5
+ export async function runCollection(params, client) {
6
+ const collection = await fetchCollection(params.collectionId, client);
7
+ let environment;
8
+ if (params.environmentId) {
9
+ environment = await fetchEnvironment(params.environmentId, client);
10
+ }
11
+ const result = await executeCollection({
12
+ collection,
13
+ environment,
14
+ params,
15
+ });
16
+ const telemetryPayload = parseToTelemetry(result, params.collectionId, collection.name);
17
+ const userOutput = formatUserOutput(result);
18
+ reportTelemetryAsync(telemetryPayload, client);
19
+ return userOutput;
20
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,8 @@
1
+ import { buildTelemetryPayload } from './telemetry.js';
2
+ export function parseToTelemetry(result, collectionId, collectionName) {
3
+ return buildTelemetryPayload(collectionId, collectionName, result);
4
+ }
5
+ export function formatUserOutput(result) {
6
+ const durationSec = (result.durationMs / 1000).toFixed(2);
7
+ return `${result.output}\n⏱️ Duration: ${durationSec}s`;
8
+ }
@@ -0,0 +1,178 @@
1
+ import { RUNNER_ACCEPT_HEADER } from '../../constants.js';
2
+ import { v4 as uuidv4 } from 'uuid';
3
+ export function buildTelemetryPayload(collectionId, collectionName, result) {
4
+ const durationMs = result.durationMs;
5
+ const stats = result.summary?.run?.stats || {};
6
+ const iterations = stats.iterations || { total: 0, pending: 0, failed: 0 };
7
+ const items = stats.items || { total: 0, pending: 0, failed: 0 };
8
+ const scripts = stats.scripts || { total: 0, pending: 0, failed: 0 };
9
+ const prerequests = stats.prerequests || { total: 0, pending: 0, failed: 0 };
10
+ const requests = stats.requests || { total: 0, pending: 0, failed: 0 };
11
+ const tests = stats.tests || { total: 0, pending: 0, failed: 0 };
12
+ const assertions = stats.assertions || { total: 0, pending: 0, failed: 0 };
13
+ const testScripts = stats.testScripts || { total: 0, pending: 0, failed: 0 };
14
+ const prerequestScripts = stats.prerequestScripts || { total: 0, pending: 0, failed: 0 };
15
+ const responses = result.summary?.run?.executions || [];
16
+ const totalResponseTime = responses.reduce((sum, exec) => {
17
+ return sum + (exec?.response?.responseTime || 0);
18
+ }, 0);
19
+ const averageResponseTime = responses.length > 0 ? totalResponseTime / responses.length : 0;
20
+ const totalDataReceived = responses.reduce((sum, exec) => {
21
+ return sum + (exec?.response?.responseSize || 0);
22
+ }, 0);
23
+ const iterationsData = (result.summary?.run?.executions || []).map((exec) => {
24
+ const item = exec.item || {};
25
+ const request = exec.request || {};
26
+ const response = exec.response || {};
27
+ const assertions = exec.assertions || [];
28
+ const testResults = assertions.map((assertion) => ({
29
+ name: assertion.assertion || 'Unnamed test',
30
+ status: assertion.error ? 'fail' : 'pass',
31
+ error: assertion.error
32
+ ? {
33
+ name: assertion.error.name || 'AssertionError',
34
+ message: assertion.error.message || 'Unknown error',
35
+ stack: assertion.error.stack || '',
36
+ }
37
+ : null,
38
+ }));
39
+ const requestData = {
40
+ url: request.url?.toString() || '',
41
+ method: request.method || 'GET',
42
+ headers: request.headers || {},
43
+ };
44
+ if (request.body) {
45
+ requestData.body = {
46
+ mode: request.body.mode || 'raw',
47
+ raw: request.body.raw || '',
48
+ };
49
+ if (request.body.options) {
50
+ requestData.body.options = request.body.options;
51
+ }
52
+ }
53
+ const responseData = {
54
+ code: response.code || 0,
55
+ name: response.status || '',
56
+ time: response.responseTime || 0,
57
+ size: response.responseSize || 0,
58
+ headers: response.headers || [],
59
+ };
60
+ if (response.stream) {
61
+ try {
62
+ responseData.body = response.stream.toString();
63
+ }
64
+ catch {
65
+ responseData.body = '';
66
+ }
67
+ }
68
+ return {
69
+ id: uuidv4(),
70
+ name: item.name || 'Request',
71
+ error: exec.requestError
72
+ ? {
73
+ name: exec.requestError.name || 'Error',
74
+ message: exec.requestError.message || 'Unknown error',
75
+ }
76
+ : null,
77
+ tests: testResults,
78
+ request: requestData,
79
+ response: responseData,
80
+ };
81
+ });
82
+ return {
83
+ collectionRun: {
84
+ id: uuidv4(),
85
+ collection: collectionId,
86
+ name: collectionName,
87
+ status: 'finished',
88
+ source: 'postman-cli',
89
+ delay: 0,
90
+ currentIteration: iterations.total || 1,
91
+ failedTestCount: result.testStats.failed,
92
+ skippedTestCount: 0,
93
+ passedTestCount: result.testStats.passed,
94
+ totalTestCount: result.testStats.total,
95
+ iterations: iterationsData.length > 0 ? [iterationsData] : [],
96
+ totalTime: durationMs,
97
+ totalRequests: requests.total || 0,
98
+ startedAt: result.startTime,
99
+ createdAt: result.endTime,
100
+ branchSource: 'local',
101
+ branch: process.env.GIT_BRANCH || 'main',
102
+ },
103
+ runOverview: {
104
+ collectionName: collectionName,
105
+ runDurationInMiliseconds: durationMs,
106
+ averageResponseTimeInMiliseconds: Math.round(averageResponseTime),
107
+ totalDataReceivedInBytes: totalDataReceived,
108
+ statistics: {
109
+ iterations: {
110
+ total: iterations.total || 0,
111
+ pending: iterations.pending || 0,
112
+ failed: iterations.failed || 0,
113
+ },
114
+ items: {
115
+ total: items.total || 0,
116
+ pending: items.pending || 0,
117
+ failed: items.failed || 0,
118
+ },
119
+ scripts: {
120
+ total: scripts.total || 0,
121
+ pending: scripts.pending || 0,
122
+ failed: scripts.failed || 0,
123
+ },
124
+ prerequests: {
125
+ total: prerequests.total || 0,
126
+ pending: prerequests.pending || 0,
127
+ failed: prerequests.failed || 0,
128
+ },
129
+ requests: {
130
+ total: requests.total || 0,
131
+ pending: requests.pending || 0,
132
+ failed: requests.failed || 0,
133
+ },
134
+ tests: {
135
+ total: tests.total || 0,
136
+ pending: tests.pending || 0,
137
+ failed: tests.failed || 0,
138
+ },
139
+ assertions: {
140
+ total: assertions.total || 0,
141
+ pending: assertions.pending || 0,
142
+ failed: assertions.failed || 0,
143
+ },
144
+ testScripts: {
145
+ total: testScripts.total || 0,
146
+ pending: testScripts.pending || 0,
147
+ failed: testScripts.failed || 0,
148
+ },
149
+ prerequestScripts: {
150
+ total: prerequestScripts.total || 0,
151
+ pending: prerequestScripts.pending || 0,
152
+ failed: prerequestScripts.failed || 0,
153
+ },
154
+ responses: {
155
+ total: responses.length || 0,
156
+ pending: 0,
157
+ failed: requests.failed || 0,
158
+ totalResponseTime: Math.round(totalResponseTime),
159
+ },
160
+ },
161
+ },
162
+ };
163
+ }
164
+ export function reportTelemetryAsync(payload, client) {
165
+ setImmediate(async () => {
166
+ try {
167
+ await client.post('/collectionruns', {
168
+ body: JSON.stringify(payload),
169
+ headers: {
170
+ Accept: RUNNER_ACCEPT_HEADER,
171
+ },
172
+ });
173
+ }
174
+ catch (error) {
175
+ console.error('[TelemetryReporter] Failed to post collection run data:', error.message);
176
+ }
177
+ });
178
+ }
@@ -0,0 +1,69 @@
1
+ import { z } from 'zod';
2
+ import { asMcpError, McpError } from './utils/toolHelpers.js';
3
+ export const method = 'searchPostmanElements';
4
+ export const description = 'Searches for Postman elements in the public network.\n\n**When to Use This Tool:**\n- When the user asks for a specific named request (e.g., "find PayPal requests", "search for Stripe API requests")\n- When the user explicitly wants to search the public network\n- Do NOT use this for searching the user\'s own workspaces or collections (use getCollections or getWorkspaces instead)\n\n**Search Scope:**\n- Only searches the public network (public workspaces and collections)\n- Does not search private workspaces, team workspaces, or personal collections\n- Currently supports searching for requests (entityType: "requests")\n';
5
+ export const parameters = z.object({
6
+ entityType: z
7
+ .literal('requests')
8
+ .describe('The type of Postman [entity](https://learning.postman.com/docs/getting-started/basics/postman-elements/) (element) to search for. At this time, this only accepts the `requests` value.'),
9
+ q: z
10
+ .string()
11
+ .min(1)
12
+ .max(512)
13
+ .describe('The query used to search for Postman elements.')
14
+ .optional(),
15
+ publisherIsVerified: z
16
+ .boolean()
17
+ .describe('Filter the search results to only return entities from publishers [verified](https://learning.postman.com/docs/collaborating-in-postman/public-api-network/verify-your-team/) by Postman.')
18
+ .optional(),
19
+ nextCursor: z
20
+ .string()
21
+ .describe('The cursor to get the next set of results in the paginated response. If you pass an invalid value, the API returns empty results.')
22
+ .optional(),
23
+ limit: z
24
+ .number()
25
+ .int()
26
+ .gte(1)
27
+ .lte(10)
28
+ .describe('The max number of search results returned in the response.')
29
+ .default(10),
30
+ });
31
+ export const annotations = {
32
+ title: 'Searches for Postman elements in the public network.\n\n**When to Use This Tool:**\n- When the user asks for a specific named request (e.g., "find PayPal requests", "search for Stripe API requests")\n- When the user explicitly wants to search the public network\n- Do NOT use this for searching the user\'s own workspaces or collections (use getCollections or getWorkspaces instead)\n\n**Search Scope:**\n- Only searches the public network (public workspaces and collections)\n- Does not search private workspaces, team workspaces, or personal collections\n- Currently supports searching for requests (entityType: "requests")\n',
33
+ readOnlyHint: true,
34
+ destructiveHint: false,
35
+ idempotentHint: true,
36
+ };
37
+ export async function handler(args, extra) {
38
+ try {
39
+ const endpoint = `/search/${args.entityType}`;
40
+ const query = new URLSearchParams();
41
+ if (args.q !== undefined)
42
+ query.set('q', String(args.q));
43
+ if (args.publisherIsVerified !== undefined)
44
+ query.set('publisherIsVerified', String(args.publisherIsVerified));
45
+ if (args.nextCursor !== undefined)
46
+ query.set('nextCursor', String(args.nextCursor));
47
+ if (args.limit !== undefined)
48
+ query.set('limit', String(args.limit));
49
+ const url = query.toString() ? `${endpoint}?${query.toString()}` : endpoint;
50
+ const options = {
51
+ headers: extra.headers,
52
+ };
53
+ const result = await extra.client.get(url, options);
54
+ return {
55
+ content: [
56
+ {
57
+ type: 'text',
58
+ text: `${typeof result === 'string' ? result : JSON.stringify(result, null, 2)}`,
59
+ },
60
+ ],
61
+ };
62
+ }
63
+ catch (e) {
64
+ if (e instanceof McpError) {
65
+ throw e;
66
+ }
67
+ throw asMcpError(e);
68
+ }
69
+ }