🛡️ mcp-rampart
针对作为 MCP 服务器暴露的 FastAPI 应用的安全防御工事。
起飞前审计。运行时 prompt 注入护栏。一个包搞定。
```
from fastapi import FastAPI
from mcp_rampart import MCPRampart
app = FastAPI()
# ... 你现有的路由 ...
rampart = MCPRampart(app) # 1. Speak MCP.
report = rampart.audit() # 2. Audit what you'd expose.
if report.has_blockers():
report.print_text(); raise SystemExit(1)
rampart.enable_guardrails(policy="block") # 3. Block prompt-injection at runtime.
```
## MCP 安全的 4 个层级
MCP 在 2026 年从“实验性技术”迅速发展到**每月超过 9700 万次安装**。安全工具直到最近才跟上步伐,而且其中大多数解决的问题与您面临的并不相同。以下是全景图:
```
┌─────────────────────────────────────────────────────────────────┐
│ Layer 1 — The LLM itself (Claude, GPT, Gemini) │
│ Worry: hallucination, jailbreaks at the model level │
│ → out of scope for everyone — model provider's problem │
└──────────────────────┬──────────────────────────────────────────┘
│
▼ (JSON-RPC over MCP transport)
┌─────────────────────────────────────────────────────────────────┐
│ Layer 2 — The MCP CLIENT (Claude Desktop, Cursor, agents) │
│ Worry: the LLM calls something risky or exfiltrates data │
│ Tools: pipelock, mcp-firewall, SecretiveShell/MCP-Bridge │
└──────────────────────┬──────────────────────────────────────────┘
│
▼ (HTTP / SSE)
┌─────────────────────────────────────────────────────────────────┐
│ Layer 3 — The GATEWAY / proxy in front of the MCP server │
│ Worry: who's allowed to talk to this server, with what auth │
│ Tools: apache/casbin-gateway, hyprmcp/mcp-gateway │
└──────────────────────┬──────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ Layer 4 — The MCP SERVER itself ← 🛡️ mcp-rampart │
│ Worry: did I expose dangerous routes? is an injection hiding │
│ inside the arguments of every tools/call? │
│ Tools: mcp-rampart (this project) │
└──────────────────────┬──────────────────────────────────────────┘
│
▼ (which servers are even installed?)
┌─────────────────────────────────────────────────────────────────┐
│ Layer 5 — The USER's MCP config (~/.mcp.json, etc.) │
│ Worry: am I installing a malicious server on my machine │
│ Tools: apisec-inc/mcp-audit, ModelContextProtocol-Security/ │
│ mcpserver-audit │
└─────────────────────────────────────────────────────────────────┘
```
| 层级 | 它回答的问题 | 代表性工具 |
|--:|---|---|
| 1 | “模型本身安全吗?” | (模型提供商) |
| 2 | “我的 agent 是否在泄露信息或调用有风险的内容?” | [pipelock](https://github.com/luckyPipewrench/pipelock) (583⭐) |
| 3 | “谁被允许与我的服务器对话,使用什么身份验证?” | [casbin-gateway](https://github.com/apache/casbin-gateway) (559⭐), [hyprmcp/mcp-gateway](https://github.com/hyprmcp/mcp-gateway) (92⭐) |
| **4** | **“我是不是刚把管理端点的访问权限交给了语言模型?是否有注入内容正悄悄混入每次调用的参数中?”** | **mcp-rampart** |
| 5 | “我正在安装的这个 MCP 服务器真的安全吗?” | [apisec mcp-audit](https://github.com/apisec-inc/mcp-audit) (149⭐), [mcpserver-audit](https://github.com/ModelContextProtocol-Security/mcpserver-audit) (16⭐) |
**您可能需要不止一个层级。** mcp-rampart 是唯一一个在第 4 层运行的库——该层级解决的是 *MCP 服务器作者* 的问题,而不是运维人员或用户的问题。
## 为什么是“框架感知”,而不是“MCP 通用”
您会注意到第 2、3 和 5 层的每个工具都是**框架无关的**——它们拦截传输流(HTTP、JSON-RPC、配置文件),而不关心其背后运行的是什么。这对它们来说行得通,因为它们不需要关心。
mcp-rampart **需要**看透背后。起飞前审计根本无法作为代理存在:
| mcp-rampart 能看到什么 | 原因(它被安装 *在* 应用内部) |
|---|---|
| `@app.get("/api/admin/users/...")` 装饰器 | 直接读取 `app.routes` |
| 声明了 `email`、`phone`、`ssn` 的 Pydantic 响应模型 | 内省 `route.response_model` |
| 工具处理程序上缺失 docstring | 读取 `route.endpoint.__doc__` |
| 回退到 `str` 的未类型化参数 | 读取 `inspect.signature(handler)` |
| 看起来像 `/auth/`、`/oauth/`、`/internal/` 的路径模式 | 对 `route.path` 进行模式匹配 |
第 3 层的代理只能看到 `POST /mcp {"method":"tools/call","name":"delete_user","args":{...}}`。它**看**不到上游的 `@app.delete("/api/admin/users/{user_id}")`。因此,它无法告诉您“您即将将管理接口暴露给 LLM”——它只能告诉您“有人刚刚调用了 delete_user”。
代价是:mcp-rampart 目前**仅支持 FastAPI**。我们现阶段是有意付出这个代价的,因为深度内省正是审计的价值所在。Node.js 和 Flask/Django 已被列入下一步的发展路线图。
## mcp-rampart 的独特定位
以下是 mcp-rampart 成为唯一 ✅ 的五个具体维度:
| | mcp-rampart | pipelock | casbin-gw | apisec mcp-audit | hyprmcp |
|---|:--:|:--:|:--:|:--:|:--:|
| **在您的 FastAPI 应用内部运行**(无需额外进程) | ✅ | ❌ | ❌ | ❌ | ❌ |
| 审计 **您即将暴露的路由**(而不是别人的) | ✅ | ❌ | ❌ | ❌ | ❌ |
| 遇到 **CRITICAL**(严重)发现时拒绝启动服务器 | ✅ | ❌ | ❌ | ❌ | ❌ |
| 在每次 `tools/call` 时进行 **Prompt 注入** 检测 | ✅ | 部分 | ❌ | ❌ | ❌ |
| 三种策略模型(`block` / `alert` / `log`)+ 可插拔的 callback | ✅ | ❌ | ❌ | ❌ | ❌ |
不同的形态,不同的问题,不同的代价。
## 快速开始
```
pip install mcp-rampart
```
```
from fastapi import FastAPI
from mcp_rampart import MCPRampart
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"}
rampart = MCPRampart(app) # auto-discovers routes, mounts /mcp
print(rampart.summary())
# 预检审计 — 遇到 CRITICAL 发现时拒绝启动服务器
report = rampart.audit()
report.print_text()
if report.has_blockers():
raise SystemExit(1)
# Runtime guardrail — 扫描每一个传入的 tools/call
rampart.enable_guardrails(policy="block")
```
您的应用现在将暴露:
- `GET /mcp` — 服务器信息和工具列表
- `POST /mcp` — MCP JSON-RPC 端点(Streamable HTTP 传输)
任何 MCP 客户端(Claude Desktop、ChatGPT、Gemini、Cursor、Codex)都可以连接。
## 起飞前审计详解
`rampart.audit()` 会遍历每个暴露的工具并运行 **7 项检查**。每个发现都会获得一个严重性标签、一个建议以及一个您可以在 CI 中进行匹配的类别代码。
| 严重性 | 检查项 | 触发条件... |
|---|---|---|
| 🔴 CRITICAL | `EXPOSED_AUTH` | 路由路径匹配 `/auth/`、`/login`、`/token`、`/oauth` 等 |
| 🔴 CRITICAL | `EXPOSED_ADMIN` | 路由路径匹配 `/admin/`、`/internal/`、`/debug/` 等 |
| 🟠 HIGH | `MISSING_DOCSTRING` | 没有描述 → LLM 将靠猜测并调用错误的工具 |
| 🟠 HIGH | `SENSITIVE_PARAM_NAME` | 参数名包含 `password`、`token`、`api_key` 等 |
| 🟠 HIGH | `PII_IN_RESPONSE` | 响应 schema 声明了如 `email`、`phone`、`ssn` 等字段 |
| 🟡 MEDIUM | `DESTRUCTIVE_METHOD` | 暴露了 `DELETE` / `PUT` / `PATCH` 且没有显式确认流程 |
| 🔵 LOW | `UNTYPED_PARAMETER` | 3 个或更多参数回退到 `str` 类型 —— LLM 可能发送格式错误的输入 |
在一个故意留有缺陷的应用上的示例输出:
```
🛡️ MCPRampart 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
…
```
在 CI 中使用:
```
- run: python -c "from myapp import rampart; r = rampart.audit(); r.print_text(); exit(1 if r.has_blockers() else 0)"
```
## 运行时护栏详解
审计在启动时进行一次。**而护栏将永远运行——在每次 `tools/call` 请求上。**
它会根据精选的 prompt 注入模式目录,递归扫描调用的 `arguments`(在字典和列表中):
| 置信度 | 会被拦截的内容 |
|---|---|
| 🔴 HIGH | `ignore previous instructions`、`you are now …`、`developer/admin/jailbreak mode`、聊天模板控制 token(`<\|im_start\|>`)、`[[system]]` 标记、`SYSTEM: do …` |
| 🟠 MEDIUM | `system prompt`、`act as …`、`pretend to be …`、“reveal your instructions”、“repeat everything above”、“begin new session as” |
| 🔵 LOW | 数据窃取动词(`send your tokens to …`)、`