内容
# MCP 网关

基于 Python 的 MCP(模型上下文协议)代理/网关,整合多个后端 MCP 服务器到单个端点。构建用于 **令牌优化**、**动态管理** 和 **生产可靠性**。
## 关键功能
| 功能 | 描述 |
|---------|-------------|
| **元工具模式** | 只暴露 3 个元工具而不是 43+。与完整功能相比节省约 90% 的令牌。 |
| 延迟模式加载 | 仅在调用时获取工具模式,而不是在发现时。节省 60-80% 的令牌。 |
| 多协议 | 支持 HTTP(流式)、SSE 和 Stdio 后端 |
| 动态管理 | 通过 API 或 Web UI 在运行时添加/删除后端 |
| Web 仪表盘 | 内置 UI 用于 CRUD、监控、搜索和备份/恢复 |
| 工具过滤 | 每个后端包含/排除列表以控制暴露的内容 |
| 持久状态 | 通过 state.json 添加的 API 后端在重启后存活 |
| 自动重连 | 断开连接的后端采用指数退避重试 |
| 请求日志 | 每次调用的指标,包括延迟、成功/错误跟踪 |
| 身份验证 | 网关 API 密钥 + 每个后端的身份验证头 |
| 实时状态 | WebSocket 提供实时后端状态变化 |
## 架构
```
┌──────────────────┐ ┌───────────────────────────────────┐
│ AI 支撑系统 │ │ MCP 网关 │
│ (Kiro/Claude/etc)│────────▶│ :8080/mcp (MCP 协议) │
└──────────────────┘ POST │ :8080/ (Web UI) │
│ :8080/admin (REST API) │
│ :8080/ws (WebSocket 状态) │
└──────────────┬────────────────────┘
│ 路由工具调用
┌─────────────┼───────────────┐
▼ ▼ ▼
┌──────────┐ ┌──────────┐ ┌──────────┐
│ HTTP 服务器 │ │ SSE 服务器 │ │ 标准输入输出 │
│ :3001 │ │ :3000 │ │ (npx) │
└──────────┘ └──────────┘ └──────────┘
```
## 快速开始
### Docker(推荐)
```bash
cd mcp_gateway
cp config.example.yaml config.yaml # 编辑后端配置
docker compose up --build
```
### 本地开发
```bash
cd mcp_gateway
pip install -r requirements.txt
python -m src -c config.yaml
```
### 连接您的 AI 支撑系统
用单个网关连接替换所有独立的服务器条目:
```json
{
"mcpServers": {
"gateway": {
"url": "http://localhost:8080/mcp"
}
}
}
```
在浏览器中打开 `http://localhost:8080` 以访问管理仪表盘。
## Web 管理界面
内置仪表盘提供:
- **仪表盘** — 统计概览、后端卡片,带有实时状态点
- **后端** — 添加/编辑/删除后端,备份/恢复配置
- **工具** — 所有已注册工具的可搜索列表,按后端分组
- **活动日志** — 实时调用历史记录,延迟和错误跟踪
- **设置** — 查看网关配置状态
所有更改自动持久化到 `state.json`。
## 配置
### config.yaml
```yaml
gateway:
host: "0.0.0.0"
port: 8080
log_level: "info"
tool_cache_ttl: 300
lazy_schema_loading: true
state_file: "state.json"
auto_reconnect: true
reconnect_interval: 30
api_key: "${MCP_GATEWAY_API_KEY}" # 可选
mode: "meta" # "proxy"(所有工具)或 "meta"(3 个元工具,约 92% 令牌节省)
backends:
- name: my-server
url: "http://localhost:3000/mcp"
transport: "http" # http, sse, 或 stdio
description: "我的 MCP 服务器"
lazy: true
headers: # 此后端的身份验证
Authorization: "Bearer ${MY_TOKEN}"
tools: # 工具过滤
include: ["tool_a", "tool_b"]
```
### 环境变量
config.yaml 中的值支持 `${VAR}` 和 `${VAR:-default}` 语法。
| 变量 | 默认值 | 描述 |
|----------|---------|-------------|
| `MCP_GATEWAY_CONFIG` | `./config.yaml` | 配置文件路径 |
| `MCP_GATEWAY_HOST` | `0.0.0.0` | 绑定地址 |
| `MCP_GATEWAY_PORT` | `8080` | 监听端口 |
| `MCP_GATEWAY_LOG_LEVEL` | `info` | 日志详细程度 |
| `MCP_GATEWAY_API_KEY` | (无) | 访问所需的 API 密钥 |
## 传输协议
| 传输协议 | 配置 | 自动检测 |
|-----------|--------|----------------|
| HTTP(流式) | `url: "http://.../mcp"` | URL 기반 默认 |
| SSE | `url: "http://.../sse"` | 自动如果 URL 包含 `/sse` |
| 标准输入输出 | `command: "npx"` + `args: [...]` | 自动如果设置了 `command` |
通过显式 `transport: "http"`、`"sse"` 或 `"stdio"` 覆盖自动检测。
## 工具过滤
控制每个后端暴露哪些工具:
```yaml
backends:
- name: fleet
url: "http://localhost:3001/mcp"
tools:
include: ["get_routers", "get_groups"] # 仅这些
# 或
exclude: ["reboot_router", "reboot_group"] # 除这些之外
```
在添加/编辑后端时,可通过 Web UI 配置。
## 身份验证
### 网关 API 密钥
当在配置中设置 `api_key` 时,所有请求(除 `/health`、`/`、`/static`)都需要密钥:
```bash
# 通过头部
curl -H "X-API-Key: your-key" http://localhost:8080/admin/backends
# 通过 Authorization 头部
curl -H "Authorization: Bearer your-key" http://localhost:8080/mcp
# 通过查询参数
curl http://localhost:8080/admin/backends?api_key=your-key
```
### 后端身份验证头部
每个后端可以有自定义头部(通常用于身份验证):
```yaml
backends:
- name: protected-server
url: "http://api.example.com/mcp"
headers:
Authorization: "Bearer ${API_TOKEN}"
X-Custom-Header: "value"
```
## 自动重连
启用后,网关对断开连接的后端进行指数退避重试:
- 基准间隔:`reconnect_interval`(默认 30 秒)
- 退避:30 秒 → 60 秒 → 120 秒 → 240 秒 → 300 秒(上限 5 分钟)
- 成功重新连接后重置
- 重新连接后重新发现工具
## 持久状态
通过管理 API 或 Web UI 添加的后端保存到 `state.json`。重启时:
1. 首先加载 `config.yaml` 中的后端
2. 加载 `state.json` 中的额外后端(无重复)
## 备份和恢复
### 通过 Web UI
- 点击 **备份** 下载 JSON 快照
- 点击 **恢复** 上传并应用以前的快照
### 通过 API
```bash
# 下载备份
curl http://localhost:8080/admin/backup > backup.json
# 从备份恢复(替换所有当前后端)
curl -X POST http://localhost:8080/admin/restore \
-H "Content-Type: application/json" \
-d @backup.json
```
## API 参考
### MCP 协议
| 端点 | 方法 | 描述 |
|----------|--------|-------------|
| `/mcp` | POST | MCP JSON-RPC 协议端点 |
### Web 和健康检查
| 端点 | 方法 | 描述 |
|----------|--------|-------------|
| `/` | GET | Web 管理仪表盘 |
| `/health` | GET | 健康检查(无需身份验证) |
| `/ws/status` | WebSocket | 实时后端状态事件 |
### 管理 API
| 端点 | 方法 | 描述 |
|----------|--------|-------------|
| `/admin/backends` | GET | 列出所有后端及其指标 |
| `/admin/backends` | POST | 添加新后端 |
| `/admin/backends/{name}` | DELETE | 删除后端 |
| `/admin/refresh` | POST | 强制重新发现所有工具 |
| `/admin/tools` | GET | 列出所有已注册的工具 |
| `/admin/metrics` | GET | 聚合调用指标 |
| `/admin/logs` | GET | 最近的工具调用日志 |
| `/admin/backup` | GET | 下载状态备份 |
| `/admin/restore` | POST | 从备份恢复状态 |
### 添加后端(POST /admin/backends)
```json
{
"name": "my-server",
"url": "http://localhost:3000/mcp",
"transport": "http",
"description": "我的服务器描述",
"lazy": true,
"headers": {
"Authorization": "Bearer token123"
},
"tools": {
"include": ["tool_a", "tool_b"]
}
}
```
## 延迟加载的工作原理
1. 连接时,网关调用每个后端的 `tools/list`
2. 它只缓存 `name` + `description` — 不需要 `inputSchema`
3. 您的 AI 支撑系统看到一个最小的工具列表(巨大的令牌节省)
4. 当调用工具时,网关直接路由它 — 不需要路由的模式
## 元工具模式(最大令牌节省)
元工具模式是最激进的令牌优化。代替暴露所有上游工具给 LLM(即使使用延迟加载,每个工具仍然消耗约 150 个令牌的模式在系统中),元模式只暴露 **3 个通用工具**:
| 元工具 | 目的 |
|-----------|---------|
| `mcp_gateway_discover` | 按后端或关键词查找工具 — 返回名称 + 描述 |
| `mcp_gateway_describe` | 获取特定工具的完整参数模式(按需) |
| `mcp_gateway_execute` | 按名称调用任何上游工具并带有参数 |
### 令牌比较
| 模式 | 上下文中的工具 | 近似令牌 | 节省 |
|------|-----------------|---------------|---------|
| 无网关(4 个服务器) | 43 个完整模式 | ~6,500 | — |
| 代理模式 | 43 个(延迟,无模式) | ~4,500 | ~30% |
| **元模式** | 3 个元工具模式 | ~500 | **~92%** |
### 工作原理
```
LLM 网关 上游服务器
│ │ │
│─ discover(query="radio")──│ │
│ │── (搜索内部注册表)│
│◀─ get_radios, manage_radio│ │
│ │ │
│─ describe("get_radios") ──│ │
│ │── tools/list ───────────────▶│
│◀─ {radio_id, network...} │◀─────────────────────────────│
│ │ │
│─ execute("get_radios", │ │
│ {network: "..."}) ─│── tools/call("get_radios")──▶│
│◀─ [radio 结果] │◀─────────────────────────────│
```
### 配置
```yaml
gateway:
mode: "meta" # "proxy"(默认)或 "meta"
```
网关仍然连接到所有后端并在内部发现所有工具 — 它只是不将它们暴露给客户端。随时切换回 `mode: "proxy"` 而不会失去功能。
## 项目结构
```
mcp_gateway/
├── src/
│ ├── __init__.py
│ ├── __main__.py # CLI 入口点
│ ├── config.py # YAML + 环境变量配置,ToolFilter,BackendServer
│ ├── meta_tools.py # 元工具定义 + 分发器(发现/描述/执行)
│ ├── transport.py # HTTP,SSE 和 Stdio 传输协议,带有身份验证头部
│ ├── registry.py # 工具注册表,指标,自动重连,WebSocket 事件
│ ├── persistence.py # 状态文件管理,备份/恢复
│ └── server.py # MCP + 管理 API + 身份验证中间件 + WebSocket + Web UI
├── static/
│ ├── index.html # Web 仪表盘
│ ├── css/style.css # UI 样式
│ ├── js/app.js # 前端逻辑(CRUD,搜索,WebSocket,备份)
│ └── libs/ # jQuery,Font Awesome(已移植)
├── config.yaml # 活动配置
├── config.example.yaml # 完整参考配置
├── .env.example # 环境变量参考
├── Dockerfile
├── docker-compose.yaml
└── requirements.txt
```
## Docker
```bash
cp config.example.yaml config.yaml
cp .env.example .env
docker compose up --build
```
容器使用 `host.docker.internal` 访问主机网络上的服务。挂载 `config.yaml` 作为卷进行编辑,无需重建。