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.
MCPGen 1.0.0 is a stable production-oriented MVP. The current config keys, CLI commands, generated file names, and safe-by-default execution model are intended to be the first stable developer contract.
Stable in this MVP:
mcpgen init, mcpgen generate, mcpgen inspect, mcpgen doctor, and mcpgen eval-routingtools.json, tools.all.json, tools.embeddings.json, safety_report.json, tool_catalog.md, and mcpgen.runtime.json1.x config compatibility contractStill experimental:
mcpgen initGET = lowPOST, PUT, PATCH = mediumDELETE = high$ref resolution for OpenAPI componentstools/list and tools/callGET tools onlydoctor diagnostics for specs and config readinessinit, generate, inspect, doctor, eval-routingmcpgen.yamlOpenAPI spec
-> parser
-> tool generator
-> tool selection
-> risk classifier
-> local schema/ref resolver
-> safety filter
-> tools.json / tools.all.json / tools.embeddings.json / safety_report.json / tool_catalog.md
-> 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]
Install from PyPI after publishing:
pip install openapi-mcpgen
Optional semantic routing dependency:
pip install "openapi-mcpgen[semantic]"
Create a starter project:
mcpgen init --directory demo_mcpgen
cd demo_mcpgen
For offline development with mock execution enabled:
mcpgen init --directory demo_mcpgen --profile mock
Inspect a spec:
mcpgen inspect --from openapi.yaml --config mcpgen.yaml
Run diagnostics:
mcpgen doctor --from openapi.yaml --config mcpgen.yaml
Run a smoke test:
mcpgen smoke --from openapi.yaml --config mcpgen.yaml --cases routing_eval.yaml
Create a watchdog baseline:
mcpgen watchdog --from openapi.yaml --config mcpgen.yaml --cases routing_eval.yaml --write-baseline
Check for spec drift:
mcpgen watchdog --from openapi.yaml --config mcpgen.yaml --cases routing_eval.yaml
Generate a FastAPI server:
mcpgen generate --from openapi.yaml --config mcpgen.yaml --mode fastapi --output generated_jsonplaceholder
Evaluate routing:
mcpgen eval-routing --from openapi.yaml --config mcpgen.yaml --cases routing_eval.yaml
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.
Every generated server includes tool_catalog.md, a human-readable review file for developers and security reviewers.
It summarizes:
Example:
## get_user_by_id
- Method: `GET`
- Path: `/users/{id}`
- Risk: `low`
- Exposed: `yes`
### Inputs
- `id`: integer, required
### Response
- `id`: integer
- `name`: string
- `email`: string
mcpgen eval-routing checks whether natural-language queries route to expected safe tools.
Example routing_eval.yaml:
- query: list all users
expected:
- list_users
- query: get user by id
expected:
- get_user_by_id
Run:
mcpgen eval-routing --from openapi.yaml --config mcpgen.yaml --cases routing_eval.yaml
Example output:
Routing eval: 2/2 passed
Accuracy: 100%
Routing mode: semantic
Top K: 5
[PASS] list all users
Expected: list_users
Returned: list_users, get_user_by_id
If any case fails, the command exits with code 1, making it useful in CI before exposing a generated server to an agent.
mcpgen smoke runs a lightweight end-to-end confidence check in a temporary directory.
It checks:
doctor diagnosticsRun:
mcpgen smoke --from openapi.yaml --config mcpgen.yaml --cases routing_eval.yaml
Smoke failures exit with code 1, so teams can use them in CI before publishing or deploying generated servers.
API specs change. mcpgen watchdog helps catch when an OpenAPI change breaks the generated tool surface or routing expectations.
Create and commit a baseline:
mcpgen watchdog \
--from openapi.yaml \
--config mcpgen.yaml \
--cases routing_eval.yaml \
--baseline mcpgen.baseline.json \
--write-baseline
Check for drift in CI:
mcpgen watchdog \
--from openapi.yaml \
--config mcpgen.yaml \
--cases routing_eval.yaml \
--baseline mcpgen.baseline.json
Watchdog detects:
Removed tools and changed tool contracts fail the command. Added tools are reported as warnings so developers can review and update the baseline intentionally.
MCPGen is no longer a proof-of-concept MVP. It is an early-stage production-oriented framework with a stable 1.x developer contract.
Current production-readiness work includes:
Remaining maturity work:
See SECURITY.md and docs/CONFIG_COMPATIBILITY.md.
MCPGen includes example scenarios under examples/:
examples/jsonplaceholder uses the public JSONPlaceholder API.examples/github-like demonstrates repository issues, pull requests, bearer passthrough config, and withheld writes.examples/billing-api demonstrates billing-style tools, API key config, mocks, and withheld invoice writes/deletes.Each example includes:
mcpgen.yamlrouting_eval.yamlFrom 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 the optional sentence-transformers backend, install the extra and set the backend:
pip install "openapi-mcpgen[semantic]"
export MCPGEN_EMBEDDING_BACKEND=sentence-transformers
PowerShell:
pip install "openapi-mcpgen[semantic]"
$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.
v0.8.0 adds generation-time controls for narrowing large OpenAPI specs before safety filtering and server generation.
Config:
include_tools: []
exclude_tools:
- delete_user
include_paths:
- /users*
exclude_paths:
- /admin/*
- /internal/*
include_methods:
- GET
exclude_methods:
- DELETE
Selection is applied before tools.json, tools.all.json, tools.embeddings.json, and safety_report.json are written. It never weakens MCPGen safety: excluded tools are removed from the generated surface, but selected POST, PUT, PATCH, and DELETE tools are still withheld unless the policy layer allows a safe dry-run or confirmation flow.
Useful commands:
mcpgen inspect --from examples/jsonplaceholder.openapi.yaml --config mcpgen.yaml
mcpgen doctor --from examples/jsonplaceholder.openapi.yaml --config mcpgen.yaml
inspect reports discovered, selected, excluded, exposed, and withheld tool counts. doctor warns if selection excludes every generated tool.
v0.9.0 adds better schema handling for real-world specs:
#/components/schemas/UserallOf object schemasmcpgen doctor when unresolved refs remainExample generated tool excerpt:
{
"name": "get_user_by_id",
"method": "GET",
"path": "/users/{id}",
"response_schema": {
"type": "object",
"properties": {
"id": {"type": "integer"},
"name": {"type": "string"}
}
}
}
This is still intentionally MVP-level. Remote refs, complex composition rules, discriminators, and full JSON Schema validation are not implemented yet.
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"
}
v0.6.0 validates tool inputs before dry-run previews or safe GET execution. Validation uses the generated input_schema from OpenAPI parameters and JSON request bodies.
MCPGen currently checks:
string, integer, number, boolean, array, objectExample validation error:
{
"valid": false,
"status": "validation_error",
"tool_name": "get_user_by_id",
"errors": [
{
"field": "id",
"reason": "required field is missing"
}
]
}
Validation runs in:
POST /tools/{tool_name}/dry-runPOST /executetools/callThis validation is intentionally MVP-level. It catches common input mistakes before network calls, but it is not a full JSON Schema validator yet.
MCPGen validates successful safe execution and mock responses against generated response_schema when one is available.
Example response metadata:
{
"tool": "get_user_by_id",
"status": "success",
"status_code": 200,
"data": {
"id": 1,
"name": "Ada"
},
"response_validation": {
"valid": true,
"status": "valid",
"tool_name": "get_user_by_id",
"errors": []
}
}
Current response validation checks:
In 1.4.0, response validation is reported as metadata and does not block successful upstream responses. This keeps integrations tolerant while still surfacing schema drift. Stricter failure modes are planned for a future release.
v0.7.0 adds mock execution so developers can test generated servers without a live API, database, credentials, or internet access.
v0.9.0 improves mock responses by using OpenAPI response schemas when they are available.
Config:
mock:
enabled: true
mode: schema
seed: 123
list_size: 3
When mock mode is enabled, safe GET execution returns deterministic mock data instead of calling the upstream API. Policy, validation, rate limiting, audit logging, and metrics still apply. If a tool has a response_schema, MCPGen uses it to shape mock objects and arrays.
Example mock response:
{
"tool": "get_user_by_id",
"status": "success",
"status_code": 200,
"data": {
"id": 1,
"name": "Users 1",
"mock": true
},
"mocked": true
}
List-style tools return arrays using mock.list_size.
Failure injection lets developers simulate common upstream failures and observe how their server, agent, or LLM workflow responds.
Config:
failure_injection:
enabled: true
scenarios:
get_user_by_id: not_found
list_posts: timeout
Supported MVP scenarios:
timeoutnot_foundserver_errormalformed_jsonExample simulated response:
{
"tool": "get_user_by_id",
"status": "error",
"status_code": 404,
"data": {
"error": "Simulated not found."
},
"simulated": true
}
Failure injection takes precedence over mock mode when both are enabled for the same tool.
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.5.0 adds lightweight in-memory rate limiting for generated servers.
Config:
rate_limit:
enabled: true
per_tool: 10
global: 100
window_seconds: 60
Defaults:
rate_limit:
enabled: false
per_tool: 10
global: 100
window_seconds: 60
mock:
enabled: false
mode: schema
seed: 123
list_size: 3
failure_injection:
enabled: false
scenarios: {}
circuit_breaker:
enabled: false
failure_threshold: 5
recovery_seconds: 60
FastAPI mode applies the global limit to operational requests:
POST /toolsPOST /tools/{tool_name}/dry-runPOST /executePer-tool limits apply to:
POST /tools/{tool_name}/dry-runPOST /executetools/callHealth and root endpoints are not rate limited.
When a request exceeds the limit, FastAPI returns 429 with a Retry-After header:
{
"status": "rate_limited",
"scope": "per_tool",
"retry_after": 30,
"reason": "rate limit exceeded"
}
Rate-limited events are recorded in both audit logs and aggregate metrics:
total_rate_limitedper_tool.<tool>.rate_limitedLimitations: rate limiting is in-memory only, resets when the generated server restarts, and is not distributed across processes or machines. Redis and distributed rate limiting are intentionally out of scope for this MVP.
v1.6.0 adds lightweight in-memory circuit breaker protection for flaky upstream APIs.
Config:
circuit_breaker:
enabled: true
failure_threshold: 5
recovery_seconds: 60
When enabled, each tool gets an independent circuit:
closed: normal executionopen: fail fast without calling the upstream APIhalf_open: one trial request after the recovery windowIf a tool fails repeatedly, MCPGen opens the circuit and returns immediately:
{
"tool": "list_issues",
"status": "error",
"status_code": 503,
"data": {
"error": "Circuit breaker is open.",
"state": "open",
"retry_after": 60,
"do_not_retry_until": "2026-05-08T10:31:12+00:00",
"agent_instruction": "Do not retry this specific tool for at least 60 seconds."
}
}
Circuit breaker events are written to audit logs and metrics:
circuit_openedcircuit_blockedtotal_circuit_openedtotal_circuit_blockedper_tool.<tool>.circuit_openedper_tool.<tool>.circuit_blockedLimitations: circuit breaker state is in-memory only, resets on server restart, and is not shared across processes.
v1.7.0 adds optional retry/backoff for transient safe GET execution failures.
Config:
retry:
enabled: true
max_attempts: 3
backoff_seconds: 0.5
retry_statuses:
- 429
- 500
- 502
- 503
- 504
Retries are deliberately narrow:
GET execution is retriedmax_attempts includes the first attemptRetry attempts are written as execution_retry audit events and counted in metrics:
total_execution_retriesper_tool.<tool>.retriesWhen a circuit is open, the response includes retry_after, do_not_retry_until, and an agent_instruction telling the caller not to retry that specific tool during the cooldown window.
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
include_tools: []
exclude_tools: []
include_paths: []
exclude_paths: []
include_methods: []
exclude_methods: []
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
rate_limit:
enabled: false
per_tool: 10
global: 100
window_seconds: 60
retry:
enabled: false
max_attempts: 3
backoff_seconds: 0.5
retry_statuses:
- 429
- 500
- 502
- 503
- 504
For the JSONPlaceholder demo, set:
api_base_url: https://jsonplaceholder.typicode.com
mcpgen doctor runs read-only checks against an OpenAPI spec and optional config file:
mcpgen doctor --from examples/jsonplaceholder.openapi.yaml --config mcpgen.yaml
It checks:
max_toolsExample output:
MCPGen doctor: warn
[PASS] config: Config loaded successfully.
[PASS] openapi: Parsed 6 endpoint(s).
[WARN] api_base_url: api_base_url is using the default placeholder.
[PASS] tool_selection: Tool selection config is valid.
[PASS] safety: 4 low-risk tool(s) will be exposed.
doctor exits with code 1 if a failing check is found, which makes it useful in CI.
GET execution only.allOf, but not remote refs, discriminators, or complex composition.See CHANGELOG.md for release history.
MCPGen publishes through GitHub Actions using PyPI Trusted Publishing. No PyPI API token should be committed to the repository.
Workflow:
.github/workflows/publish.yml
Recommended flow:
Publish Python Package workflow.repository = testpypi.repository = pypi.Install from TestPyPI:
pip install --index-url https://test.pypi.org/simple/ --extra-index-url https://pypi.org/simple openapi-mcpgen
Install from PyPI:
pip install openapi-mcpgen
Выполни в терминале:
claude mcp add mcpgen -- npx Да, MCPGen MCP бесплатный — установка в один клик через Unyly без оплаты.
Нет, MCPGen работает без API-ключей и переменных окружения.
Self-hosted: сервер запускается локально на твоей машине командой из раздела установки.
Открой MCPGen на unyly.org, выбери вкладку своего клиента (Claude Desktop, Claude Code, Cursor) и нажми Install — конфиг сгенерируется автоматически, без правки JSON.
CSA PROJECT - FZCO © 2026 IFZA Business Park, DDP, Premises Number 31174 - 001
Безопасность
Низкий рискАвтоматическая эвристика по публичным данным — не гарантия безопасности.