# Show HN: Mcpgen – Turn OpenAPI/Postman Specs into Python MCP Servers

> Source: <https://github.com/JnanaSrota/mcpgen>
> Published: 2026-07-23 05:09:47+00:00

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
# 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:**

``` bash
#!/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:**

- macOS:
`~/Library/Application Support/Claude/claude_desktop_config.json`

- Windows:
`%APPDATA%\Claude\claude_desktop_config.json`

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.

| 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

`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.

```
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
```

- 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](/JnanaSrota/mcpgen/blob/main/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.
