Turn any API into an MCP server in 30 seconds.
Point mcpgen at an OpenAPI spec or Postman collection. Get back a complete Python MCP server you own — no runtime dependency on mcpgen, no proxy, no lock-in. Read it. Modify it. Ship it.
pip install mcpgen-cli Quickstart # From a URL mcpgen https://petstore3.swagger.io/api/v3/openapi.json # From a local OpenAPI file (JSON or YAML) mcpgen stripe.yaml # From a Postman collection mcpgen postman_collection.json # Preview without writing anything mcpgen openapi.json --dry-run # Custom output directory and server name mcpgen openapi.json --output ~ /my-mcp-servers --name " My API " That's it. mcpgen reads your spec and writes a Python MCP server to disk.
A self-contained directory with source code you own:
stripe_api_mcp/ ├── server.py ← the MCP server (read it, edit it, it's yours) └── requirements.txt ← httpx, mcp Run it immediately:
cd stripe_api_mcp pip install -r requirements.txt export STRIPE_API_TOKEN= " sk_live_... " python server.py The generated server.py looks like this:
#!/usr/bin/env python3 """ Stripe API MCP Server Generated by mcpgen — https://github.com/JnanaSrota/mcpgen This file is yours. Modify it freely. """ import os , asyncio , httpx from mcp . server import Server from mcp . server . stdio import stdio_server from mcp import types BASE_URL = "https://api.stripe.com" AUTH_TOKEN = os . environ . get ( "STRIPE_API_TOKEN" , "" ) app = Server ( "stripe-api" ) @ app . call_tool () async def _handle_get_customers ( name : str , arguments : dict ): if name != "get_customers" : raise ValueError ( f"Unknown tool: { name } " ) url = BASE_URL + "/v1/customers" query_params = {} if "limit" in arguments : query_params [ "limit" ] = arguments [ "limit" ] async with httpx . AsyncClient ( timeout = 30.0 ) as client : response = await client . get ( url , params = query_params , headers = { "Authorization" : f"Bearer { AUTH_TOKEN } " }) response . raise_for_status () return [ types . TextContent ( type = "text" , text = response . text )] # ... one function per API endpoint @ app . list_tools () async def list_tools (): ... if __name__ == "__main__" : asyncio . run ( stdio_server ( app )) No black box. No dependencies at runtime. Just Python.
mcpgen prints the exact config snippet to paste into your claude_desktop_config.json :
{ "mcpServers" : { "stripe-api-mcp" : { "command" : " python " , "args" : [ " /path/to/stripe_api_mcp/server.py " ], "env" : { "STRIPE_API_TOKEN" : " your-key-here " } } } } Config file location:
Most API-to-MCP tools are runtime proxies — they sit between Claude and your API forever, and you depend on them to stay running.
mcpgen generates source code you own . The output is a plain Python file. You can:
mcpgen is a build tool. Needed once, at generation time. Never at runtime.
Format Example OpenAPI 3.x JSON mcpgen openapi.json OpenAPI 3.x YAML mcpgen api.yaml URL pointing to OpenAPI spec mcpgen https://api.example.com/openapi.json Postman Collection v2.1 mcpgen collection.json Tested with:
mcpgen auto-detects your API's auth scheme from the spec:
OAuth2 flows are approximated as bearer token — you supply the token manually.
Usage: mcpgen [OPTIONS] INPUT Turn any API into an MCP server in 30 seconds. Arguments: INPUT OpenAPI JSON/YAML file, Postman collection, or URL [required] Options: -o, --output PATH Output directory (default: current directory) -n, --name TEXT Override the generated server name --dry-run Print generated code without writing files --no-color Disable colored output -v, --version Show version and exit --help Show this message and exit Roadmap TypeScript output ( --lang ts ) using @modelcontextprotocol/sdk Swagger 2.x support (auto-detected, separate parser) Auto-detect OpenAPI URL from base domain ( /.well-known/openapi.json , /api-docs , etc.) --update-claude-config flag to patch claude_desktop_config.json automatically Recursive $ref resolution for deeply nested schemas PRs welcome. See CONTRIBUTING.md .
git clone https://github.com/JnanaSrota/mcpgen cd mcpgen pip install -e " .[dev] " pytest tests/ -v The codebase has three clean layers:
Input (file / URL) ↓ loader.py ← detects format, routes to correct parser ↓ openapi.py / postman.py ← parse to MCPSpec (internal IR) ↓ generator/python.py ← renders Jinja2 templates → server.py To add a new input format: write a parser that returns MCPSpec . Nothing else changes. To add a new output language: write a generator that consumes MCPSpec . Nothing else changes.
MIT — the generated code is yours to do whatever you want with.
Hacker News
news.ycombinator.com