Skip to content

rendis/mcp-openapi-proxy

Repository files navigation

mcp-openapi-proxy

Turn any OpenAPI 3.x spec into a lightweight MCP navigator/executor server — automatically.

Every REST API has an OpenAPI spec. Every AI agent speaks MCP.
This bridge connects the two with zero code — point it at a spec, get a usable MCP navigator/executor.

Go License MCP


The Problem

You have a REST API with 50+ endpoints and an OpenAPI spec that documents every one of them. You want an AI agent (Claude Code, Codex, Gemini CLI) to call your API through MCP. The standard approach: write one MCP tool definition per endpoint — input schemas, handlers, auth wiring — thousands of lines of boilerplate that breaks every time the API changes.

mcp-openapi-proxy eliminates that. One binary. One environment variable pointing to your spec. The proxy indexes every endpoint at startup, then exposes a small MCP navigator/executor surface that agents can actually load. No codegen, no generated files, no maintenance.

And authentication makes it worse. Production APIs use OIDC, OAuth2, or token-based auth — the agent needs valid credentials, tokens that expire need refreshing, and secrets need secure storage. mcp-openapi-proxy handles this end-to-end: static tokens for development, browser-based OIDC PKCE for production, with automatic token refresh and secure on-disk storage.

How It Works

Architecture diagram showing OpenAPI spec flowing through parser, endpoint indexing, and a 3-tool MCP server to connect AI agents with target APIs

flowchart LR
    A["OpenAPI Spec<br/><small>YAML · JSON · URL</small>"] --> B["Spec Parser<br/><small>kin-openapi</small>"]
    B --> C["Endpoints[]"]
    C --> D["Endpoint Index<br/><small>toolName + metadata</small>"]
    D --> E["3 MCP Tools<br/><small>list · describe · call</small>"]
    E <-->|"tool calls"| F["AI Agent<br/><small>Claude · Codex · Gemini</small>"]

    style A fill:#24283b,stroke:#bb9af7,color:#bb9af7
    style B fill:#24283b,stroke:#7dcfff,color:#7dcfff
    style C fill:#24283b,stroke:#e0af68,color:#e0af68
    style D fill:#24283b,stroke:#9ece6a,color:#9ece6a
    style E fill:#24283b,stroke:#bb9af7,color:#bb9af7
    style F fill:#1a1b26,stroke:#7aa2f7,color:#7aa2f7
Loading
  1. The OpenAPI spec is loaded and parsed into a list of endpoints (method, path, parameters, request body)
  2. Each endpoint gets a stable toolName identifier using {prefix}_{method}_{sanitized_path}
  3. The proxy registers exactly three MCP tools: list_endpoints, describe_endpoint, and call_endpoint
  4. Agents discover endpoints cheaply, fetch the full contract only when needed, and execute calls through the shared HTTP runtime

Features

  • OpenAPI 3.x — parses paths, parameters, request bodies, and security schemes via kin-openapi
  • Local and remote specs — load from a file path or any http:// / https:// URL
  • Navigator/executor MCP surface — exactly 3 tools per server, regardless of API size
  • Stable endpoint identifiers — every endpoint still gets a deterministic toolName
  • Production-ready authentication — built-in OIDC Authorization Code + PKCE flow with browser-based login, automatic token refresh, and secure on-disk storage (0600). Works with any OIDC provider: Keycloak, Auth0, Okta, Google, and more. No auth code to write.
  • Development auth — static bearer token via MCP_AUTH_TOKEN or per-scheme credentials via MCP_AUTH_<SCHEME>_*
  • Spec-aware auth resolution — resolves http bearer, http basic, apiKey, oauth2, and openIdConnect from OpenAPI security requirements
  • Configurable tool prefix — namespace tools to avoid collisions when running multiple proxies
  • Extra headers — inject custom headers (workspace IDs, API versions) into every request
  • Lightweight discoverytools/list stays small while describe_endpoint exposes the full OpenAPI contract on demand
  • Structured call outputcall_endpoint returns a typed envelope with status, content_type, headers, and body
  • Forms and binary payloads — supports multipart/form-data, application/x-www-form-urlencoded, text payloads, and application/octet-stream
  • stdio transport — compatible with Claude Code, OpenAI Codex, Gemini CLI, and any MCP client

What it does NOT do

  • No codegen — tools are created dynamically at startup, no build step
  • No API modification — read-only proxy, never changes the spec or backend
  • No response rewriting — returns the real API response envelope, even when the backend deviates from the spec
  • No OpenAPI 2.0 — only 3.x specs (convert older specs with swagger2openapi)
  • No SSE/WebSocket — stdio transport only

Quick Start

# Install
go install github.com/rendis/mcp-openapi-proxy/cmd/mcp-openapi-proxy@latest

# Run with a local spec and static token
MCP_SPEC=./openapi.yaml \
MCP_BASE_URL=https://api.example.com \
MCP_AUTH_TOKEN=your-token \
mcp-openapi-proxy

If MCP_BASE_URL is omitted, the proxy falls back to a single absolute server declared in the OpenAPI spec when one can be resolved unambiguously.

Minimal setup flow

  1. Install the binary:

    go install github.com/rendis/mcp-openapi-proxy/cmd/mcp-openapi-proxy@latest
  2. Start from the generic Claude Code example in .mcp.json.example and copy it into your project as .mcp.json.

  3. Choose one auth path:

    • Static token: add MCP_AUTH_TOKEN to .mcp.json
    • OIDC login: add MCP_OIDC_ISSUER and MCP_OIDC_CLIENT_ID to .mcp.json, then run mcp-openapi-proxy login once with the same values
  4. Open your MCP client and use the server through list_endpoints, describe_endpoint, and call_endpoint.

If your spec comes from swag init

swag init generates Swagger 2.0 output. mcp-openapi-proxy only accepts OpenAPI 3.x specs, so do not point MCP_SPEC directly at the generated swagger.json.

Convert it first, then use the converted OpenAPI 3.x file:

swag init -g cmd/api/main.go
swagger2openapi ./docs/swagger.json -o ./docs/openapi.json
MCP_SPEC=./docs/openapi.json mcp-openapi-proxy

Installation

go install github.com/rendis/mcp-openapi-proxy/cmd/mcp-openapi-proxy@latest

Or build from source:

git clone https://github.com/rendis/mcp-openapi-proxy.git
go build -C mcp-openapi-proxy -o bin/mcp-openapi-proxy ./cmd/mcp-openapi-proxy

Configuration

All configuration is done through environment variables.

Variable Required Default Description
MCP_SPEC Yes Path or URL to an OpenAPI 3.x spec (YAML or JSON)
MCP_BASE_URL No Explicit API base URL. If omitted, the proxy uses a single absolute OpenAPI server when available
MCP_TOOL_PREFIX No api Prefix for the 3 MCP navigator tools and for endpoint toolName identifiers
MCP_AUTH_PROFILE No MCP_TOOL_PREFIX or default Namespace for stored OIDC tokens
MCP_AUTH_TOKEN No Global bearer token fallback
MCP_OIDC_ISSUER No OIDC issuer URL (used with login command)
MCP_OIDC_CLIENT_ID No OIDC client ID (used with login command)
MCP_OIDC_SCOPES No Scopes discovered from MCP_SPEC or openid profile email offline_access Override OIDC login scopes
MCP_EXTRA_HEADERS No Comma-separated key:value pairs added to every request
MCP_MAX_BODY_BYTES No 10485760 Maximum response body size to buffer and return
MCP_ALLOW_INSECURE_HTTP No 0 Allow sending resolved credentials over non-loopback http:// URLs
MCP_EXCLUDE_DEPRECATED No 0 Skip deprecated endpoints when generating tools

Important

Auth resolution: MCP_AUTH_<SCHEME>_* → global MCP_AUTH_TOKEN → OIDC token cache for MCP_AUTH_PROFILE.

Trailing slashes on MCP_BASE_URL are stripped automatically.

Commands

Command Description
mcp-openapi-proxy Start the MCP server (default, same as serve)
mcp-openapi-proxy serve Start the MCP server explicitly
mcp-openapi-proxy login Browser-based OIDC Authorization Code + PKCE login
mcp-openapi-proxy logout Remove stored tokens from disk
mcp-openapi-proxy status Display current authentication state

Usage with AI Agents

Claude Code.mcp.json

Generic example:

{
  "mcpServers": {
    "my-api": {
      "command": "mcp-openapi-proxy",
      "env": {
        "MCP_SPEC": "./openapi.yaml",
        "MCP_BASE_URL": "https://api.example.com",
        "MCP_TOOL_PREFIX": "myapi",
        "MCP_AUTH_PROFILE": "myapi"
      }
    }
  }
}

Static token variant:

{
  "mcpServers": {
    "my-api": {
      "command": "mcp-openapi-proxy",
      "env": {
        "MCP_SPEC": "./openapi.yaml",
        "MCP_BASE_URL": "https://api.example.com",
        "MCP_TOOL_PREFIX": "myapi",
        "MCP_AUTH_PROFILE": "myapi",
        "MCP_AUTH_TOKEN": "your-token"
      }
    }
  }
}

OIDC variant:

{
  "mcpServers": {
    "my-api": {
      "command": "mcp-openapi-proxy",
      "env": {
        "MCP_SPEC": "./openapi.yaml",
        "MCP_BASE_URL": "https://api.example.com",
        "MCP_TOOL_PREFIX": "myapi",
        "MCP_AUTH_PROFILE": "myapi",
        "MCP_OIDC_ISSUER": "https://auth.example.com/realms/myrealm",
        "MCP_OIDC_CLIENT_ID": "my-client"
      }
    }
  }
}

If you use the OIDC variant, run login once before starting Claude Code:

MCP_AUTH_PROFILE=myapi \
MCP_OIDC_ISSUER=https://auth.example.com/realms/myrealm \
MCP_OIDC_CLIENT_ID=my-client \
mcp-openapi-proxy login
OpenAI Codex.codex/config.toml
[mcp_servers.my-api]
command = "mcp-openapi-proxy"

[mcp_servers.my-api.env]
MCP_SPEC = "./openapi.yaml"
MCP_BASE_URL = "https://api.example.com"
MCP_TOOL_PREFIX = "myapi"
MCP_AUTH_TOKEN = "your-token"
Gemini CLI~/.gemini/settings.json
{
  "mcpServers": {
    "my-api": {
      "command": "mcp-openapi-proxy",
      "env": {
        "MCP_SPEC": "./openapi.yaml",
        "MCP_BASE_URL": "https://api.example.com",
        "MCP_TOOL_PREFIX": "myapi",
        "MCP_AUTH_TOKEN": "your-token"
      }
    }
  }
}

MCP Surface

The server always registers exactly these 3 MCP tools:

Tool Purpose
{prefix}_list_endpoints Lightweight discovery with filtering and pagination
{prefix}_describe_endpoint Full OpenAPI contract for one endpoint
{prefix}_call_endpoint Execute one endpoint by toolName

Agents should always inspect these registered tools first. The proxy no longer exposes one MCP tool per endpoint.

Endpoint IDs

Each OpenAPI operation still gets a stable identifier called toolName:

{prefix}_{method}_{sanitized_path}

Path segments are lowercased. Special characters (/, -, {, }, .) are replaced with underscores. Consecutive underscores are collapsed.

Method Path Prefix Endpoint toolName
GET /users api api_get_users
POST /users api api_post_users
GET /users/{id} api api_get_users_id
DELETE /admin/features/{key} fe fe_delete_admin_features_key
GET /v1/health.check svc svc_get_v1_health_check

toolName is the identifier passed to describe_endpoint and call_endpoint. It is no longer a registered MCP tool by itself.

Discovery Flow

Recommended agent flow:

  1. Call {prefix}_list_endpoints to find candidate endpoints quickly
  2. Call {prefix}_describe_endpoint for the exact endpoint contract
  3. Call {prefix}_call_endpoint with toolName plus path/query/headers/cookies/body

Feature Flags API example with MCP_TOOL_PREFIX=fe:

Operation Endpoint toolName
GET /admin/features fe_get_admin_features
POST /admin/features fe_post_admin_features
GET /admin/features/{key} fe_get_admin_features_key
PATCH /admin/features/{key}/toggle fe_patch_admin_features_key_toggle
GET /admin/workspaces fe_get_admin_workspaces

There is no semantic alias layer in the product. Do not invent names from verbs like list, create, or toggle.

list_endpoints

Input:

{
  "q": "feature",
  "tag": "admin",
  "path_prefix": "/admin",
  "method": "PATCH",
  "auth": "bearer",
  "deprecated": false,
  "cursor": "Mg",
  "limit": 25
}

Output:

{
  "items": [
    {
      "toolName": "fe_patch_admin_features_key_toggle",
      "method": "PATCH",
      "path": "/admin/features/{key}/toggle",
      "description": "Toggle a feature flag",
      "requiredAuth": "bearer",
      "tags": ["flags", "admin"],
      "deprecated": false
    }
  ],
  "nextCursor": ""
}

describe_endpoint

Input:

{
  "toolName": "fe_patch_admin_features_key_toggle"
}

Output includes the full normalized contract for that endpoint:

  • toolName, method, path, summary, description, deprecated
  • requiredAuth and full securityRequirements
  • parameters.path/query/headers/cookies with schemas by location
  • requestBody with media types, schema, examples, and encoding hints
  • responses with status, description, headers, and body schemas
  • externalDocs, servers, baseURLHint

call_endpoint

Input:

{
  "toolName": "fe_patch_admin_features_key_toggle",
  "path": {
    "key": "checkout"
  },
  "headers": {
    "X-Workspace": "acme"
  },
  "body": {
    "enabled": true
  }
}

Workspace context is passed as a header per request, for example headers: {"X-Workspace": "acme"}, or globally through MCP_EXTRA_HEADERS. There is no separate set_workspace tool.

call_endpoint returns the same envelope in both MCP StructuredContent and pretty JSON text:

{
  "status": 200,
  "content_type": "application/json",
  "headers": {
    "X-Trace": ["abc123"]
  },
  "body": {
    "id": "item-1"
  }
}
  • API 4xx/5xx responses set IsError=true but preserve the real HTTP response
  • Proxy/runtime failures return status: 0 plus a proxy_error object
  • Binary responses are wrapped as { "encoding": "base64", "data_base64": "...", "size_bytes": 123 }

Authentication

Most MCP server implementations require you to handle authentication yourself — embedding tokens in environment variables, writing refresh logic, managing secrets. mcp-openapi-proxy handles the full authentication lifecycle so your AI agent can call protected APIs without any auth code in your project.

Static Token (Development)

Set MCP_AUTH_TOKEN to any bearer token. The proxy sends it as Authorization: Bearer <token> when a bearer-compatible security requirement needs it.

MCP_AUTH_TOKEN=dev-token mcp-openapi-proxy

If you use Claude Code, the same token can be placed directly in .mcp.json under env.MCP_AUTH_TOKEN. For shared repositories, prefer keeping the token outside version control and injecting it locally.

Per-Scheme Credentials

When an endpoint declares named OpenAPI security schemes, the proxy can resolve credentials per scheme instead of using the global bearer fallback.

Supported patterns:

  • http bearer, oauth2, openIdConnectMCP_AUTH_<SCHEME>_TOKEN
  • http basicMCP_AUTH_<SCHEME>_USERNAME and MCP_AUTH_<SCHEME>_PASSWORD
  • apiKeyMCP_AUTH_<SCHEME>_KEY

Scheme names are uppercased and sanitized by replacing ., -, /, and spaces with underscores. Example: partner-auth/v2PARTNER_AUTH_V2.

# http bearer / oauth2 / openIdConnect
MCP_AUTH_PARTNER_AUTH_V2_TOKEN=secret-token

# http basic
MCP_AUTH_ADMIN_BASIC_USERNAME=alice
MCP_AUTH_ADMIN_BASIC_PASSWORD=s3cr3t

# apiKey
MCP_AUTH_X_API_KEY_KEY=dev-key

The proxy injects each resolved credential according to the OpenAPI scheme definition: Authorization header for bearer/basic, or header/query/cookie placement for apiKey.

OIDC PKCE (Production)

For production APIs protected by an OIDC provider, the proxy includes a browser-based login flow using Authorization Code + PKCE — the most secure OAuth 2.0 flow for public clients. No client secret is needed; authentication relies on a cryptographic code verifier that proves the login was initiated by the same process.

sequenceDiagram
    participant U as User
    participant P as mcp-openapi-proxy
    participant B as Browser
    participant IdP as OIDC Provider

    U->>P: mcp-openapi-proxy login
    P->>P: Generate PKCE verifier + challenge (SHA256)
    P->>P: Start localhost callback server (127.0.0.1:random-port)
    P->>B: Open authorization URL
    B->>IdP: Authorization request + PKCE challenge
    IdP->>B: User authenticates
    B->>P: Redirect with authorization code
    P->>IdP: Exchange code + verifier for tokens
    IdP->>P: Access token + refresh token
    P->>P: Save to ~/.mcp-openapi-proxy/ (0600)
    Note over P: Auto-refreshes when<br/>within 30s of expiry
Loading

Discovery Modes

The login command needs to know the OIDC provider's authorization and token endpoints. Two discovery modes are supported:

Standard OIDC discovery (recommended) — works with any OIDC provider (Keycloak, Auth0, Okta, Google, etc.):

MCP_OIDC_ISSUER=https://auth.example.com/realms/myrealm \
MCP_OIDC_CLIENT_ID=my-client \
mcp-openapi-proxy login

When you use an MCP client config file such as .mcp.json, keep the same MCP_AUTH_PROFILE, MCP_OIDC_ISSUER, and MCP_OIDC_CLIENT_ID values there so the running server reads the token cache created by login.

Fetches {issuer}/.well-known/openid-configuration and extracts authorization_endpoint and token_endpoint. This is the standard OIDC Discovery mechanism.

Application-specific discovery — for APIs that expose a proprietary config endpoint:

MCP_BASE_URL=https://api.example.com mcp-openapi-proxy login

Fetches {baseURL}/api/v1/auth/config and parses the OIDC provider configuration from the response. This mode only works if your API implements this specific endpoint.

Note

The MCP_BASE_URL-only mode is a proprietary extension, not standard OIDC. Use MCP_OIDC_ISSUER + MCP_OIDC_CLIENT_ID for any generic OIDC provider.

Login

The login command opens a browser window, starts a temporary localhost callback server, and waits up to 5 minutes for the user to authenticate. If the browser doesn't open automatically, the authorization URL is printed to stderr for manual use.

Platform support: macOS (open), Linux (xdg-open), Windows (rundll32).

Scopes

Default scopes come from MCP_OIDC_SCOPES when set. Otherwise, if MCP_SPEC is available, the proxy unions the scopes referenced by the OpenAPI security requirements. If neither source is available, it falls back to openid profile email offline_access.

The offline_access scope is always enforced — if your custom scopes omit it, the proxy appends it automatically. This ensures the provider returns a refresh token for automatic renewal.

Token Storage

Tokens are stored at ~/.mcp-openapi-proxy/<profile>-tokens.json, where <profile> is MCP_AUTH_PROFILE (or the tool prefix / default fallback):

{
  "access_token": "eyJhbGci...",
  "refresh_token": "dGhpcyBp...",
  "expires_at": "2026-03-22T15:30:00Z",
  "token_endpoint": "https://auth.example.com/token",
  "client_id": "my-client"
}
  • File permissions: 0600 (user read/write only)
  • Directory permissions: 0700
  • Writes are atomic: temp file + rename to prevent corruption from concurrent processes

Token Refresh

The proxy automatically refreshes tokens before they expire:

  • Refresh margin: 30 seconds before expires_at
  • Refresh timeout: 15 seconds per attempt
  • Detached context: refresh uses context.Background() so it won't be cancelled if the agent times out the tool call
  • Fallback on failure: if refresh fails but the current token hasn't expired yet, the existing token is used silently
  • Missing expires_in: if the provider omits expires_in from the refresh response, defaults to 1 hour

Status and Logout

# Show current auth state
mcp-openapi-proxy status

# Example output:
# Status: logged in
# Token file:     ~/.mcp-openapi-proxy/<profile>-tokens.json
# Token endpoint: https://auth.example.com/token
# Client ID:      my-client
# Expires at:     2026-03-22T15:30:00Z
# Remaining:      23m14s
# Refresh token:  present (auto-refresh enabled)
# Remove stored tokens (idempotent — safe to run if already logged out)
mcp-openapi-proxy logout

OIDC Troubleshooting

Symptom Cause Fix
OIDC discovery from ... failed Issuer URL wrong or unreachable Verify MCP_OIDC_ISSUER URL, check /.well-known/openid-configuration is accessible
token endpoint returned 400: ... Wrong client ID, expired code, or PKCE mismatch Check MCP_OIDC_CLIENT_ID, run login again
login timed out after 5m0s Browser didn't redirect back in time Check firewall/proxy rules, try the URL printed to stderr manually
no refresh token received Provider not returning refresh tokens Ensure offline_access scope is allowed in your provider config
API is in dummy-auth mode API discovery returned dummyAuth=true Use MCP_AUTH_TOKEN with a static token instead
access token expired and no refresh token Refresh token was never stored Re-run login to obtain fresh tokens with offline_access
token refresh failed Token endpoint unreachable or refresh token revoked Check network, re-run login

Architecture

Request lifecycle showing tool call flowing through argument parsing, path resolution, query building, HTTP request, and response handling

cmd/mcp-openapi-proxy/       Entry point, CLI subcommands, env var parsing
pkg/
  spec/                      OpenAPI 3.x parser (kin-openapi)
    parser.go                Loads spec from file or URL, extracts endpoints
  server/                    MCP server setup, endpoint index, and navigator tools
    server.go                Creates MCP server, runs stdio transport
    generator.go             Endpoint `toolName` and path sanitization helpers
    navigator.go             Builds the endpoint index and registers list/describe/call
  auth/                      Authentication providers
    provider.go              TokenProvider interface + StaticTokenProvider
    oidc_provider.go         OIDC token storage, loading, and transparent refresh
    discovery.go             OIDC .well-known/openid-configuration discovery
    login.go                 Browser-based OIDC Authorization Code + PKCE flow
    logout.go                Token file removal
    status.go                Print current auth state
  client/                    HTTP client for API calls
    client.go                Request execution, content-type aware decoding, size limits
    errors.go                Client-side transport/body limit errors

Request Lifecycle

  1. Agent calls list_endpoints with optional filters and gets lightweight endpoint metadata plus toolName
  2. Agent optionally calls describe_endpoint for the full contract of one endpoint
  3. Agent calls call_endpoint with toolName plus location-aware input sections, for example:
    {"toolName": "api_patch_users_id", "path": {"id": "abc123"}, "query": {"include_roles": true}}
  4. Handler resolves the base URL from MCP_BASE_URL or a single usable OpenAPI server
  5. Path, query, header, and cookie parameters are serialized with their declared OpenAPI style and explode rules
  6. Request bodies are encoded using the selected declared media type (application/json, multipart/form-data, application/x-www-form-urlencoded, text/*, application/octet-stream, etc.)
  7. Auth is resolved from the endpoint security requirements (bearer, basic, apiKey, oauth2, openIdConnect) plus any configured extra headers
  8. Response returned as a structured envelope:
    {"status": 200, "content_type": "application/json", "headers": {"X-Total-Count": ["42"]}, "body": {...}}
    • JSON body → parsed object in body
    • Text/XML/CSV/HTML → raw string in body
    • Binary responses → {encoding, data_base64, size_bytes} in body
    • Empty responses → body is null
    • 4xx/5xx API responses keep the same envelope and set IsError=true
    • Proxy failures use status: 0 plus proxy_error

What the Agent Sees

The agent sees a small, stable MCP surface:

  • list_endpoints — cheap discovery with toolName, method, path, description, requiredAuth, tags, deprecated
  • describe_endpoint — the full normalized OpenAPI contract for one endpoint
  • call_endpoint — the shared execution tool; it returns the HTTP envelope and reuses the full runtime auth/serialization behavior
  • Deprecated endpoints — listed by default and removed from the index only when MCP_EXCLUDE_DEPRECATED=1

Examples

Feature Flags API
{
  "mcpServers": {
    "feature-flags": {
      "command": "mcp-openapi-proxy",
      "env": {
        "MCP_SPEC": "https://api.flags.example.com/openapi.yaml",
        "MCP_BASE_URL": "https://api.flags.example.com",
        "MCP_TOOL_PREFIX": "ff",
        "MCP_EXTRA_HEADERS": "X-Workspace:my-workspace"
      }
    }
  }
}
Mock Server (Local Development)
{
  "mcpServers": {
    "mock": {
      "command": "mcp-openapi-proxy",
      "env": {
        "MCP_SPEC": "./api/openapi.yaml",
        "MCP_BASE_URL": "http://localhost:4010",
        "MCP_TOOL_PREFIX": "mock",
        "MCP_AUTH_TOKEN": "dev-token"
      }
    }
  }
}
Multiple APIs Side by Side

Use distinct prefixes to run multiple proxies without tool name collisions:

{
  "mcpServers": {
    "users-api": {
      "command": "mcp-openapi-proxy",
      "env": {
        "MCP_SPEC": "./specs/users.yaml",
        "MCP_BASE_URL": "https://users.example.com",
        "MCP_TOOL_PREFIX": "users",
        "MCP_AUTH_TOKEN": "token-a"
      }
    },
    "billing-api": {
      "command": "mcp-openapi-proxy",
      "env": {
        "MCP_SPEC": "./specs/billing.yaml",
        "MCP_BASE_URL": "https://billing.example.com",
        "MCP_TOOL_PREFIX": "billing",
        "MCP_AUTH_TOKEN": "token-b"
      }
    }
  }
}

Agent Skill

This project includes a versioned LLM agent skill at skills/mcp-openapi-proxy/SKILL.md. That file is the source of truth for how agents should install the binary, configure MCP clients, and use the proxy correctly against OpenAPI-backed APIs.

Install globally so it's available in every project:

npx skills add --global https://github.com/rendis/mcp-openapi-proxy --skill mcp-openapi-proxy

Once installed, agents automatically know how to set up MCP servers from OpenAPI specs, configure authentication, and wire up multiple APIs. The skill is intentionally shorter than this README and focuses on the operational path: install the binary, configure the MCP client, supply auth, and call tools with the current input/output contract.

If you already installed the skill globally, reinstall or republish it after updating the repo copy. The installed global copy does not update automatically when skills/mcp-openapi-proxy/SKILL.md changes in the repository.

Troubleshooting

Symptom Cause Fix
MCP_SPEC environment variable is required Missing MCP_SPEC env var Set MCP_SPEC to a path or URL pointing to your OpenAPI 3.x spec
no usable base URL found No MCP_BASE_URL and the spec does not resolve a single absolute server Set MCP_BASE_URL explicitly or fix the servers section
load spec: ... Spec file not found or URL unreachable Check the file path or URL; verify network/VPN if remote
401 Unauthorized on tool calls Missing per-scheme auth or expired token Set MCP_AUTH_<SCHEME>_*, MCP_AUTH_TOKEN, or run mcp-openapi-proxy login
authentication required but not configured The endpoint's OpenAPI security requirement could not be satisfied Configure credentials for one of the declared auth alternatives
insecure_transport proxy error Authenticated request targets non-loopback http:// Use HTTPS or set MCP_ALLOW_INSECURE_HTTP=1 explicitly
Parse error on spec Spec is Swagger 2.0 output, often generated by swag init, not OpenAPI 3.x Convert swagger.json with swagger2openapi and point MCP_SPEC at the converted OpenAPI 3.x file
warning: no auth token configured No global token and no prior OIDC login Expected if your API doesn't require auth; otherwise configure credentials

Tech Stack

Component Purpose
Go 1.26+ Runtime
kin-openapi OpenAPI 3.x parsing
go-sdk MCP server implementation
jsonschema-go JSON Schema for tool inputs

Contributing

Contributions are welcome.

  1. Fork the repository
  2. Create a feature branch (git checkout -b feature/my-feature)
  3. Commit your changes
  4. Push to the branch and open a Pull Request

Note

Please include tests for any changes. Run go test ./... to verify.

License

MIT

About

Turn any OpenAPI 3.x spec into a fully functional MCP server — automatically. One binary, zero codegen, built-in OIDC PKCE auth with auto-refresh. Works with Claude Code, Codex, Gemini CLI, and any MCP client.

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages