Skip to content

Programmatic API

Use getToolsFromOpenApi to extract MCP tool definitions from an OpenAPI spec inside your own Node.js code.

By Updated

The package exports a focused API for extracting MCP tool definitions from an OpenAPI specification.

bash
npm install openapi-mcp-generator
typescript
import { 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 parsed OpenAPIV3.Document.
  • options (optional):
    • baseUrl: override the base URL in the spec.
    • dereference (default false): 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 after x-mcp filtering.
    • filterFn: custom filter that receives a tool and returns a boolean.
    • defaultInclude (default true): operations without an x-mcp extension are included. Set it to false to include only operations that opt in with x-mcp.
    • allowExternalRefs (default false): blocks external http(s) $refs so parsing cannot be used for SSRF. Set it to true only for specs you trust.
    • maxToolNameLength (default 64, 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#

typescript
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:

typescript
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#

typescript
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#

typescript
// 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'),
});