Convert any OpenAPI spec into a working MCP server — no code required.
Project description
specmcp
Convert any OpenAPI spec into a working MCP server — no code required.
specmcp runs a load → normalize → simplify → expose pipeline at startup and then answers MCP tools/list and tools/call requests by proxying the real upstream API. Give it a spec URL or file and a config file, and your LLM can call the API immediately.
specmcp serve --spec https://api.example.com/openapi.json
Install
pip install mcp-from-spec
Requires Python 3.10+.
Quickstart
1. Scaffold a config file
specmcp init --spec https://petstore3.swagger.io/api/v3/openapi.json
This writes mcp.config.yaml and .env.example to the current directory.
2. Start the server
specmcp serve --config mcp.config.yaml
The server runs over stdio by default, which is what Claude Desktop and most MCP clients expect. You should see something like:
specmcp serving 19 tools from https://petstore3.swagger.io/api/v3/openapi.json [transport=stdio]
3. Point your MCP client at it
In Claude Desktop's claude_desktop_config.json:
{
"mcpServers": {
"petstore": {
"command": "specmcp",
"args": ["serve", "--config", "/path/to/mcp.config.yaml"]
}
}
}
Config file reference
mcp.config.yaml controls everything: which spec to load, which operations to expose, auth credentials, timeouts, and more.
version: "1"
spec:
source: ./openapi.json # path or URL
auth:
myKey:
type: apiKey
in: header # header | query | cookie
name: X-API-Key
value_from: env(MY_API_KEY) # env(VAR) or literal(value)
server:
base_url_override: https://api.example.com # override spec's servers[]
include_operations: # allowlist by operationId
- listPets
- createPet
exclude_tags:
- admin
dispatch:
default_timeout_seconds: 30
response_size_limit_bytes: 1048576 # 1 MiB
text_truncate_bytes: 65536 # 64 KiB
simplify:
truncate_description_chars: 400
drop_spec_metadata: true
Auth types
API key
auth:
myKey:
type: apiKey
in: header # header | query | cookie
name: X-API-Key
value_from: env(MY_API_KEY)
Bearer token
auth:
myToken:
type: bearer
value_from: env(MY_BEARER_TOKEN)
OAuth 2.0 — Client Credentials
specmcp fetches a short-lived access token at the token_url and refreshes it automatically before expiry. No user interaction required.
auth:
myApi:
type: oauth2_client_credentials
token_url: https://auth.example.com/oauth/token
client_id_from: env(MY_CLIENT_ID)
client_secret_from: env(MY_CLIENT_SECRET)
scopes:
- read
- write
OAuth 2.0 — Authorization Code + PKCE
For APIs that require user-delegated OAuth (GitHub, Google, Salesforce, etc.). Requires --transport http so specmcp can receive the IdP callback.
auth:
myOAuth:
type: oauth2_authorization_code
authorization_url: https://auth.example.com/oauth/authorize
token_url: https://auth.example.com/oauth/token
redirect_uri: http://localhost:8765/auth/callback
client_id_from: env(MY_CLIENT_ID)
client_secret_from: env(MY_CLIENT_SECRET)
scopes:
- read
- write
See OAuth 2.0 Authorization Code flow below for the full walkthrough.
CLI reference
specmcp serve [OPTIONS]
| Flag | Default | Description |
|---|---|---|
--spec / -s |
(from config) | Path or URL to the OpenAPI spec |
--config / -c |
mcp.config.yaml |
Config file path |
--transport / -t |
stdio |
stdio or http |
--watch / -w |
false |
Hot-reload spec/config on file change |
--verbose / -v |
false |
DEBUG-level logging |
--management-port |
8766 |
Port for the management listener (HTTP transport only) |
--management-bind |
loopback |
loopback or all |
--token-store |
memory |
memory or sqlite (encrypted at rest) |
--token-store-path |
~/.specmcp/tokens.db |
SQLite database path |
--token-store-key-env |
SPECMCP_TOKEN_KEY |
Env var holding the encryption key |
Other commands:
specmcp init # scaffold mcp.config.yaml + .env.example
specmcp inspect # list all exposed tools without starting the server
specmcp validate # lint the spec and config and exit with a status code
HTTP transport
Start the server over HTTP/SSE instead of stdio:
specmcp serve --config mcp.config.yaml --transport http
The MCP server listens on http://127.0.0.1:8765 by default.
transport:
http:
host: "127.0.0.1"
port: 8765
Use HTTP transport when you need:
- Multiple simultaneous MCP clients (one SSE connection each)
- OAuth 2.0 Authorization Code flows (requires an HTTP callback endpoint)
- Per-session auth tokens
OAuth 2.0 Authorization Code flow
When oauth2_authorization_code is configured and the server is running with --transport http, the flow works like this:
- LLM calls a tool — specmcp checks whether the session has a valid token.
- No token — specmcp returns an
AuthRequirederror containing alogin_url. - Client presents the URL — the user opens the URL in a browser.
- User authenticates — the IdP redirects to specmcp's
/auth/callback. - Token stored — specmcp exchanges the code for tokens and stores them.
- LLM retries — the tool call proceeds with the access token in the
Authorizationheader.
Subsequent calls use the stored token (silently refreshed before expiry).
Start the HTTP server
specmcp serve \
--config mcp.config.yaml \
--transport http \
--token-store sqlite \
--token-store-key-env SPECMCP_TOKEN_KEY
Set the encryption key for the SQLite token store:
export SPECMCP_TOKEN_KEY="$(openssl rand -hex 32)"
OAuth endpoints
| Route | Purpose |
|---|---|
GET /auth/login?nonce=<token> |
Redirect to the IdP authorization page |
GET /auth/callback?code=&state= |
Exchange the authorization code for tokens |
GET /auth/status?session=<id> |
Poll {"authenticated": true|false} |
DELETE /auth/session/<id> |
Revoke a session's tokens (management endpoint) |
The management endpoint (DELETE /auth/session/<id>) runs on a dedicated port (default 8766) bound to loopback only. Set --management-bind all and management.management_token_from in config to expose it externally with Bearer auth.
Hot-reload (--watch)
specmcp serve --config mcp.config.yaml --watch
Watches the spec and config files for changes and atomically reloads the ToolRegistry without dropping the stdio connection. Useful when iterating on a spec. Requires watchfiles:
pip install watchfiles
Note: changes to the
auth:section ofmcp.config.yamlare not picked up on hot-reload. A full restart is required for auth changes.
Examples
The examples/ directory contains ready-to-run configs:
| Example | Description |
|---|---|
examples/petstore/ |
Swagger Petstore — basic quickstart |
examples/github/ |
GitHub REST API (bearer token) |
examples/stripe/ |
Stripe API (API key) |
examples/oauth2-example/ |
OAuth 2.0 Authorization Code + PKCE |
Run any example:
export GITHUB_TOKEN=ghp_...
specmcp serve --config examples/github/mcp.config.yaml
How it works
Point specmcp at any OpenAPI spec and it instantly becomes an MCP server. Every operation in the spec becomes a callable tool — with a name, description, and typed arguments that the LLM can use directly.
specmcp handles all the translation automatically: it reads the spec, builds the tools, injects your credentials, and proxies calls to the real upstream API. No code generation, no wrappers to maintain. When the spec changes, restart specmcp (or use --watch) and the tools update automatically.
License
Apache 2.0
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file mcp_from_spec-1.4.1.tar.gz.
File metadata
- Download URL: mcp_from_spec-1.4.1.tar.gz
- Upload date:
- Size: 150.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a117c0644ff158b05fce35977efcf123389635f6abb2f3a6295f524b92f3e2d6
|
|
| MD5 |
30b1038979da209955270cbbce532efe
|
|
| BLAKE2b-256 |
d390d7a50492656ee80901573eefbc3fdc452e49f8a9053620494d9c26dfbdfe
|
Provenance
The following attestation bundles were made for mcp_from_spec-1.4.1.tar.gz:
Publisher:
publish.yml on Kibrom1/specmcp
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
mcp_from_spec-1.4.1.tar.gz -
Subject digest:
a117c0644ff158b05fce35977efcf123389635f6abb2f3a6295f524b92f3e2d6 - Sigstore transparency entry: 1707192542
- Sigstore integration time:
-
Permalink:
Kibrom1/specmcp@86148d5cceba21e52d2417e19fa2e774b21aa757 -
Branch / Tag:
refs/tags/v1.4.1 - Owner: https://github.com/Kibrom1
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@86148d5cceba21e52d2417e19fa2e774b21aa757 -
Trigger Event:
push
-
Statement type:
File details
Details for the file mcp_from_spec-1.4.1-py3-none-any.whl.
File metadata
- Download URL: mcp_from_spec-1.4.1-py3-none-any.whl
- Upload date:
- Size: 90.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
11fa033b2ff57c56d9f7d2ff7bc75bbcd6b20f5c6b4c8d62b031238da47fb7e9
|
|
| MD5 |
6bd43ae10354cd5c891e785816913e42
|
|
| BLAKE2b-256 |
48e59b1ae3e8c6e8a8062f20ac8e2db76acc82bd6bda952b96cc4e436d00c28a
|
Provenance
The following attestation bundles were made for mcp_from_spec-1.4.1-py3-none-any.whl:
Publisher:
publish.yml on Kibrom1/specmcp
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
mcp_from_spec-1.4.1-py3-none-any.whl -
Subject digest:
11fa033b2ff57c56d9f7d2ff7bc75bbcd6b20f5c6b4c8d62b031238da47fb7e9 - Sigstore transparency entry: 1707192553
- Sigstore integration time:
-
Permalink:
Kibrom1/specmcp@86148d5cceba21e52d2417e19fa2e774b21aa757 -
Branch / Tag:
refs/tags/v1.4.1 - Owner: https://github.com/Kibrom1
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@86148d5cceba21e52d2417e19fa2e774b21aa757 -
Trigger Event:
push
-
Statement type: