Skip to main content

mcpify

Python License

English | Türkçe

mcpify in action — listing and serving OpenAPI endpoints as MCP tools

Tests CodeQL Platforms MCP Registry CI Python Code style: ruff Types: mypy PyPI PyPI Downloads Run with uvx Dependencies

Turn any OpenAPI REST API into an MCP server — so Claude Code, Cursor, and every other MCP client can call your API directly.

mcpify is focused, production-ready, and CLI-first: one job (OpenAPI → MCP), zero runtime dependencies. Focused doesn't mean small — 294 tests across eighteen suites, two transports (stdio + HTTP), dual MCP-spec compatibility, OAuth2, a policy layer, caching, safe retries, and health probes back that one job.

Your company has a REST API. Your AI agent needs to call it. Until now that meant hand-writing a custom MCP server for every API. With mcpify:

mcpify serve https://your-company.com/openapi.json

That's it — every endpoint just became a tool your AI agent can discover, understand, and call.

Deep docs: Usage guide — auth patterns, scoping, Docker, troubleshooting · Architecture · Self-hosting · Contributing · Changelog · Security

The launch story: How a live weather API broke this tool — and made it better

Why you'll like it

  • 60 seconds to working — point it at any OpenAPI 3.x spec (file or URL)
  • Credentials never touch the spec or the model — pulled from your environment at call time (--auth-env), sent as Authorization: Bearer, a custom header, or a query parameter
  • Every operation becomes a first-class MCP tool — input schemas are generated from parameters + requestBody, internal $refs are resolved
  • Scope it down--read-only (GET only), --tag payments, --include /v1/orders, --exclude /admin, plus a policy layer for real-world APIs: --deny REGEX hides mutating GETs, --allow REGEX re-includes read-style POST endpoints. Deny always wins.
  • mcpify doctor — tells you if your spec is agent-friendly before you ship
  • Multiple environments? Pick one. Specs declaring prod/staging/dev servers[] get --server 2 or --server staging (description or URL match) instead of a hand-typed --base-url
  • Two transports, one tool surface. serve speaks stdio to local agents; serve --http 8080 speaks MCP Streamable HTTP so a whole team (or a gateway) can share one server — optional bearer token with --http-token, stateless per the current MCP spec
  • OAuth2 client-credentials built in — point it at your identity provider's token endpoint; tokens are fetched, cached, refreshed, and re-fetched automatically on a mid-flight 401 (RFC 6749, stdlib only)
  • Auth reads the spec, not a dashboard — the security declarations in your OpenAPI document configure --auth-env automatically (bearer, HTTP basic, header or query with the right name); secured specs with no credential print the exact flags to run. Rate-limited APIs can be honored too: --wait-on-429 waits out Retry-After once, within a cap
  • Several APIs, one MCP server — list multiple OpenAPI documents as [apis.NAME] sections in .mcpify.toml and one serve process fronts them all: a single tool surface with per-API auth, caching, retries and filters, automatic renames when two APIs ship the same tool name, an aggregated health report, and mcpify status that probes every API in parallel. The feature hosted gateways bill for, in a config file
  • Host it yourself for freedeploy/docker-compose.yml (with automatic-HTTPS Caddy) and a hardened systemd unit turn a $5 VPS into what hosted-MCP plans bill $9–$229/month for: Self-hosting guide
  • mcpify try — an interactive terminal REPL to call the generated tools without any agent client: pick a tool, fill the arguments, see the real response. Same execution path as MCP tools/call
  • mcpify output-server — bake a serve command into a small shareable script: teammates run python3 server.py and get the identical MCP server
  • Operational, not just functional. mcpify init wizard + .mcpify.toml configs with per-environment sections, GET response caching (--cache-ttl), safe retries (--retry — idempotent methods only, 502/503/504 only), verbose/log-file logging with masked credentials, XML→JSON conversion, strict argument mode, origin auto-discovery, legacy batch tolerance, and a health probe (mcpify status / mcpify_health)
  • Zero runtime dependencies — the entire tree is auditable stdlib Python; YAML specs need an optional pip install 'mcpify[yaml]'
  • Agent-grade surface. Tool annotations derived from HTTP semantics (clients auto-approve read-only tools), structured output via MCP outputSchema/structuredContent, remediation-grade errors that teach the next call, dry-run request previews, and a --lazy search-then-call mode that cut api.weather.gov's listing by 95.5% (38,882 → 1,741 chars)
  • 327 tests across twenty suites — including full MCP protocol runs over stdio and over HTTP against real local APIs and the live api.weather.gov document (69 tools, 16 enum'd parameters)

Quick start

# run without installing (uvx — pulls from PyPI on demand)
uvx --from mcpify-openapi mcpify list ./openapi.json --read-only

# first time? the wizard writes a config for you
uvx --from mcpify-openapi mcpify init

# or install (installs the `mcpify` command)
pipx install mcpify-openapi

# ...as a container (GHCR, published on every release)
docker run -i ghcr.io/furkan708/mcpify:latest serve ./openapi.json --read-only

# ...or from source
git clone https://github.com/furkan708/mcpify.git
cd mcpify && pip install .

# 1. preview the tools that will be generated
mcpify list examples/petstore.json

# 2. validate the spec is agent-friendly
mcpify doctor examples/petstore.json

# 3. serve it over MCP
mcpify serve examples/petstore.json --base-url https://petstore.example.com/v1

# 4. no agent client at hand? try the tools in your terminal
mcpify try examples/petstore.json --base-url https://petstore.example.com/v1

# 5. or share it over HTTP with the whole team
mcpify serve examples/petstore.json --http 8080 --http-token $SHARED_TOKEN

With authentication

# Bearer token read from the environment (never hardcoded)
export PETSTORE_KEY="sk-..."
mcpify serve petstore.json \
  --base-url https://petstore.example.com/v1 \
  --auth-env PETSTORE_KEY \
  --auth-style bearer \            # optional: auto-detected from the spec
  --read-only

No explicit style needed in the common case — the spec's security declarations pick bearer/basic/header/query (with the right name) for you. For HTTP Basic, the env variable holds username:password: --auth-style basic --auth-env CREDS.

Flag Meaning
--auth-env VAR environment variable holding the credential
--auth-style bearer|basic|header|query how it is sent (default: auto-detected from the spec)
--auth-name NAME header / query name for non-bearer styles (e.g. X-API-Key)

With OAuth2 (client credentials)

For APIs behind an OAuth2 identity provider (RFC 6749 §4.4). Credentials live in the environment; the access token is fetched, cached until its expires_in, refreshed transparently, and re-fetched automatically once if the API answers 401 mid-flight:

export OAUTH2_CLIENT_ID="..."
export OAUTH2_CLIENT_SECRET="..."
mcpify serve api.json \
  --oauth2-token-url https://idp.example.com/oauth2/token \
  --oauth2-client-id-env OAUTH2_CLIENT_ID \
  --oauth2-client-secret-env OAUTH2_CLIENT_SECRET \
  --oauth2-scope "read write"        # optional; --oauth2-client-auth body for token endpoints that reject Basic

Multiple APIs in one server

Put several OpenAPI documents in one config and serve them as a single tool surface — no gateway, no per-API process:

# .mcpify.toml
[apis.catalog]
spec = "https://shop.example.com/openapi.json"
auth-env = "CATALOG_TOKEN"          # per-API credential
cache-ttl = 60

[apis.crm]
spec = "./crm.yaml"
read-only = true                    # per-API policy
base-url = "https://crm.internal/v2"

[apis.weather]
spec = "https://api.weather.gov/openapi.json"
timeout = 10

Surface switches (--lazy, --enable-preview, --http, --format) are server-wide flags; credentials, policies, caching and retries are per-API.

mcpify serve            # stdio, all three APIs, prefixed on collisions
mcpify serve --http 8080
mcpify try              # REPL across every API
mcpify status           # probes each API concurrently

mcpify status reports per API — [catalog] reachable (status 200, 0.03s) — https://shop.example.com — 31 tools — and exits non-zero if any API is unreachable. When two APIs expose the same tool name (list_pets), both get renamed with their label (catalog_list_pets, crm_list_pets) so nothing silently wins; non-conflicting names stay untouched. The mcpify_health tool returns one report covering every API. Precedence per key: CLI flags > [apis.NAME] > [serve]. Pass a positional spec or [apis.*] sections — never both.

Plug it into your agent

Claude Code:

claude mcp add my-api -- mcpify serve openapi.json --read-only

Claude Desktop / Cursor / any MCP client (claude_desktop_config.json):

{
  "mcpServers": {
    "petstore": {
      "command": "mcpify",
      "args": ["serve", "~/specs/petstore.json", "--auth-env", "PETSTORE_KEY"]
    }
  }
}

HTTP transport (team-shared server) — run mcpify serve api.json --http 0.0.0.0:8080 --http-token $TOKEN once, then point HTTP-capable clients at it:

{
  "mcpServers": {
    "petstore": {
      "type": "http",
      "url": "http://your-host:8080",
      "headers": { "Authorization": "Bearer <token>" }
    }
  }
}

Now ask your agent: "list the pets, then create one named Milo" — it discovers list_pets and create_pet, fills the arguments, and performs real HTTP calls.

How operations become tools

OpenAPI mcpify
operationId tool name (sanitized; falls back to method_path)
summary / description tool description the agent reads
deprecated: true shown by mcpify list before you expose old endpoints
parameters (path/query/header) individual typed arguments with enums
requestBody (JSON) a body object argument
$ref pointers resolved inline (components → real schemas)
servers[0].url default base URL (override: --base-url)

The agent only ever sees the tool list and your API's JSON responses — mcpify adds no middleware, caches nothing, and sends credentials nowhere except your API.

Doctor

$ mcpify doctor my-api.json
openapi: 3.0.3
title:   Acme API
paths:   23
tools:   41 operations
servers: https://api.acme.com
warning: 12/41 operations have no operationId (names fall back to method_path)
warning: 30/41 operations have no summary (agents see no description)

CLI reference

mcpify list <spec> [--tag T] [--include P] [--exclude P] [--read-only] [--json]
mcpify serve <spec> [--base-url URL] [--server INDEX|NAME] [--name N] [--auth-env VAR]
                    [--auth-style bearer|header|query] [--auth-name NAME]
                    [--oauth2-token-url URL --oauth2-client-id-env VAR
                     --oauth2-client-secret-env VAR] [--timeout S]
                    [--read-only] [--tag T] [--include P] [--exclude P]
                    [--http [HOST:]PORT] [--http-token TOKEN] [--wait-on-429 SEC]
mcpify try <spec> [same serve flags]        # interactive REPL, no agent needed
mcpify output-server <spec> -o FILE [-- <any serve flags>]
mcpify doctor <spec>

# multi-API: define [apis.NAME] sections in .mcpify.toml, then run
#   mcpify serve|try|status   (no positional spec) — one process, every API

Notes & limitations

  • JSON specs work out of the box; YAML specs need pip install 'mcpify[yaml]'
  • Only local $ref pointers are resolved (bundle external docs first — most tools do anyway)
  • Request bodies are exposed as a single body object argument — predictable over clever
  • HTTP transport serves one JSON-RPC message per request (batching was removed from the MCP spec) and responds application/json — a stateless server has nothing to stream
  • Spec versions: OpenAPI 3.x and Swagger 2.x roots are accepted; 3.x is the happy path

Hardened against the real world

mcpify is audited on every release against a 10-category checklist of MCP best practices and published production failure modes — not just our own examples:

  • Hostile-spec corpus (12/12): circular $refs, multipart uploads, allOf schemas, server URL variables, relative base URLs, oversized responses — every scenario derived from a documented real-world failure, fixed, and locked in by a regression test. Sources include the arXiv study of REST→MCP generation across 18 real APIs.
  • Live integration: the real api.weather.gov spec loads in CI — the case that found (and fixed) our last crash-class bug.
  • MCP lifecycle enforced: tools are unreachable until the client completes the initialize handshake.
  • Blast-radius controls: read-only mode, deny/allow policy layer, 40k-char response truncation, --timeout, credentials never logged.

Full checklist with per-item status: docs/AUDIT-CHECKLIST.md

Tests

327 passing, plus one live-integration test that loads the real api.weather.gov document (auto-skipped when offline). Every suite runs on Python 3.10–3.12 across Linux and Windows; ruff, strict mypy and CodeQL gate every push.

Suite Tests What it pins down
Spec parsing & resolution 13 OpenAPI 3.x + YAML loading, $ref chains, allOf merge, server variables, malformed input
Tool translation 19 operationId naming with collision suffixing, input schemas, enums, body handling, annotation & output-schema derivation
Agent surface 32 HTTP-derived annotations, structured output contract, remediation errors, --lazy search, dry-run previews
CLI 15 list / doctor / serve flags, --json output, deprecated badges
Hostile corpus 11 circular $refs, multipart bodies, relative base URLs, 300 KB truncation, 500-op performance — each traced to a documented real-world failure
Lifecycle & hygiene 8 initialize handshake (-32002), byte-pure stdio, credentials never logged
Protocol end-to-end 9 real JSON-RPC over stdio against a live local HTTP API, wire-level assertions
Policy layer 7 --read-only, --allow / --deny precedence, mutating-GET protection
$ref parameters 4 parameter schemas resolved against the full spec — the weather.gov bug class (one test hits the live document)
Ops & configuration 47 config files + env precedence, init wizard, cache TTL & bounds, retry safety, XML conversion, discovery, batching, status/health
Protocol version compat 5 2026-07-28 stateless _meta requests and the legacy 2025-06-18 handshake, on the same wire
HTTP transport 19 Streamable HTTP: lifecycle over POST, 405/411/413/415 error ladder, parse/batch rejections, bearer enforcement, bind-string parser
OAuth2 client-credentials 18 token fetch/cache/refresh with a fake clock, Basic vs body client auth, public clients, every failure mode, 401 self-heal end-to-end
try REPL 26 piped-stdin sessions: selection by number/name, typed prompts, re-prompt on bad input, :raw/:info, clean EOF/Ctrl+C exits, read-only surface
output-server 11 embedded spec integrity, guard rails (existing file, bad spec, unknown flags), secret warnings, and a real subprocess E2E handshake
Server selection 17 `--server INDEX
Auth auto-detection & Basic 22 securitySchemes → style/name resolution (OpenAPI + Swagger 2.0), requirement-order precedence, operation-level security, exact hint text, HTTP Basic header encoding, CLI/try/doctor wiring, explicit-style override
Rate-limit courtesy (--wait-on-429) 9 Retry-After honored once within cap, cap exceeded returns 429 untouched, missing header falls back to retry delay, HTTP-date form never waits, POST never auto-waited, CLI wiring
Multi-API aggregation 26 [apis.*] merge with two-sided collision prefixes and _2 suffixes, per-API routing/auth/cache isolation, concurrent aggregated health (dead-API named in hint), lazy search across APIs incl. label match, preview routing, status exit codes, --env inheritance, both-rejected flag combos
CLI connectivity glue 10 --http wiring, MCPIFY_HTTP_TOKEN fallback, OAuth2 flag rules, config-file keys, wizard option 5, try smoke test

Policy on failures: every bug found in the wild becomes a pinned regression test before the fix ships — the suite only grows.

Run it locally:

pip install pytest pyyaml
pytest -v

Project Structure

mcpify/
├── mcpify/
│   ├── spec.py          # OpenAPI loading, $ref resolution, operation walking
│   ├── tools.py         # operation -> MCP tool, argument -> HTTP request
│   ├── http_client.py   # execution (urllib, HTTP errors become tool results), OAuth2 flow
│   ├── api_server.py    # the MCP server core (JSON-RPC 2.0, tools, policy)
│   ├── aggregate.py     # multi-API composition ([apis.*] -> one tool surface)
│   ├── http_transport.py# Streamable HTTP transport (--http)
│   ├── repl.py          # `mcpify try` interactive terminal REPL
│   ├── standalone.py    # `mcpify output-server` script generator
│   └── cli.py           # list / serve / try / output-server / doctor / status / init
├── examples/petstore.json
└── tests/

Roadmap

  • SSE streaming responses for the HTTP transport (server-initiated messages)
  • Multi-API aggregation: one serve process fronting several OpenAPI documents — shipped in v1.9.0
  • HTTP transport, OAuth2 client-credentials, mcpify try REPL, --output-server — shipped in v1.6.0

License

MIT — see the LICENSE file for details.

Download files

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

Source Distribution

mcpify_openapi-1.9.1.tar.gz (106.3 kB view details)

Uploaded Source

Built Distribution

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

mcpify_openapi-1.9.1-py3-none-any.whl (64.5 kB view details)

Uploaded Python 3

File details

Details for the file mcpify_openapi-1.9.1.tar.gz.

File metadata

  • Download URL: mcpify_openapi-1.9.1.tar.gz
  • Upload date:
  • Size: 106.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for mcpify_openapi-1.9.1.tar.gz
Algorithm Hash digest
SHA256 56e7a6764e0fbb15e0637220399b403f548f6ec4dc0cf772f1c32b961b879631
MD5 b5cfcbd29729cd5993a5477db9c2046c
BLAKE2b-256 5268bd19622c1ba0dcd3a775fdc64ede399e4df4380cab25f4dc5ad63b4515b0

See more details on using hashes here.

Provenance

The following attestation bundles were made for mcpify_openapi-1.9.1.tar.gz:

Publisher: release.yml on furkan708/mcpify

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

File details

Details for the file mcpify_openapi-1.9.1-py3-none-any.whl.

File metadata

  • Download URL: mcpify_openapi-1.9.1-py3-none-any.whl
  • Upload date:
  • Size: 64.5 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for mcpify_openapi-1.9.1-py3-none-any.whl
Algorithm Hash digest
SHA256 02fa204a5532bb5c07c9058253aad384eb262eea166aa5644ffcfa15f53fe554
MD5 ed03d0e7a2fd3e9778d3a008e4f85a49
BLAKE2b-256 c4bb2b09792929537ec31111ba0aa5f74810c6f3e0c99d822a3e8ebf89c38d87

See more details on using hashes here.

Provenance

The following attestation bundles were made for mcpify_openapi-1.9.1-py3-none-any.whl:

Publisher: release.yml on furkan708/mcpify

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

Release history Release notifications | RSS feed

1.19.0

2 files

1.18.1

2 files

1.18.0

2 files

1.17.2

2 files

1.17.1

2 files

1.17.0

2 files

1.16.1

2 files

1.16.0

2 files

1.15.0

2 files

1.14.0

2 files

1.13.0

2 files

1.12.1

2 files

1.12.0

2 files

1.11.0

2 files

1.10.0

2 files

This release

1.9.1 This release

2 files

1.9.0

2 files

1.8.0

2 files

1.7.0

2 files

1.6.2

2 files

1.6.1

2 files

1.6.0

2 files

1.5.3

2 files

1.5.2

2 files

1.5.1

2 files

1.5.0

2 files

1.4.0

2 files

1.3.0

2 files

1.2.0

2 files

1.1.0

2 files

1.0.4

2 files

1.0.3

2 files

1.0.2

2 files

1.0.1

2 files

1.0.0

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page