You have an OpenAPI spec. You want Claude to call your API. The "standard" approach? Write a custom MCP server, define every tool by hand, wire up auth, handle errors, deploy it.
Or just run one command:
npx mcp-openapi --spec https://petstore3.swagger.io/api/v3/openapi.json
Done. Every endpoint in your spec is now an MCP tool. Claude can call them all.
No code generation. No config files. No boilerplate.
The Problem: MCP Servers Are Tedious to Build
Model Context Protocol (MCP) is the open standard that lets AI assistants like Claude call external tools. It's powerful — but building an MCP server for your existing REST API means:
- Reading your OpenAPI spec manually
- Writing a tool definition for each endpoint
- Mapping parameters (path, query, headers, body) correctly
- Handling authentication
- Adding error handling, retries, response formatting
- Deploying and maintaining it
For an API with 50 endpoints, that's days of repetitive glue code. And every time the API changes, you update the MCP server too.
What if the spec itself is the server?
How mcp-openapi Works
mcp-openapi reads your OpenAPI 3.x or Swagger 2.0 spec and generates MCP tools at runtime. No build step. No generated files.
OpenAPI Spec → mcp-openapi → MCP Tools → Claude Desktop
(URL/file) (runtime) (auto-generated) (calls your API)
Each endpoint becomes one tool:
-
Name is derived from
operationId(e.g.,listPets,createUser) - Description comes from the spec's summary/description
- Parameters are flattened into a single schema (more on this below)
- Responses are automatically truncated to fit LLM context windows
Add It to Claude Desktop
Edit your claude_desktop_config.json:
{
"mcpServers": {
"petstore": {
"command": "npx",
"args": [
"mcp-openapi",
"--spec", "https://petstore3.swagger.io/api/v3/openapi.json"
]
}
}
}
Restart Claude Desktop. Ask "List all available pets" — Claude calls find_pets_by_status automatically.
Why Flat Parameter Schemas Matter
Here's something most OpenAPI-to-MCP converters get wrong: they pass nested JSON objects to the LLM.
Consider an endpoint like POST /users with a JSON body. A naive converter gives the LLM this input schema:
{
"body": {
"type": "object",
"properties": {
"name": { "type": "string" },
"email": { "type": "string" },
"address": {
"type": "object",
"properties": {
"street": { "type": "string" },
"city": { "type": "string" }
}
}
}
}
}
LLMs frequently mess up nested structures — missing braces, wrong nesting levels, hallucinated fields. The deeper the nesting, the worse the accuracy.
mcp-openapi flattens everything into a single level:
{
"name": { "type": "string" },
"email": { "type": "string" },
"address_street": { "type": "string" },
"address_city": { "type": "string" }
}
Path parameters, query parameters, headers, and body fields all live in one flat object. The tool reconstructs the proper HTTP request internally. The LLM only sees simple key-value pairs.
This isn't just a nice-to-have. Flat schemas dramatically improve tool-calling accuracy — fewer malformed calls, fewer retries, better user experience.
Authentication Built In
Most real APIs require auth. mcp-openapi supports three methods out of the box:
Bearer Token
{
"mcpServers": {
"github": {
"command": "npx",
"args": [
"mcp-openapi",
"--spec", "https://raw.githubusercontent.com/github/rest-api-description/main/descriptions/api.github.com/api.github.com.json",
"--auth-type", "bearer",
"--auth-token", "$GITHUB_TOKEN",
"--prefix", "github",
"--include", "listReposForAuthenticatedUser,getRepo,listIssues"
],
"env": {
"GITHUB_TOKEN": "ghp_your_token_here"
}
}
}
}
API Key
npx mcp-openapi \
--spec https://api.example.com/openapi.json \
--auth-type api-key \
--auth-name X-API-Key \
--auth-value '$MY_API_KEY' \
--auth-in header
OAuth2 Client Credentials
npx mcp-openapi \
--spec https://api.example.com/openapi.json \
--auth-type oauth2 \
--auth-client-id $CLIENT_ID \
--auth-client-secret $CLIENT_SECRET \
--auth-token-url https://auth.example.com/token
Note the $ENV_VAR syntax — tokens are never hardcoded.
Filter Endpoints
Large APIs can have hundreds of endpoints. You don't want Claude to see all of them. Use --include or --exclude to control which endpoints become tools:
# Only expose specific operations
npx mcp-openapi --spec ./api.json --include 'listUsers,getUser,createUser'
# Exclude destructive operations
npx mcp-openapi --spec ./api.json --exclude 'deleteUser,dropDatabase'
# Add a prefix to avoid naming conflicts
npx mcp-openapi --spec ./api.json --prefix myapi
With --prefix myapi, tools become myapi_list_users, myapi_get_user, etc. — useful when connecting multiple APIs to the same Claude session.
Real-World Example: GitHub API
Let's connect Claude to the GitHub API with read-only access:
1. Get a GitHub token with repo scope from github.com/settings/tokens
2. Add to Claude Desktop config:
{
"mcpServers": {
"github": {
"command": "npx",
"args": [
"mcp-openapi",
"--spec", "https://raw.githubusercontent.com/github/rest-api-description/main/descriptions/api.github.com/api.github.com.json",
"--auth-type", "bearer",
"--auth-token", "$GITHUB_TOKEN",
"--prefix", "gh",
"--include", "listReposForAuthenticatedUser,getRepo,listIssues,createIssue,listPullRequestsForRepo"
],
"env": {
"GITHUB_TOKEN": "ghp_your_token"
}
}
}
}
3. Ask Claude:
"List my GitHub repos sorted by recent activity, then show open issues for the most active one."
Claude calls gh_list_repos_for_authenticated_user, picks the top repo, then calls gh_list_issues — all from the same conversation.
Comparison with Alternatives
| Feature | mcp-openapi | openapi-mcp-generator | Hand-written servers |
|---|---|---|---|
| Zero config (no build step) | Yes | No (generates code) | No |
| Swagger 2.0 support | Yes | No | Manual |
| Flat parameter schemas | Yes | No | Manual |
| Built-in auth (Bearer/API key/OAuth2) | Yes | Partial | DIY |
| Auto-retry with backoff | Yes | No | DIY |
| Response truncation | Yes | No | DIY |
| Endpoint filtering | Yes | Yes | Manual |
| Runtime overhead | Minimal | None (pre-generated) | None |
When to use code generation instead: If you need to customize tool behavior per-endpoint (custom validation, response transforms, side effects), a code-generated approach gives you more control. mcp-openapi is for the 80% case where you just want your API callable.
Getting Started
Install and run:
npx mcp-openapi --spec <your-spec-url-or-path>
Or add to any MCP client config:
{
"mcpServers": {
"my-api": {
"command": "npx",
"args": ["mcp-openapi", "--spec", "https://your-api.com/openapi.json"]
}
}
}
Supports:
- OpenAPI 3.0.x, 3.1.x
- Swagger 2.0
- JSON and YAML specs
- Remote URLs and local files
- Node.js 18+
What's Next
We're working on Pro features for teams and enterprise use cases:
- Multi-API composition — connect multiple specs into one MCP session with intelligent routing
- Context window management — smart pagination and response streaming for large API responses
- Usage analytics — track which tools Claude calls most, optimize your API accordingly
- Custom response transforms — shape API responses before they reach the LLM
Star the repo if this is useful: github.com/Docat0209/mcp-openapi
mcp-openapi is open source (MIT). Contributions welcome.
Top comments (0)