Skip to main content
Glama
Frz2004

MCP-ToolHub

by Frz2004

MCP-ToolHub

企业级 MCP 工具中台 —— 依据 MCP 2026-07-28 规范自研:6 个 MCP Server + 无状态 MCP 网关 + 混合检索按需挂载 + OAuth 2.1 / RBAC / MRTR 安全体系 + 熔断/舱壁/重试韧性层 + DAG 工作流编排,为 AI Agent 提供统一、安全、可观测的工具接入层。

已开源 4 个 MCP Server(kb / db / repo / browser,terminal 与 ticket 为企业内部版演示实现,接口同构)。

架构

 Agent (LangGraph / 自研 Harness / IDE / MCP Client)
   │  Streamable HTTP (Mcp-Method / Mcp-Name 头路由)      │ stdio 桥(本地)
   ▼                                                       ▼
┌────────────────────── 无状态 MCP 网关 (水平扩容) ──────────────────────┐
│  OAuth 2.1 (issuer/JWKS) ─ RBAC 白名单 ─ 金丝雀版本路由                 │
│  MRTR 人工二次确认 ─ 速率配额(Redis) ─ 两级结果缓存(L1 LRU + L2 Redis)  │
│  韧性层: 熔断 + 舱壁 + 全抖动重试 ─ 凭证托管代理层                      │
│  截断/脱敏 ─ MCP Apps 富结果渲染 ─ 审计 JSONL ─ Prometheus             │
│  DAG 工作流引擎(拓扑并行 + 模板引用 + 检查点恢复)                        │
└──────┬──────────┬──────────┬──────────┬──────────┬──────────┬────────┘
   kb Server   db Server  repo Server  ticket Srv  browser Srv  terminal Srv
  知识库检索   只读SQL     代码仓库     工单系统     浏览器       沙箱终端
        (23 个原子工具 + 60 个实例级变体 = 83 工具目录)

核心特性

1. 无状态网关(Streamable HTTP)

  • 按 MCP 2026-07-28 新版规范:无 initialize 握手、无 Mcp-Session-Id,请求自携带协议版本(Mcp-Protocol-Version)与客户端能力

  • 可部署在普通轮询负载均衡后水平扩容,无需会话粘滞与共享会话存储

  • 网关直接解析 Mcp-Method / Mcp-Name 请求头完成路由、限流与计量,无需解包 JSON-RPC 报文;未带头的老客户端自动回退 body 解析(兼容模式)

  • tools/list 返回 ttlMs + catalogVersion,客户端据此缓存工具目录,冷启动拉取开销下降约 70%

  • 本地场景 python -m clients.stdio_bridge 一键切换 stdio 传输

2. 混合检索 + 按需挂载(解决工具规模膨胀)

83 工具全量注入 schema 约占 5k token(len(json)/4 估算)。按需挂载机制:

  1. 每轮按用户意图经 BM25 + 512 维稳定哈希向量 + 加权 RRF 融合(k=60)检索 Top-K 工具(默认 8 个)

  2. 中英混合分词(英文按词 / 中文二元组)+ ~90 条领域同义词词典("提交记录"→log、谁动过配置→log)+ 泛化/点名双加成

  3. 仅将选中工具 schema 注入上下文,其余保留为可检索目录(常驻元工具 toolhub.search_tools 支持换装)

在 105 条标注评测集上的真实测量(scripts/benchmark.py retrieval 复现):

检索器

accuracy@1

recall@3

MRR

两阶段(v1.0)

20.9%

43.8%

0.339

混合 RRF(v1.1)

95.2%

99.1%

0.968

单查询延迟 mean 1.8ms / p95 2.0ms(纯 Python、零第三方依赖); 按需挂载实测 5.1k → 0.5k token(-89%)。完整迭代过程与方法论见 docs/benchmarks.md

3. 网关韧性层(熔断 / 舱壁 / 重试)

  • 熔断器:按工具粒度,CLOSED → OPEN(连续上游故障)→ HALF_OPEN(恢复窗口后探测);OPEN 时快速失败 43001 并附 retryAfterMs

  • 错误分类:业务错误(校验/未找到)是确定性失败,不熔断不重试UpstreamError/超时按瞬时故障处理

  • 舱壁隔离:per-tool 信号量,槽满立即拒绝 43002 不排队,单个工具打挂不拖垮整池

  • 全抖动重试:仅幂等工具,sleep = random(0, base·2^attempt),防止重试风暴同步化

  • /admin/resilience 实时内省熔断与缓存状态

4. 两级结果缓存 + 目录 ETag

  • tools/call 结果缓存:L1 进程内 LRU(1024 条)+ L2 Redis(redis.asyncio 协议注入),键 = sha256(工具+参数+目录版本);仅缓存幂等只读工具

  • stale-while-revalidate:条目过期但仍在 stale 窗口内时立即返回旧值并后台刷新,P99 不为缓存刷新买单

  • tools/list ETag/304:目录指纹(含金丝雀路由状态)不变则 304,客户端继续用本地缓存

  • 命中率 / stale 服务数经 /admin/resilience 与 Prometheus tcache_events 暴露

5. 安全体系

机制

认证

OAuth 2.1:issuer 校验、JWKS 验签(缓存 + 失败刷新)、exp/aud/nbf

授权

RBAC 租户级工具白名单(通配符)+ scope 校验

凭证

统一托管在代理层,由 MCP Server 代取注入 ToolContext模型全程不接触明文密钥;出口兜底脱敏

人审

危险工具(danger=high)经 MRTR 返回 input_required + HMAC 确认令牌(绑定 工具+参数哈希+用户+TTL),人工批准后带令牌重放才执行

审计

全量 tool call 写 JSONL 审计日志(含 redacted 参数、延迟、重试、截断信息)

6. DAG 工作流编排(固定套路不再逐工具决策)

Agent 逐工具决策在稳定流程上是浪费。声明式工作流(mcp_toolhub/orchestration/):

  • 拓扑执行:节点声明 depends_on,无依赖节点 asyncio.gather 并行,延迟取决于关键路径

  • 输出模板引用:参数可写 {{node.path.to.value}}(整串保留原类型 / 嵌入做内插),上游输出跨节点注入

  • MRTR 感知:高危节点返回 input_required 时整个流暂停,状态序列化为检查点;人工确认后从断点恢复,不重跑已完成节点

  • 检查点跨副本:FlowState 纯 JSON,恢复无需粘滞到原网关副本(无状态原则:状态在调用方)

内置示例流:incident_triage(知识库检索 ∥ 失败调用聚合 → 自动建单)、terminal_debug(跨服务模板 + MRTR 暂停恢复):

curl -X POST http://127.0.0.1:8080/admin/flows/incident_triage/run \
  -H "Authorization: Bearer dev-token-1"

7. 工具版本金丝雀 + MCP Apps 富结果

  • 金丝雀发布:同一工具名注册多实现版本(register_version / promote / rollback),按权重路由;目录指纹包含版本路由状态,切流瞬间客户端 ETag 与结果缓存全部自动失效。路由分布经 Prometheus tool_version_calls 打点

  • MCP Apps 富结果:纯函数把 tool result 渲染为 resourceLinks / 列表卡片 / 表格 / 操作按钮(按钮 = 带参数预填的合法 tools/call),声明式、可随缓存复用、危险操作按钮联动 MRTR

8. 可观测

  • 每次 tool call 记录延迟、成功率、重试、缓存命中、版本路由,Prometheus 指标 + Grafana 看板(deploy/grafana-dashboard.json

  • /metrics 指标;/healthz/admin/tools/admin/resilience/admin/flows 运维端点

9. 工具设计约定(6 Server 通用)

  • 统一命名<server>.<action>[_<instance>]

  • 原子性:一个工具只做一件事,组合交给上层 Agent

  • 正交性:工具间不重复覆盖动作空间;错误码分层(网关 40001-40999 / 工具 41001-41999 / MRTR 42001)

  • 输出可截断:verbose 字段丢弃 → 文本窗口截断 → 硬截断 + 续读 token,避免长结果撑爆上下文

6 个 MCP Server

Server

工具(原子)

说明

kb

search / fetch / list_sources / related

内部知识库检索(后端协议化,内置内存实现)

db

query / list_tables / explain

只读 SQL:强制 SELECT-only、行数上限

repo

search_code / read_file / list_dir / log

代码仓库(本地目录 / Git Provider 适配)

ticket

search / get / create / update

工单系统(写操作 danger=medium,入审计)

browser

navigate / extract_links / extract_text / find_in_page

网页抓取,SSRF 防护 + 站点白名单

terminal

exec / write_file / read_file / list_dir

沙箱终端:命令 allowlist、超时、danger=high 走 MRTR

实例级变体由 mcp_toolhub/servers/variants.json 配置驱动(如 db.query_analyticsrepo.search_code_gateway),热加载、复用底层 handler。

快速开始

# 安装
pip install -e ".[test]"          # LangGraph 集成: pip install ".[langgraph]"

# 启动网关(dev 模式自带静态令牌)
cp .env.example .env
python -m uvicorn mcp_toolhub.gateway.app:app --port 8080

# 头路由调用(不解包 JSON-RPC)
curl -s http://127.0.0.1:8080/mcp \
  -H "Authorization: Bearer dev-token-1" \
  -H "Mcp-Method: tools/call" -H "Mcp-Name: kb.search" \
  -H "Mcp-Protocol-Version: 2026-07-28" \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"kb.search","arguments":{"query":"oauth"}}}'

# 工具目录(带 ttlMs 缓存约定)
curl -s http://127.0.0.1:8080/mcp \
  -H "Authorization: Bearer dev-token-1" -H "Mcp-Method: tools/list" -d '{}'

# 本地 stdio 一键切换(给 IDE / Claude Desktop 用)
python -m clients.stdio_bridge --url http://127.0.0.1:8080 --token dev-token-1

# 测试
pytest -q

Agent 接入

LangGraph

from mcp_toolhub.adapters.gateway_client import GatewayClient
from mcp_toolhub.adapters.tool_mounting import ToolMounter
from mcp_toolhub.adapters.langgraph_adapter import to_langchain_tools
from mcp_toolhub.servers.catalog import build_catalog

mounter = ToolMounter(build_catalog(), top_k=8, token_budget=2048)
client = GatewayClient("http://127.0.0.1:8080", token="dev-token-1")

# 只挂载与当前意图相关的 Top-K 工具(而不是 80+ 全量)
tools = to_langchain_tools(mounter, client, query="查询数据库表结构")
# tools 直接喂给 langgraph.prebuilt.ToolNode

自研 Harness

from mcp_toolhub.adapters.harness_adapter import Harness, EchoModelClient

harness = Harness(mounter, client, model=MyModelClient(), max_steps=8)
run = await harness.run("统计上周工具调用成功率")
if run.paused_for_confirmation:      # MRTR: 等人工确认
    run = await harness.resume(run, confirmationToken=approved_token)
print(run.to_dict())

部署

docker compose up -d   # gateway x2(无状态,轮询LB) + redis + prometheus + grafana

生产检查清单:

  1. DEV_MODE=0,配置 OAUTH_ISSUER(JWKS 端点)

  2. MRTR_SECRET 换成强随机值;凭证接 Vault/KMS(实现 CredentialSource 协议)

  3. REDIS_URL 指向共享 Redis(限流与审计 Stream)

  4. Grafana 导入 deploy/grafana-dashboard.json

项目结构

mcp_toolhub/
├── common/        统一错误码 / 输出截断 / Tool 抽象
├── servers/       6 个 MCP Server + variants.json 目录扩展
├── gateway/       无状态网关: app / router / registry(金丝雀) / resilience(熔断舱壁重试) / cache(两级) / apps(富结果) / ratelimit / metering / audit / credentials
├── auth/          OAuth 2.1 校验 / RBAC 策略
├── mrtr/          人工二次确认(HMAC 令牌)
├── retrieval/     混合检索: BM25 + 哈希向量 + 加权 RRF + 评测集(105 case)
├── orchestration/ DAG 工作流引擎 + 内置流
└── adapters/      工具按需挂载 / GatewayClient / LangGraph / Harness
clients/           stdio 桥
scripts/           benchmark.py(本 README 与 docs/benchmarks.md 数据来源) / smoke
tests/             pytest 82 用例(检索评测门/韧性/缓存/编排/金丝雀/网关端到端)
deploy/            prometheus / grafana 看板

License

MIT