Programmatic API
Use getToolsFromOpenApi to extract MCP tool definitions from an OpenAPI spec inside your own Node.js code.
By Harsha VardhanUpdated
The package exports a focused API for extracting MCP tool definitions from an OpenAPI specification.
npm install openapi-mcp-generatorimport { getToolsFromOpenApi } from 'openapi-mcp-generator';getToolsFromOpenApi(specPathOrUrl, options)#
Returns a promise that resolves to an array of McpToolDefinition objects.
Parameters
specPathOrUrl: a path to a local spec file, a URL to a remote spec, or an already parsedOpenAPIV3.Document.options(optional):baseUrl: override the base URL in the spec.dereference(defaultfalse): resolve$refs before extracting tools.excludeOperationIds: operation IDs to leave out.includeTags/excludeTags: keep only, or drop, operations with these tags (case-insensitive).includeOperations/excludeOperations: keep only, or drop, these operations (operationId or tool name). These four match the CLI flags: include lists combine, exclusions win, and they apply afterx-mcpfiltering.filterFn: custom filter that receives a tool and returns a boolean.defaultInclude(defaulttrue): operations without anx-mcpextension are included. Set it tofalseto include only operations that opt in withx-mcp.allowExternalRefs(defaultfalse): blocks externalhttp(s)$refs so parsing cannot be used for SSRF. Set it totrueonly for specs you trust.maxToolNameLength(default64, the Claude Desktop limit): longer tool names are shortened.
Filters run in this order: tag and operation filters, then excludeOperationIds, then filterFn last.
Swagger 2.0 documents are rejected with an error. Convert them to OpenAPI 3 first with npx swagger2openapi swagger.json -o openapi.json.
Example#
import { getToolsFromOpenApi } from 'openapi-mcp-generator';
// Basic usage
const tools = await getToolsFromOpenApi('./petstore.json', { dereference: true });
// With options
const filteredTools = await getToolsFromOpenApi(
'https://petstore3.swagger.io/api/v3/openapi.json',
{
baseUrl: 'https://petstore3.swagger.io/api/v3',
dereference: true,
includeTags: ['pet'],
excludeOperations: ['deletePet'],
filterFn: (tool) => tool.annotations?.readOnlyHint === true,
}
);
for (const tool of filteredTools) {
console.log(`Tool: ${tool.name} (${tool.title ?? 'untitled'})`);
console.log(` Description: ${tool.description}`);
console.log(` Method: ${tool.method.toUpperCase()} ${tool.pathTemplate}`);
console.log(` OperationId: ${tool.operationId}`);
}Without dereference, $refs stay unresolved in the tool schemas and a warning is logged for each one.
You can also pass a parsed document:
import SwaggerParser from '@apidevtools/swagger-parser';
import type { OpenAPIV3 } from 'openapi-types';
import { getToolsFromOpenApi } from 'openapi-mcp-generator';
const api = (await SwaggerParser.dereference('./openapi.yaml')) as OpenAPIV3.Document;
const tools = await getToolsFromOpenApi(api);Tool definition#
interface McpToolDefinition {
name: string; // unique tool name
title?: string; // x-mcp-title, else the summary
description: string; // human-readable description
inputSchema: JSONSchema7 | boolean; // JSON Schema 2020-12 for the input
method: string; // HTTP method (get, post, ...)
pathTemplate: string; // URL path with placeholders
parameters: OpenAPIV3.ParameterObject[];
executionParameters: { name: string; in: string }[];
requestBodyContentType?: string;
securityRequirements: OpenAPIV3.SecurityRequirementObject[];
serverUrl?: string; // operation or path server, variables resolved
operationId: string; // original operationId
tags?: string[];
deprecated?: boolean;
annotations?: McpToolAnnotations; // readOnlyHint, destructiveHint, idempotentHint, openWorldHint
outputSchema?: JSONSchema7; // lowest 2xx application/json response, when it is an object
}At runtime each returned tool also carries baseUrl, the resolved base URL or an empty string. McpToolDefinition does not declare it yet, so TypeScript code needs a cast to read it.
inputSchema is JSON Schema 2020-12: integers stay integer, example becomes examples, and properties marked readOnly are left out of request inputs. annotations follow the HTTP method (GET is read-only, DELETE is destructive) unless the operation sets x-mcp-annotations. Deprecated operations get a [DEPRECATED] prefix in their description.
Filtering recipes#
// Read-only tools
const readOnly = await getToolsFromOpenApi(specUrl, {
filterFn: (tool) => tool.annotations?.readOnlyHint === true,
});
// One tag, minus a delete
const petTools = await getToolsFromOpenApi(specUrl, {
includeTags: ['pets'],
excludeOperations: ['deletePet'],
});
// Only operations that declare security
const secured = await getToolsFromOpenApi(specUrl, {
filterFn: (tool) => tool.securityRequirements.length > 0,
});
// A path prefix, minus a few write operations
const userTools = await getToolsFromOpenApi(specUrl, {
excludeOperationIds: ['deleteUser', 'updateUser'],
filterFn: (tool) => tool.pathTemplate.includes('/user'),
});