内容
# mcpstead
[](https://github.com/ahkohd/mcpstead/actions/workflows/ci.yml) [](https://www.npmjs.com/package/@ahkohd/mcpstead) [](https://crates.io/crates/mcpstead) [](https://opensource.org/licenses/MIT)
MCP 网关
一个下游 `/mcp` 端点,面向多个上游 MCP 服务器。具有重新连接的持久上游连接,带有合格名称的工具注册表,基于客户端 `Accept` 的 JSON 或 SSE 响应,每个上游的身份验证,Prometheus 指标。
## 安装
```bash
# npm (macOS, Linux, WSL)
npm i -g @ahkohd/mcpstead
# homebrew (macOS, Linux)
brew install ahkohd/mcpstead/mcpstead
# cargo
cargo install mcpstead --locked --force
# 验证
mcpstead --version
```
## 快速开始
```bash
# 1. 编写配置文件
mkdir -p ~/.config/mcpstead
cat > ~/.config/mcpstead/config.yaml <<'EOF'
host: 0.0.0.0
port: 8766
mcp:
auth:
mode: none
servers:
- name: example
url: http://127.0.0.1:3000/mcp
protocol: streamable
auth: none
EOF
# 2. 运行
mcpstead --config ~/.config/mcpstead/config.yaml
```
然后将任何 MCP 客户端指向 `http://127.0.0.1:8766/mcp`。
## Docker
```bash
docker build -t mcpstead .
docker run --rm \
-p 8766:8766 \
-v "$PWD/config:/etc/mcpstead:ro" \
mcpstead
```
Dockerfile 从 crates.io 安装。
## HTTP API
| 方法 | 路径 | 目的 |
|---|---|---|
| `POST` | `/mcp` | MCP over HTTP JSON-RPC |
| `GET` | `/mcp` | 返回 405 |
| `DELETE` | `/mcp` | 终止下游会话 |
| `GET` | `/health` | 上游状态,工具计数,最后看到,重连 |
| `GET` | `/metrics` | Prometheus 文本格式 |
| `POST` | `/-/reload` | 重载配置,无需重启 |
## MCP 客户端设置
本地,无身份验证:
```yaml
mcpstead:
url: http://127.0.0.1:8766/mcp
tools:
resources: false
prompts: false
```
Bearer 身份验证:
```yaml
mcpstead:
url: http://127.0.0.1:8766/mcp
headers:
Authorization: Bearer ${MCPSTEAD_BEARER_TOKEN}
tools:
resources: false
prompts: false
```
工具以合格名称出现 - `<server>__<tool>` - 因此多个上游可以发送重叠的工具名称而不会发生冲突。
## 身份验证
### 下游(客户端到 mcpstead)
默认无身份验证:
```yaml
mcp:
auth:
mode: none
```
Bearer 身份验证:
```yaml
mcp:
auth:
mode: bearer
```
令牌来自环境变量:
```bash
export MCPSTEAD_BEARER_TOKEN='replace-with-strong-secret'
mcpstead --config ~/.config/mcpstead/config.yaml
```
客户端发送 `Authorization: Bearer <token>`。缺少或错误的令牌返回 401。 配置文件中的 `mcp.auth.bearer_token` 在启动时被拒绝。
`/health` 和 `/metrics` 无论身份验证模式如何都保持开放 - 用于无需暴露令牌的监控。
### 上游(mcpstead 到 MCP 服务器)
在 `servers` 列表中每个服务器单独设置。 三种模式:
```yaml
servers:
- name: local
url: http://127.0.0.1:3000/mcp
auth: none
- name: workflow
url: https://workflow.example/mcp-server/http
auth:
type: bearer
token_env: WORKFLOW_TOKEN
- name: custom
url: https://api.example/mcp
headers:
X-API-Key: '${EXAMPLE_KEY}'
```
`token_env` 在启动和配置重载时解析命名的环境变量。
## 配置
使用 `--config <path>` 或 `MCPSTEAD_CONFIG` 环境变量设置配置路径。
```yaml
host: 0.0.0.0
port: 8766
mcp:
auth:
mode: none # none | bearer
session:
idle_ttl_seconds: 3600
gc_interval_seconds: 60
shutdown_grace_seconds: 5
servers:
- name: local
url: http://127.0.0.1:3000/mcp
protocol: streamable # streamable | sse | auto
required: false # 如果为 true,网关将在没有此上游的情况下启动
auth: none
reconnect:
max_attempts: 0 # 0 = 无限
backoff_base_ms: 1000
backoff_max_ms: 30000
tools:
ttl_seconds: 300
tls_skip_verify: false
quirks:
normalize_sse_events: true
inject_accept_header: 'application/json, text/event-stream'
metrics:
enabled: true
logging:
level: info
```
### 热重载
mcpstead 在以下情况下重载其配置而无需重启:
- SIGHUP (`systemctl reload mcpstead` 或 `kill -HUP <pid>`)
- `POST /-/reload`(在 bearer 模式下由 bearer 身份验证控制)
热重载:
- 上游列表
- 每个上游的身份验证,头部,怪癖,重连,工具,URL,协议和 TLS 设置
- `mcp.auth.mode` 和 `MCPSTEAD_BEARER_TOKEN`
- `metrics.enabled`
需要重启:
- `host`
- `port`
- `logging.level`
- `mcp.session.*`
重载是尽力而为的。 错误的配置被拒绝并被忽略;正在运行的配置保持不变。 检查日志和 `mcpstead_config_reloads_total{result="error"}` 以获取失败信息。
### MCP 会话配置键
- `mcp.session.idle_ttl_seconds` - 在此秒数后释放空闲会话(默认 `3600`)
- `mcp.session.gc_interval_seconds` - 空闲会话 GC 唤醒间隔(默认 `60`)
- `mcp.session.shutdown_grace_seconds` - 最大关闭会计扫描时间(默认 `5`)
### 每个上游的配置键
- `name` - 必需,用于工具前缀
- `url` - 必需,MCP 端点
- `protocol` - `streamable | sse | auto`(默认 `auto`)
- `required` - 如果上游初始化失败,则阻止启动(默认 `false`)
- `auth` - `none`,`bearer`(带有 `token_env`),或 `headers` 映射
- `reconnect.max_attempts` - `0` = 无限(默认)
- `reconnect.backoff_base_ms` / `backoff_max_ms` - 指数退避边界
- `tools.ttl_seconds` - 在此间隔后刷新缓存的 `tools/list`
- `tls_skip_verify` - 为此上游禁用 TLS 证书检查(默认 `false`,仅在受信任的本地网络中使用)
- `quirks.normalize_sse_events` - 从上游 SSE 响应中剥离 `event:` 行
- `quirks.inject_accept_header` - 覆盖发送给上游的 Accept 头部
## 可观察性
### 指标
`/metrics` 公开 Prometheus 格式的计数器,仪表和直方图。 标签基数假设是一组小的有界上游和工具;工具调用系列由 `(server, tool)` 键控。
```
mcpstead_build_info{version="...",rust_version="...",git_sha="..."}
mcpstead_start_time_seconds
mcpstead_uptime_seconds
mcpstead_process_resident_memory_bytes
mcpstead_process_virtual_memory_bytes
mcpstead_process_cpu_seconds_total
mcpstead_process_open_fds
mcpstead_process_max_fds
mcpstead_process_threads
mcpstead_upstream_connected{server="..."}
mcpstead_upstream_tools_count{server="..."}
mcpstead_upstream_reconnects_total{server="..."}
mcpstead_upstream_last_seen_seconds{server="..."}
mcpstead_upstream_initialize_total{server="...",result="success|error"}
mcpstead_upstream_initialize_duration_seconds_bucket{server="...",le="..."}
mcpstead_upstream_health_checks_total{server="...",result="success|failure"}
mcpstead_upstream_reconnect_attempts_total{server="...",result="success|error"}
mcpstead_upstream_backoff_seconds_total{server="..."}
mcpstead_upstream_in_backoff{server="..."}
mcpstead_upstream_current_backoff_seconds{server="..."}
mcpstead_upstream_session_resets_total{server="...",reason="unknown_session|expired|terminated"}
mcpstead_upstream_tools_refresh_total{server="...",result="success|error"}
mcpstead_upstream_tools_refresh_duration_seconds_bucket{server="...",le="..."}
mcpstead_upstream_tools_last_refresh_timestamp_seconds{server="..."}
mcpstead_upstream_bytes_total{server="...",direction="sent|received"}
mcpstead_downstream_sessions_active
mcpstead_downstream_sessions_total
mcpstead_downstream_session_duration_seconds_bucket{le="..."}
mcpstead_downstream_session_terminations_total{reason="..."}
mcpstead_mcp_requests_total{method="...",result="success|error"}
mcpstead_mcp_request_duration_seconds_bucket{method="...",le="..."}
mcpstead_mcp_auth_attempts_total{result="success|failure"}
mcpstead_mcp_auth_failures_total{reason="..."}
mcpstead_config_reloads_total{result="success|error"}
mcpstead_config_last_reload_timestamp_seconds
mcpstead_tool_calls_total{server="...",tool="..."}
mcpstead_tool_call_errors_total{server="...",tool="...",reason="..."}
mcpstead_tool_call_duration_seconds_bucket{server="...",tool="...",le="..."}
```
### 抓取配置
```yaml
- job_name: mcpstead
metrics_path: /metrics
static_configs:
- targets: ['mcpstead:8766']
```
### 健康检查
```bash
curl http://127.0.0.1:8766/health
```
返回 JSON:每个上游的连接状态,工具计数,最后成功的联系,重连计数,最后错误。
## 故障排除
- **所有工具列表为空** - 至少有一个上游未能 `initialize`。 检查 `/health` 获取每个服务器的状态;检查上游 URL,身份验证和可达性。
- **偶发的 `SSE parse failed`** - 上游发送的 SSE 方言 mcpstead 无法识别。 尝试为该服务器设置 `quirks.normalize_sse_events: true`,或设置 `quirks.inject_accept_header: 'application/json'` 以强制 JSON。
- **`tools/call` 返回身份验证错误** - 上游拒绝了 bearer 令牌。 确认 `token_env` 解析为启动时的正确值;检查上游的预期头部名称。
- **针对自签名上游的 TLS 握手错误** - 在该服务器上设置 `tls_skip_verify: true`。 仅在受信任的本地网络上安全。
- **401 from `/mcp` with mode bearer** - 客户端缺少或发送错误的 `Authorization: Bearer <token>`。 验证 `MCPSTEAD_BEARER_TOKEN` 与客户端发送的内容是否匹配。
- **上游在 `/health` 中反复变为红色** - 检查 `mcpstead_upstream_reconnects_total` 和 `mcpstead_upstream_last_seen_seconds`。 如果上游需要更长的恢复时间,请调整 `reconnect.backoff_max_ms`。
连接信息
猜你喜欢
暂无推荐服务器