Skip to main content

Turn any OpenAPI specification into a Model Context Protocol (MCP) server with a single command.

Project description

OpenAPI MCP Gateway

CI PyPI version PyPI Downloads Python Version License: MIT

Turn any OpenAPI (Swagger) spec into a Model Context Protocol (MCP) server with one command, or expose your existing FastAPI app the same way by decorating routes with @mcp_tool. Run several APIs from one process, each on its own mount path, with Bearer, API Key, and OAuth2 (authorization_code for per-user delegation, client_credentials for service tokens) auth built in. Works with Claude Desktop, Cursor, Cline, and any other MCP client.

openapi-mcp-gateway --spec https://petstore3.swagger.io/api/v3/openapi.json
# Server live at http://127.0.0.1:8000/api/mcp
  • Zero glue code. Every operation in your spec becomes an MCP tool automatically.
  • Multi-API. Expose GitHub, Slack, and internal services from one process, each on its own mount path.
  • Auth built in. Bearer, API Key, and OAuth2, including per-user delegation (authorization_code) and service tokens (client_credentials).
  • FastAPI native. Decorate routes with @mcp_tool to expose them as MCP tools in-process, with no extra network hop and no separate spec to maintain.
  • Flexible transport. Streamable HTTP, SSE, or stdio for desktop clients.

Installation

pip install openapi-mcp-gateway

Or with uv:

uv add openapi-mcp-gateway

Optional extras:

pip install "openapi-mcp-gateway[redis]"   # Redis token store, used for auth memoization

Requires Python 3.11+.

Quick Start

1. Public API, No Auth

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

Connect an MCP client to http://127.0.0.1:8000/petstore/mcp.

2. Bearer Token

export GITHUB_TOKEN="ghp_..."
openapi-mcp-gateway \
    --spec https://raw.githubusercontent.com/github/rest-api-description/main/descriptions/api.github.com/api.github.com.json \
    --name github \
    --auth-type bearer \
    --auth-token '${GITHUB_TOKEN}'

3. OAuth2, Per-User Delegation (authorization_code)

The gateway runs its own OAuth server so each MCP client authenticates as its own end-user; tokens are minted per session.

export ASANA_CLIENT_ID="..." ASANA_CLIENT_SECRET="..."
openapi-mcp-gateway \
    --spec https://raw.githubusercontent.com/Asana/openapi/master/defs/asana_oas.yaml \
    --name asana \
    --auth-type oauth2 \
    --auth-client-id '${ASANA_CLIENT_ID}' \
    --auth-client-secret '${ASANA_CLIENT_SECRET}' \
    --auth-scopes "openid,email,profile,users:read,workspaces:read"

4. OAuth2, Service Token (client_credentials)

When the gateway holds its own credentials and shares one upstream token across every MCP client. No per-user OAuth dance:

export SVC_CLIENT_ID="..." SVC_CLIENT_SECRET="..."
openapi-mcp-gateway \
    --spec ./service-api.json \
    --name svc \
    --auth-type oauth2 \
    --auth-flow client_credentials \
    --auth-client-id '${SVC_CLIENT_ID}' \
    --auth-client-secret '${SVC_CLIENT_SECRET}'

5. Multiple APIs at Once

Mix public, bearer, and OAuth2 services in a single config. Each server is mounted at /{name}/mcp:

# servers.yml
host: "0.0.0.0"
port: 8000
url: http://localhost:8000   # public base URL for OAuth callbacks

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, profile, users:read, workspaces:read]
export GITHUB_TOKEN="ghp_..."
export ASANA_CLIENT_ID="..." ASANA_CLIENT_SECRET="..."
openapi-mcp-gateway --config servers.yml

Runnable variants of this multi-server setup live in examples/: multi-server.yml, asana.yml, github.yml, petstore.yml. Each YAML lists its prerequisites at the top.

${ENV_VAR} and ${ENV_VAR:-default} work in any string field, resolved at request time. For OAuth2, authorizationUrl / tokenUrl / scopes are auto-detected from the spec's securitySchemes. Override auth.authorization_url, auth.token_url, or auth.scopes when the spec is incomplete.

6. Local Desktop Client (stdio)

For Claude Desktop, IDE integrations, or any MCP client that prefers stdio:

{
  "mcpServers": {
    "petstore": {
      "command": "openapi-mcp-gateway",
      "args": ["--spec", "/abs/path/to/openapi.json", "--transport", "stdio"]
    }
  }
}

Configuration

Run openapi-mcp-gateway --help for the CLI reference. The Quick Start examples cover most setups; the full field reference is below.

When values appear in more than one place, the rule is defaults < YAML (--config) < CLI flags < Gateway.run(...) kwargs, and a layer only overrides what it actually sets. Sub-trees (logging, per-server auth) merge field-by-field; the servers list is replaced wholesale.

Top-Level Fields
Field Type Default Description
host string 0.0.0.0 Bind address (0.0.0.0 = all interfaces). Clients on the same machine usually open http://localhost:{port} or http://127.0.0.1:{port}.
port int 8000 Bind port
url string (empty) Public base URL for OAuth redirects and discovery. When unset: http://localhost:{port} if host is 0.0.0.0, otherwise http://{host}:{port}. Override when your registered redirect URI uses another host (tunnel, reverse proxy, etc.).
transport string streamable-http sse, streamable-http, or stdio
store.type string memory memory or redis
store.redis_url string redis://localhost:6379 Redis URL when store.type: redis
logging.level string INFO DEBUG, INFO, WARNING, ERROR, CRITICAL
logging.format string text text or json
logging.file string Mirror logs to this file
servers list required List of per-server config entries
Per-Server Fields
Field Type Default Description
name string required Unique identifier; mount path defaults to /{name}
spec string required Path or URL to OpenAPI document (JSON or YAML)
base_url string from spec Override the upstream base URL
auth.type string none none, bearer, api_key, or oauth2
auth.token string Required for bearer / api_key
auth.api_key_header string X-API-Key Header name for api_key
auth.client_id, auth.client_secret string Required for oauth2
auth.scopes, auth.authorization_url, auth.token_url from spec OAuth2 overrides when securitySchemes is incomplete
policy.allow list Only expose matching operations
policy.deny list Exclude matching operations
timeout float 90 HTTP timeout in seconds

Filtering Operations

Use policy.allow and policy.deny with fnmatch syntax against operation IDs (getUsers, create*) or method + path (GET /users/*):

policy:
  allow: ["GET /repos/*"]
  deny:  ["GET /repos/*/actions/secrets*"]

Or mark operations directly in the spec and enable marked_only:

# openapi.yml
paths:
  /users:
    get:
      operationId: listUsers
      x-mcp-integration:
        expose:
          tool: {}
# servers.yml
policy:
  marked_only: true

Filters apply in order: marked_only, then allow, then deny.

Logging

Configure via the logging.* YAML keys or via CLI flags (--log-level, --log-format, --log-file); -v and -q are shortcuts for DEBUG and WARNING. CLI flags override YAML field-by-field, following the precedence rule above.

Python API

Use the gateway as a library inside your own Python application:

from openapi_mcp_gateway import Gateway

gateway = Gateway()
gateway.add_server(
    name="petstore",
    spec="https://petstore3.swagger.io/api/v3/openapi.json",
)
gateway.add_server(
    name="github",
    spec="./github-openapi.json",
    auth={"type": "bearer", "token": "${GITHUB_TOKEN}"},
    policy={"allow": ["GET /repos/*"]},
)
gateway.run(port=8000)

Expose Your FastAPI App as MCP Tools

Already running FastAPI? Decorate the routes you want to expose with @mcp_tool and the gateway picks them up. No second spec, no separate process. Routes run in-process via httpx.ASGITransport, so there is no extra network hop:

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()

Auth is auto-detected from the app's securitySchemes. If the gateway and the app share an OAuth realm, the MCP client's Authorization header passes through verbatim; for client_credentials schemes the gateway mints upstream tokens from its own credentials. Mix and match by passing an explicit auth=AuthConfig(...) to Gateway.from_fastapi.

Got routes you can't decorate at definition (third-party app, include_router from another package)? Use mark_tool(func) to attach the same metadata imperatively.

To mount the MCP routes onto an existing FastAPI app instead of running standalone, use Gateway.mount(app).

License

MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

openapi_mcp_gateway-0.2.0.tar.gz (142.8 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

openapi_mcp_gateway-0.2.0-py3-none-any.whl (48.5 kB view details)

Uploaded Python 3

File details

Details for the file openapi_mcp_gateway-0.2.0.tar.gz.

File metadata

  • Download URL: openapi_mcp_gateway-0.2.0.tar.gz
  • Upload date:
  • Size: 142.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for openapi_mcp_gateway-0.2.0.tar.gz
Algorithm Hash digest
SHA256 c82ff556a49d45db62a4e64e1e861f8538b3aac50ea054d4b89d694abb7f04d0
MD5 29010bc714a39dea51b528fd4ec64143
BLAKE2b-256 d25e74b59e2da0f62af8c71bbacb17c7af286d7817f713be327cd3df664a879e

See more details on using hashes here.

Provenance

The following attestation bundles were made for openapi_mcp_gateway-0.2.0.tar.gz:

Publisher: release.yml on mroops0111/openapi-mcp-gateway

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file openapi_mcp_gateway-0.2.0-py3-none-any.whl.

File metadata

File hashes

Hashes for openapi_mcp_gateway-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 65578b59a1fb17d8028b4c873f2eb55007aa53fc9215b71940330830eeff58f9
MD5 83aaa3c806a95de36ed2456cd437f8c2
BLAKE2b-256 28ac09730ee983f515f52a84064a4ab4b129b0d566f1abdfeb60ba17730a8025

See more details on using hashes here.

Provenance

The following attestation bundles were made for openapi_mcp_gateway-0.2.0-py3-none-any.whl:

Publisher: release.yml on mroops0111/openapi-mcp-gateway

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page