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.
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.
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
- The OpenAPI spec is loaded and parsed into a list of endpoints (method, path, parameters, request body)
- Each endpoint gets a stable
toolNameidentifier using{prefix}_{method}_{sanitized_path} - The proxy registers exactly three MCP tools:
list_endpoints,describe_endpoint, andcall_endpoint - Agents discover endpoints cheaply, fetch the full contract only when needed, and execute calls through the shared HTTP runtime
- 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_TOKENor per-scheme credentials viaMCP_AUTH_<SCHEME>_* - Spec-aware auth resolution — resolves
http bearer,http basic,apiKey,oauth2, andopenIdConnectfrom 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 discovery —
tools/liststays small whiledescribe_endpointexposes the full OpenAPI contract on demand - Structured call output —
call_endpointreturns a typed envelope withstatus,content_type,headers, andbody - Forms and binary payloads — supports
multipart/form-data,application/x-www-form-urlencoded, text payloads, andapplication/octet-stream - stdio transport — compatible with Claude Code, OpenAI Codex, Gemini CLI, and any MCP client
- 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
# 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-proxyIf 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.
-
Install the binary:
go install github.com/rendis/mcp-openapi-proxy/cmd/mcp-openapi-proxy@latest
-
Start from the generic Claude Code example in
.mcp.json.exampleand copy it into your project as.mcp.json. -
Choose one auth path:
- Static token: add
MCP_AUTH_TOKENto.mcp.json - OIDC login: add
MCP_OIDC_ISSUERandMCP_OIDC_CLIENT_IDto.mcp.json, then runmcp-openapi-proxy loginonce with the same values
- Static token: add
-
Open your MCP client and use the server through
list_endpoints,describe_endpoint, andcall_endpoint.
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-proxygo install github.com/rendis/mcp-openapi-proxy/cmd/mcp-openapi-proxy@latestOr 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-proxyAll 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.
| 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 |
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 loginOpenAI 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"
}
}
}
}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.
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.
Recommended agent flow:
- Call
{prefix}_list_endpointsto find candidate endpoints quickly - Call
{prefix}_describe_endpointfor the exact endpoint contract - Call
{prefix}_call_endpointwithtoolNamepluspath/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.
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": ""
}Input:
{
"toolName": "fe_patch_admin_features_key_toggle"
}Output includes the full normalized contract for that endpoint:
toolName,method,path,summary,description,deprecatedrequiredAuthand fullsecurityRequirementsparameters.path/query/headers/cookieswith schemas by locationrequestBodywith media types, schema, examples, and encoding hintsresponseswith status, description, headers, and body schemasexternalDocs,servers,baseURLHint
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/5xxresponses setIsError=truebut preserve the real HTTP response - Proxy/runtime failures return
status: 0plus aproxy_errorobject - Binary responses are wrapped as
{ "encoding": "base64", "data_base64": "...", "size_bytes": 123 }
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.
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-proxyIf 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.
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,openIdConnect→MCP_AUTH_<SCHEME>_TOKENhttp basic→MCP_AUTH_<SCHEME>_USERNAMEandMCP_AUTH_<SCHEME>_PASSWORDapiKey→MCP_AUTH_<SCHEME>_KEY
Scheme names are uppercased and sanitized by replacing ., -, /, and spaces with underscores. Example: partner-auth/v2 → PARTNER_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-keyThe proxy injects each resolved credential according to the OpenAPI scheme definition: Authorization header for bearer/basic, or header/query/cookie placement for apiKey.
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
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 loginWhen 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 loginFetches {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.
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).
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.
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 +
renameto prevent corruption from concurrent processes
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 omitsexpires_infrom the refresh response, defaults to 1 hour
# 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| 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 |
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
- Agent calls
list_endpointswith optional filters and gets lightweight endpoint metadata plustoolName - Agent optionally calls
describe_endpointfor the full contract of one endpoint - Agent calls
call_endpointwithtoolNameplus location-aware input sections, for example:{"toolName": "api_patch_users_id", "path": {"id": "abc123"}, "query": {"include_roles": true}} - Handler resolves the base URL from
MCP_BASE_URLor a single usable OpenAPIserver - Path, query, header, and cookie parameters are serialized with their declared OpenAPI
styleandexploderules - 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.) - Auth is resolved from the endpoint security requirements (bearer, basic, apiKey, oauth2, openIdConnect) plus any configured extra headers
- 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}inbody - Empty responses →
bodyisnull 4xx/5xxAPI responses keep the same envelope and setIsError=true- Proxy failures use
status: 0plusproxy_error
- JSON body → parsed object in
The agent sees a small, stable MCP surface:
list_endpoints— cheap discovery withtoolName,method,path,description,requiredAuth,tags,deprecateddescribe_endpoint— the full normalized OpenAPI contract for one endpointcall_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
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"
}
}
}
}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-proxyOnce 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.
| 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 |
| Component | Purpose |
|---|---|
| Go 1.26+ | Runtime |
| kin-openapi | OpenAPI 3.x parsing |
| go-sdk | MCP server implementation |
| jsonschema-go | JSON Schema for tool inputs |
Contributions are welcome.
- Fork the repository
- Create a feature branch (
git checkout -b feature/my-feature) - Commit your changes
- Push to the branch and open a Pull Request
Note
Please include tests for any changes. Run go test ./... to verify.
MIT