关于 conductor
conductor 是面向 Claude Code 的 MCP 服务,在返回工具结果前验证输入并检查输出。它用确定性代码处理机械任务,用隔离的 `claude -p` 调用处理判断类任务,并可在验证失败时重试或使用备用方案。
主要功能
- 01按 Schema 验证输入
- 02通过必要检查验证输出
- 03根据验证反馈重试模型调用
- 04对失败结果使用声明的备用方案
- 05无需模型调用即可执行确定性任务
- 06在流水线中串联已验证的命令
适用场景
- 01从文本提取并验证联系人信息
- 02脱敏 PII 并检查是否移除
- 03逐步检查多阶段文本转换
- 04使用生成 slug 或编码等纯代码工具
MCP 相关
该项目是 MCP 服务,向包括 Claude Code 在内的 MCP 客户端提供 23 个经验证的工具。它可作为 Claude Code 插件安装,也可通过 stdio 供其他 MCP 客户端运行。
配置
本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。
工具
23收录的工具列表 · 更新于 1 天前
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| text | string | 是 |
详细介绍
conductor
为 Claude Code 提供经过验证的工具调用:一个 MCP 服务(可作为插件安装),其 23 个工具会在你获取输出前,先用代码检查输出。
当模型被要求“提取电子邮件”或“隐藏电话号码”时,通常都能做对,但如果做错了,你却无从得知。conductor 为每项任务设定了契约:输入架构、输出架构和必需的验证器。确定性任务(slug、base64、URL 提取)由纯代码执行,完全不会调用模型。需要判断的任务(摘要、PII 隐藏)则通过隔离的 claude -p 调用执行;如果答案未通过验证器检查,就会将失败信息作为反馈进行重试、交给后备方案处理,或作为失败返回;绝不会在不提示的情况下直接传递结果。
整个流程骨架(验证 -> 执行 -> 验证 -> 重试 -> 后备方案 -> 记录)都是不可跳过的真实代码;模型只会被隔离在需要判断的环节中。
快速开始
你需要安装并登录 Claude Code,并确保 PATH 中有 Node.js 20 或更高版本(插件使用 node 启动服务)。
claude plugin marketplace add fainir/conductor
claude plugin install conductor@conductor
重启 Claude Code(或在正在运行的会话中执行 /reload-plugins)。在会话中执行的步骤相同,分别是 /plugin marketplace add fainir/conductor 和 /plugin install conductor@conductor。
检查服务是否已启动:
$ claude mcp list
plugin:conductor:conductor: node /Users/you/.claude/plugins/cache/conductor/conductor/0.4.1/dist/conductor-mcp.cjs - ✓ Connected
然后用简单的话描述任务,例如“用 conductor 隐藏这张工单中的 PII”。
这些工具会以 mcp__plugin_conductor_conductor__<tool> 的形式出现。
使用 claude plugin marketplace update conductor && claude plugin update conductor@conductor 更新,使用 claude plugin uninstall conductor@conductor 移除。
不使用插件系统。 dist/conductor-mcp.cjs 是已提交的独立打包文件,因此克隆后无需安装或构建:
git clone https://github.com/fainir/conductor
claude mcp add conductor -s user -- node "$PWD/conductor/dist/conductor-mcp.cjs"
其他 MCP 客户端可以运行 npx -y @fainir/conductor,通过 stdio 提供 MCP 服务。npm 上另一个无关的 conductor-mcp 包属于其他人。
用法
每个工具都返回相同的封装结构。以下是使用 {"text": "hello"} 调用 base64_encode 时,打包文件返回的真实输出:
{ "ok": true, "output": { "encoded": "aGVsbG8=" }, "via": "base64_encode", "attempts": 1, "verdict": { "ok": true, "score": 1 } }
via 表示生成答案的环节(工具本身、声明的后备方案,或纯 Claude 的 freeform),attempts 统计该环节的尝试次数。失败时会将 ok: false 和工具结果的 isError 设为失败状态,判定结果会说明原因,以便调用模型修正调用方式:
{ "ok": false, "via": "redact_pii", "attempts": 0,
"verdict": { "ok": false, "score": 0, "feedback": "invalid input: input: missing required property 'text'; input: unexpected property 'txt' (allowed: text)" } }
克隆项目后,你可以通过 CLI 驱动相同的流程(先运行 npm ci)。纯代码工具会立即响应且不产生费用:
$ npx tsx src/cli.ts slugify '{"text":"Hello, World!"}'
{
"ok": true,
"output": {
"slug": "hello-world"
},
"command": "slugify",
"attempts": 1,
"verdict": {
"ok": true,
"score": 1
},
"costUsd": 0
}
$ npx tsx src/cli.ts normalize_phone '{"phone":"call me"}'
{
"ok": false,
"command": "normalize_phone",
"attempts": 1,
"verdict": {
"ok": false,
"score": 0,
"feedback": "shape: normalized must match /^\\+?\\d{6,15}$/ (an optional leading '+' followed by 6-15 digits)"
},
"costUsd": 0
}
结果未通过验证时,CLI 会以状态码 1 退出。模型驱动的工具也能以相同方式工作(npx tsx src/cli.ts extract_contact '{"text":"Call Sam at [email protected]"}'),每次尝试都会调用一次 claude -p。不带参数运行 npx tsx src/cli.ts 会列出命令;运行 npx tsx src/cli.ts route "make a url slug" '{"text":"Hello World"}' 则会让模型选择命令。
工具
纯工具(仅代码,不调用模型)
| 工具 | 输入 | 功能 | 验证器检查 |
|---|---|---|---|
slugify | text | 将字符串转换为适用于 URL 的 slug。 | 非空;仅包含由单个连字符连接的 [a-z0-9] 字符串 |
word_count | text | 统计字符串中的单词数和字符数。单词是去除首尾空白后的文本中以空白分隔的词元;字符数是输入的原始长度。 | 重新计算两项统计结果 |
normalize_phone | phone | 去除非数字字符,将电话号码字符串规范化为标准的 '[+]digits' 格式。 | 重新计算;可选的 + 后跟 6-15 位数字 |
truncate | text, max | 将文本截断至最多 max 个字符;需要截断时,在末尾添加一个单字符省略号。 | 长度不超过 max;重新计算 |
dedupe_lines | text | 按换行符拆分文本,并保留每行首次出现的内容,同时保持原有顺序。 | 没有重复行;每行均来自输入;重新计算 |
extract_urls | text | 从文本中提取所有 http(s) URL,去重并保留首次出现的顺序。 | 符合 http(s) 格式;每个 URL 均出现在输入中 |
template_fill | template, data | 将模板中的每个 {{key}} 标记替换为 String(data[key]);如果 data 中缺少任何 {{key}} 的值,则失败并指出对应的键名。 | 重新计算;不残留 {{placeholder}} |
base64_encode | text | 将 UTF-8 字符串编码为 base64。 | 解码后与输入相同 |
base64_decode | encoded | 将 base64 字符串解码为 UTF-8 文本。 | 重新编码后与输入相同(忽略填充和换行) |
extract_emails | text | 从文本中提取所有电子邮件地址。 | 符合电子邮件格式;每个地址均出现在输入中 |
json_minify | json | 删除无关空白字符,压缩 JSON 字符串。 | 解析后的值与输入相同 |
to_snake_case | text | 将字符串转换为 snake_case。 | 重新计算 |
基于模型的工具(隔离的 claude -p 调用)
| 工具 | 输入 | 功能 | 验证器检查 |
|---|---|---|---|
extract_contact | text | 从一段文本中提取联系人的姓名、电子邮件和电话号码。 | 架构;电子邮件和电话号码格式;电子邮件必须出现在输入中(依据) |
summarize | text, max_sentences? | 仅使用文本本身,将一段文本总结为不超过几句话。 | 不超过 max_sentences 句(默认值为 2);长度不超过输入 |
extract_keywords | text, k? | 提取最多 k 个在输入文本中原样出现的显著关键词或短语。 | 1 到 k 个项目(默认值为 5);每个项目都在输入中原样出现 |
classify_sentiment | text | 将一段文本的整体情感分类为正面、负面或中性。 | 标签为三者之一;置信度在 0..1 范围内 |
redact_pii | text | 将输入文本中的所有电子邮件地址和电话号码替换为 [REDACTED],其他文本保持不变。 | 不残留电子邮件或电话号码格式;输入中包含 PII 时须出现 [REDACTED] |
json_extract | text, fields | 从一段文本中提取调用方指定的字段列表,并组成 JSON 对象。每个请求的字段都成为一个键;如果文本中缺少该字段,其值为 null。 | data 是一个对象,且包含每个请求字段作为键 |
fix_json | text | 将格式错误或格式宽松的 JSON 文本修复为有效的 JSON 值。 | json 是对象或数组 |
extract_dates | text | 提取一段文本中提及的所有日期,并规范化为 ISO 格式 YYYY-MM-DD。 | 每个日期都是 YYYY-MM-DD 格式的有效日历日期 |
categorize | text, categories | 从给定的类别列表中,为一段文本选择最匹配的单个类别。 | 恰好是给定类别之一 |
translate | text, target_language | 将一段文本翻译成指定的目标语言。 | 非空;长度不超过输入长度的 5 倍 + 20 |
detect_language | text | 检测文本所用的自然语言。 | 非空名称;两个字母的小写 ISO 639-1 代码 |
以 ? 结尾的输入为可选项。前三列来自命令清单
(若内容不一致,npm test 会失败);MCP 服务公布的是完整的 JSON Schema。
工作原理
input -> inputSchema check -> run the command -> verify -> ok? -> result
^ | no
+-- retry with the verifier's feedback (model-backed, up to maxAttempts)
| still no
declared fallbacks -> freeform (plain Claude, same schema, same verifier)
- 强制校验器的契约。 每条命令都位于
commands/<name>/index.ts:包含清单(输入和输出 JSON Schema、确定性、提示词、回退方案)以及一个verify()导出项。没有校验器的命令无法注册(src/library.ts、src/registry.ts)。首先执行机械校验(Schema、正则表达式、重新计算、依据性);对于无法通过代码表达的标准,src/verify.ts中提供了llmJudge。 - 前置条件关卡。 如果输入不符合命令的
inputSchema,会在任何操作运行前被拒绝,并指出问题所在。 - 确定性梯度。
pure命令是确定性代码:运行一次、会缓存、免费,且永远不会发送给模型。stochastic命令由模型运行,并在收到反馈后重试。effectful专用于需要人工审核的命令。 - 回退到自由格式调用。 每个基于模型的阶梯最终都会回退到普通 Claude,并受相同的输出 Schema 和校验器约束,因此 conductor 的表现绝不会差于原始调用;适用经过验证的命令时,只会更好。
- 隔离调用。 每次模型调用(
src/cc.ts)都会替换系统提示词(--system-prompt),不加载任何设置(--setting-sources ""),不启用任何工具(--tools ""),不保存会话,并通过--json-schema限制答案格式。这一点很重要:一次天真的调用曾返回操作者自己的电子邮件,而不是输入中的地址,因为环境上下文(全局 CLAUDE.md、身份信息、钩子)发生了泄漏。 - 组合。
runPipeline(src/pipeline.ts)会串联命令;每一步都会在下一步运行前经过校验,因此错误不会悄无声息地累积。 - 遥测与路由。 每次尝试都会写入一行 JSONL。
npm run scores会根据 CLI 和评估日志输出各命令的可靠性,而route()(src/router.ts)会将这些比率提供给一个小型模型,该模型必须从命令名称枚举中进行选择。
配置
| 设置 | 默认值 | 作用 |
|---|---|---|
CONDUCTOR_HOME 环境变量 | ~/.conductor | MCP 服务追加写入 telemetry.jsonl 的位置 |
清单中的 model | sonnet | 该命令调用所用的模型(路由器和 llmJudge 使用 haiku) |
清单中的 maxAttempts | 2 | 切换到下一回退阶梯前,附带反馈的重试次数(纯代码命令只运行一次) |
基于模型的调用会使用当前登录到 claude CLI 的账户。
结果(作者测得)
评估使用相同输入,对比单次天真的 claude -p 调用与 conductor:
- 单次提取(
eval/run-eval.ts):基准 100%,conductor 100%。对于简单的单次任务,前沿模型本身已经很可靠,因此没有可弥补的差距。 - 组合(
eval/contact-card.ts、extract_contact -> normalize_phone -> slugify):基准 80%,conductor 100%。基准调用输出的电话号码中含有一个不可见的 Unicode 字符;确定性的normalize_phone步骤在结构上不会受到这类错误的影响。成本:conductor 的调用次数更多,约为基准的 2 倍。
可靠性差距会随着组合深度和机器读取的输出而扩大,而不是随着简单的单次调用而扩大。评估会调用 claude,并产生费用;使用 REPEATS=2 npx tsx eval/run-eval.ts、REPEATS=2 npx tsx eval/contact-card.ts 和 REPEATS=1 npx tsx eval/triage.ts(一个 4 步工作流)重新运行。
添加命令
-
创建
commands/<name>/index.ts。纯命令导出manifest、run和verify:import type { CommandManifest, SolveContext, Verdict } from "../../src/types.js"; import { check, combine } from "../../src/verify.js"; export const manifest: CommandManifest = { name: "reverse_text", // must equal the directory name description: "Reverse the characters of a string.", determinism: "pure", trust: "human-verified", inputSchema: { type: "object", properties: { text: { type: "string" } }, required: ["text"], additionalProperties: false }, outputSchema: { type: "object", properties: { reversed: { type: "string" } }, required: ["reversed"], additionalProperties: false }, fallbacks: [], effectful: false, }; export async function run(input: { text: string }): Promise<{ reversed: string }> { return { reversed: [...input.text].reverse().join("") }; } export async function verify(output: { reversed: string }, input: { text: string }, _ctx: SolveContext): Promise<Verdict> { return combine( check("string", typeof output?.reversed === "string", "reversed must be a string"), check("round-trip", [...(output?.reversed ?? "")].reverse().join("") === input.text, "reversing the output must give the input back"), ); }由模型驱动的命令设置
determinism: "stochastic",省略run,并添加systemPrompt、promptTemplate、model、maxAttempts和fallbacks: ["freeform"];其verify会检查模型的回答(参见commands/extract_contact/index.ts)。 -
添加
commands/<name>/tests/cases.json,其中包含一组{ "input": ..., "expect"?: ... }。 -
在
src/library.ts中导入该模块并将其添加到MODULES(打包工具无法扫描 目录)。 -
在上方相应表格中添加一行。
npm test会检查契约、使用测试用例运行纯命令,并打印它所期望的 README 行。 -
运行
npm run build,并提交dist/conductor-mcp.cjs,插件运行的就是这个文件。
开发
npm ci
npm test # hermetic: a fake `claude` on PATH, no model calls, no network
npm run typecheck
npm run build # rebuild dist/conductor-mcp.cjs and commit it (CI fails if it is stale)
npm run mcp # run the server from source over stdio
npm run scores # reliability scoreboard from projects/*/telemetry.jsonl
# These call the real claude CLI and cost money:
npx tsx eval/run-tests.ts [command] # each command's tests/cases.json through the full loop
npm run eval # eval/run-eval.ts
npm run release [patch|minor|major] 会更新 package.json 和两个插件清单中的版本、重新构建、提交并推送;已安装的用户随后可通过 claude plugin update conductor@conductor 获取更新。
src/
mcp.ts MCP server: one tool per command, every call through solve()
solve.ts the loop: input check -> execute -> verify -> retry -> fallback
schema.ts the JSON Schema subset used by command contracts
cc.ts one hermetic `claude -p` call
verify.ts verifier helpers: check/combine, zod schema, email/phone patterns, llmJudge
pipeline.ts compose verified commands
router.ts reliability-weighted command choice
telemetry.ts JSONL log + per-command scores
library.ts static command list (what the bundle ships)
registry.ts directory scan used by the CLI and evals
cli.ts manual driver
scores.ts prints the scoreboard
commands/<name>/ index.ts (manifest + run? + verify) and tests/cases.json
eval/ baseline-vs-conductor evals (paid)
test/ node:test suite (free)
dist/ committed bundle the plugin runs
限制与常见问题
- 由模型驱动的工具需要已登录的
claudeCLI,且每次尝试都会启动一个独立的claude -p进程,因此每次调用需要几秒钟,并会消耗你账户的用量。纯工具则不需要。 - 验证器只执行机械检查。 它们会检查格式、依据和预算违规,但不会发现所有语义错误:
categorize可能会选出一个允许但错误的类别,而摘要即使符合预算,也仍可能偏离重点。目前没有已发布的命令使用llmJudge。 - 部分检测器使用启发式规则。
redact_pii会将任意一串较长的数字和分隔符视为电话号码,因此输出中的 ISO 日期或订单号会导致检查失败,并可能在重试时被脱敏。summarize使用.``!和?统计句子数量,因此缩写也会计入。 - 遥测数据仅保存在你的机器上(MCP 服务的数据位于
~/.conductor/telemetry.jsonl,CLI 和评测的数据位于projects/)。输入只会发送给claude -p调用本身。 - 将此仓库作为 Claude Code 项目打开时,
.mcp.json中的conductor服务会显示为失败状态;${CLAUDE_PLUGIN_ROOT}仅在已安装的插件中展开。这不会造成影响。 - 尚未构建: 由模型驱动的分解(流水线需显式定义)、
effectful命令的人为关卡、MCP 服务内部的路由(客户端模型会自行选择工具)、将跟踪记录转化为新命令,以及在路由器上进行 RL。
许可证
Nir Feinstein 的更多开源项目
- agent-harness - 让 Claude Code 或 Codex 扮演产品团队、CEO 或投资组合经理
- frugal - 让你的 Agent 在花钱之前找到更省钱且诚实的做法
- flipback - 为每一轮自动添加
/goal:在 Claude Code 提前停止时让它继续 - recall - 通过 MCP 将你的 Claude Code 记忆用于每个 Agent
- 所有项目
评论
一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。