
matthewhand/mcp-openapi-proxy
API developmentis a Python package that implements a Model Context Protocol (MCP) server, designed to dynamically expose REST APIs—defined by OpenAPI specifications—as MCP tools.
Обзор
is a Python package that implements a Model Context Protocol (MCP) server, designed to dynamically expose REST APIs—defined by OpenAPI specifications—as MCP tools.
README
mcp-openapi-proxy
mcp-openapi-proxy is a Python package that implements a Model Context Protocol (MCP) server, designed to dynamically expose REST APIs—defined by OpenAPI specifications—as MCP tools. This facilitates seamless integration of OpenAPI-described APIs into MCP-based workflows.
What’s New in 0.2.0
Works with every modern MCP-enabled client we tested. Strict MCP clients can now discover and call tools — the low-level server advertises correct capabilities and no longer crashes during resource/prompt discovery, and a slow spec download no longer crash-loops short-timeout clients. Verified live against the full list of mainstream agent CLIs:
- ✅ Codex, Gemini, Qwen, Kilocode, opencode — native tool calls over stdio
- ✅ Vibe — native discovery and read calls (writes were CLI-flaky, not a proxy issue)
- ✅ Letta — Cloud (via a remote streamable-HTTP MCP URL) and self-hosted (via stdio)
See the client matrix for attach mechanisms, models, and exact results.
📄 Full write-up: Verification case study — what the proxy is, the API + client matrices, and every defect found & fixed.
Prompts and resources are real now — including custom resources. Both MCP surfaces are functional and tested: the summarize_spec / whimsical_blog prompts and the spec_file resource, plus a new ADDITIONAL_RESOURCES env var that serves your own use-case documents (e.g. a NetBox naming policy or an Asana project-layout guide) as MCP resources — see examples/resources/.
Bug fixes (every one live-verified):
- MCP client discovery: empty capability set + a crash in resource discovery left strict clients seeing zero tools (#23) — fixed, with a full stdio-handshake test harness.
IGNORE_SSL_TOOLSwas ignored by the low-level dispatcher (#14) — fixed (original patch by @robbycochran, #15).- Server crash-loop when a slow spec fetch outran a client’s connect timeout (#28) — handshake now answers immediately, spec loads lazily, closed streams exit cleanly.
API_AUTH_TYPEcustom schemes (e.g. NetBoxToken) sent no auth header at all (#24) — fixed.TOOL_WHITELISTnever matched Slack-style dot paths like/users.list(#27) — fixed.TOOL_NAME_MAX_LENGTHwas not respected, and name-truncation collisions silently dropped tools (#11) — fixed.- Array parameters were emitted without
items, which the OpenAI API rejects (#16) — fixed. EXTRA_HEADERSnow accepts a JSON array and literal\nseparators, not just real newlines (#17).- Dead Render spec URL (#26) and incomplete ElevenLabs example (#29) — fixed; the GetZep example is documented for self-hosted Zep CE since the hosted endpoint now 401s (#38).
- Added a
Dockerfile+glama.jsonfor the Glama listing (#13); collapsible README examples + a verified-client/API matrix (#35).
Full environment-variable reference is in Environment Variables.
Table of Contents
- Overview
- Features
- Installation
- Modes of Operation
- Environment Variables
- Verified Clients & Live Results (2026-06-12)
- Examples — Glama, Fly.io, Render, Slack, GetZep, Virustotal, Notion, Asana, APIs.guru, NetBox, Box, WolframAlpha, WordPress (collapsed)
- Troubleshooting
- License
Overview
The package offers two operational modes:
- Low-Level Mode (Default): Dynamically registers tools corresponding to all valid API endpoints specified in an OpenAPI document (e.g.
/chat/completionsbecomeschat_completions()). - FastMCP Mode (Simple Mode): Provides a streamlined approach by exposing a predefined set of tools (e.g.
list_functions()andcall_function()) based on static configurations.
Features
- Dynamic Tool Generation: Automatically creates MCP tools from OpenAPI endpoint definitions.
- Simple Mode Option: Offers a static configuration alternative via FastMCP mode.
- OpenAPI Specification Support: Compatible with OpenAPI v3 with potential support for v2.
- Flexible Filtering: Allows endpoint filtering through whitelisting by paths or other criteria.
- Payload Authentication: Supports custom authentication via JMESPath expressions (e.g. for APIs like Slack that expect tokens in the payload not the HTTP header).
- Header Authentication: Uses
Bearerby default forAPI_KEYin the Authorization header, customizable for APIs like Fly.io requiringApi-Key. - MCP Integration: Seamlessly integrates with MCP ecosystems for invoking REST APIs as tools.
Installation
Install the package directly from PyPI using the following command:
uvx mcp-openapi-proxy
MCP Ecosystem Integration
To incorporate mcp-openapi-proxy into your MCP ecosystem configure it within your mcpServers settings. Below is a generic example:
{
"mcpServers": {
"mcp-openapi-proxy": {
"command": "uvx",
"args": ["mcp-openapi-proxy"],
"env": {
"OPENAPI_SPEC_URL": "${OPENAPI_SPEC_URL}",
"API_KEY": "${API_OPENAPI_KEY}"
}
}
}
}
Refer to the Examples section below for practical configurations tailored to specific APIs.
Modes of Operation
FastMCP Mode (Simple Mode)
- Enabled by: Setting the environment variable
OPENAPI_SIMPLE_MODE=true. - Description: Exposes a fixed set of tools derived from specific OpenAPI endpoints as defined in the code.
- Configuration: Relies on environment variables to specify tool behavior.
Low-Level Mode (Default)
- Description: Automatically registers all valid API endpoints from the provided OpenAPI specification as individual tools.
- Tool Naming: Derives tool names from normalized OpenAPI paths and methods.
- Behavior: Generates tool descriptions from OpenAPI operation summaries and descriptions.
Environment Variables
OPENAPI_SPEC_URL: (Required) The URL to the OpenAPI specification JSON file (e.g.https://example.com/spec.jsonorfile:///path/to/local/spec.json).OPENAPI_SIMPLE_MODE: (Optional) Set totrueto enable FastMCP mode.TOOL_WHITELIST: (Optional) A comma-separated list of endpoint paths to expose as tools.ADDITIONAL_RESOURCES: (Optional) Comma-separatedname=/path/to/fileentries served as extra MCP resources (use-case docs such as naming policies or layout conventions; seeexamples/resources/).TOOL_NAME_PREFIX: (Optional) A prefix to prepend to all tool names.API_KEY: (Optional) Authentication token for the API sent asBearerin the Authorization header by default.API_AUTH_TYPE: (Optional) Overrides the defaultBearerAuthorization scheme.api-keysends the key in the header named byAPI_AUTH_HEADER; any other value is used as a custom scheme prefix (e.g.Tokenfor NetBox →Authorization: Token).STRIP_PARAM: (Optional) JMESPath expression to strip unwanted parameters (e.g.tokenfor Slack).DEBUG: (Optional) Enables verbose debug logging when set to “true”, “1”, or “yes”.EXTRA_HEADERS: (Optional) One or more outgoing HTTP headers. Accepts a JSON array (["X-A: 1","X-B: 2"]), oneHeader: Valueper line, or literal\n-separated entries (for configs that cannot embed newlines).SERVER_URL_OVERRIDE: (Optional) Overrides the base URL from the OpenAPI specification when set, useful for custom deployments.TOOL_NAME_MAX_LENGTH: (Optional) Truncates tool names to a max length.- Additional Variable:
OPENAPI_SPEC_URL_– a variant for unique per-test configurations (falls back toOPENAPI_SPEC_URL). IGNORE_SSL_SPEC: (Optional) Set totrueto disable SSL certificate verification when fetching the OpenAPI spec.IGNORE_SSL_TOOLS: (Optional) Set totrueto disable SSL certificate verification for API requests made by tools.API_AUTH_HEADER: (Optional) Header name used whenAPI_AUTH_TYPE=api-key(e.g.x-apikeyfor VirusTotal,xi-api-keyfor ElevenLabs). Defaults toAuthorization.OPENAPI_SPEC_FORMAT: (Optional) Set toyamlto parsefile://specs as YAML (remote specs auto-detect). Defaultjson.OPENAPI_SPEC_CACHE_TTL_SECONDS: (Optional) Live-first disk cache for remote specs: the cached copy is served only when the live fetch fails or stalls (and respawned servers fail fast to it). Default86400; set0to disable.ENABLE_TOOLS/ENABLE_PROMPTS/ENABLE_RESOURCES: (Optional) Feature gates for the three MCP surfaces in low-level mode; each defaults to enabled. Disabling a feature removes its handlers and its capability advertisement.CAPABILITIES_TOOLS/CAPABILITIES_PROMPTS/CAPABILITIES_RESOURCES: (Optional) AdvertiselistChangedon the corresponding capability (for clients that key on it). Defaultfalse.
Verified Clients & Live Results (2026-06-12)
The example configurations below were exercised against the live APIs, and the proxy was attached to a range of agent CLIs over stdio MCP. Results from that verification run:
API example results
| API example | Tools | Sample call proven | Extra env needed |
|---|---|---|---|
| glama | 6 | get_v1_attributes |
none |
| apis.guru | 7 | get_metrics_json |
none |
| wolframalpha | 2 | get_v1_llm_api |
API_KEY |
| virustotal | 4 | IP report | API_KEY + API_AUTH_TYPE=api-key + API_AUTH_HEADER=x-apikey |
| asana | 73 (whitelist /workspaces,/projects,/tasks) |
created project + 11 tasks | SERVER_URL_OVERRIDE + API_KEY |
| render | 52 | get_services |
API_KEY (NEW spec URL render-public-api-1.json) |
| notion | 4–5 (whitelist /v1/users,/v1/search or /v1/pages) |
page create + title read-back | SERVER_URL_OVERRIDE + EXTRA_HEADERS (Notion-Version) + API_KEY |
| elevenlabs | 19 | TTS mp3 generated | SERVER_URL_OVERRIDE + API_AUTH_TYPE=api-key + API_AUTH_HEADER=xi-api-key |
| flyio | 34–35 | apps + machine health | API_KEY |
| slack | 7 (exact dot-path whitelist until #27 fix) | auth.test + postMessage |
API_KEY |
| netbox | 9 (whitelist /ipam/ip-addresses) |
IPAM write + read | API_KEY + API_AUTH_TYPE=Token |
| homeassistant | 21 | get_config (200) + call_service (light.turn_on, 200) |
SERVER_URL_OVERRIDE + API_KEY (${HA_TOKEN}, sent as Bearer) |
Client matrix
| Agent CLI | Model used (live test) | MCP attach mechanism | Tool calls | Prompts/Resources surfaced to model? |
|---|---|---|---|---|
| opencode | (CLI default) | ~/.config/opencode/opencode.json mcp |
✅ native | prompts: ✅ (slash) · resources: ✅ — most complete ‖ |
| Codex | gpt-5-codex (OpenAI API) |
codex exec -c mcp_servers.* |
✅ native | prompts: ❌ low-level · ✅ fastmcp (via call_function→get_prompt) · resources: ✅ (read_mcp_resource) ‖ |
| Kilocode | kilo-auto/free |
global settings/mcp_settings.json |
✅ native | prompts: ❌ (no prompt mechanism) · resources: ✅ (access_mcp_resource) ‖ |
| Qwen | agent group via local LiteLLM gateway |
project .qwen/settings.json |
✅ native (live invoke auth-blocked) | prompts: ✅ (slash /summarize_spec) · resources: ❌ (no client support) ‖ |
| Gemini | Google OAuth free tier (CLI default model) | project .gemini/settings.json mcpServers |
✅ native | prompts: interactive slash only · resources: interactive @ only (neither reaches the model headless) ‖ |
| Vibe | mistral-medium-3.5 |
~/.vibe/config.toml [[mcp_servers]] |
✅ discovery + reads | prompts: ❌ · resources: ❌ (tools-only client) ‖ |
| agy | — | — | ❌ headless cannot enable MCP | n/a |
| letta (cloud / self-hosted) | Letta Cloud / PUT /v1/tools/mcp/servers |
streamable-HTTP / stdio | ✅ (per prior sweep) | unknown — needs a running Letta server to test ‡ |
How to read the prompts/resources column. MCP has three surfaces — tools, prompts, resources. mcp-openapi-proxy serves all three, advertised by default since 0.3.0. Whether they reach the model is up to the client, and that varies:
- ‖ re-verified 2026-06-15 against the published 0.3.3 release with no flags set (validating the default-on advertising), driving each real client binary, in both server modes (low-level and FastMCP simple). Per-client results were the same across modes except where noted.
- Tools work on every client tested. Prompts→model: opencode & Qwen (slash commands); Gemini interactive-only. Resources→model: opencode, Codex, Kilocode. opencode is the only client that surfaces all three in any mode.
- FastMCP simple mode exception: a client with no native prompt surface can still reach prompts through the static
call_function→get_promptindirection. Observed with Codex (prompts ❌ in low-level, ✅ in FastMCP simple mode). Vibe stays tools-only regardless of mode.- ‡ unknown — Letta can’t be exercised without standing up a Letta server + model; left untested rather than guessed.
- Every cell on the proxy side was confirmed via a raw stdio handshake (
initializeadvertises tools+prompts+resources;prompts/getandresources/readreturn content). The remaining ❌ are client-side gaps, not proxy limitations.(All pre-0.3.0 prompt/resource findings were voided — they were measured while advertising defaulted off, so they reflected the server default, not the clients.)
Minimal sanitized configs per client (the no-auth Glama spec is used as the smallest working example; substitute your own spec URL and $YOUR_KEY as needed):
agy (headless) and letta cloud could not attach a stdio MCP server at all, so no config snippet applies: agy provides no way to enable MCP in headless mode, and letta cloud only accepts remote MCP servers.
Tips
- Big specs need
TOOL_WHITELIST. Large APIs (Asana, Render, Fly.io, Slack, NetBox) expose dozens of endpoints; whitelist the paths you need to keep the tool count manageable for clients. - Dot-style paths need exact whitelist entries for now. Slack-style paths like
/chat.postMessageare not matched by prefix whitelisting — list each path exactly (issue #27 / PR #32). - Slow remote specs can crash-loop short-timeout clients. If a client kills the server before a large spec finishes downloading, fetch the spec once and point
OPENAPI_SPEC_URLat a localfile://copy (issue #28). - Custom auth schemes are handled via
API_AUTH_TYPE(PR #25), e.g.Tokenfor NetBox, orapi-keywithAPI_AUTH_HEADERfor Virustotal/ElevenLabs-style header keys.
Examples
For testing you can run the uvx command as demonstrated in the examples then interact with the MCP server via JSON-RPC messages to list tools and resources. See the “JSON-RPC Testing” section below.
Each example below is collapsed — click to expand.
Troubleshooting
JSON-RPC Testing
For alternative testing, you can interact with the MCP server via JSON-RPC. After starting the server, paste the following initialization message:
{"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"claude-ai","version":"0.1.0"}},"jsonrpc":"2.0","id":0}
Expected response:
{"jsonrpc":"2.0","id":0,"result":{"protocolVersion":"2024-11-05","capabilities":{"experimental":{},"prompts":{"listChanged":false},"resources":{"subscribe":false,"listChanged":false},"tools":{"listChanged":false}},"serverInfo":{"name":"sqlite","version":"0.1.0"}}}
Then paste these follow-up messages:
{"method":"notifications/initialized","jsonrpc":"2.0"}
{"method":"resources/list","params":{},"jsonrpc":"2.0","id":1}
{"method":"tools/list","params":{},"jsonrpc":"2.0","id":2}
- Missing OPENAPI_SPEC_URL: Ensure it’s set to a valid OpenAPI JSON URL or local file path.
- Invalid Specification: Verify the OpenAPI document is standard-compliant.
- Tool Filtering Issues: Check
TOOL_WHITELISTmatches desired endpoints. - Authentication Errors: Confirm
API_KEYandAPI_AUTH_TYPEare correct. - Logging: Set
DEBUG=truefor detailed output to stderr. - Test Server: Run directly:
uvx mcp-openapi-proxy
License
Установка
uvx mcp-openapi-proxyКонфигурация
{
"mcpServers": {
"mcp-openapi-proxy": {
"command": "uvx",
"args": ["mcp-openapi-proxy"],
"env": {
"OPENAPI_SPEC_URL": "${OPENAPI_SPEC_URL}",
"API_KEY": "${API_OPENAPI_KEY}"
}
}
}
}