mcpstead

mcpstead

MCP Gateway that aggregates multiple upstream MCP servers into a single endpoint with persistent connections, tool registry, and authentication.

Category
访问服务器

README

mcpstead

CI npm version Crates.io License

MCP Gateway

One downstream /mcp endpoint that fronts many upstream MCP servers. Persistent upstream connections with reconnect, tool registry with qualified names, JSON or SSE responses based on client Accept, per-upstream auth, Prometheus metrics.

Install

# npm (macOS, Linux, WSL)
npm i -g @ahkohd/mcpstead

# homebrew (macOS, Linux)
brew install ahkohd/tap/mcpstead

# cargo
cargo install mcpstead --locked --force

# verify
mcpstead --version

Quick start

# 1. write a config
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. run
mcpstead --config ~/.config/mcpstead/config.yaml

Then point any MCP client at http://127.0.0.1:8766/mcp.

Docker

docker build -t mcpstead .
docker run --rm \
  -p 8766:8766 \
  -v "$PWD/config:/etc/mcpstead:ro" \
  mcpstead

The Dockerfile installs from crates.io.

HTTP API

Method Path Purpose
POST /mcp MCP over HTTP JSON-RPC
GET /mcp returns 405
DELETE /mcp terminate downstream session
GET /health upstream status, tool counts, last-seen, reconnects
GET /metrics Prometheus text format
POST /-/reload reload config without restart

MCP client setup

Local, no-auth:

mcpstead:
  url: http://127.0.0.1:8766/mcp
  tools:
    resources: false
    prompts: false

Bearer auth:

mcpstead:
  url: http://127.0.0.1:8766/mcp
  headers:
    Authorization: Bearer ${MCPSTEAD_BEARER_TOKEN}
  tools:
    resources: false
    prompts: false

Tools appear with qualified names - <server>__<tool> - so multiple upstreams can ship overlapping tool names without collision.

Auth

Downstream (clients to mcpstead)

Default is no auth:

mcp:
  auth:
    mode: none

Bearer auth:

mcp:
  auth:
    mode: bearer

Token comes from env:

export MCPSTEAD_BEARER_TOKEN='replace-with-strong-secret'
mcpstead --config ~/.config/mcpstead/config.yaml

Clients send Authorization: Bearer <token>. Missing or wrong token returns 401. mcp.auth.bearer_token in config is rejected at startup.

/health and /metrics stay open regardless of auth mode - for monitoring without exposing the token.

Upstream (mcpstead to MCP servers)

Per-server in the servers list. Three modes:

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 resolves the named env var at startup and on config reload.

Config

Set the config path with --config <path> or the MCPSTEAD_CONFIG env var.

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      # if true, gateway won't start without this upstream
    auth: none
    reconnect:
      max_attempts: 0    # 0 = infinite
      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

Hot reload

mcpstead reloads its config without restart on:

  • SIGHUP (systemctl reload mcpstead or kill -HUP <pid>)
  • POST /-/reload (gated by bearer auth in bearer mode)

Hot-reloadable:

  • upstream list
  • per-upstream auth, headers, quirks, reconnect, tools, URL, protocol, and TLS settings
  • mcp.auth.mode and MCPSTEAD_BEARER_TOKEN
  • metrics.enabled

Restart required:

  • host
  • port
  • logging.level
  • mcp.session.*

Reload is best-effort. Bad config is rejected and ignored; the running config stays in place. Check logs and mcpstead_config_reloads_total{result="error"} for failures.

MCP session config keys

  • mcp.session.idle_ttl_seconds - evict inactive sessions after this many seconds (default 3600)
  • mcp.session.gc_interval_seconds - idle-session GC wake interval (default 60)
  • mcp.session.shutdown_grace_seconds - max shutdown accounting sweep time (default 5)

Per-upstream config keys

  • name - required, used as tool prefix
  • url - required, MCP endpoint
  • protocol - streamable | sse | auto (default auto)
  • required - block startup if upstream fails to initialize (default false)
  • auth - none, bearer (with token_env), or headers map
  • reconnect.max_attempts - 0 = infinite (default)
  • reconnect.backoff_base_ms / backoff_max_ms - exponential backoff bounds
  • tools.ttl_seconds - refresh cached tools/list after this interval
  • tls_skip_verify - disable TLS cert checks for this upstream (default false, use only for trusted local nets)
  • quirks.normalize_sse_events - strip event: lines from upstream SSE responses
  • quirks.inject_accept_header - override the Accept header sent upstream

Observability

Metrics

/metrics exposes Prometheus-format counters, gauges, and histograms. Label cardinality assumes a small bounded set of upstreams and tools; tool-call series are keyed by (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="..."}

Scrape config

- job_name: mcpstead
  metrics_path: /metrics
  static_configs:
    - targets: ['mcpstead:8766']

Health check

curl http://127.0.0.1:8766/health

Returns JSON: per-upstream connection state, tool counts, last successful contact, reconnect counts, last error.

Troubleshooting

  • All tools list empty - at least one upstream failed to initialize. Check /health for per-server status; check upstream URL, auth, and reachability.
  • Sporadic SSE parse failed - upstream sends an SSE dialect mcpstead doesn't recognize. Try quirks.normalize_sse_events: true for that server, or set quirks.inject_accept_header: 'application/json' to force JSON.
  • tools/call returns auth error - upstream rejected the bearer token. Confirm token_env resolves to the right value at startup; check upstream's expected header name.
  • TLS handshake errors against a self-signed upstream - set tls_skip_verify: true on that server. Only safe on trusted local networks.
  • 401 from /mcp with mode bearer - client missing or sending wrong Authorization: Bearer <token>. Verify MCPSTEAD_BEARER_TOKEN matches what the client sends.
  • Upstream goes red in /health repeatedly - check mcpstead_upstream_reconnects_total and mcpstead_upstream_last_seen_seconds. Tune reconnect.backoff_max_ms if the upstream needs longer recovery.

推荐服务器

Baidu Map

Baidu Map

百度地图核心API现已全面兼容MCP协议,是国内首家兼容MCP协议的地图服务商。

官方
精选
JavaScript
Playwright MCP Server

Playwright MCP Server

一个模型上下文协议服务器,它使大型语言模型能够通过结构化的可访问性快照与网页进行交互,而无需视觉模型或屏幕截图。

官方
精选
TypeScript
Magic Component Platform (MCP)

Magic Component Platform (MCP)

一个由人工智能驱动的工具,可以从自然语言描述生成现代化的用户界面组件,并与流行的集成开发环境(IDE)集成,从而简化用户界面开发流程。

官方
精选
本地
TypeScript
Audiense Insights MCP Server

Audiense Insights MCP Server

通过模型上下文协议启用与 Audiense Insights 账户的交互,从而促进营销洞察和受众数据的提取和分析,包括人口统计信息、行为和影响者互动。

官方
精选
本地
TypeScript
VeyraX

VeyraX

一个单一的 MCP 工具,连接你所有喜爱的工具:Gmail、日历以及其他 40 多个工具。

官方
精选
本地
graphlit-mcp-server

graphlit-mcp-server

模型上下文协议 (MCP) 服务器实现了 MCP 客户端与 Graphlit 服务之间的集成。 除了网络爬取之外,还可以将任何内容(从 Slack 到 Gmail 再到播客订阅源)导入到 Graphlit 项目中,然后从 MCP 客户端检索相关内容。

官方
精选
TypeScript
Kagi MCP Server

Kagi MCP Server

一个 MCP 服务器,集成了 Kagi 搜索功能和 Claude AI,使 Claude 能够在回答需要最新信息的问题时执行实时网络搜索。

官方
精选
Python
e2b-mcp-server

e2b-mcp-server

使用 MCP 通过 e2b 运行代码。

官方
精选
Neon MCP Server

Neon MCP Server

用于与 Neon 管理 API 和数据库交互的 MCP 服务器

官方
精选
Exa MCP Server

Exa MCP Server

模型上下文协议(MCP)服务器允许像 Claude 这样的 AI 助手使用 Exa AI 搜索 API 进行网络搜索。这种设置允许 AI 模型以安全和受控的方式获取实时的网络信息。

官方
精选