UNPKG

@postman/postman-mcp-server

Version:

Postman MCP Server — connect AI agents (Claude Code, Cursor, VS Code Copilot, Gemini CLI) to your Postman collections, specifications, and environments via Model Context Protocol (MCP)

84 lines (83 loc) • 3.89 kB
import { z } from 'zod'; import { ContentType } from '../clients/postman.js'; import { asMcpError, McpError } from './utils/toolHelpers.js'; export const method = 'createSpec'; export const title = 'Create a spec'; 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, 3.0, and 3.1), AsyncAPI (2.0 and 3.0), protobuf (2 and 3), GraphQL, and Smithy 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 12 MB in size.\n"; export const parameters = z.object({ workspaceId: z.string().describe("The workspace's ID."), name: z.string().describe("The specification's name."), type: z .preprocess((v) => (typeof v === 'string' ? v.toUpperCase() : v), z.enum([ 'OPENAPI:2.0', 'OPENAPI:3.0', 'OPENAPI:3.1', 'ASYNCAPI:2.0', 'ASYNCAPI:3.0', 'PROTOBUF:2', 'PROTOBUF:3', 'GRAPHQL', 'SMITHY:2.0', ])) .describe('The type of API specification.'), files: z .array(z.union([ z.object({ path: z.string().describe("The file's path. Accepts .json, .yaml, and .proto types."), content: z.string().describe("The file's stringified contents."), type: z .preprocess((v) => (typeof v === 'string' ? v.toUpperCase() : v), z.enum(['DEFAULT', 'ROOT'])) .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'), }), z.object({ path: z .string() .describe("The file's path. Accepts .json, .yaml, .proto, .graphql, and .smithy file types."), content: z.string().describe("The file's stringified contents."), }), ])) .describe("A list of the specification's files and their contents."), }); export const annotations = { title: 'Create a spec', readOnlyHint: false, openWorldHint: true, destructiveHint: false, idempotentHint: false, }; export async function handler(args, extra) { try { const endpoint = `/specs`; const query = new URLSearchParams(); if (args.workspaceId !== undefined) query.set('workspaceId', String(args.workspaceId)); const url = query.toString() ? `${endpoint}?${query.toString()}` : endpoint; const bodyPayload = {}; if (args.name !== undefined) bodyPayload.name = args.name; if (args.type !== undefined) bodyPayload.type = args.type; if (args.files !== undefined) bodyPayload.files = args.files; const options = { body: JSON.stringify(bodyPayload), contentType: ContentType.Json, headers: extra.headers, }; const result = await extra.client.post(url, options); return { content: [ { type: 'text', text: `${typeof result === 'string' ? result : JSON.stringify(result, null, 2)}`, }, ], }; } catch (e) { if (e instanceof McpError) { throw e; } throw asMcpError(e); } }