agentifyapi
Reads FastAPI applications and converts their routes, Pydantic models, docstrings, and dependencies into governed MCP tools, exposing endpoints with owner approval, incremental discovery, response projection, per-caller policy, PII redaction, and audit logging.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@agentifyapilist the enabled tools for the orders service"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
agentifyapi
Make your FastAPI services agent-ready with owner-approved MCP tools.
Install
pip install agentifyapiPython 3.11 or newer.
For the latest commit on main:
pip install "git+https://github.com/SomilAgg123/agentifyapi"Related MCP server: MCP Flow
Example
A tiny FastAPI app. One mount(app) call adds a governed /mcp endpoint. Routes become MCP tools,
but they stay hidden until you approve them.
# examples/quickstart.py
from fastapi import FastAPI
from agentifyapi import mount
from agentifyapi.config import PolicyConfig
app = FastAPI(title="inventory")
@app.get("/stock/{sku}")
def get_stock(sku: str) -> dict:
"""Units on hand for a SKU."""
return {"sku": sku, "on_hand": 42, "warehouse": "nyc"}
mount(app, owner="you", database="quickstart.db", policy=PolicyConfig(allow_anonymous=True)) # local demo onlyuvicorn examples.quickstart:app --port 8200
# another terminal:
agentifyapi --db quickstart.db tools list
# inventory.get_stock - disabled 1
agentifyapi --db quickstart.db tools approve inventory.get_stock --by you # approved, still disabled
agentifyapi --db quickstart.db tools enable inventory.get_stock # now agents can call itAn MCP client pointed at http://127.0.0.1:8200/mcp then discovers and calls the tool:
>>> list_tools(server="inventory") # before approve → []
>>> invoke_tool(tool="inventory.get_stock", args={"sku": "KB-1"})
{"sku": "KB-1", "on_hand": 42, "warehouse": "nyc"}From a clone of this repo, uv run python scripts/verify_quickstart.py runs the whole flow (CI runs it on every push).
For many services behind one gateway, see Standalone gateway.
How this differs from fastapi_mcp and FastMCP
fastapi_mcp and FastMCP
(FastMCP.from_fastapi / from_openapi) are the quickest ways to expose FastAPI routes as MCP tools.
Use them if you want every selected route live immediately. agentifyapi converts routes too, but its focus
is what happens around the tools when many teams' APIs are involved.
agentifyapi | fastapi_mcp | FastMCP | |
Approval | New and changed tools wait in a pending state until an owner approves and enables them. | Selected routes are live on mount. | Selected routes are live on convert. |
Change control | Each tool has versioned revisions with a reviewable diff and one-command rollback. | — | — |
Policy | Per-caller and per-role allow-lists by API key, default deny. | FastAPI dependencies and OAuth on the MCP endpoint. | OAuth, JWT, and other auth providers. |
Data protection | PII redaction (emails, phones, SSNs, Luhn-checked cards) and response projection. | — | — |
Audit | A JSON-lines record of every call: caller, decision, status, redaction count. | — | Middleware you add. |
Discovery at scale | Four meta-tools ( | One flat tool list. | One flat tool list; routes can also be resources. |
A dash means that control is not built in. The other projects have strengths this one doesn't, such as broader MCP feature coverage and richer authentication. agentifyapi is young (0.x).
Inspired by Uber's Designing MCP Gateway, rebuilt as a small open-source project for the FastAPI ecosystem. Not affiliated with Uber.
Why
Problem | What agentifyapi does |
Your APIs were built before MCP | Converts FastAPI routes (Pydantic models, docstrings, tags, dependencies) into MCP tools. Falls back to any |
Auto-generated tools are risky | Every tool starts disabled. An owner reviews a diff, approves, and enables it. Spec changes become pending revisions, so the live version never changes silently. Rollback is one command. |
Thousands of tools flood the context window | Agents use 4 meta-tools ( |
Agents shouldn't see everything | Per-caller and per-role policy via API keys. Discovery only lists the tools a caller is allowed to invoke. Regex PII redaction (emails, phones, SSNs, Luhn-valid cards). JSON-lines audit log. |
Architecture
flowchart LR
subgraph Sources
A1[FastAPI app<br/>myservice.main:app] -->|import + introspect| C
A2[Running service<br/>/openapi.json] -->|HTTP| C
A3[Protobuf / gRPC<br/>roadmap] -.-> C
end
C[Crawler<br/>schema translation<br/>LLM or template descriptions<br/>contract hash] -->|pending revisions| R[(Registry<br/>SQLite, versioned)]
O[Owner<br/>CLI / admin UI] -->|diff, approve, enable, rollback| R
R -->|approved + enabled only| G[Gateway<br/>/mcp meta-tools<br/>/servers/name/mcp]
AG[Agents and coding agents<br/>MCP client] <-->|streamable HTTP + API key| G
G -->|policy, forward, project, redact, audit| S[Your FastAPI services]Two deployment shapes share the same core.
Embedded.
mount(app)serves one service with in-process dispatch and no extra deployment.Standalone gateway.
agentifyapi serveputs many services behind one MCP endpoint and re-crawls them on an interval.
Standalone gateway
With pip only
Put the gateway in front of a FastAPI service that is already running (here on port 8300). Write a
small config.yaml with the registry path and who may call what:
# config.yaml
database: registry.db
policy:
api_keys:
my-agent-key: { name: my-agent, roles: [agent] }
rules:
- tools: ["inventory.*"]
roles: [agent]pip install agentifyapi
agentifyapi -c config.yaml crawl --url http://127.0.0.1:8300 --name inventory # reads /openapi.json
agentifyapi -c config.yaml tools list
agentifyapi -c config.yaml tools approve "inventory.*" --by you # approved, still disabled
agentifyapi -c config.yaml tools enable inventory.get_stock # now agents can call it
agentifyapi -c config.yaml serve --port 8000Agents connect to http://127.0.0.1:8000/mcp with the header x-api-key: my-agent-key. To crawl
by importing the app instead, use crawl --app myservice.main:app --base-url http://127.0.0.1:8300
from the service's directory (or pass --app-dir).
Demo from a clone
git clone https://github.com/SomilAgg123/agentifyapi && cd agentifyapi
uv sync # or: pip install -e ".[dev]"
uv run python scripts/run_demo.py # `make demo` does the sameThe demo starts two mock FastAPI services (orders, users) and crawls them. Orders is crawled
by importing the app, and users over HTTP. It then shows a diff, approves three tools, starts the
gateway on :8000, and runs examples/agent_client.py, an MCP SDK client that discovers and calls tools.
>>> invoke_tool({"tool": "orders.get_order", "args": {"order_id": 101}, "fields": ["id", "status", "items.sku", "card_number", "shipping_note"]})
{ "id": 101, "status": "shipped", "items": [{"sku": "KB-1"}],
"card_number": "[REDACTED:card]", "shipping_note": "Call [REDACTED:phone] on arrival" }uv run python scripts/run_demo.py --keep leaves it running so you can open the admin UI at
http://127.0.0.1:8000/admin/.
uv run python scripts/verify_demo.py (or make verify) boots the same demo and checks 8 behaviors against it over real MCP HTTP: discovery,
approval gating, projection, redaction, policy denial, the per-server endpoint, the admin UI, and the audit log.
Connect a coding agent
Point any MCP client that speaks streamable HTTP at http://127.0.0.1:8000/mcp with the header
x-api-key: demo-support-key (or Authorization: Bearer ...). If you prefer a flat tool list, use
http://127.0.0.1:8000/servers/orders/mcp. Each enabled tool appears there directly, with an optional _fields argument.
The approval flow
crawl ──► v1 pending ──approve──► v1 live (disabled) ──enable──► served to agents
spec changes ──► v2 pending (v1 keeps serving) ──approve──► v2 live ──rollback──► v1 live
endpoint removed upstream ──► hidden + disabled, history keptChanges are detected with a hash of the tool contract (method, path, schemas, docs). The generated description is excluded, so LLM wording never creates noise revisions.
agentifyapi tools list | diff | approve | reject | enable | disable | rollback | history <tool>.server.*works for bulk actions; bulk approve and reject skip tools with nothing pending.--byrecords the reviewer.The admin UI (
/adminstandalone,/mcp-adminembedded) shows the same diff with buttons.To exclude a route entirely, use
@app.post(..., openapi_extra={"x-agentifyapi-exclude": True}).
FastAPI-native introspection
--app module:app (or app: in config) imports your app, including app factories, and walks its
APIRoutes. On top of app.openapi() it recovers these details.
Clean tool names from the route function (
get_order, notget_order_orders__order_id__get).Full docstrings as tool documentation.
The
Depends()graph (for examplerequire_admin), shown to reviewers in the diff.Exact Pydantic request and response schemas with
$refs inlined. Recursive models are cut safely.
If the import fails (missing dependencies or environment) and a base_url is set, it falls back to /openapi.json.
Configuration reference
database: registry.db # SQLite registry
audit_log: audit.log # JSON lines; "" to disable
crawl_interval_seconds: 300 # 0 = on demand only
redact_pii: true
services:
- name: orders # tool names become orders.<route_name>
owner: team-commerce
app: services.orders.main:app # FastAPI-native crawl (optional)
app_dir: ../orders-repo # put this repo on sys.path first (optional)
base_url: http://orders:8000 # where calls are forwarded (+ /openapi.json fallback)
openapi_url: http://... # override spec location (optional)
# proto: path/to/descriptor.pb # extension point, not implemented in v1
policy:
allow_anonymous: false # dev only: callers without a key may use any enabled tool
api_keys:
<key>: { name: support-agent, roles: [support] }
rules: # allow-list, default deny; discovery hides tools a caller can't invoke
- tools: ["orders.*"] # fnmatch on tool names
roles: [support]
callers: [some-agent]
admin: # admin UI (/admin, /mcp-admin) access
username: admin
password: null # set to require HTTP Basic auth; null = localhost-only
insecure_allow_remote: false # dev only: no password and reachable from anywhereEnv var | Effect |
| LLM-written tool descriptions ( |
| Force the deterministic template describer |
| Require HTTP Basic auth for the admin UI (username defaults to |
mount(app, name=, owner=, database=, policy=, prefix=, admin_path=, auto_approve=False, redact_pii=True, audit_log=, admin_password=None, admin_username="admin", admin_allow_remote=False).
auto_approve=True is for local development only. Without an admin password the admin UI only
answers localhost; see SECURITY.md.
Development
uv sync && uv run pre-commit install
uv run pytest --cov && uv run pyright
uv run python scripts/verify_quickstart.py # README example
uv run python scripts/verify_demo.py # multi-service gatewaySee CONTRIBUTING.md, AGENTS.md for coding agents, and SECURITY.md to report vulnerabilities.
The tests cover schema translation, FastAPI introspection, the crawler and approval flow (with a
fake spec), the CLI, the admin UI, projection, redaction, and policy. Two end-to-end runs go over
real MCP streamable HTTP: the standalone gateway against a live mock service, and embedded mount().
Roadmap
gRPC / Protobuf crawling from descriptor sets (
proto:is a stub today).OAuth passthrough for third-party APIs and end-user identity propagation.
Code-mode CLI that writes tool output to files so coding agents grep them instead of loading results into context.
Semantic discovery index with embeddings for
discover_serversandlist_tools(keyword scoring today).Field-level PII policy from Pydantic annotations, per-caller rate limits, OpenTelemetry traces.
Postgres registry and multi-reviewer approvals.
Credits
The architecture follows the ideas in Uber Engineering's Designing MCP Gateway. Those ideas are crawling existing API definitions, registering virtual MCP servers disabled by default, owner approval, incremental discovery, response projection, and PII stripping. Built on the official MCP Python SDK and FastAPI.
MIT licensed. See CONTRIBUTING.md, docs/DESIGN.md and docs/DECISIONS.md.
This server cannot be deployed
Maintenance
Related MCP Connectors
MCP server for your apps' tools and custom tools, plus hosted AI agents and approval-gated workflows
The OpenRouter for tools. One MCP connection gives any AI agent 254 hosted tools, pay per call.
Zero-setup MCP gateway securely connecting AI to your tools with authentication and workflows
Find, vet, and run MCP tools through a secure audited gateway with prompt-injection risk scoring
Related MCP Servers
- FlicenseNot gradedqualityDmaintenanceTurns OpenAPI specs into MCP tools with secure defaults, risk inspection, confirmation gates, response limits, audit logging, and secret redaction.-
- FlicenseNot gradedqualityBmaintenanceEnables turning existing REST APIs into governed, agent-callable MCP servers by analyzing OpenAPI schemas, generating tool definitions, and gating releases behind automated evals.-
- AlicenseNot gradedqualityCmaintenanceTurns any OpenAPI/Swagger or REST API into callable MCP tools for Claude, ChatGPT, Copilot, and Cursor, with secure authentication and role-based access control.9 npm1AGPL 3.0
- AlicenseNot gradedqualityBmaintenanceEnables OpenAI-compatible clients to access external tools, APIs, repositories, and data sources through MCP with allowlisting, approval, and argument validation, deployable as a FastAPI service or Python library.MIT