关于 Legion MCP
Legion 是面向多模型协作工作流的 MCP 原生模型议事系统。它将配置的模型分别公开为工具,并提供 quorum 和预设议事组,用于辩论、评审团及其他结构化讨论。
主要功能
- 01每个已配置模型对应一个 MCP 工具
- 02跨多个模型发起 quorum 调用
- 03用于结构化讨论的预设议事组
- 04顺序、并行、私密和独立模式
- 05可热重载的模型与议事组配置
适用场景
- 01围绕问题开展多模型辩论
- 02收集盲审小组答复并投票
- 03将预设议事组用于代码评审
- 04通过多轮模型讨论完善答复
MCP 相关
将每个已配置模型、quorum 功能和各预设议事组分别作为 MCP 工具公开。它通过 OpenAI Responses API 线格式调用模型端点,可直接连接或经兼容网关转发。
配置
本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。
npx -y legion-mcpMCP_TRANSPORTRun the installed package over MCP stdio (the binary defaults to HTTP).DEFAULT_BASE_URLDefault OpenAI Responses-compatible API root for model files that omit baseUrl.DEFAULT_API_KEY密钥Default provider or gateway key for model files that omit apiKey.ALLOW_NO_MODELSBoot without any config/models/*.json. The package ships only key-free examples, which the scanner ignores, so this must stay true until a real model file exists beside the working directory.OPENAI_API_KEY密钥Optional. A model file's apiKey may be "env:VAR" to read the key from an environment variable named VAR at startup; name those variables here as needed. The bundled gpt example reads this one.MAX_ROUNDSMaximum discussion rounds accepted by a council run.TOKEN_BUDGET密钥Optional soft cumulative token budget for a council run.DYNAMIC_ROLESAllow callers to define ad-hoc council roles inline.LOG_LEVELLogging verbosity. Logs are written to stderr for stdio safety.ASYNC_TOOLSRun councils in the background: council tools return a job handle and poll/cancel tools are exposed. Jobs live in process memory only.TRUST_PROXY_AUTH密钥Bind async jobs to the caller named by X-MS-CLIENT-PRINCIPAL-* headers. Only safe behind an ingress that strips client-supplied copies.本地服务需要在你的电脑上安装运行环境(如 Node.js 18+)。各客户端的配置文件位置不同,请参考客户端文档。
工具
17收录的工具列表 · 更新于 3 周前
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| models | array | 是 | 两个或更多选择器:"model" 或 "model:role"。同一模型可通过不同角色多次出现(例如 "gpt:judge" 和 "gpt:skeptic");每个选择器都是独立的发言者。可用模型: |
| rounds | integer | 否 | 讨论轮数(默认 1;上限由 MAX_ROUNDS 设置) |
| objectives | object | 否 | 团队运行的可选逐团队目标:从团队标签到该团队共同目标的映射。使用 model:role@team 标记发言者;每个已标记团队都必须在此设置目标。每位队友都能看到自己团队的目标;综合器可以看到所有目标。 |
| tokenBudget | integer | 否 | 整个运行过程的可选软性累计 token 预算(覆盖 TOKEN_BUDGET)。顺序模式会在轮次之间检查;并行模式会在轮次边界检查,因此触发前可能超出预算。超出后会妥善跳过剩余轮次,但仍会执行综合。这不是硬性上限。参见 structuredContent.budget。 |
| prompt | string | 是 | 要发送给模型的问题或任务 |
| context | string | 否 | 可选的补充文本(代码、文档、数据),作为单独的区块附加到提示词 |
| system | string | 否 | 可选的系统提示词/指令 |
| temperature | number | 否 | 可选的采样温度 |
| maxTokens | integer | 否 | 可选的最大输出令牌数。预算需同时涵盖可见回答和任何隐藏推理:推理模型会在输出文本前消耗令牌进行思考,因此上限过紧可能导致没有空间输出可见回答。为推理模型预留充足余量;在多轮 quorum 中应进一步提高上限,因为记录会随着每轮增长。 |
| roles | object | 否 | 此 quorum 调用的临时角色:角色名称到指令的映射。在 models 中使用 "model:role" 引用。 |
| mode | enum | 否 | 每轮发言者能看到多少对话记录:sequential(默认;可见之前的所有发言)、parallel(仅可见之前已完成的轮次,看不到同轮参与者的发言)、private(仅可见自己的先前发言)、independent(直到综合阶段前,只能看到提示词)。sequential | parallel | private | independent |
| synthesize | string | 否 | 综合发言轮次的选择器。综合者不会在常规轮次发言,只会在其综合时段发言。 |
| synthesizeEvery | any | 否 | 综合者的运行时机(仅在设置 synthesize 时有效):数字 N 表示每 N 轮综合一次(并始终在最后综合);1 = 每轮;0 = 仅在结束时综合。"end" 是 0 的别名(默认值)。 |
| frame | string | 否 | 可选的中立主持者选择器:负责在第 1 轮开场,通过明确讨论重点并塑造问题来引导讨论的主持者。不得表明立场,也不能带有团队标签。与负责收尾的 synthesize 相对应;主持者负责开场。 |
| reframeEvery | any | 否 | 主持者再次发言的时机(仅在设置 frame 时有效):数字 N 表示每 N 轮重新设定框架,以引导/调整讨论方向;0 或 "end"(默认值)表示仅在开场设定框架。 |
| closingStatements | boolean | 否 | 为 true 时,每位轮次发言者都会在综合前,针对完整对话记录给出一条最终并行陈述。需要设置 synthesize。 |
| vote | string | 否 | 可选的匿名同伴投票说明。设置后,每位参与的轮次发言者都会投一张隐藏选票,从候选项菜单中选择一个数字(除非 allowSelfVote 为 false,否则包含自己的席位),或选择 0 弃权。对话记录中只会添加按候选标签统计的匿名票数(不会显示谁投给了谁);选票会从日志中删去。该机制仅提供建议,不会淘汰候选人或选出获胜者;如果希望据此采取行动,请在提示词中指示综合者/裁判。 |
| voteEvery | any | 否 | 匿名投票的触发时机(仅在设置 vote 时有效):数字 N 表示每 N 轮投票一次(始终包括最后一轮);0 或 "end"(默认值)表示仅在最后投票一次。 |
| voteVisibility | enum | 否 | 投票结果的显示范围:"aggregate"(默认)仅显示计数;"ballots" 还会显示匿名化后的选票文本(不显示投票者身份)。aggregate | ballots |
| allowSelfVote | boolean | 否 | 投票者自己的席位是否显示在其投票菜单中(仅在设置 vote 时有效)。默认值为 true(投票者可以选择自己,如同真实选举)。对于自投属于钻规则空子的同伴评判投票(例如 "vote the weakest peer"),请设为 false。 |
详细介绍
Legion
“我乃军团,因为我们人数众多。”
一个原生支持 MCP 的模型议会。Legion 将 大模型作为独立工具公开,并将它们编排成辩论、陪审团、盲评小组、私密精进 挑战、研讨会和自定义多模型审议。
每个模型都通过 OpenAI Responses API 线路格式调用。可直接使用 OpenAI 或 Azure,也可通过兼容网关(例如 LiteLLM 代理)路由其他提供商,并通过支持热重载的文件配置整个议会。
目录
工作原理
flowchart LR
AI[Calling AI] -->|claude / gpt / gemini …| Legion
Legion -->|Responses API| GPT[OpenAI / Azure — direct]
Legion -->|Responses API| GW[Gateway e.g. LiteLLM]
GW --> Claude & Gemini & Llama
- 每个模型对应一个工具,工具名称取自模型名称的 slug 化形式(例如
Claude→claude)。每个工具都接受一个prompt,以及可选的context、role、system、temperature和maxTokens。 quorum工具将一个提示词分发给多个模型——支持角色、多轮讨论、可见性模式 和综合整理——并分别返回每个答案。编排选项请参阅预设。- 预设是命名的预配置议会(辩论、法庭、代码审查……),每个预设都作为独立工具公开。
- 身份信息和遥测数据通过
structuredContent传递,而不放在答案文本中。 日志输出到 stderr(适用于 stdio)。
设计决策
- 没有提供商适配器。 不包含针对特定提供商的代码,也没有内置模型列表。Legion 使用一种线路格式;不原生支持该格式的模型需通过网关接入。支持新模型无需修改此处代码。
- 模型是配置,不是代码。 添加模型只需添加一个 JSON 文件。通过 HTTP 使用时,每次 请求都会重新读取目录,因此无需重新构建或重启;通过 stdio 使用时,每个连接只读取一次 配置(见热重载)。
- 每个模型对应一个工具。 在发起调用的 AI 看来,每个模型都是拥有独立描述的工具,
而不是带模型参数的单一工具。
quorum工具适用于临时的多模型场景,而config/presets/中的每个预设都作为独立的、强制执行预配置人员编组的议会工具公开。 - 无状态。 每次调用都是一次性的,并设置
store: false。不会保留任何内容,因此无需 数据库,也无需管理对话状态。 - 精简。 数千行 TypeScript、一个打包输出文件、五个依赖项。
要求
- Node.js 24+
- 至少一个兼容 OpenAI Responses 的端点(直接使用提供商 API,或使用 LiteLLM 等网关 为需要桥接的模型提供支持)
设置
通过 npm 安装——无需克隆或构建:
npx legion-mcp
从克隆的仓库安装:
npm install
copy .env.example .env # then edit .env
配置
所有配置都位于 config/ 目录中。随附的默认配置始终是基础层;当前工作目录中的 config/ 文件夹会按文件叠加在其上:
- 目录资源(
models/、roles/、presets/、tools/):本地文件会覆盖同名的随附文件;仅存在于本地的文件会被添加;未修改的随附文件都会保留。因此,放入一个config/presets/refine.json只会覆盖该预设,其他随附预设仍会保留。 - 单文件文本(
prompts.json、errors.json、schema.json):按键合并——随附配置 < 本地配置。本地文件可以不完整,只覆盖其中设置的键。 description.md:若存在本地文件,则整份采用本地版本;否则采用随附版本。
叠加配置可以覆盖或添加条目,但不能删除随附条目。要选择哪些随附预设注册为工具,请使用 PRESETS(见下文)。
从 npm 安装?你必须提供自己的模型文件。随附配置仅包含不含密钥的
*.example.json模型文件,扫描器会有意忽略这些文件,因此配置包不会提供任何真实模型。若没有真实模型文件,服务会在启动时快速失败(No model files found in ...)。请在运行服务的位置旁放入一个config/models/<name>.json(见下文)——其余配置会回退到随附默认值。
无论采用哪种方式,下面的目录结构都相同。
热重载
配置会在构建服务实例时重新读取,不会在构建时固化——但具体何时读取取决于传输方式:
| 传输方式 | 重新读取配置的时机 | 编辑配置文件的效果 |
|---|---|---|
http | 每个请求 | 下次调用时立即生效,无需重启。 |
stdio | 每次连接时一次 | 客户端重新连接时生效。 |
HTTP 会为每个请求构建一个全新的服务,因此新增的模型、角色、预设或文本覆盖会在下次调用时立即生效。Stdio 会在整个连接期间固定使用同一个服务,因此相同的编辑要等客户端重启 Legion 后才会生效——对大多数桌面 MCP 客户端来说,这意味着重新加载服务,而不是重启机器。
本 README 中提到的“下次调用时立即生效”,指的是 HTTP 的行为;使用 stdio 时,则要等到下次连接。
Models — config/models/*.json
至少需要一个模型文件,否则服务会快速失败。
每个 JSON 文件都会变成一个工具,工具名称取自经过 slug 化的文件名
(config/models/fable.json → 工具 fable):
{
"model": "claude-fable-5",
"description": "Claude Fable — fast, creative, general purpose.",
"baseUrl": "https://api.example.com",
"apiKey": "sk-optional-per-model-key"
}
model(必填)— 端点路由到的已部署模型 ID。description— 帮助调用方 AI 选择合适的模型。system— 可选的基础系统指令,会内置到对该模型的每次调用中。baseUrl/apiKey— 可选;省略的值会回退到DEFAULT_BASE_URL/DEFAULT_API_KEY。apiKey可通过env:前缀引用环境变量——"apiKey": "env:GPT_KEY"会从环境中读取GPT_KEY(未设置或为空会导致启动失败),因此密钥可以完全不写入文件。omitParams— 可选的请求参数列表,用于针对该模型删除参数,例如["temperature"]。服务保持与提供方无关:不会假定哪些模型会拒绝哪些参数——你可以在此声明每个模型的特性。适用于推理模型以及某些会拒绝temperature的部署。reasoning— 可选的"minimal" | "low" | "medium" | "high",作为reasoning.effort发送。推理模型可能会耗尽全部maxTokens进行思考而不输出内容;设置"low"可能有助于为答案留出空间。端点必须支持此参数;它不会绕过内容过滤。maxTokens— 可选的该模型输出硬上限。每个请求都会发送min(requested, ceiling),无论调用方或预设角色要求多少。适用于思考空间越大就思考越深入、并可能返回空结果的模型。
热加载: 每次请求都会重新扫描目录——添加或编辑模型文件后,下次调用即可生效,无需重启。
密钥与 Git: 模型文件可能包含 API 密钥,因此 config/models/*.json
会被 Git 忽略。复制一个 *.example.json(已跟踪、不含密钥且会被扫描器忽略)即可开始:
copy config\models\gpt.example.json config\models\gpt.json # then add your key
Roles — config/roles/*.md
可选的热加载指令文件。每个 .md 文件都会变成一个具名角色
(名称取自经过 slug 化的文件名)。放入文件后,下次调用即可生效。此仓库提供 skeptic.md、builder.md、judge.md 和 short.md(一种简短的“立即回答,不作推敲”角色,适用于输出受限的轮次)作为开箱即用的起始文件——可随意编辑或删除(其中不含密钥)。
工具中可用的选择器会变成 roleName,例如传入 role: "skeptic"
或在 quorum.models 中使用 "model:skeptic"。
Presets — config/presets/*.json
可选的热加载评审团方案,每个预设对应一个 JSON 文件(名称取自经过 slug 化的文件名,与模型文件类似)。每个预设都会变成独立的工具——放入 config/presets/code_review.json 后,下次请求就会出现 code_review 工具。每个预设都有一个 description、一个 roles 列表,以及可选的权威 mode / synthesizer 默认值。每个角色都内联定义其行为——角色的 description 就是其指令(行为约定);没有 description 的角色会回退到匹配的 config/roles/<role>.md 文件:
{
"description": [
"Free-for-all: pit several contestants against each other, then crown a winner.",
"",
"Staff `contestant` with as many models as you like; one `judge` decides."
],
"mode": "parallel",
"synthesizer": "judge",
"roles": [
{ "role": "contestant", "description": "Argue why your answer beats the others.", "min": 2, "max": null },
{ "role": "judge", "description": "Crown a single winner and justify it.", "min": 1, "max": 1 }
]
}
调用方 AI 会直接调用预设工具(例如 code_review),仍需编写 models 选择器,将任意模型分配给任意预设角色。预设会强制执行:每个选择器都必须使用预设角色,并且每个角色都必须按照其基数配置人员,否则结果会报错并指出需要修正的内容。
键:
| 键 | 类型 | 默认值 | 描述 |
|---|---|---|---|
description | string | string[] | 必填 | 预设工具的 MCP 描述。 |
roles | PresetRole[] | 必填 | 预设接受的角色。 |
mode | "sequential" | "parallel" | "private" | "independent" | "sequential" | 控制每轮发言者能看到哪些先前轮次的内容。 |
defaultRounds | 正整数 | 1 | 调用未提供 rounds 时使用的轮数。 |
synthesizer | string | 无 | 负责生成综合总结轮次的角色。单席位角色为中立角色,不参与各轮讨论;多席位角色(max > 1 或 null)会参与讨论,并由其首个活跃席位进行综合总结。 |
synthesizeEvery | "end" | non-negative integer | "end" | 在结束时或每隔 N 轮进行一次综合总结。 |
eliminator | string | 无 | 负责淘汰发言者的中立角色。与 eliminateEvery 配合使用时为必填项。 |
framer | string | 无 | 负责开启讨论并引导讨论方向的中立角色。 |
reframeEvery | "end" | non-negative integer | "end" | 仅在开场时重新设定讨论方向,或在开场后每隔 N 轮重新设定一次。 |
closingStatements | boolean | false | 在最终综合总结前运行总结陈词阶段。 |
eliminateEvery | non-negative integer | "spread" | 0 | 允许淘汰者每隔 N 轮淘汰一名发言者,或均匀分配淘汰次数。仅限预设。 |
eliminationsOptional | boolean | false | 允许淘汰者选择不淘汰任何人。仅限预设。 |
enterEvery | non-negative integer | 0 | 每队先安排一名发言者,然后每隔 N 轮加入一名候补发言者。仅限预设。 |
vote | string | 无 | 投票说明;启用匿名投票。 |
voteEvery | "end" | non-negative integer | "end" | 在结束时或每隔 N 轮投票。 |
voteVisibility | "aggregate" | "ballots" | "aggregate" | 在记录中仅包含票数总计,或同时包含匿名化的投票选项。 |
allowSelfVote | boolean | true | 在每位投票者的候选项列表中包含其自身席位。 |
voteByTeam | boolean | false | 为每个 @team 提供一个选项,并按团队汇总投票。 |
角色对象键:
| 键 | 类型 | 默认值 | 描述 |
|---|---|---|---|
role | string | 必填 | 用于 model:role 选择器的角色名称。 |
description | string | string[] | 对应的角色文件 | 内联指令;数组会以换行符连接。否则必须存在 config/roles/<role>.md。 |
min | 非负整数 | 1 | 最少发言者人数;0 表示该角色可选。 |
max | 正整数或 null | 1 | 最多发言者人数;null 表示不限人数。 |
maxTokens | 正整数 | 8192 | 调用方未提供 maxTokens 时,该角色的输出上限。调用方显式指定的值会覆盖所有角色,包括中立角色。包含隐藏推理。 |
silent | boolean | false | 允许该角色在常规轮次中旁听和投票,但不发言。 |
voter | boolean | 所有符合条件的角色 | 如果有任何角色被标记,则仅允许标记的角色匿名投票。 |
candidate | boolean | 所有符合条件的角色 | 如果有任何角色被标记,则仅将标记的角色列为投票选项。 |
closing | boolean | 所有符合条件的角色 | 仅允许标记的角色进行总结陈词;每个团队或无团队归属的角色中,只有首位被标记的发言者进行总结陈词。 |
closingLast | boolean | false | 在并行总结陈词之后,由该角色发言,且其上下文包含这些陈词;要求设置 closing: true。 |
tagTeam | boolean | false | 每个 @team 轮换一名被标记的发言者参与每个常规轮次。不能与 enterEvery 同时使用。 |
cameo | boolean | false | 仅在一轮发言——即调用的 cameoRound 指定的轮次,否则为中间轮次——而非每轮发言。属于临时客串,而非常驻角色。 |
例如,法庭辩论调用会使用 @team 标签为双方分配律师。每方列出的第一位律师代表该方作总结陈词:
{
"models": [
"gpt:lawyer@prosecution",
"grok:lawyer@prosecution",
"claude:lawyer@defense",
"kimi:juror",
"llama:juror",
"mistral:juror",
"opus:judge"
],
"objectives": {
"prosecution": "Prove liability.",
"defense": "Defeat liability."
}
}
编写说明
编写预设时需要注意的事项:
- 淘汰时必须使用准确标签。 裁判从可选菜单中复制一条
CUT <label>命令,然后在下一行给出理由。可选淘汰也允许使用KEEP ALL。只写数字或选择含糊不清都会被拒绝。本地淘汰提示词覆盖项必须使用此命令格式。 - 必需淘汰可能会停滞。 有限次数的清理重试无法保证只剩一名胜者。若运行未能解决,将保留其摘要和讨论,但返回
isError: true和包含剩余标签的structuredContent.incomplete。可选淘汰竞赛不受此要求约束。 min/max统计该角色下的所有发言者,而非每队人数。 它们限制的是所有队伍中该角色的人数总和。因此,必须涵盖 2v2 至 6v6 阵营的 tag-teamwrestler角色应设置为min: 4, max: 12;3v3 只是此范围内的一种有效人员配置(6 名摔跤手),并非其自身的max: 3。引擎无法强制执行“双方人数相等”或“每方一人”,请改为在description中说明。- 中立角色不能加入队伍。
eliminator、framer和单席位的synthesizer不接受@team标签,因此,属于某一方的角色不能担任这些职务。 - 某些键需要搭配其他键使用,违反要求会在加载时被捕获:
synthesizeEvery需要synthesizer,eliminateEvery需要eliminator,reframeEvery需要framer,closingLast需要closing,投票选项需要vote,而单席位的 synthesizer 至少需要另一个必需角色(它不会参与常规轮次)。enterEvery和tagTeam不能同时使用,因为两者都会决定谁发言。 - 格式错误的预设会被跳过,不会导致致命错误。 系统会记录
❌ preset skipped — Invalid <file>: <reason>,其他所有工具仍会注册,因此,如果某个预设工具没有出现,请检查服务日志。 - 成本按
speakers × rounds次串行模型调用计算,因此defaultRounds和较大的max会让成本迅速增加。mode: "parallel"会让每轮的耗时取决于最慢的发言者,但代价是发言者在同一轮内无法看到彼此。
此代码库附带以下预设,可随意编辑或删除:
battle_royale- 自由混战;由监督者选出获胜者。
brainstorm- 跨模型发散式创意生成。
bullying- 一个模型为某个立场辩护,其余模型群起攻之;可选的教师会裁定它是否站得住脚。
code_review- 结构化的多模型代码审查。
courtroom- 标记团队的律师分别为对立双方辩护,陪审员按立场投票,法官作出裁决。
debate- 对立双方围绕某个问题展开辩论,最终综合出结论。
double_blind- 独立盲审小组——任何人都看不到其他人的内容。
election- 候选人展开竞选,随后由全体成员进行无记名投票——匿名投票即为裁决,而非由法官决定。可选的
incumbent为过往表现辩护;可选的沉默electorate会阅读每一轮内容并投票,但不参与竞选。 final_girl- 每轮淘汰一名幸存者,直到只剩一人。
focus_group- 由主持人引导、成员相互启发的讨论小组。
gauntlet- 跨轮次进行的私下自我完善竞赛。
quick_take- 快速的双声部直觉评估——一个给出直接回答,另一个提出一句反驳。
refine- 接力润色现有成果。
tag_team- 双打比赛:每轮每支队伍中只有一名标记为
@team的摔跤手可以上场,其余成员在场边等候;每轮轮换,因此三人一方会轮流派上全部三人。可选的run_in会在单轮中闯入擂台(通过cameoRound安排),可选的announcer会解说比赛,ref则宣布获胜队伍。 tiebreak- 由具有决定性的一票解决僵局。
war_games- 分批入场的团队笼斗:标记为
@team的参赛者逐一入场,中立裁判判定犯规并宣布获胜队伍;也可选配负责安排比赛的booker。 workshop- 分工明确的创意团队。
哪些内置预设会注册为工具,由
PRESETS 控制——未设置时会全部注册;而你在自己的
config/presets/ 下添加的仅本地预设(即没有对应内置版本的标识名)无论如何都会注册,因为编写预设本身就代表选择启用。使用内置标识名的本地文件是对该预设的自定义,而非新预设,因此仍受 PRESETS 控制。如果没有内置预设,也没有本地预设,就不会有预设工具——不过请注意,内置预设是基础层,因此即使你没有随项目附带自己的 config/presets/ 文件夹,也会获得所有内置预设。
角色文本会引导输出,但不会限制输出长度——如需硬性限制每轮输出,请使用
maxTokens;对于推理模型和多轮法定人数,请预留充足预算。tokenBudget是另一个维度:它会统计整个运行中每次调用的输入 和输出,因此应根据turns × (context + maxTokens)估算,并将其设得高于估算值——随着对话记录增长,输入会占据大部分用量。
AI 指引——config/description.md
可选的 Markdown 文档,会作为 MCP instructions 提供给客户端——介绍你的
模型,以及 AI 应在何时使用各个模型。模板请参阅本仓库中的副本。字面标记 {longRuns} 会根据
ASYNC_TOOLS 的设置替换为 config/sync/long-runs.md 或
config/async/long-runs.md,因此交付指引(阻塞并等待,或启动/轮询/取消)会随模式变化,
而无需重复文件的其余内容。若本地 description.md 不包含该标记,则会按原样提供;启动时会发出警告,提示缺少模式指引。
工具、字段和消息文本——config/*.json 和 config/tools/*.md
调用方读取并用于驱动 Legion 的文本——工具和字段描述、提示词框架、运行时错误——位于配置中,而不在代码中。(outputSchema 中的结果结构描述、生成的 staffing 行以及启动错误仍保留在代码中。)每个文件都会按键合并到内置 JSON 基础配置上,因此只需覆盖所需内容;打开随附文件即可查看完整键集合和
{token} 占位符:
config/tools/quorum.md、poll.md、cancel.md——quorum工具以及(仅异步模式下)poll/cancel工具的描述。删除其中一个文件即可回退到内置字符串。模型工具使用其模型文件中的description来描述自身,预设工具则使用预设自身的描述。config/sync/long-runs.md、config/async/long-runs.md——按模式区分的内容块,会在{longRuns}处插入description.md。config/schema.json——输入字段描述(prompt= 共享字段,quorum= 仅限 quorum;名称冲突时以quorum键为准)。config/prompts.json——模型读取的提示词构造模板:角色约定、上下文块、记录标题、轮次横幅。可在此调整角色约束的强度和轮次的呈现方式。config/errors.json——显示给调用 AI 的运行时错误消息。
(启动/配置验证错误仍保留在代码中——报告配置文件损坏的消息不可能放在该文件本身中。)
环境变量
| 变量 | 必需 | 描述 |
|---|---|---|
DEFAULT_BASE_URL | 否* | 对于没有 baseUrl 的模型,其 API 根路径 — SDK 会附加 /responses。例如:https://api.openai.com/v1、https://<res>.openai.azure.com/openai/v1;LiteLLM 代理可使用其纯根路径。 |
DEFAULT_API_KEY | 否* | 对于没有 apiKey 的模型,其 API 密钥。密钥仅保留在服务端。 |
ALLOW_NO_MODELS | 否 | true 表示即使不存在模型文件也启动:不提供任何模型工具;quorum 和预设工具仍会注册,但在 config/models/*.json 文件出现之前无法使用(每次请求时热重载)。适用于只需列出工具的演示和注册表沙盒。默认值为 false — 缺少模型仍会导致启动失败。 |
MCP_TRANSPORT | 否 | http(默认值)或 stdio。 |
HOST | 否 | HTTP 绑定地址(默认值为 127.0.0.1)。设为 0.0.0.0 可对外提供服务 — 此时应设置 ALLOWED_HOSTS。 |
ALLOWED_HOSTS | 否 | 用逗号分隔的主机名,用于保护非 localhost 绑定免受 DNS 重绑定攻击。 |
PORT | 否 | HTTP 端口(默认值为 5000;stdio 模式下忽略)。 |
MAX_ROUNDS | 否 | quorum 工具接受的最大讨论轮数(默认值为 5)。 |
MAX_TOKENS | 否 | 每轮调用方可请求的最大可见 maxTokens;超出此值的请求将被拒绝(默认值为 3000)。超出此值的预设角色限制会被截断至此值。每个模型的推理额度会在此基础上额外增加。 |
MODEL_TIMEOUT | 否 | 每次模型调用的超时时间,单位为毫秒(默认值为 90000),因此一个卡住的席位不会拖住整个议会。失败后会重试一次,因此席位在轮次失败前的最坏情况耗时大约会翻倍。 |
TOKEN_BUDGET | 否 | quorum 运行的默认软性累计令牌预算(未设置 = 无限制;单次调用的 tokenBudget 会覆盖此值)。 |
DYNAMIC_ROLES | 否 | 允许调用方 AI 在行内定义临时 quorum 角色(默认值为 true)。 |
PRESETS | 否 | 以逗号分隔的允许列表,指定要注册为工具的内置预设 slug(例如 code_review,debate)。未设置 = 注册所有内置预设,因此升级不会悄悄移除任何预设;已设置 = 仅注册所列预设,因此新发布的内置预设不会在未经允许时出现。你在自己的 config/presets/ 下添加的预设始终会注册 — 编写预设即表示选择启用;而与内置预设 slug 相同的本地文件会自定义该预设,但仍受此列表约束。未知名称会被忽略。 |
LOG_LEVEL | 否 | debug | info | warn | error(默认值为 info)。 |
ASYNC_TOOLS | 否 | true 表示在后台运行议会:quorum 和预设工具会立即返回作业句柄,并开放 poll / cancel 工具。适用于工具调用会在议会完成前超时的宿主。作业仅保存在此进程的内存中。默认值为 false。 |
TRUST_PROXY_AUTH | 否 | true 表示将每个作业绑定到由 X-MS-CLIENT-PRINCIPAL-* 标头指定的调用方(Azure Container Apps Easy Auth)。仅可在会移除客户端所提供副本的入口后安全使用。默认值为 false — 任何持有 jobId 的人都可以轮询或取消作业。 |
JOB_MAX_ACTIVE / JOB_MAX_RETAINED / JOB_RETAIN_MS / JOB_POLL_INTERVAL_MS / JOB_SHUTDOWN_GRACE_MS | 否 | 异步作业限制:并发议会数(3)、保留的已完成作业数(20)、保留时长(1800000 毫秒)、建议的轮询间隔(10000 毫秒)、SIGTERM 排空时长(20000 毫秒)。 |
* 每个模型都必须从其文件或默认值中解析出 baseUrl 和 apiKey — 并在启动时进行验证。
模型目录缺失或为空(除非 ALLOW_NO_MODELS=true)、模型文件无效、无法解析端点或密钥,或者两个文件名 slug 化后对应同一工具时,服务会在启动时立即失败。
路由
每次工具调用都是无状态、仅执行一次的 Responses API 请求。原生支持 Responses 的模型端点(OpenAI、Azure OpenAI / Foundry)会将 baseUrl 设为直接调用;其余模型则回退到默认值 — 通常是 LiteLLM 这类可桥接至原生 API 的 OpenAI 兼容网关。
日志记录
info(蓝色):服务器启动信息,以及每次模型调用的一行元数据——模型、延迟、令牌用量、角色、上下文是否存在。不包含提示词或响应内容。debug(灰色):此外还会记录完整的提示词和响应(会标明上下文是否存在,但不会打印上下文内容)。warn(橙色)/error(红色):回退和故障。
当标准错误输出不是 TTY 时,会自动禁用颜色。
运行
仅有一个入口点;传输方式由 MCP_TRANSPORT 决定(默认值为 http,桌面 MCP 客户端请设置为 stdio)。
从 npm 运行(legion-mcp 可执行文件——请在包含 config/ 的目录中运行):
npx legion-mcp # Streamable HTTP transport on :$PORT/mcp
$env:MCP_TRANSPORT='stdio'; npx legion-mcp # stdio transport
全局安装或作为依赖安装后,可通过 PATH 使用同一个可执行文件:
npm install -g legion-mcp
legion-mcp
开发模式(无需构建步骤,通过 tsx 运行):
npm run dev # Streamable HTTP transport on :$PORT/mcp
npm run dev:stdio # stdio transport
生产模式(编译至 bin/server.js):
npm run build
npm start # http
npm run start:stdio # stdio
异步模式 — ASYNC_TOOLS=true
一次议会运行可能需要数分钟;托管版 ChatGPT 和 Codex 远早于此时就会放弃工具调用(观察到约 124 秒;Codex 默认超时为 60 秒)。启用 ASYNC_TOOLS=true 后,quorum 和预设工具会验证请求,在后台启动议会,并立即返回一个句柄,其中包含 structuredContent.jobId、当前的 state/phase,以及建议的 pollIntervalMs。此时会额外提供两个工具:
poll— 只读;返回任务状态、截至目前已完成的答案和公开备注,以及(任务结束后)result:完整的议会输出,格式与通常相同。full: true会添加渲染后的转录和时间线。cancel— 中止正在进行的模型调用,返回已完成的内容,并将任务结束状态设为cancelled,不进行综合。可重复调用。
各个模型工具仍保持同步。服务器的 instructions 会切换为启动/轮询/取消工作流,以便调用模型了解后续操作。任务保存在进程内存中,并受 JOB_* 上限控制——重启会丢失任务,而路由到其他副本的请求也无法访问这些任务,因此启用此模式时请运行单个实例(或固定会话路由)。在 Azure Container Apps Easy Auth 后方运行时,设置 TRUST_PROXY_AUTH=true,将每个任务绑定到经过身份验证的调用者;否则,持有 jobId 即是唯一的访问控制方式。
试用
使用 MCP Inspector 列出工具:
npx @modelcontextprotocol/inspector -e MCP_TRANSPORT=stdio npx tsx ts/server.ts
在 VS Code 中使用
将以下内容添加到 mcp.json — 从 npm 安装:
{
"servers": {
"legion": {
"command": "npx",
"args": ["-y", "legion-mcp"],
"cwd": "path/to/your/config/parent",
"env": {
"MCP_TRANSPORT": "stdio",
"DEFAULT_BASE_URL": "https://your-gateway.example.com",
"DEFAULT_API_KEY": "sk-your-key"
}
}
}
}
或者从克隆的仓库安装:
{
"servers": {
"legion": {
"command": "node",
"args": ["bin/server.js"],
"cwd": "path/to/legion",
"env": {
"MCP_TRANSPORT": "stdio",
"DEFAULT_BASE_URL": "https://your-gateway.example.com",
"DEFAULT_API_KEY": "sk-your-key"
}
}
}
}
对于 HTTP 传输,请将客户端指向 http://<host>:<PORT>/mcp。
健康检查
GET /health— 轻量级的存活状态检查:确认进程正在运行且配置已加载。返回{ status: "ok", name, version, models }(数量)。不会发起外部调用。容器HEALTHCHECK和 Kubernetes 存活/就绪探针应访问此端点。GET /health?deep— 可选的连通性检查:向每个模型发送一条简短提示词,并报告各模型的可访问性(若任一模型失败则返回503)。每个模型都会产生一次真实的计费调用,因此请手动使用——不要将其接入自动探针。
部署
可直接使用的容器部署示例(Azure App Service、Azure Container Apps、Docker Compose、Kubernetes,以及用于 HTTPS 的 Compose + Caddy)位于 examples/ 中——每个示例都会从 npm 安装 Legion,并附带一套完整的即插即用 config/。
评论
一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。