# mcpgen **Turn any API into an MCP server in 30 seconds.** [![PyPI version](https://img.shields.io/pypi/v/mcpgen-cli?style=flat-square&color=blue)](https://pypi.org/project/mcpgen-cli/) [![Python](https://img.shields.io/badge/python-3.10+-blue?style=flat-square&logo=python&logoColor=white)](https://www.python.org) [![License: MIT](https://img.shields.io/badge/license-MIT-green?style=flat-square)](https://opensource.org/licenses/MIT) [![MCP](https://img.shields.io/badge/MCP-protocol-purple?style=flat-square)](https://modelcontextprotocol.io) [![GitHub stars](https://img.shields.io/github/stars/JnanaSrota/mcpgen?style=flat-square&color=yellow)](https://github.com/JnanaSrota/mcpgen/stargazers) [![CI](https://img.shields.io/github/actions/workflow/status/JnanaSrota/mcpgen/ci.yml?style=flat-square&branch=main)](https://github.com/JnanaSrota/mcpgen/actions)
---
mcpgen demo
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. --- ## Install ```bash pip install mcpgen-cli ``` ## Quickstart ```bash # 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. --- ## What you get 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:** ```bash 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:** ```python #!/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. --- ## Add to Claude Desktop `mcpgen` prints the exact config snippet to paste into your `claude_desktop_config.json`: ```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:** - macOS: `~/Library/Application Support/Claude/claude_desktop_config.json` - Windows: `%APPDATA%\Claude\claude_desktop_config.json` --- ## Why source code instead of a proxy? 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: - Read every line of what's happening - Modify auth logic, add retries, adjust error handling - Deploy it anywhere without any dependency on `mcpgen` - Commit it to your own repo, version it, review it in PRs `mcpgen` is a build tool. Needed once, at generation time. Never at runtime. --- ## Supported inputs | 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:** - [Petstore](https://petstore3.swagger.io/api/v3/openapi.json) — 19 endpoints → 19 tools - GitHub REST API (subset) - Stripe API (subset) - Any valid OpenAPI 3.x spec --- ## Auth support `mcpgen` auto-detects your API's auth scheme from the spec: | Scheme | OpenAPI declaration | Generated env var | |---|---|---| | Bearer token | `type: http, scheme: bearer` | `YOUR_API_TOKEN` | | API Key | `type: apiKey` | `YOUR_API_API_KEY` | | Basic Auth | `type: http, scheme: basic` | `YOUR_API_CREDENTIALS` | | None | No `securitySchemes` | — | OAuth2 flows are approximated as bearer token — you supply the token manually. --- ## CLI reference ``` 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](./CONTRIBUTING.md). --- ## Contributing ```bash 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. --- ## License MIT — the generated code is yours to do whatever you want with.