oas2mcp
Load an OpenAPI document and expose every operation it describes as a tool of a Model Context Protocol server — without writing a line of glue code.
Each OpenAPI operation becomes one
MCP tool. When a client calls the tool,
oas2mcp builds and sends the corresponding HTTP request to the upstream API
and returns the response. In other words, it turns any HTTP API that ships an
OpenAPI description into something an MCP-capable agent can drive.
Features
Section titled “Features”- Input from a file or a URL — the document is fetched/read at startup, in
JSON or YAML. A non-public document URL can be authenticated with
--openapi-header. - OpenAPI 3.0 and 3.1 — both revisions are read by the same code path.
Schemas are passed through to MCP clients exactly as the document writes
them, so 3.1’s JSON Schema 2020-12 keywords (
typearrays such as[string, "null"],const,prefixItems, numericexclusiveMinimum, boolean schemas,$defs) survive intact, as do 3.0’s (nullable,example). 3.1’scomponents.pathItems,$refsiblings and optionalpathsare supported too. - Periodic reload — with
--reload-every, a document loaded from a URL is re-fetched on an interval and the exposed tool set is rebuilt in place, without restarting the server. The fetch can authenticate via OAuth2client_credentials(auto-refreshed token), so reloads keep working on a long-running server where a static token would expire. The client authenticates with either a shared secret or a signed JWT assertion (private_key_jwt, RFC 7523 §2.2), for providers that will not issue a secret. - One tool per operation —
operationIdbecomes the tool name (falling back to<method>_<path>); path, query and header parameters become top-level tool arguments, and a JSON request body is passed as abodyargument. Local$refs are inlined into each tool’s input schema, whatever they point at (#/components/schemas/…,#/$defs/…, …); a recursive schema collapses to a bare object rather than expanding forever. - Readable tool names
— rewrite the names an OpenAPI document produces with chained
--renameregex rules, and cap their length with--max-name-len(64 by default, the limit Anthropic and OpenAI enforce). GitLab’spostApiV4ProjectsIdMergeRequestsNoteableIdDiscussionsDiscussionIdNotesbecomespost_projmrdiscNotes, which fits under a gateway prefix and is far easier for a model to pick. Filters keep matching the originaloperationId. - Behaviour hints — each tool
carries the MCP annotations its HTTP method implies (
readOnlyHinton aGET,destructiveHinton aDELETE, …), so a client can tell a read from a write. - Three transports — the MCP server can be exposed over:
stdio— for a local subprocess MCP client.http— the current remote transport, singlePOST /mcpendpoint. By default each request is answered with a singleapplication/jsonbody (stateless), which is the most interoperable mode — notably with strict proxies such as Envoy AI Gateway. Pass--stream-responsesto reply with atext/event-stream(SSE) flow and keep stateful sessions instead.sse— the legacy HTTP+SSE transport (deprecated by the MCP spec, kept for compatibility with older clients).
- Auth passthrough
— attach arbitrary static headers (e.g. a bearer token) to every upstream
request, or forward the MCP client’s own request headers (e.g.
Authorization) upstream per call (httponly). - OAuth for the upstream API — obtain the
upstream
Authorization: Bearerfrom an OAuth2 grant, refreshed automatically before it expires, instead of a static token that goes stale. Authenticates with a client secret or a signed JWT assertion (RFC 7523 §2.2), and is configured independently of the document-fetch grant. - Acting on behalf of the caller
— with the
jwt-bearergrant (RFC 7523 §2.1), obtain a per-caller upstream token from the identity in their verified JWT, so the upstream API sees who is really acting and applies its own authorization, instead of every call arriving as one shared service account. - Caller authentication and role-based tool access
— verify the caller’s JWT against a JWKS, and optionally gate which tools they
can see and call by mapping each
roleto a regex over operation names (httponly). Tools mapped to the reserved role*stay open to everyone, token or not. - MCP authorization discovery
— with
--inbound-resource,/mcpbehaves as the OAuth protected resource the MCP authorization spec describes: a request without a valid token is answered401with aWWW-Authenticatechallenge, and the Protected Resource Metadata (RFC 9728) tells the client which authorization server to log in with. - JWT claim tracing
— with
--trace-claim, echo selected claims from the verified token (e.g.sub,email,tenant_id) onto each tool-call log line to see who made each call, without inflating metric cardinality. - OpenTelemetry metrics — count and time every
tool call, labelled by tool and outcome (kept low-cardinality), exported over
OTLP and/or a Prometheus
/metricsendpoint. - Custom CA trust — point
--ca-certat a PEM bundle to trust a private or corporate CA for every outbound TLS connection (upstream API, document fetch, OAuth, JWKS), on top of the built-in public roots. - Graceful shutdown on
SIGTERM/SIGINT.