Show HN: Mcpgen – Turn OpenAPI/Postman Specs into Python MCP Servers
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.
Hacker News
news.ycombinator.com