loading…
Search for a command to run...
loading…
Generates safe-by-default MCP servers from OpenAPI specs, exposing only low-risk GET tools and blocking write operations.
Generates safe-by-default MCP servers from OpenAPI specs, exposing only low-risk GET tools and blocking write operations.
Generate secure, policy-aware MCP servers from OpenAPI specs.
MCPGen is a production-oriented MVP Python framework that turns OpenAPI specifications into safe-by-default tool servers. It can generate a FastAPI demo server or an MCP-style stdio server, while keeping write operations blocked unless future policy work explicitly enables them.
AI applications often need access to many APIs, databases, and internal systems. Without a framework, teams tend to rebuild the same integrations repeatedly, expose too many tools to the model, and skip safety controls such as risk classification, audit logs, and write-operation guardrails.
MCP servers make tools available to AI systems, but fast prototypes can accidentally expose dangerous operations like DELETE, POST, PATCH, or PUT without review.
MCPGen reads an OpenAPI YAML or JSON file and generates:
GET executionThe default behavior is intentionally conservative: only low-risk GET tools are exposed.
GET = lowPOST, PUT, PATCH = mediumDELETE = hightools/list and tools/callGET tools onlygenerate, inspectmcpgen.yamlOpenAPI spec
-> parser
-> tool generator
-> risk classifier
-> safety filter
-> tools.json / tools.all.json / tools.embeddings.json / safety_report.json
-> generated FastAPI or MCP server
-> policy engine
-> semantic/keyword router
-> dry-run or safe GET execution
-> audit log
-> metrics summary
Install locally:
pip install -e .[dev]
Inspect a spec:
mcpgen inspect --from examples/jsonplaceholder.openapi.yaml
Generate a FastAPI server:
mcpgen generate --from examples/jsonplaceholder.openapi.yaml --mode fastapi --output generated_jsonplaceholder
Run it:
cd generated_jsonplaceholder
export API_BASE_URL=https://jsonplaceholder.typicode.com
uvicorn server:app --reload --port 8001
PowerShell:
cd generated_jsonplaceholder
$env:API_BASE_URL = "https://jsonplaceholder.typicode.com"
uvicorn server:app --reload --port 8001
Open:
http://127.0.0.1:8001/
http://127.0.0.1:8001/docs
http://127.0.0.1:8001/tools
http://127.0.0.1:8001/safety
http://127.0.0.1:8001/metrics
Demo spec:
examples/jsonplaceholder.openapi.yaml
It includes:
GET /usersGET /users/{id}GET /postsGET /posts/{id}POST /postsDELETE /posts/{id}Excerpt:
paths:
/users/{id}:
get:
operationId: getUserById
summary: Get user by ID
parameters:
- name: id
in: path
required: true
schema:
type: integer
Generated safe tool:
{
"name": "get_user_by_id",
"description": "Get user by ID",
"method": "GET",
"path": "/users/{id}",
"risk_level": "low",
"enabled": true,
"operation_id": "getUserById",
"input_schema": {
"type": "object",
"properties": {
"id": {
"type": "integer",
"description": "User ID",
"x-mcpgen-location": "path"
}
},
"required": ["id"]
}
}
Withheld tools such as create_post and delete_post remain in tools.all.json and are explained in safety_report.json.
From the project root:
mcpgen generate --from examples/jsonplaceholder.openapi.yaml --mode fastapi --output generated_jsonplaceholder
cd generated_jsonplaceholder
export API_BASE_URL=https://jsonplaceholder.typicode.com
uvicorn server:app --reload --port 8001
PowerShell:
mcpgen generate --from examples/jsonplaceholder.openapi.yaml --mode fastapi --output generated_jsonplaceholder
cd generated_jsonplaceholder
$env:API_BASE_URL = "https://jsonplaceholder.typicode.com"
uvicorn server:app --reload --port 8001
List exposed safe tools:
curl http://127.0.0.1:8001/tools
Route tools by query:
curl -X POST http://127.0.0.1:8001/tools \
-H "Content-Type: application/json" \
-d "{\"query\":\"get user by id\"}"
PowerShell equivalent:
Invoke-RestMethod -Method Post http://127.0.0.1:8001/tools `
-ContentType "application/json" `
-Body '{"query":"get user by id"}'
Dry-run a safe tool:
curl -X POST http://127.0.0.1:8001/tools/get_user_by_id/dry-run \
-H "Content-Type: application/json" \
-d "{\"inputs\":{\"id\":1}}"
Execute a safe GET tool:
curl -X POST http://127.0.0.1:8001/execute \
-H "Content-Type: application/json" \
-d "{\"tool_name\":\"get_user_by_id\",\"params\":{\"id\":1}}"
PowerShell equivalent:
Invoke-RestMethod -Method Post http://127.0.0.1:8001/execute `
-ContentType "application/json" `
-Body '{"tool_name":"get_user_by_id","params":{"id":1}}'
Show blocked POST behavior:
curl -X POST http://127.0.0.1:8001/tools/create_post/dry-run \
-H "Content-Type: application/json" \
-d "{\"inputs\":{\"title\":\"Hello\",\"body\":\"Demo\",\"userId\":1}}"
Show blocked DELETE behavior:
curl -X POST http://127.0.0.1:8001/tools/delete_post/dry-run \
-H "Content-Type: application/json" \
-d "{\"inputs\":{\"id\":1}}"
Show audit log:
cat logs/audit.log
PowerShell equivalent:
Get-Content logs\audit.log
Show metrics:
curl http://127.0.0.1:8001/metrics
PowerShell equivalent:
Invoke-RestMethod http://127.0.0.1:8001/metrics
Generate an MCP-style stdio server:
mcpgen generate --from examples/jsonplaceholder.openapi.yaml --mode mcp --output generated_jsonplaceholder_mcp
Run:
cd generated_jsonplaceholder_mcp
export API_BASE_URL=https://jsonplaceholder.typicode.com
python server.py
PowerShell:
cd generated_jsonplaceholder_mcp
$env:API_BASE_URL = "https://jsonplaceholder.typicode.com"
python server.py
Example tools/list JSON-RPC input:
{"jsonrpc":"2.0","id":1,"method":"tools/list"}
Example tools/call dry-run input:
{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"get_user_by_id","arguments":{"id":1}}}
MCP mode uses the same tools.json, policy engine, and audit logging as FastAPI mode. In the current MVP, tools/call is dry-run by default. With execution_mode: safe-execute, it can execute only low-risk GET tools.
MCPGen writes tools.embeddings.json during generation and uses it when:
routing_mode: semantic
If embeddings are unavailable or semantic ranking fails, MCPGen automatically falls back to keyword routing. You can force keyword routing with:
routing_mode: keyword
Tool text combines the tool name, description, and optional tags. Example:
create_invoice create invoice for customer billing payments
By default, MCPGen uses a deterministic local embedding fallback so demos and tests work without model downloads. To use sentence-transformers, set:
export MCPGEN_EMBEDDING_BACKEND=sentence-transformers
PowerShell:
$env:MCPGEN_EMBEDDING_BACKEND = "sentence-transformers"
Compare routing modes by changing routing_mode in mcpgen.yaml and regenerating the server. Semantic mode ranks by vector similarity; keyword mode ranks by normalized token overlap and includes matched terms.
MCPGen is safe by default:
GET tools are exposed in tools.json.DELETE tools are always blocked.GET tools.Policy decisions return:
{
"allowed": false,
"status": "blocked",
"reason": "Medium-risk tool is not listed in enabled_tools.",
"risk_level": "medium",
"tool_name": "create_post"
}
Audit logs are JSONL records written to:
logs/audit.log
Config:
audit_enabled: true
audit_log_path: logs/audit.log
routing_mode: semantic
Each event includes:
Actions include:
policy_evaluationdry_runexecution_startedexecution_successexecution_errorexecution_blockedAudit is the event trail. It answers what happened, when, and why for each attempt.
v0.3.0 adds lightweight aggregate metrics for generated servers. Metrics are stored as JSON at:
logs/metrics.json
Config:
metrics_enabled: true
metrics_path: logs/metrics.json
Metrics track:
FastAPI mode exposes:
GET /metrics
POST /metrics/reset
Example response:
{
"total_tool_routes": 1,
"total_policy_evaluations": 2,
"total_executions": 1,
"total_execution_success": 1,
"total_execution_errors": 0,
"total_execution_blocked": 1,
"total_dry_runs": 1,
"total_confirmation_required": 0,
"per_tool": {
"get_user_by_id": {
"routed": 1,
"policy_allowed": 2,
"policy_blocked": 0,
"dry_runs": 1,
"executions": 1,
"successes": 1,
"errors": 0,
"blocked": 0,
"average_execution_latency_ms": 42.5
}
},
"last_updated": "2026-05-05T12:00:00+00:00"
}
Metrics are MVP-level and file-based. They are useful for local demos and development visibility, but they are not a replacement for production telemetry systems.
v0.4.0 adds safe upstream authentication support for generated servers. Secrets are never written into generated files, audit logs, metrics, or responses.
Default config:
auth:
mode: none
api_key_env: API_KEY
api_key_header: X-API-Key
No auth headers are sent upstream.
auth:
mode: none
FastAPI mode can forward an incoming Authorization header to the upstream API only when it starts with Bearer .
auth:
mode: bearer_passthrough
Example request:
curl -X POST http://127.0.0.1:8001/execute \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $TOKEN" \
-d "{\"tool_name\":\"get_user_by_id\",\"params\":{\"id\":1}}"
PowerShell:
$env:TOKEN = "your-token"
Invoke-RestMethod -Method Post http://127.0.0.1:8001/execute `
-ContentType "application/json" `
-Headers @{ Authorization = "Bearer $env:TOKEN" } `
-Body '{"tool_name":"get_user_by_id","params":{"id":1}}'
MCP stdio mode does not have HTTP headers. For bearer_passthrough, provide explicit auth metadata in tools/call arguments:
{
"jsonrpc": "2.0",
"id": 2,
"method": "tools/call",
"params": {
"name": "get_user_by_id",
"arguments": {
"id": 1,
"auth": {
"authorization": "Bearer your-token"
}
}
}
}
The auth metadata is stripped before tool parameter handling and is not logged.
API key mode reads a key from an environment variable and injects it into the configured upstream header.
auth:
mode: api_key
api_key_env: JSONPLACEHOLDER_API_KEY
api_key_header: X-API-Key
Bash:
export JSONPLACEHOLDER_API_KEY=your-api-key
PowerShell:
$env:JSONPLACEHOLDER_API_KEY = "your-api-key"
If the environment variable is missing, /execute returns a clear error and does not call the upstream API.
OAuth2 is not implemented yet. It is listed in the roadmap.
Default config:
max_tools: 5
allowed_methods:
- GET
output_dir: generated_mcp_server
api_base_url: https://api.example.com
enabled_tools: []
execution_mode: dry-run
audit_enabled: true
audit_log_path: logs/audit.log
routing_mode: semantic
metrics_enabled: true
metrics_path: logs/metrics.json
auth:
mode: none
api_key_env: API_KEY
api_key_header: X-API-Key
For the JSONPlaceholder demo, set:
api_base_url: https://jsonplaceholder.typicode.com
Add this to claude_desktop_config.json and restart Claude Desktop.
{
"mcpServers": {
"mcpgen": {
"command": "npx",
"args": []
}
}
}