server.ts•7.8 kB
import "dotenv/config";
import {
McpServer,
ResourceTemplate,
} from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
import {
listWorkspaces,
getWorkspace,
getCollectionsForWorkspace,
createCollection,
getCollection,
updateCollection,
createRequest,
getRequest,
updateRequest,
createResponse,
getResponse,
updateResponse,
runMonitor,
} from "./postman.js";
const server = new McpServer(
{ name: "postman-mcp", version: "0.1.0" },
{
capabilities: {
tools: {},
resources: {},
},
},
);
const jsonResult = (data: unknown) => ({
content: [{ type: "text" as const, text: JSON.stringify(data, null, 2) }],
});
server.registerTool(
"listWorkspaces",
{
description:
"List all workspaces available to the authenticated API key.",
},
async () => jsonResult(await listWorkspaces()),
);
server.registerTool(
"getWorkspace",
{
description:
"Retrieve details of a specific workspace, including its collections and other elements.",
inputSchema: {
workspaceId: z.string(),
},
},
async ({ workspaceId }) => jsonResult(await getWorkspace(workspaceId)),
);
server.registerTool(
"listCollectionsInWorkspace",
{
description:
"List collection references contained in a workspace by workspace ID.",
inputSchema: {
workspaceId: z.string(),
},
},
async ({ workspaceId }) =>
jsonResult(await getCollectionsForWorkspace(workspaceId)),
);
server.registerTool(
"createCollection",
{
description:
"Create a new collection using the Postman Collection v2 format. Optionally specify a workspaceId to add the collection to.",
inputSchema: {
collection: z.record(z.any()),
workspaceId: z.string().optional(),
},
},
async (input) => jsonResult(await createCollection(input.collection, input.workspaceId)),
);
server.registerTool(
"getCollection",
{
description: "Retrieve the full definition of a collection by its UID.",
inputSchema: {
collectionUid: z.string(),
},
},
async ({ collectionUid }) => jsonResult(await getCollection(collectionUid)),
);
server.registerTool(
"updateCollection",
{
description:
"Replace an existing collection with a new Postman Collection v2 definition.",
inputSchema: {
collectionUid: z.string(),
collection: z.record(z.any()),
},
},
async (input) =>
jsonResult(await updateCollection(input.collectionUid, input.collection)),
);
server.registerTool(
"createRequest",
{
description:
"Create a request item within a collection. The request object must follow the Postman Collection v2 specification.",
inputSchema: {
collectionId: z.string(),
request: z.record(z.any()),
},
},
async (input) => jsonResult(await createRequest(input.collectionId, input.request)),
);
server.registerTool(
"getRequest",
{
description:
"Retrieve a single request within a collection by collection ID and request ID.",
inputSchema: {
collectionId: z.string(),
requestId: z.string(),
},
},
async (input) =>
jsonResult(await getRequest(input.collectionId, input.requestId)),
);
server.registerTool(
"updateRequest",
{
description:
"Update an existing request item in a collection. Replaces the request definition with the provided object.",
inputSchema: {
collectionId: z.string(),
requestId: z.string(),
request: z.record(z.any()),
},
},
async (input) =>
jsonResult(
await updateRequest(input.collectionId, input.requestId, input.request),
),
);
server.registerTool(
"createResponse",
{
description:
"Create a response example (sample response) for a request in a collection. Requires the parent request ID.",
inputSchema: {
collectionId: z.string(),
requestId: z.string(),
response: z.record(z.any()),
},
},
async (input) =>
jsonResult(
await createResponse(
input.collectionId,
input.requestId,
input.response,
),
),
);
server.registerTool(
"getResponse",
{
description: "Retrieve a response example by collection ID and response ID.",
inputSchema: {
collectionId: z.string(),
responseId: z.string(),
},
},
async (input) =>
jsonResult(await getResponse(input.collectionId, input.responseId)),
);
server.registerTool(
"updateResponse",
{
description: "Update an existing response example within a collection.",
inputSchema: {
collectionId: z.string(),
responseId: z.string(),
response: z.record(z.any()),
},
},
async (input) =>
jsonResult(
await updateResponse(
input.collectionId,
input.responseId,
input.response,
),
),
);
server.registerTool(
"runMonitor",
{
description:
"Run a Postman monitor synchronously and return its run results.",
inputSchema: {
monitorUid: z.string(),
},
},
async ({ monitorUid }) => jsonResult(await runMonitor(monitorUid)),
);
const workspaceResourceTemplate = new ResourceTemplate(
"postman://workspace/{workspaceId}",
{
list: async () => {
const data = await listWorkspaces();
const workspaces = Array.isArray((data as any)?.workspaces)
? (data as any).workspaces
: [];
const resources = workspaces.flatMap((workspace: any) => {
const id = workspace?.id ?? workspace?.uid ?? null;
if (!id) return [];
return [
{
uri: `postman://workspace/${id}`,
name: id,
title: workspace?.name ?? id,
description:
workspace?.description ??
`Postman ${workspace?.type ?? "team"} workspace`,
mimeType: "application/json",
},
];
});
return { resources };
},
complete: {
workspaceId: async (value) => {
const query = value?.trim().toLowerCase();
const data = await listWorkspaces();
const workspaces = Array.isArray((data as any)?.workspaces)
? (data as any).workspaces
: [];
const entries: Array<{ id: string; name: string }> = workspaces
.map((workspace: any) => ({
id: (workspace?.id ?? workspace?.uid ?? "") as string,
name: (workspace?.name ?? "") as string,
}))
.filter(
(entry: { id: string; name: string }) => entry.id.length > 0,
);
return entries
.filter((entry) => {
if (!query) return true;
return (
entry.id.toLowerCase().includes(query) ||
entry.name.toLowerCase().includes(query)
);
})
.map((entry) => entry.id);
},
},
},
);
server.resource(
"postman-workspace",
workspaceResourceTemplate,
{
title: "Postman workspace metadata",
description:
"Workspace information fetched from the Postman API. Use the workspace ID or UID in the URI.",
mimeType: "application/json",
},
async (uri, variables) => {
const rawWorkspaceId = variables.workspaceId;
const workspaceId = Array.isArray(rawWorkspaceId)
? rawWorkspaceId[0]
: rawWorkspaceId;
if (!workspaceId) {
throw new Error("Missing workspaceId in resource URI.");
}
const workspace = await getWorkspace(workspaceId);
return {
contents: [
{
uri: uri.toString(),
mimeType: "application/json",
text: JSON.stringify(workspace, null, 2),
},
],
};
},
);
const transport = new StdioServerTransport();
await server.connect(transport);