问题概述
当 MCP‑Server 采用 SSE 模式(transport="sse")独立后台运行,默认情况任何人知道 http://ip:8001/sse 地址就可以连接并调用所有工具(RAG 检索、查订单、创建工单),存在严重安全隐患。
针对 Fast‑MCP 的鉴权方案分 4 种,由轻到重:
- 请求头 Token 鉴权(简单,适合内网);
- API‑Key + HTTPS(外网生产推荐);
- OAuth2 / JWT(对接用户登录系统);
- 反向代理(Nginx 鉴权,业务代码无侵入)。
注意:原生
fast‑mcp内置的sse模式本身没有提供鉴权参数,底层是基于 Starlette 框架;我们有两种实现方式:
- 方案 A:自定义 Starlette 中间件拦截请求(推荐,代码内实现);
- 方案 B:Nginx 反向代理做鉴权(生产最佳实践,解耦业务代码)。
环境依赖
pip install fast‑mcp python‑multipart python‑jose passlib
方案 1:自定义中间件,Header 携带 API‑KEY(代码层面鉴权,最常用)
原理:客户端发起 SSE 连接时,在请求头携带 X‑MCP‑Api‑Key,服务端中间件校验密钥,密钥不对直接拒绝连接。
mcp_server/server.py(修改 MCP‑Server 源码)
from fastmcp import FastMCP
from starlette.applications import Starlette
from starlette.middleware.base import BaseHTTPMiddleware
from starlette.requests import Request
from vector_db import search_kb
# 全局密钥,生产环境放到环境变量,不要硬编码
VALID_API_KEY = "sk‑mcp‑123456789"
mcp = FastMCP("knowledge‑server")
@mcp.tool(description="查询售后知识库")
def rag_search(query: str) -> str:
return search_kb(query)
# 鉴权中间件
class AuthMiddleware(BaseHTTPMiddleware):
async def dispatch(self, request: Request, call_next):
# 只对sse接口鉴权
if request.url.path == "/sse":
client_key = request.headers.get("X‑MCP‑Api‑Key")
if client_key != VALID_API_KEY:
from starlette.responses import PlainTextResponse
return PlainTextResponse("Unauthorized", status_code=401)
response = await call_next(request)
return response
if __name__ == "__main__":
# 获取底层starlette实例,挂载中间件
app: Starlette = mcp.sse_app()
app.add_middleware(AuthMiddleware)
app.run(host="0.0.0.0", port=8001)
LangChain‑MCP‑Client 携带 Header
langchain‑mcp‑adapters的MultiServerMCPClient可以通过headers参数配置请求头:
client = MultiServerMCPClient({
"rag_server": {
"url": "http://127.0.0.1:8001/sse",
"transport": "sse",
"headers": {
"X‑MCP‑Api‑Key": "sk‑mcp‑123456789"
}
}
})
- 密钥错误,会抛出 401 拒绝建立 SSE 连接,完全不能调用工具。
- 优化:生产环境将
VALID_API_KEY从环境变量读取,不要写死代码:os.getenv("MCP_API_KEY")。
方案 2:Nginx 反向代理鉴权(生产环境首选,推荐)
不修改 Python 代码,把鉴权交给 Nginx;MCP‑Server 只监听127.0.0.1:8001,外部网络访问不到服务,Nginx 对外开放 443 并且强制 HTTPS。
- MCP‑Server 仅监听本机:
mcp.run(sse, host="127.0.0.1", port=8001); - Nginx 配置 HTTPS 证书;
- Nginx 校验请求头
X‑MCP‑Api‑Key;密钥不对直接 401; - 并且配置 IP 白名单,只允许你的后端服务 IP 访问。
server {
listen 443 ssl;
server_name mcp‑api.xxx.com;
ssl_certificate cert/xxx.pem;
ssl_certificate_key cert/xxx.key;
set $api_key "";
if ($http_x_mcp_api_key != "sk‑mcp‑123456789"){
return 401;
}
# IP白名单,只允许你的服务机器
allow 你的agent服务器IP;
deny all;
location /sse {
proxy_pass http://127.0.0.1:8001;
proxy_http_version 1.1;
proxy_set_header Connection '';
proxy_buffering off;
}
}
优点:
- Python 代码干净;
- 强制 HTTPS,防止密钥抓包;
- IP 白名单 + HTTPS + API‑Key 三重防护;
- 可以配置限流、防高频调用。
生产环境必须开启 HTTPS,如果是 HTTP,header 里面的 API‑Key 会明文传输,密钥会被窃取。
方案 3:JWT 鉴权(适用于多租户场景)
当你的 MCP 服务给多个业务方使用时,用 JWT 代替固定 API‑Key:
- Agent 服务先向认证服务获取短期 JWT 令牌(有效期 1‑2 小时);
- Client 请求头:
Authorization: Bearer xxx; - MCP‑Server 中间件解析 JWT,验证签名是否合法,并且解析租户 ID;
- 根据租户 ID 隔离知识库,实现多租户 RAG。
服务端解析 JWT 示例片段:
from jose import jwt
SECRET_KEY = os.getenv("JWT_SECRET")
payload = jwt.decode(token, SECRET_KEY, algorithms=["HS256"])
user_id = payload["sub"]
方案 4:stdio 模式(完全不暴露端口,开发环境最安全)
如果你只有 LangChain Agent 调用 MCP‑Server,优先使用 stdio 模式:
- MCP‑Server 不会开启任何网络端口;
- Agent 通过子进程启动 MCP‑Server,通信是进程内管道;外部网络完全无法访问 MCP‑Server,不存在被外部调用风险;
缺点:只能同一台机器部署;Agent 和 MCP‑Server 不能分开部署在不同服务器。
5. 进阶安全加固要点(落地必做)
(1)网络层面
- SSE 服务只监听
127.0.0.1,由 Nginx 代理暴露; - 安全组:云服务器防火墙关闭 8001 端口,仅开放 443;
- 严格配置 IP 白名单,只有 Agent 服务能访问 MCP 服务。
(2)应用层面
- API‑Key 设置足够复杂,定期轮换;不要硬编码在代码,统一环境变量或者配置中心;
- 开启 HTTPS,禁止 HTTP 访问;
- 给 MCP 工具增加权限控制:例如:
- Agent‑A 只能调用
rag_search; - Agent‑B 才可以调用查询订单的工具;
在中间件解析 token 之后,动态过滤可用工具列表;
- Agent‑A 只能调用
- 请求限流:Nginx 或者 Starlette‑limiter 限制每秒调用次数,防止被刷向量服务;
- 日志审计:每次调用 MCP 工具记录调用方标识、入参,方便排查安全问题。
(3)容器部署注意事项
Docker 部署 MCP‑Server 时:
- 容器内端口 8001 不映射到宿主机;
- 只有同一 docker‑network 里 Agent 容器能够访问 MCP‑Server。
选型建议总结
- 开发环境:stdio 模式,无需鉴权,最安全;
- 内网分离部署:Header+API‑Key 中间件鉴权即可;
- 公网环境(生产强烈推荐):Nginx + HTTPS + API‑Key + IP 白名单;
- 多租户平台:采用 JWT 鉴权 + 租户隔离知识库。

评论 (0)