MCPSentry
Provides security auditing for FastAPI apps using Auth0 authentication, catching exposed auth endpoints and PII leaks.
Planned support for bridging Django views to MCP tools, extending MCPSentry to Django applications.
Bridges FastAPI routes to MCP tools, enabling LLMs to interact with any FastAPI-based API.
Planned support for bridging Flask routes to MCP tools, extending MCPSentry to Flask applications.
Demo integration with Mealie recipe manager, allowing LLMs to search recipes, plan meals, and build shopping lists via MCP tools.
Click on "Install 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., "@MCPSentryaudit my FastAPI routes for security issues"
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.
from fastapi import FastAPI
from mcpsentry import MCPSentry
app = FastAPI()
# ... your existing routes ...
bridge = MCPSentry(app) # Your app now speaks MCP.
report = bridge.audit() # Pre-flight security audit.
if report.has_blockers():
report.print_text() # π΄ CRITICAL: /api/admin exposed, /api/auth/login exposedβ¦
raise SystemExit(1)
bridge.enable_guardrails(policy="block") # Runtime prompt-injection blocking.MCPSentry = a one-line FastAPI β MCP bridge plus a pre-flight security auditor plus a runtime guardrail that scans every
tools/callfor prompt-injection. Because handing an LLM root access to your API by accident is no longer hypothetical.
Why MCPSentry exists
A FastAPI β MCP bridge alone isn't enough. The hard problem isn't exposing routes β it's making sure you only expose the routes you meant to.
When LLMs become callers of your API, three things go wrong silently:
Risk | What happens | MCPSentry catches it |
Admin endpoints exposed |
| π΄ CRITICAL β refuses to start by default |
PII leaks in responses |
| π HIGH β flags PII-named fields |
Auth flows accessible to LLM |
| π΄ CRITICAL β refuses to start |
Missing docstrings | LLM guesses what each tool does β calls the wrong one | π HIGH β fail the audit |
Untyped params | Path param is | π΅ LOW β flag for explicit typing |
Destructive DELETE/PUT/PATCH | LLM mutates data without confirmation | π‘ MEDIUM β informational |
Other libraries give you a bridge. MCPSentry gives you a bridge and a security checklist that fails the build if you got it wrong.
Quick Start
pip install mcpsentryfrom fastapi import FastAPI
from mcpsentry import MCPSentry
app = FastAPI(title="My App")
@app.get("/api/users/{user_id}")
async def get_user(user_id: int):
"""Get a user by their ID."""
return {"id": user_id, "name": "Alice"}
bridge = MCPSentry(app) # Auto-discovers routes, mounts /mcp
print(bridge.summary())
# Always audit before deploying
report = bridge.audit()
report.print_text()
if report.has_blockers():
raise SystemExit(1)Your app now exposes:
GET /mcpβ Server info and tool listingPOST /mcpβ MCP JSON-RPC endpoint (Streamable HTTP transport)
Any MCP client (Claude, ChatGPT, Gemini, Cursor, Codex) can connect.
The audit, explained
bridge.audit() walks every exposed tool and runs 7 checks. Sample output:
π‘οΈ MCPSentry audit report
13 tools from 13 routes
π΄ 2 critical Β· π 4 high Β· π‘ 3 medium Β· π΅ 1 low
π΄ [CRITICAL] POST /api/auth/login β Authentication endpoint exposed to LLM clients
β³ Add '/api/auth/login' to exclude_paths
π΄ [CRITICAL] DELETE /api/admin/users/{user_id} β Admin / internal endpoint exposed to LLM clients
β³ Exclude this route from MCP exposure
π [HIGH ] GET /api/users/me β Response may leak PII fields: email, phone, address
β³ Mask PII before returning, or restrict this route from MCP exposure
π [HIGH ] POST /api/internal/run β No docstring β an LLM will guess this tool's purpose
β³ Add a clear docstring or `summary=` to the route decorator
π‘ [MEDIUM ] DELETE /api/recipes/{recipe_id} β Destructive DELETE β an LLM call can mutate/delete data
β³ Document the consequences in the docstring; consider requiring confirmation
π΅ [LOW ] GET /api/search β 3 parameters fall back to 'string' β LLMs may send malformed inputs
β³ Annotate parameters with explicit types (int, bool, list[str], Pydantic models)Programmatic use
report = bridge.audit()
# Counts
report.critical_count # β 2
report.high_count # β 4
# Structured access (CI/CD, dashboards, reporting)
import json
print(json.dumps(report.to_dict(), indent=2))
# Iterate
for finding in report.findings:
if finding.severity == Severity.CRITICAL:
send_to_security_team(finding)Use it in CI
# .github/workflows/mcp-audit.yml
- run: python -c "from myapp import bridge; r = bridge.audit(); r.print_text(); exit(1 if r.has_blockers() else 0)"π― Real-world findings β see case-studies/
We ran bridge.audit() against the official examples of tadata-org/fastapi_mcp (the most popular FastAPIβMCP library, 11.9k β).
Example | π΄ Crit | π High | π‘ Med | π΅ Low | Verdict |
| 0 | 0 | 2 | 0 | β |
| 0 | 0 | 2 | 0 | β |
| 0 | 0 | 2 | 0 | β |
| 0 | 1 | 2 | 0 | β |
| 3 | 6 | 0 | 1 | β BLOCK |
Headline: the official Auth0 example exposes /oauth/authorize, /oauth/register, and /.well-known/oauth-authorization-server to LLM clients. MCPSentry catches all three and refuses to start the server. Full breakdown in case-studies/01-fastapi-mcp-examples.md.
Advanced configuration
bridge = MCPSentry(
app,
name="My Recipe Manager",
description="Search recipes, plan meals",
include_paths=["/api/*"], # Only expose API routes
exclude_paths=["/api/admin/*", "/api/auth/*"], # Hide sensitive endpoints
max_tools=30, # Limit context window usage
mcp_endpoint="/mcp",
)
# Refine specific tools for better LLM understanding
bridge.tool("/api/recipes/search", description="Find recipes by name or ingredients")
bridge.exclude("/api/internal/*")Live demo: Mealie Recipe Manager
The examples/mealie_demo/ directory contains a working demo that simulates Mealie (11.5K β), a popular self-hosted recipe manager built with FastAPI.
git clone https://github.com/miloudbelarebia/mcpsentry
cd mcpsentry
pip install -e .
python examples/mealie_demo/app.pyWhat you'll see:
Auto-discovery β 13 Mealie routes β 13 MCP tools
Pre-flight audit β runs before the server starts
Server starts at
http://localhost:9925/mcp(only if audit passes)
Connect from Claude Desktop:
{
"mcpServers": {
"mealie": { "url": "http://localhost:9925/mcp" }
}
}Then ask Claude: "What's for dinner tonight?" β it'll search your recipes, propose a meal plan, build a shopping list.
How it works
βββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Your FastAPI app β
β β
β @app.get("/api/recipes") β Existing routes β
β @app.post("/api/recipes") β
β @app.delete("/api/admin/...") β BAD β
β β
β βββββββββββββββββββββββββββββββββββββββββββββββ β
β β MCPSentry (embedded) β β
β β β β
β β 1. Introspect routes at startup β β
β β 2. Extract Pydantic schemas + type hints β β
β β 3. β‘ Pre-flight security audit β β
β β β³ refuse to start on CRITICAL findings β β
β β 4. Generate MCP tool definitions β β
β β 5. Serve MCP JSON-RPC on /mcp β β
β βββββββββββββββββββββββββββββββββββββββββββββββ β
β β
β POST /mcp β LLM clients connect here β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β
βΌ
βββββββββββββ βββββββββββββ βββββββββββββ βββββββββββ
β Claude β β ChatGPT β β Gemini β β Cursor β
βββββββββββββ βββββββββββββ βββββββββββββ βββββββββββComparison with other tools
MCPSentry | fastapi_mcp | FastMCP | openapi-mcp-server | |
1-line setup | β | β | β | β (CLI + config) |
Embedded (no separate process) | β | β | β | β |
Auto-discovers routes (no OpenAPI spec) | β | β | β | β |
Pre-flight security audit | β | β | β | β |
Refuses to start on CRITICAL findings | β | β | β | β |
Runtime prompt-injection detection | β | β | β | β |
Block / alert / log policy on tools/call | β | β | β | β |
Multi-framework (FastAPI + Flask + Django) | π§ v0.4 | β | partial (OpenAPI) | spec-based |
If you don't care about auditing what you expose or what comes back through it, fastapi_mcp and FastMCP are fine. If you do, MCPSentry is the only one that fails your build when you got it wrong β and your runtime when something looks off.
Runtime guardrails (v0.3)
The audit happens once, at startup. The guardrail runs on every single tools/call request while the server is alive.
It scans the call's arguments (recursively, in dicts and lists) against a curated catalogue of prompt-injection patterns:
Confidence | Examples of what gets caught |
π΄ HIGH |
|
π MEDIUM |
|
π΅ LOW | exfiltration verbs ( |
Aggregate decision:
any HIGH match β BLOCK
2+ MEDIUM matches β BLOCK
1 MEDIUM or LOW β WARN
nothing β ALLOW
Enable in one line
bridge = MCPSentry(app)
bridge.audit() # pre-flight
bridge.enable_guardrails(policy="block") # runtimeThree policies, your call
bridge.enable_guardrails(policy="block") # refuse the call (default)
bridge.enable_guardrails(policy="alert") # let it through, log loudly + on_alert callback
bridge.enable_guardrails(policy="log") # shadow mode β just observePlug your alerting in
def to_security_team(decision):
slack.post(f"β οΈ MCPSentry blocked {decision.tool_name}: {decision.reason}")
bridge.enable_guardrails(policy="block", on_block=to_security_team)Inspect what happened
bridge.guardrail.stats()
# β {"total": 1284, "blocked": 7, "alerted": 23, "clean": 1254}
for entry in bridge.guardrail.recent(10):
print(entry.tool_name, entry.decision.allowed, entry.decision.reason)What an MCP client sees when blocked:
{
"isError": true,
"content": [{
"type": "text",
"text": "π‘οΈ Blocked by MCPSentry runtime guardrail.\nReason: Prompt-injection detected (HIGH:1 MEDIUM:0 LOW:0)\nTop matches: high instruction_override @ arguments.query"
}]
}Roadmap
v0.1 β FastAPI introspection, MCP Streamable HTTP transport, examples
v0.2 β
bridge.audit()with 7 security checks, severity levels, JSON/text outputv0.3 β Runtime guardrails: prompt-injection detection + block/alert/log policy + structured callbacks
v0.4 β Multi-framework: Flask + Django adapters (the real white space)
v0.5 β Custom audit & guardrail rules (decorators / config / plugins)
v0.6 β OAuth2 / API key auth passthrough, stdio transport
v1.0 β Smart tool grouping (collapse CRUD into fewer tools), policy-as-code
Contributing
git clone https://github.com/miloudbelarebia/mcpsentry
cd mcpsentry
pip install -e ".[dev]"
pytestSee CONTRIBUTING.md for guidelines.
License
MIT β see LICENSE.
This server cannot be installed
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Latest Blog Posts
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/miloudbelarebia/mcpsentry'
If you have feedback or need assistance with the MCP directory API, please join our Discord server