OpenAPI Integration
STDIOMCP server exposing OpenAPI endpoints for LLMs to discover and interact with REST APIs.
MCP server exposing OpenAPI endpoints for LLMs to discover and interact with REST APIs.
A Model Context Protocol (MCP) server that exposes OpenAPI endpoints as MCP resources. This server allows Large Language Models to discover and interact with REST APIs defined by OpenAPI specifications through the MCP protocol.
This section covers how to use the MCP server as an end user with Claude Desktop, Cursor, or other MCP-compatible tools.
This MCP server can be used in two ways:
npx @ivotoby/openapi-mcp-server
directly with command-line arguments for quick setupOpenAPIServer
class in your own Node.js applications for custom implementationsThe server supports two transport methods:
No need to clone this repository. Simply configure Claude Desktop to use this MCP server:
Locate or create your Claude Desktop configuration file:
~/Library/Application Support/Claude/claude_desktop_config.json
Add the following configuration:
{ "mcpServers": { "openapi": { "command": "npx", "args": ["-y", "@ivotoby/openapi-mcp-server"], "env": { "API_BASE_URL": "https://api.example.com", "OPENAPI_SPEC_PATH": "https://api.example.com/openapi.json", "API_HEADERS": "Authorization:Bearer token123,X-API-Key:your-api-key" } } } }
API_BASE_URL
: The base URL of your APIOPENAPI_SPEC_PATH
: URL or path to your OpenAPI specificationAPI_HEADERS
: Comma-separated key:value pairs for API authentication headersTo use the server with HTTP clients:
npx @ivotoby/openapi-mcp-server \ --api-base-url https://api.example.com \ --openapi-spec https://api.example.com/openapi.json \ --headers "Authorization:Bearer token123" \ --transport http \ --port 3000
# Initialize a session (first request) curl -X POST http://localhost:3000/mcp \ -H "Content-Type: application/json" \ -d '{"jsonrpc":"2.0","id":0,"method":"initialize","params":{"protocolVersion":"2025-03-26","capabilities":{},"clientInfo":{"name":"curl-client","version":"1.0.0"}}}' # The response includes a Mcp-Session-Id header that you must use for subsequent requests # and the InitializeResult directly in the POST response body. # Send a request to list tools # This also receives its response directly on this POST request. curl -X POST http://localhost:3000/mcp \ -H "Content-Type: application/json" \ -H "Mcp-Session-Id: your-session-id" \ -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}' # Open a streaming connection for other server responses (e.g., tool execution results) # This uses Server-Sent Events (SSE). curl -N http://localhost:3000/mcp -H "Mcp-Session-Id: your-session-id" # Example: Execute a tool (response will arrive on the GET stream) # curl -X POST http://localhost:3000/mcp \ # -H "Content-Type: application/json" \ # -H "Mcp-Session-Id: your-session-id" \ # -d '{"jsonrpc":"2.0","id":2,"method":"tools/execute","params":{"name":"yourToolName", "arguments": {}}}' # Terminate the session when done curl -X DELETE http://localhost:3000/mcp -H "Mcp-Session-Id: your-session-id"
The server can be configured through environment variables or command line arguments:
API_BASE_URL
- Base URL for the API endpointsOPENAPI_SPEC_PATH
- Path or URL to OpenAPI specificationOPENAPI_SPEC_FROM_STDIN
- Set to "true" to read OpenAPI spec from standard inputOPENAPI_SPEC_INLINE
- Provide OpenAPI spec content directly as a stringAPI_HEADERS
- Comma-separated key:value pairs for API headersSERVER_NAME
- Name for the MCP server (default: "mcp-openapi-server")SERVER_VERSION
- Version of the server (default: "1.0.0")TRANSPORT_TYPE
- Transport type to use: "stdio" or "http" (default: "stdio")HTTP_PORT
- Port for HTTP transport (default: 3000)HTTP_HOST
- Host for HTTP transport (default: "127.0.0.1")ENDPOINT_PATH
- Endpoint path for HTTP transport (default: "/mcp")TOOLS_MODE
- Tools loading mode: "all" (load all endpoint-based tools), "dynamic" (load only meta-tools), or "explicit" (load only tools specified in includeTools) (default: "all")DISABLE_ABBREVIATION
- Disable name optimization (this could throw errors when name is > 64 chars)npx @ivotoby/openapi-mcp-server \ --api-base-url https://api.example.com \ --openapi-spec https://api.example.com/openapi.json \ --headers "Authorization:Bearer token123,X-API-Key:your-api-key" \ --name "my-mcp-server" \ --server-version "1.0.0" \ --transport http \ --port 3000 \ --host 127.0.0.1 \ --path /mcp \ --disable-abbreviation true
The MCP server supports multiple methods for loading OpenAPI specifications, providing flexibility for different deployment scenarios:
Load the OpenAPI spec from a remote URL:
npx @ivotoby/openapi-mcp-server \ --api-base-url https://api.example.com \ --openapi-spec https://api.example.com/openapi.json
Load the OpenAPI spec from a local file:
npx @ivotoby/openapi-mcp-server \ --api-base-url https://api.example.com \ --openapi-spec ./path/to/openapi.yaml
Read the OpenAPI spec from standard input (useful for piping or containerized environments):
# Pipe from file cat openapi.json | npx @ivotoby/openapi-mcp-server \ --api-base-url https://api.example.com \ --spec-from-stdin # Pipe from curl curl -s https://api.example.com/openapi.json | npx @ivotoby/openapi-mcp-server \ --api-base-url https://api.example.com \ --spec-from-stdin # Using environment variable export OPENAPI_SPEC_FROM_STDIN=true echo '{"openapi": "3.0.0", ...}' | npx @ivotoby/openapi-mcp-server \ --api-base-url https://api.example.com
Provide the OpenAPI spec content directly as a command line argument:
npx @ivotoby/openapi-mcp-server \ --api-base-url https://api.example.com \ --spec-inline '{"openapi": "3.0.0", "info": {"title": "My API", "version": "1.0.0"}, "paths": {}}' # Using environment variable export OPENAPI_SPEC_INLINE='{"openapi": "3.0.0", ...}' npx @ivotoby/openapi-mcp-server --api-base-url https://api.example.com
All loading methods support both JSON and YAML formats. The server automatically detects the format and parses accordingly.
For containerized deployments, you can mount OpenAPI specs or use stdin:
# Mount local file docker run -v /path/to/spec:/app/spec.json your-mcp-server \ --api-base-url https://api.example.com \ --openapi-spec /app/spec.json # Use stdin with docker cat openapi.json | docker run -i your-mcp-server \ --api-base-url https://api.example.com \ --spec-from-stdin
The server provides detailed error messages for spec loading failures:
Only one specification source can be used at a time. The server will validate that exactly one of the following is provided:
--openapi-spec
(URL or file path)--spec-from-stdin
--spec-inline
If multiple sources are specified, the server will exit with an error message.
Based on the Stainless article "What We Learned Converting Complex OpenAPI Specs to MCP Servers" (https://www.stainless.com/blog/what-we-learned-converting-complex-openapi-specs-to-mcp-servers), the following flags were added to control which API endpoints (tools) are loaded:
--tools <all|dynamic|explicit>
: Choose tool loading mode:
all
(default): Load all tools from the OpenAPI spec, applying any specified filtersdynamic
: Load only dynamic meta-tools (list-api-endpoints
, get-api-endpoint-schema
, invoke-api-endpoint
)explicit
: Load only tools explicitly listed in --tool
options, ignoring all other filters--tool <toolId>
: Import only specified tool IDs or names. Can be used multiple times.--tag <tag>
: Import only tools with the specified OpenAPI tag. Can be used multiple times.--resource <resource>
: Import only tools under the specified resource path prefixes. Can be used multiple times.--operation <method>
: Import only tools for the specified HTTP methods (get, post, etc). Can be used multiple times.Examples:
# Load only dynamic meta-tools npx @ivotoby/openapi-mcp-server --api-base-url https://api.example.com --openapi-spec https://api.example.com/openapi.json --tools dynamic # Load only explicitly specified tools (ignores other filters) npx @ivotoby/openapi-mcp-server --api-base-url https://api.example.com --openapi-spec https://api.example.com/openapi.json --tools explicit --tool GET::users --tool POST::users # Load only the GET /users endpoint tool (using all mode with filtering) npx @ivotoby/openapi-mcp-server --api-base-url https://api.example.com --openapi-spec https://api.example.com/openapi.json --tool GET-users # Load tools tagged with "user" under the "/users" resource npx @ivotoby/openapi-mcp-server --api-base-url https://api.example.com --openapi-spec https://api.example.com/openapi.json --tag user --resource users # Load only POST operations npx @ivotoby/openapi-mcp-server --api-base-url https://api.example.com --openapi-spec https://api.example.com/openapi.json --operation post
The stdio transport is designed for direct integration with AI systems like Claude Desktop that manage MCP connections through standard input/output. This is the simplest setup and requires no network configuration.
When to use: When integrating with Claude Desktop or other systems that support stdio-based MCP communication.
The HTTP transport allows the MCP server to be accessed over HTTP, enabling web applications and other HTTP-capable clients to interact with the MCP protocol. It supports session management, streaming responses, and standard HTTP methods.
Key features:
initialize
and tools/list
requests are sent synchronously on the POST.tools/execute
results, notifications) are streamed over a GET connection using Server-Sent Events (SSE).When to use: When you need to expose the MCP server to web clients or systems that communicate over HTTP rather than stdio.
To see debug logs:
When using stdio transport with Claude Desktop:
When using HTTP transport:
npx @ivotoby/openapi-mcp-server --transport http &2>debug.log
This section is for developers who want to use this package as a library to create custom MCP servers.
Create dedicated MCP servers for specific APIs by importing and configuring the OpenAPIServer
class. This approach is ideal for:
AuthProvider
interfaceimport { OpenAPIServer } from "@ivotoby/openapi-mcp-server" import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js" const config = { name: "my-api-server", version: "1.0.0", apiBaseUrl: "https://api.example.com", openApiSpec: "https://api.example.com/openapi.json", specInputMethod: "url" as const, headers: { Authorization: "Bearer your-token", "X-API-Key": "your-api-key", }, transportType: "stdio" as const, toolsMode: "all" as const, // Options: "all", "dynamic", "explicit" } const server = new OpenAPIServer(config) const transport = new StdioServerTransport() await server.start(transport)
The toolsMode
configuration option controls which tools are loaded from your OpenAPI specification:
// Load all tools from the spec (default) const config = { // ... other config toolsMode: "all" as const, // Optional: Apply filters to control which tools are loaded includeTools: ["GET::users", "POST::users"], // Only these tools includeTags: ["public"], // Only tools with these tags includeResources: ["users"], // Only tools under these resources includeOperations: ["get", "post"], // Only these HTTP methods } // Load only dynamic meta-tools for API exploration const config = { // ... other config toolsMode: "dynamic" as const, // Provides: list-api-endpoints, get-api-endpoint-schema, invoke-api-endpoint } // Load only explicitly specified tools (ignores other filters) const config = { // ... other config toolsMode: "explicit" as const, includeTools: ["GET::users", "POST::users"], // Only these exact tools // includeTags, includeResources, includeOperations are ignored in explicit mode }
For APIs with token expiration, refresh requirements, or complex authentication:
import { OpenAPIServer, AuthProvider } from "@ivotoby/openapi-mcp-server" import { AxiosError } from "axios" class MyAuthProvider implements AuthProvider { async getAuthHeaders(): Promise<Record<string, string>> { // Called before each request - return fresh headers if (this.isTokenExpired()) { await this.refreshToken() } return { Authorization: `Bearer ${this.token}` } } async handleAuthError(error: AxiosError): Promise<boolean> { // Called on 401/403 errors - return true to retry if (error.response?.status === 401) { await this.refreshToken() return true // Retry the request } return false } } const authProvider = new MyAuthProvider() const config = { // ... other config authProvider: authProvider, // Use AuthProvider instead of static headers }
📁 See the examples/ directory for complete, runnable examples including:
The AuthProvider
interface enables sophisticated authentication scenarios that static headers cannot handle:
interface AuthProvider { /** * Get authentication headers for the current request * Called before each API request to get fresh headers */ getAuthHeaders(): Promise<Record<string, string>> /** * Handle authentication errors from API responses * Called when the API returns 401 or 403 errors * Return true to retry the request, false otherwise */ handleAuthError(error: AxiosError): Promise<boolean> }
class RefreshableAuthProvider implements AuthProvider { async getAuthHeaders(): Promise<Record<string, string>> { if (this.isTokenExpired()) { await this.refreshToken() } return { Authorization: `Bearer ${this.accessToken}` } } async handleAuthError(error: AxiosError): Promise<boolean> { if (error.response?.status === 401) { await this.refreshToken() return true // Retry with fresh token } return false } }
class ManualTokenAuthProvider implements AuthProvider { async getAuthHeaders(): Promise<Record<string, string>> { if (!this.token || this.isTokenExpired()) { throw new Error( "Token expired. Please get a new token from your browser:\n" + "1. Go to the API website and log in\n" + "2. Open browser dev tools (F12)\n" + "3. Copy the Authorization header from any API request\n" + "4. Update your token using updateToken()", ) } return { Authorization: `Bearer ${this.token}` } } updateToken(token: string): void { this.token = token this.tokenExpiry = new Date(Date.now() + 3600000) // 1 hour } }
class ApiKeyAuthProvider implements AuthProvider { constructor(private apiKey: string) {} async getAuthHeaders(): Promise<Record<string, string>> { return { "X-API-Key": this.apiKey } } async handleAuthError(error: AxiosError): Promise<boolean> { throw new Error("API key authentication failed. Please check your key.") } }
📖 For detailed AuthProvider documentation and examples, see docs/auth-provider-guide.md
This MCP server implements robust OpenAPI reference ($ref
) resolution to ensure accurate representation of API schemas:
$ref
pointers to parameter components in the OpenAPI specThe server intelligently merges parameters and request bodies into a unified input schema for each tool:
The MCP server handles various OpenAPI schema complexities:
npm run build
- Builds the TypeScript sourcenpm run clean
- Removes build artifactsnpm run typecheck
- Runs TypeScript type checkingnpm run lint
- Runs ESLintnpm run dev
- Watches source files and rebuilds on changesnpm run inspect-watch
- Runs the inspector with auto-reload on changesnpm install
npm run inspect-watch
src/
npm run typecheck && npm run lint
📖 For comprehensive developer documentation, see docs/developer-guide.md
Q: What is a "tool"? A: A tool corresponds to a single API endpoint derived from your OpenAPI specification, exposed as an MCP resource.
Q: How can I use this package in my own project?
A: You can import the OpenAPIServer
class and use it as a library in your Node.js application. This allows you to create dedicated MCP servers for specific APIs with custom authentication, filtering, and error handling. See the examples/ directory for complete implementations.
Q: What's the difference between using the CLI and using it as a library?
A: The CLI is great for quick setup and testing, while the library approach allows you to create dedicated packages for specific APIs, implement custom authentication with AuthProvider
, add custom logic, and distribute your server as a standalone npm module.
Q: How do I handle APIs with expiring tokens?
A: Use the AuthProvider
interface instead of static headers. AuthProvider allows you to implement dynamic authentication with token refresh, expiration handling, and custom error recovery. See the AuthProvider examples for different patterns.
Q: What is AuthProvider and when should I use it?
A: AuthProvider
is an interface for dynamic authentication that gets fresh headers before each request and handles authentication errors. Use it when your API has expiring tokens, requires token refresh, or needs complex authentication logic that static headers can't handle.
Q: How do I filter which tools are loaded?
A: Use the --tool
, --tag
, --resource
, and --operation
flags with --tools all
(default), set --tools dynamic
for meta-tools only, or use --tools explicit
to load only tools specified with --tool
(ignoring other filters).
Q: When should I use dynamic mode?
A: Dynamic mode provides meta-tools (list-api-endpoints
, get-api-endpoint-schema
, invoke-api-endpoint
) to inspect and interact with endpoints without preloading all operations, which is useful for large or changing APIs.
Q: How do I specify custom headers for API requests?
A: Use the --headers
flag or API_HEADERS
environment variable with key:value
pairs separated by commas for CLI usage. For library usage, use the headers
config option or implement an AuthProvider
for dynamic headers.
Q: Which transport methods are supported? A: The server supports stdio transport (default) for integration with AI systems and HTTP transport (with streaming via SSE) for web clients.
Q: How does the server handle complex OpenAPI schemas with references?
A: The server fully resolves $ref
references in parameters and schemas, preserving nested structures, default values, and other attributes. See the "OpenAPI Schema Processing" section for details on reference resolution and schema composition.
Q: What happens when parameter names conflict with request body properties?
A: The server detects naming conflicts and automatically prefixes body property names with body_
to avoid collisions, ensuring all properties are accessible.
Q: Can I package my MCP server for distribution?
A: Yes! When using the library approach, you can create a dedicated npm package for your API. See the Beatport example for a complete implementation that can be packaged and distributed as npx your-api-mcp-server
.
Q: Where can I find development and contribution guidelines? A: See the Developer Guide for comprehensive documentation on architecture, key concepts, development workflow, and contribution guidelines.
MIT