使用说明
MCP网关——多租户MCP代理和聚合服务器
位于MCP客户端(Claude、Cowork、定制代理)和 多个下游MCP服务器,添加 认证, 每位租户 权限, 速率限制, 使用日志记录,以及 刀具路径 在一个 单一过程。
┌──────────────┐
│ Tenant A │──┐
│ (Claude) │ │
└──────────────┘ │ ┌─────────────────────┐ ┌──────────────────┐
├───▶│ │───▶│ Filesystem MCP │
┌──────────────┐ │ │ MCP Gateway │ └──────────────────┘
│ Tenant B │──┤ │ │ ┌──────────────────┐
│ (Cowork) │ │ │ • Auth / Perms │───▶│ GitHub MCP │
└──────────────┘ │ │ • Rate Limiting │ └──────────────────┘
├───▶│ • Usage Logging │ ┌──────────────────┐
┌──────────────┐ │ │ • Tool Routing │───▶│ Google Drive MCP │
│ Tenant C │──┘ │ • Proxy / Agg │ └──────────────────┘
│ (Custom) │ │ │ ┌──────────────────┐
└──────────────┘ └─────────────────────┘───▶│ Custom MCP │
└──────────────────┘
快速开始
# Install in dev mode
pip install -e ".[dev]"
# Copy and edit the example config
cp examples/tenants.yaml tenants.yaml
# → set real API keys and downstream server commands/URLs
# Start the gateway
mcp-gateway --config tenants.yaml --port 8000
# Run the tests (80 tests, ~2 seconds)
pytest
运行后,租户使用其API密钥连接到SSE端点:
GET http://localhost:8000/sse?api_key=sk-acme-XXXXXXXXXXXXXXXXXXXX
网关还为两个HTTP端点提供服务: GET /health 返回刀具计数
以及正常运行时间信息,以及 GET /stats (仅限管理员,需要 x-api-key 头球
返回每个租户的使用统计数据。
建筑
四个层,每个层都可以独立测试,组成一个请求
管道 gateway.routing.pipeline.RequestPipeline.
第1层——身份验证和权限(gateway.middleware.auth)
每个入站请求都携带一个API密钥,作为 api_key 怎么翻译
SSE端点上的参数或作为 x-api-key 头球身份验证中间件
使用定时安全验证密钥 hmac.compare_digest,解决租户问题
身份和角色,然后运行两次授权检查:第一,是否
请求的工具与租户的匹配 allowed_tools 球状图案(例如。
filesystem:*);第二,工具的服务器前缀是否在租户的
downstream 列表。这两项检查都必须通过。
支持通过以下方式进行热重新加载 reload() 因此,租户配置可以在没有
重新启动网关。
第2层——速率限制和使用日志记录
速率限制器 (gateway.middleware.rate_limiter)--每个租户一个
滑动窗口计数器,限制每分钟的请求数。返回a RateStatus
随着 remaining 和 retry_after 字段,以便服务器层可以浮出水面
速率限制标头。 peek() 在不消耗插槽的情况下检查容量。
reload() 在速率限制未更改时保留存储桶历史记录,以及
当它们有时,会干净地重置。
使用情况记录器 (gateway.middleware.usage_logger)--异步上下文管理器
它封装了每个工具调用,记录租户ID、工具名称、延迟和
成功/失败进入有界环形缓冲区(默认50k条记录)。
query(tenant_id=..., tool_name=..., since=..., limit=...) 检索已筛选
记录,以及 stats_for() 计算包括总呼叫的聚合,
成功/失败计数、平均延迟和p99延迟。
第3层——工具路由和请求管道
路由器 (gateway.routing.router)--保存合并的工具清单。工具
名称被限定为 server:tool (例如。 github:create_issue).路由器
将合格名称解析回发起下游服务器,并
通过聚合器发送。 list_tools() 接受glob模式
每个租户过滤。
管道 (gateway.routing.pipeline)--单一入口点
组成所有层:验证→ 授权→ 速率限制→ 使用日志→
路线→ 代理。通配符租户(allowed_tools: ["*"])自动
仅限于其允许的下游服务器。暴露 reload_config() 和
refresh_tools() 用于实时重新配置。
第4层——代理和聚合(gateway.proxy.aggregator)
聚合器通过以下方式管理MCP客户端与每个下游服务器的连接
真实的 mcp.ClientSession 通过stdio或SSE传输的实例。启动时
按顺序连接到每个服务器(anyio的任务组范围要求),
获取分页的工具清单,并将其合并为一个 server:tool
具有重复检测的命名空间。失败的连接会被记录,但不会
阻止网关——其余服务器仍在服务流量。
refresh_manifest() 从所有下游重新获取工具以进行热重新加载。
call_downstream() 代理工具调用具有可配置的每次调用超时。
服务器层(gateway.server)
通过SSE传输将所有内容连接到真实的MCP服务器
Starlette和uvicorn。租户在 GET /sse 终点;一
contextvars-基于的方法将API密钥从HTTP层线程化到
MCP协议处理程序,因此每个SSE会话只能看到其授权的工具
为。
项目结构
mcp-gateway/
├── pyproject.toml
├── README.md
├── examples/
│ └── tenants.yaml # Sample config (3 tenants, 4 downstreams)
├── src/gateway/
│ ├── __init__.py
│ ├── cli.py # Click CLI: mcp-gateway --config ... --port ...
│ ├── server.py # Starlette app, SSE transport, /health, /stats
│ ├── config/
│ │ └── loader.py # YAML → Pydantic models, ${VAR} interpolation
│ ├── middleware/
│ │ ├── auth.py # Timing-safe auth, glob ACLs, downstream scoping
│ │ ├── rate_limiter.py # Sliding-window rate limiter with status/peek
│ │ └── usage_logger.py # Ring-buffer logger with query/stats
│ ├── routing/
│ │ ├── router.py # Qualified-name resolution + glob filtering
│ │ └── pipeline.py # Full middleware chain composition
│ ├── proxy/
│ │ └── aggregator.py # MCP ClientSession management + manifest merge
│ └── utils/
│ ├── env.py # ${VAR} and ${VAR:-default} interpolation
│ └── errors.py # GatewayError hierarchy + MCP error formatting
└── tests/
├── mock_downstream.py # Minimal MCP server (echo + add) for integration tests
├── test_boot_smoke.py # End-to-end: boot → connect → proxy → HTTP
├── test_aggregator.py # Layer 4 unit tests (12 tests)
├── test_pipeline.py # Layer 3 pipeline tests (10 tests)
├── test_auth.py # Layer 1 auth tests (14 tests)
├── test_rate_limiter.py # Layer 2a rate limiter tests (8 tests)
├── test_usage_logger.py # Layer 2b usage logger tests (9 tests)
├── test_server.py # HTTP endpoint tests (6 tests)
├── test_config.py # Config loading + env interpolation (7 tests)
└── test_utils.py # Env/error utility tests (11 tests)
配置
所有租户和下游服务器定义都存在于一个YAML文件中。看
examples/tenants.yaml 对于带有注释的完整模式。
租户 --每个条目定义一个 api_key, role (admin/editor/viewer),
allowed_tools (球状图案如 filesystem:* 或 github:create_issue),
rate_limit (请求/分钟),以及 downstream 租户可以访问的服务器。
下游服务器 --每个条目指定一个 transport (stdio 或 sse)
以及连接详细信息(stdio的command+args,SSE的URL)。
环境变量插值 — ${VAR} 和 ${VAR:-default} 语法
支持 api_key上海证券交易所 url,以及stdio env 领域。这保持
配置文件中的秘密:
tenants:
acme:
api_key: "${ACME_API_KEY}"
# ...
downstream_servers:
github:
transport: stdio
command: "npx"
args: ["-y", "@modelcontextprotocol/server-github"]
env:
GITHUB_PERSONAL_ACCESS_TOKEN: "${GITHUB_TOKEN}"
gdrive:
transport: sse
url: "${GDRIVE_URL:-http://localhost:3002/sse}"
延伸
添加新的下游 --向添加条目 downstream_servers 在你的YAML中,
然后在每个租户的 downstream 列表。网关发现
工具在下次启动时自动启动。
自定义角色 --the role 字段验证为 admin|editor|viewer 但是
网关仅将其用于 /stats 端点(仅限管理员)。工具级别
访问完全由以下人员控制 allowed_tools globs,所以角色是
您自己的策略层的约定。
交换存储后端 — UsageLogger 在内存中保存记录
deque.将其子类化并重写记录以写入SQLite、Postgres、,
或指标后端。这 query() 和 stats_for() 界面保持不变。
热重载 --呼叫 pipeline.reload_config(new_config) 更新租户
无需重新启动即可进行身份验证和速率限制,以及 pipeline.refresh_tools() 到
重新获取下游工具清单。
