惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

月光博客
月光博客
雷峰网
雷峰网
S
SegmentFault 最新的问题
博客园 - 【当耐特】
博客园_首页
量子位
爱范儿
爱范儿
博客园 - 叶小钗
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
Jina AI
Jina AI
V
V2EX
美团技术团队
V
Visual Studio Blog
博客园 - 三生石上(FineUI控件)
IT之家
IT之家
Hugging Face - Blog
Hugging Face - Blog
Apple Machine Learning Research
Apple Machine Learning Research
小众软件
小众软件
博客园 - 聂微东
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
The Cloudflare Blog
宝玉的分享
宝玉的分享
WordPress大学
WordPress大学
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻

DEV Community

Authentication Security Deep Dive: From Brute Force to Salted Hashing (With Java Examples) Why AI Systems Don’t Fail — They Drift Spilling beans for how i learn for exam😁"Reinforcement Learning Cheat Sheet" I Replaced Chrome with Safari for AI Browser Automation. Here's What Broke (and What Finally Worked) How Python Borrows Other People's Work The $40 Architecture: Processing 1 Billion API Requests with 99.99% Uptime Vibe Coding: A Workflow Guide (From Zero to SaaS) Most webhook security guides protect the wrong side. The scary part is delivery. Headless CMS for TanStack Start: Build a Blog with Cosmic EU Age Verification App "Hacked in 2 Minutes" — What Actually Happened Comfy Cloud’s delete function does not actually remove files Running AI Models on GPU Cloud Servers: A Beginner Guide Event-driven media intelligence with AWS Step Functions and Bedrock I scored 500 AI prompts across 8 quality dimensions — here's what broke How to Call Google Gemini API from Next.js (Free Tier, No Backend Needed) The Portal Protocol: Reclaiming Human Connection in the Age of AI How to Fix Your Team's Scattered Knowledge Problem With a Self-Hosted Forum Intro to tc Cloud Functors: A Graph-First Mental Model for the Modern Cloud Designing Multi-Tenant Backends With Both Ownership and Team Access I Built a Neumorphic CSS Library with 77+ Components — Here's What I Learned PostgreSQL Performance Optimization: Why Connection Pooling Is Critical at Scale Cómo construí un SaaS multi-rubro para gestionar expensas en Argentina con FastAPI + Vue 3 🚀 I Built an Ethical Hacking Scanner Tool – Open Source Project I Replaced /usage and /context in Claude Code With a Single Statusline A Pythonic Way to Handle Emails (IMAP/SMTP) with Auto-Discovery and AI-Ready Design I Collected 8.9 Million Polymarket Price Points — Here's What I Found About How Markets Really Move EcoTrack AI — Carbon Footprint Tracker & Dashboard Everyone's Using AI. No One Agrees How. 5 self-hosted ebook managers worth trying in 2026 Building Your First AI Agent with LangChain: From Chatbot to Autonomous Assistant
I Built openapi-mcp-gateway: Multi-Spec OpenAPI-to-MCP Wi...
MrOops · 2026-05-10 · via DEV Community

I Built openapi-mcp-gateway: Multi-Spec OpenAPI-to-MCP With Real OAuth2 In Python

This gateway started as a demo. The job was to turn our company's OpenAPI spec into an MCP server so non-technical product folks could try the API through Claude Desktop. The naive version came together quickly.

Then the demo grew. For a customer-support-style agent to be useful, it needed to chain our product API with the internal issue tracker, and both gate access on the user's identity, not a shared service token. The audience was non-technical, so streamable HTTP was the floor. But I did not know what the eventual production client would be, so I wanted stdio and SSE on the same binary as well. And because the spec was actively changing, I needed the MCP layer to track it automatically rather than be hand-maintained.

By the time everything ran stably (multi-API, real per-user OAuth2, all three MCP transports, auto-regenerated from the spec), I had built openapi-mcp-gateway.

It is a Python service that turns OpenAPI specs into MCP servers, mounts as many of them as you want in one process, and does real per-user OAuth2 with token relay. You point it at a YAML and it runs.

Try It

uvx openapi-mcp-gateway --spec https://petstore3.swagger.io/api/v3/openapi.json --name petstore
# MCP server live at http://127.0.0.1:8000/petstore/mcp

Enter fullscreen mode Exit fullscreen mode

Or add it as a dependency:

uv add openapi-mcp-gateway

Enter fullscreen mode Exit fullscreen mode

Connect from Claude Desktop, Cursor, Cline, or any other MCP client. Streamable HTTP, SSE, and stdio are all supported. Python 3.11+.

What The Gateway Actually Does

Three things, each shown in code.

1. Multiple APIs In One Process

Most agent tasks touch more than one API. GitHub for repo lookups, your internal product API for the actual work, an external CRM. Point a single YAML at all of them:

# servers.yml
host: 0.0.0.0
port: 8000
url: http://localhost:8000

servers:
  - name: petstore
    spec: https://petstore3.swagger.io/api/v3/openapi.json

  - name: github
    spec: https://raw.githubusercontent.com/github/rest-api-description/main/descriptions/api.github.com/api.github.com.json
    auth:
      type: bearer
      token: ${GITHUB_TOKEN}
    policy:
      allow: ["GET /repos/*", "GET /users/*"]
      deny:  ["GET /repos/*/actions/secrets*"]

  - name: asana
    spec: https://raw.githubusercontent.com/Asana/openapi/master/defs/asana_oas.yaml
    auth:
      type: oauth2
      client_id: ${ASANA_CLIENT_ID}
      client_secret: ${ASANA_CLIENT_SECRET}
      scopes: [openid, email, users:read, workspaces:read]

Enter fullscreen mode Exit fullscreen mode

uvx openapi-mcp-gateway --config servers.yml

Enter fullscreen mode Exit fullscreen mode

Three MCP servers at /petstore/mcp, /github/mcp, /asana/mcp. Each with its own auth. Each independently filterable with policy.allow / policy.deny, so you do not blast every endpoint of every spec at the model.

Visually, this YAML produces:

Multi-API gateway topology: one process, three mount paths, three different auth backends, three different upstream APIs

One process, three mount paths, three different auth backends, three different upstream identities. None of them leak into each other.

2. Real Per-User OAuth2 (Token Relay)

This is the constraint that drove most of the design. When a user opens Claude Desktop and connects to one of the mounted servers:

OAuth2 authorization_code with token relay: gateway as both OAuth Server to MCP client and OAuth Client to upstream API

The gateway plays two roles at once, acting as an OAuth Authorization Server to the MCP client and as an OAuth Client to the upstream API. It mints its own MCP-scoped tokens with their own TTLs and revocation, and keeps a private mapping from those tokens to the upstream tokens.

The result is the part that matters. The MCP client never holds a credential for an API it does not know about, and the upstream API's audit log shows the actual end user.

And because every mounted server has its own auth config, one gateway process can run several different OAuth setups in parallel: Asana OAuth on /asana/mcp, GitHub bearer on /github/mcp, your internal OAuth on /myapp/mcp, all at once. Three different upstream identities, three independent OAuth flows, no cross-talk in the token store, the auth resolver, or the request path. The Asana token issued to one user cannot be confused with the GitHub bearer used to look up a repo. Each (server, user) pair lives in its own namespace.

For service-to-service calls, client_credentials is also supported, with lazy fetch and concurrent-refresh dedup under an asyncio.Lock so N concurrent tool calls hitting the gateway at expiry produce exactly one IdP request. Different shape, different OAuthFlowHandler.

3. FastAPI-Native @mcp_tool

Beyond raw OpenAPI specs, the gateway also takes a live FastAPI app as input. If you already have one, decorate the routes you want exposed:

from fastapi import FastAPI
from openapi_mcp_gateway import Gateway, mcp_tool

app = FastAPI()

@app.get("/items/{item_id}")
@mcp_tool()
def read_item(item_id: int):
    return {"id": item_id}

@app.get("/internal/health")  # not decorated, not exposed
def health():
    return {"ok": True}

Gateway.from_fastapi(app, name="myapp").run()

Enter fullscreen mode Exit fullscreen mode

Tool calls run in-process through httpx.ASGITransport. No localhost roundtrip, no parallel spec, no separate uvicorn. Auth is auto-detected from the app's securitySchemes.

How It Compares To Existing Tools

I did the homework, so you do not have to. As of May 2026, here is the OpenAPI-to-MCP converter landscape (tools whose primary job is "take an OpenAPI spec, produce an MCP server"):

mroops0111/openapi-mcp-gateway fastapi_mcp awslabs/mcp ivo-toby harsha-iiiv ckanthony
Language Python Python Python TS TS (codegen) Go
Input OpenAPI or FastAPI app FastAPI app only OpenAPI OpenAPI OpenAPI OpenAPI
Multi-spec in one process Yes (YAML) No Yes No No No
OAuth2 authorization_code w/ relay Yes Yes Cognito only Yes No No
OAuth2 client_credentials Yes (lazy + lock) Not documented No Yes Yes No
stdio + SSE + Streamable HTTP All three SSE + StrHTTP stdio Partial All three Single port
FastAPI-native decorator Yes Whole-app only No No No No
Pluggable Redis token store Yes No No No No No

Reading guide:

  • If your only input is a FastAPI app, fastapi_mcp is more focused than mine. The in-process ASGI trick is its native idea, and I borrowed it for @mcp_tool.
  • If you need multi-spec on AWS and Cognito is your auth, awslabs/mcp is purpose-built for that.
  • If you want a TypeScript runtime, ivo-toby/mcp-openapi-server is the closest analogue.
  • If you want a generated Node project you control end-to-end, harsha-iiiv/openapi-mcp-generator is the right shape.
  • If you want a Python service you point at a YAML, that hosts several specs at once, that does both OAuth2 flows on real upstream providers (not just Cognito), and that has a decorator for the FastAPI app you probably already have, this gateway is for you.

Concretely, this gateway tends to be the right fit in three situations:

  • Internal agents that have to touch several in-house APIs. Customer support, finance ops, IT helpdesk. The agent needs your product API plus your issue tracker plus maybe your CRM, each with its own auth. One process, several mounts, no inter-service leakage.
  • B2B APIs where actions need to happen as the actual end user. Signing a contract, moving money, closing a ticket on someone's behalf. Anything where the audit log on the upstream side has to show the human, not a shared service account. The token relay handles the per-user identity end to end.
  • Demo or PoC stage with a moving spec. The OpenAPI spec is still evolving, you do not yet know what the production MCP client will be, and you do not want to hand-maintain an MCP server in parallel. Point the gateway at the spec, get all three transports for free, regenerate every time the spec changes.

What Is And Is Not There Yet

Shipping today: OAuth2 authorization_code with token relay, OAuth2 client_credentials with concurrency primitives, multi-server in one process (each with independent auth), FastAPI-native @mcp_tool, operation filtering (allow / deny / marked_only), Redis token store, all three MCP transports.

On the roadmap, in case any of these would unblock you:

  • Flexible OAuth2 scope mapping. Today, upstream scopes pass through to MCP one-to-one. Some setups need to expose only a subset, alias names, or group several upstream scopes under one MCP-side scope. Configurable in YAML, per server.
  • Merging multiple OpenAPI specs into one MCP server. Today one spec produces one mounted server. Some upstream APIs split a single product's docs across several spec files (by tag, by version, by section), and forcing the agent to pick across N mount paths for what is logically one API is awkward. Want a specs: list that the gateway combines into a single MCP server.
  • MCP primitives beyond tools, with per-operation YAML overrides. Today every operation in your spec becomes an MCP tool. MCP also has resources, prompts, and sampling, and not every operation is best modelled as a tool (GET /users/{id} is more naturally a resource than a tool). Want a YAML override per operation, which lets you choose the MCP primitive, rename it, hide it, or reshape its inputs.
  • Dynamic tool exposure (skill-style discovery). For large APIs with hundreds of operations, registering every operation as its own tool blows the agent's context window before the agent has done anything. Want an opt-in mode where the gateway exposes only three meta-tools (list, get, call), and the agent fetches operation details on demand. Same pattern as agent skills.

If any of these are blocking you, please open an issue with the shape of your problem. Concrete use cases drive the design more than my guesses.

Closing

This is a personal open-source side project, not anything I deploy at work. The constraints came from the kinds of agent flows I think about because of my dayjob (a B2B e-signature API), but the gateway itself is independent of that. Sharing it here in case the situations above sound like yours.