Skip to main content
Glama

Multi-Model Broker

CI License: MIT Node Tests MCP TypeScript

中文 · English

面向 MCP 的多模型任务代理层(broker):MCP 客户端(ChatGPT、Codex 或任何 harness)把任务交给它,它按能力路由(router)到本机或云端的多个 worker(本地 Codex、本地 Claude Code、DeepSeek、GLM、Gemini、mock),返回统一结构的结果,并留下可审计的 trace。读/写能力严格分离,写型 worker 只能经各自的写工具到达。

特性

  • 两种 MCP 传输:stdio(本地 harness)与 Streamable HTTP(回环 + 共享密钥,可选 SSE 应答)

  • 两种 profile(8 / 8 个工具)、9 个注册工具,读/写严格分离:写型 worker 只能经 run_agent 到达,且必须先点名 worker(本仓库不预置默认模型)

  • 确定性路由:按 requirements(coding / long_context / low_cost / chinese / batch 等)+ 能力声明选择 worker,链式回退;显式指定 worker 永不被改写

  • 长任务、并发、幂等与审计:delegate_batch 一次最多 8 个任务并行,部分失败也保留结果;idempotencyKey 去重防二次计费;trace 默认不落 prompt 原文

  • 公网可达但不开端口:relay 让本机主动外连自建中继,远程客户端经固定 URL 访问

Related MCP server: agent-orchestrator

为什么做这个

把多个模型接到同一个 MCP 接口后面:supervisor(如 ChatGPT Pro)只负责规划与复核,broker 负责把任务路由到最合适的 worker,并处理并发、追踪与审计。每个 provider 只需实现一次适配(adapter),接入侧换 transport(stdio / HTTP / 中继)即可;读路径与写路径的凭据和工具面严格分开,写文件这类有副作用的操作不会被藏进"只读"工具里。

架构

Multi-Model Broker 架构图:MCP 客户端 → 传输(本地 stdio / 公网中继)→ Broker Core(Router / Scheduler / TaskManager / TraceStore)→ Provider adapters(只读 worker 与写型 worker)

┌──────────────────────────────────────────────────────────┐
│  MCP 客户端(ChatGPT / Codex / 任意 harness)              │
└──────────────────────────┬───────────────────────────────┘
                           │ MCP(stdio / Streamable HTTP / relay)
                           ▼
┌──────────────────────────────────────────────────────────┐
│  MCP 接口层        src/interfaces/mcp                     │
│  tools · schemas · profiles · annotations · http · server │
└──────────────────────────┬───────────────────────────────┘
                           │ 只调用 Broker,不直接接触 provider
                           ▼
┌──────────────────────────────────────────────────────────┐
│  Broker Core       src/core                               │
│  broker · router · scheduler · task-manager · trace-store │
│  policy · config                                          │
└──────────────────────────┬───────────────────────────────┘
                           │ 统一的 WorkerRequest / WorkerResult
                           ▼
┌──────────────────────────────────────────────────────────┐
│  Provider 适配层    src/providers                          │
│  mock · openai-compatible(DeepSeek/GLM) · gemini          │
│  codex · claude-code                                      │
└──────────────────────────────────────────────────────────┘

分层:Core 不认识 ChatGPT,provider 不认识 MCP。broker 前面的 transport 是可替换的(今天是 stdio,明天可以是远程 MCP 端点或 harness 插件);provider 只认统一的请求/结果结构,不感知自己正被哪个 MCP 客户端驱动。

技术栈与要求

  • Node.js >= 22.5(用内置 node:sqlite),TypeScript,零原生模块、无需构建工具链

  • 依赖:@modelcontextprotocol/sdk ^1.30、zod ^4.6、yaml ^2.9

  • 测试:vitest 全量离线运行,不花任何配额(provider 用注入的假实现);用例数会随提交变化,以 CI 徽章为准

快速开始

npm ci
npm run check && npm test && npm run build
node dist/cli/index.js doctor                      # 配置/存储/provider 健康
node dist/cli/index.js mcp-stdio  --profile local-full          # 本地 stdio 接入
node dist/cli/index.js mcp-http   --profile chatgpt-agent \
  --token-env BROKER_HTTP_TOKEN --port 8789                     # 回环 HTTP 接入
node dist/cli/index.js relay setup|start|status|stop|url         # 公网(自建中继)
  • npm run check:类型检查(tsc --noEmit)

  • npm test:vitest,离线

  • npm run build:tsc,产出 dist/

用自己的环境跑起来

这个项目不假设任何特定的目录布局,也没有内置任何凭据:

  1. 至少一个 API workercp .env.example .env,填入一个 *_API_KEY(DeepSeek / GLM / Gemini 等), 然后在 config/providers.yaml 里启用对应 provider;只想先看效果就用 config/providers.mock.yaml(纯假实现,零成本)。

  2. 可选:本地 agent(写型)codex 走本机 Codex 登录(~/.codex,broker 不读也不复制任何凭据); claude-code 需要一个 Anthropic 兼容端点与令牌,两种给法任选—— export ANTHROPIC_BASE_URL=... ANTHROPIC_AUTH_TOKEN=...,或把它们写进一个文件再用 options.envScript / $CLAUDE_ENV_SCRIPT 指过去(runner 会在登录 shell 里 source 它,从不把密钥放上命令行), 默认位置是 ~/.config/multimodel-broker/claude-code.env。 Claude Code CLI 本身用自己的方式安装(claude 得能被解析到;服务化运行时建议在配置里写绝对路径)。

  3. 可选:公网接入node dist/cli/index.js relay setup 会引导你部署自建中继(Cloudflare Worker + Durable Object), 本机作为客户端主动外连,不开入站端口。

doctor 会逐个报告 provider/workspace 是否就绪,并在缺东西时说明缺什么;它从不打印任何密钥。

MCP 工具

工具

readOnlyHint

destructiveHint

用途

ping

true

false

健康探测,返回服务名;委托前先测连通性

list_workers

true

false

列出 worker 的启用/健康、provider、model、认证方式、能力、并发上限与不可用原因

run_worker

true

false

在显式指定的 API worker 上跑任务并返回答案(只消耗算力、返回文本,不写文件/仓库/第三方对象)

run_agent

false

true

唯一的写型本地 agent 入口;workercodex / codex-win / claude-code)首次必须点名,之后可省略(沿用上次选择)。三者都以全自动模式读写文件、执行命令/测试;workspace 对 Codex 是 sandbox 写入边界,对 claude-code 只是起始目录

delegate

true

false

按 requirements 确定性路由并运行,返回所选 worker、路由原因、路由审计与结果/任务 id

delegate_batch

true

false

一次调用内并行跑 1–8 个独立任务(mode 'parallel',failurePolicy 'collect_all'),部分失败保留结果

get_task

true

false

按 id 取任务状态、完成计数、子任务与(可选)已完成结果,用于轮询

get_trace

true

false

取审计 trace(goal、路由原因、provider/model、时间、工具事件、用量、错误)

cancel_task

true

false

取消排队中或运行中的任务(连同子任务);本地运维用,默认不下发给远程客户端

另有开发探针 ui_probe:仅在 BROKER_UI_PROBE=1 时注册(渲染服务端内联 UI 卡片并回报客户端是否渲染,不调用 worker、零成本),不属于任何 profile 的默认面。

写型 worker 只能经 run_agent 到达:只读工具(run_worker / delegate / delegate_batch)拒绝写型 adapter(codex-sdkclaude-code);写型 worker(本机 Codex / Windows Codex / 本地 Claude Code)只能经 run_agent 到达,该工具标注 readOnlyHint: false, destructiveHint: true,且首次必须点名 worker(本仓库不替调用方选模型)。被拒绝的 worker 不会被记住,所以不会留下一个用不了的选择。

两个 profile:

  • chatgpt-agent(默认)—— 7 个只读工具 + run_agent(8 个工具)。远程客户端用这个;能不能真写取决于该实例本地是否启用了写型 worker

  • local-full —— 只读 7 个 + cancel_task(8 个工具,本地 harness 用;唯一能取消任务的入口,默认不下发给远程)

想要"只读的远程入口",做法是给这个实例配一份不含写型 worker 的配置(写工具虽然会列出,但一调用就被拒),而不是靠 profile 名——profile 只决定工具面,决定不了权限。

模型选择是粘性的,且随时可切换

  • 点名即记住run_agent / run_worker 里显式给 worker,这个选择就成为该工具的默认,后续调用可以省略 worker 而继续用它

  • 显式即切换:换一个 worker 就换了,且新选择成为新的默认——切换永远只是一次显式参数

  • model 覆盖同理:按 worker 记住,之后省略就沿用;传 model: "auto" 则忘掉覆盖、回到该 worker 配置里的模型

  • 不是静默的list_workers 会给当前被记住的 worker 标上 defaultFor: ["run_agent"],trace 里也会有 choice.remembered 事件

  • 偏好存在实例自己的库里,两个实例互不影响;本仓库不预置任何默认模型——模型是使用者的配置

三种接入方式

方式

命令

适用

本地 stdio

node dist/cli/index.js mcp-stdio --profile local-full

本机 harness(Codex / Claude Code / Hermes / MCP Inspector)

回环 Streamable HTTP + 共享密钥

node dist/cli/index.js mcp-http --profile chatgpt-agent --token-env BROKER_HTTP_TOKEN --port 8789

ChatGPT connector,或任何无法 spawn 进程的客户端(只读实例的做法是这份配置不启用写型 worker)

自建中继(relay)

`node dist/cli/index.js relay setup

start

中继方式下,本机作为客户端主动外连到自建中继(Cloudflare Worker + Durable Object),不开端口;中继不理解业务语义,只转发 device + channel 的请求/响应,且绝不自动重放写请求(断线返回 outcome_unknown,由上层 idempotencyKey 恢复)。

安全模型

  • 密钥只从环境 / .env 读,不进配置文件、不进 argv;需要临时落盘时用 0600 临时文件,用完即删

  • prompt 默认不落库:trace.storePrompts: false,只存长度与 sha256,不存原文

  • 输出脱敏:密钥 / token 形状的字符串一律替换

  • 敏感路径拒绝表:~/.ssh~/.aws~/.hermes~/.codex/mnt/c/Windows …;可选 allowAnyWorkspace

  • 写盘需要本地显式开启:sandbox: workspace-write + allowWritableSandbox: true

  • 幂等键防二次计费:idempotencyKey 去重,重复提交只等待不重跑

  • 读/写凭据分离:只读路径走各提供方的 API key(环境变量),写型本地 agent 走本机已有的登录态(Codex 订阅 / 本地 Claude Code CLI),互不共享

生产可用性边界 / 已知限制

  • Claude Code 自报的 total_cost_usd 按 Anthropic 价目,比 DeepSeek 实收高约 500× —— 绝不当作账单

  • 服务化(systemd)下有两个 PATH 陷阱:spawn 要用 process.execPath;Claude Code CLI 要写绝对路径

  • Windows 与 WSL 是两套 Codex home(~/.codex vs C:\Users\<you>\.codex),应用侧模型清单靠 model_catalog_json

  • 中继的 SSE 长连接约 60 秒会被拆,重连窗口内的调用返回 502,原样重试即可

  • ChatGPT 侧不展示 MCP 注解(readOnlyHint 等),工具描述才是模型能看到的依据

仓库结构

src/                        核心(core / providers / interfaces/mcp / storage / security / relay / cli)
src/core/                   broker · router · scheduler · task-manager · trace-store · policy · config
src/providers/              mock · openai-compatible(DeepSeek/GLM) · gemini · codex · claude-code
src/interfaces/mcp/         tools · schemas · profiles · annotations · http · server
src/security/               redaction · secrets · paths
src/relay/                  出站中继客户端与连接管理
relay/                      中继侧(Cloudflare Worker + Durable Object)与测试
tests/                      vitest 用例(unit + integration)
config/                     示例配置(providers.*.example.yaml / *.mock.yaml / localmcp.example.json)
docs/                       架构、安全、远程接入、ADR(decisions/)、评测 fixtures
integrations/claude-code/   本地 Claude Code runner(headless)+ 测量脚本
integrations/codex-local-bridge/  自研 Responses API 最小桥(实验性,接入外部模型用)
scripts/                    冒烟/往返/中继自检脚本
plugins/                    Codex 插件包装层
.github/workflows/ci.yml    离线 CI

开发

  • 测试:vitest 全量离线运行,不花配额;用例数以 CI 徽章为准

  • CI:GitHub Actions,离线跑 check + test + build(不做需要真实配额/凭据的 smoke)

  • 贡献方式见 CONTRIBUTING.md

  • 安全报告见 SECURITY.md

License

MIT © 2026 Bowen-studying

Related MCP Connectors

Related MCP Servers