mcpField
C
conductor

conductor

本地服务
github.comAI 与智能体0今天更新

关于 conductor

conductor 是面向 Claude Code 的 MCP 服务,在返回工具结果前验证输入并检查输出。它用确定性代码处理机械任务,用隔离的 `claude -p` 调用处理判断类任务,并可在验证失败时重试或使用备用方案。

主要功能

  1. 01按 Schema 验证输入
  2. 02通过必要检查验证输出
  3. 03根据验证反馈重试模型调用
  4. 04对失败结果使用声明的备用方案
  5. 05无需模型调用即可执行确定性任务
  6. 06在流水线中串联已验证的命令

适用场景

  1. 01从文本提取并验证联系人信息
  2. 02脱敏 PII 并检查是否移除
  3. 03逐步检查多阶段文本转换
  4. 04使用生成 slug 或编码等纯代码工具

MCP 相关

该项目是 MCP 服务,向包括 Claude Code 在内的 MCP 客户端提供 23 个经验证的工具。它可作为 Claude Code 插件安装,也可通过 stdio 供其他 MCP 客户端运行。

配置

本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。

mcp.json · 通用配置
{
"mcpServers": {
"fainir-conductor": {
"command": "npx",
"args": [
"-y",
"@fainir/conductor"
]
}
}
}
启动命令npx -y @fainir/conductor

本地服务需要在你的电脑上安装运行环境(如 Node.js 18+)。各客户端的配置文件位置不同,请参考客户端文档。

安装包
npm@fainir/conductor0.4.1

工具

23

收录的工具列表 · 更新于 1 天前

参数类型必填说明
textstring是

详细介绍

conductor

CI License: MIT

为 Claude Code 提供经过验证的工具调用:一个 MCP 服务(可作为插件安装),其 23 个工具会在你获取输出前,先用代码检查输出。

当模型被要求“提取电子邮件”或“隐藏电话号码”时,通常都能做对,但如果做错了,你却无从得知。conductor 为每项任务设定了契约:输入架构、输出架构和必需的验证器。确定性任务(slug、base64、URL 提取)由纯代码执行,完全不会调用模型。需要判断的任务(摘要、PII 隐藏)则通过隔离的 claude -p 调用执行;如果答案未通过验证器检查,就会将失败信息作为反馈进行重试、交给后备方案处理,或作为失败返回;绝不会在不提示的情况下直接传递结果。

整个流程骨架(验证 -> 执行 -> 验证 -> 重试 -> 后备方案 -> 记录)都是不可跳过的真实代码;模型只会被隔离在需要判断的环节中。

conductor 的命令行界面:slugify 返回 ok,判定分数为 1;输入 'call me' 的 normalize_phone 被验证器拒绝,并附上说明预期格式的反馈。

快速开始

你需要安装并登录 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"}' 则会让模型选择命令。

工具

纯工具(仅代码,不调用模型)
工具输入功能验证器检查
slugifytext将字符串转换为适用于 URL 的 slug。非空;仅包含由单个连字符连接的 [a-z0-9] 字符串
word_counttext统计字符串中的单词数和字符数。单词是去除首尾空白后的文本中以空白分隔的词元;字符数是输入的原始长度。重新计算两项统计结果
normalize_phonephone去除非数字字符,将电话号码字符串规范化为标准的 '[+]digits' 格式。重新计算;可选的 + 后跟 6-15 位数字
truncatetext, max将文本截断至最多 max 个字符;需要截断时,在末尾添加一个单字符省略号。长度不超过 max;重新计算
dedupe_linestext按换行符拆分文本,并保留每行首次出现的内容,同时保持原有顺序。没有重复行;每行均来自输入;重新计算
extract_urlstext从文本中提取所有 http(s) URL,去重并保留首次出现的顺序。符合 http(s) 格式;每个 URL 均出现在输入中
template_filltemplate, data将模板中的每个 {{key}} 标记替换为 String(data[key]);如果 data 中缺少任何 {{key}} 的值,则失败并指出对应的键名。重新计算;不残留 {{placeholder}}
base64_encodetext将 UTF-8 字符串编码为 base64。解码后与输入相同
base64_decodeencoded将 base64 字符串解码为 UTF-8 文本。重新编码后与输入相同(忽略填充和换行)
extract_emailstext从文本中提取所有电子邮件地址。符合电子邮件格式;每个地址均出现在输入中
json_minifyjson删除无关空白字符,压缩 JSON 字符串。解析后的值与输入相同
to_snake_casetext将字符串转换为 snake_case。重新计算
基于模型的工具(隔离的 claude -p 调用)
工具输入功能验证器检查
extract_contacttext从一段文本中提取联系人的姓名、电子邮件和电话号码。架构;电子邮件和电话号码格式;电子邮件必须出现在输入中(依据)
summarizetext, max_sentences?仅使用文本本身,将一段文本总结为不超过几句话。不超过 max_sentences 句(默认值为 2);长度不超过输入
extract_keywordstext, k?提取最多 k 个在输入文本中原样出现的显著关键词或短语。1 到 k 个项目(默认值为 5);每个项目都在输入中原样出现
classify_sentimenttext将一段文本的整体情感分类为正面、负面或中性。标签为三者之一;置信度在 0..1 范围内
redact_piitext将输入文本中的所有电子邮件地址和电话号码替换为 [REDACTED],其他文本保持不变。不残留电子邮件或电话号码格式;输入中包含 PII 时须出现 [REDACTED]
json_extracttext, fields从一段文本中提取调用方指定的字段列表,并组成 JSON 对象。每个请求的字段都成为一个键;如果文本中缺少该字段,其值为 null。data 是一个对象,且包含每个请求字段作为键
fix_jsontext将格式错误或格式宽松的 JSON 文本修复为有效的 JSON 值。json 是对象或数组
extract_datestext提取一段文本中提及的所有日期,并规范化为 ISO 格式 YYYY-MM-DD。每个日期都是 YYYY-MM-DD 格式的有效日历日期
categorizetext, categories从给定的类别列表中,为一段文本选择最匹配的单个类别。恰好是给定类别之一
translatetext, target_language将一段文本翻译成指定的目标语言。非空;长度不超过输入长度的 5 倍 + 20
detect_languagetext检测文本所用的自然语言。非空名称;两个字母的小写 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 环境变量~/.conductorMCP 服务追加写入 telemetry.jsonl 的位置
清单中的 modelsonnet该命令调用所用的模型(路由器和 llmJudge 使用 haiku)
清单中的 maxAttempts2切换到下一回退阶梯前,附带反馈的重试次数(纯代码命令只运行一次)

基于模型的调用会使用当前登录到 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 步工作流)重新运行。

添加命令

  1. 创建 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)。

  2. 添加 commands/<name>/tests/cases.json,其中包含一组 { "input": ..., "expect"?: ... }。

  3. 在 src/library.ts 中导入该模块并将其添加到 MODULES(打包工具无法扫描 目录)。

  4. 在上方相应表格中添加一行。npm test 会检查契约、使用测试用例运行纯命令,并打印它所期望的 README 行。

  5. 运行 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

限制与常见问题

  • 由模型驱动的工具需要已登录的 claude CLI,且每次尝试都会启动一个独立的 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。

许可证

MIT


Nir Feinstein 的更多开源项目
  • agent-harness - 让 Claude Code 或 Codex 扮演产品团队、CEO 或投资组合经理
  • frugal - 让你的 Agent 在花钱之前找到更省钱且诚实的做法
  • flipback - 为每一轮自动添加 /goal:在 Claude Code 提前停止时让它继续
  • recall - 通过 MCP 将你的 Claude Code 记忆用于每个 Agent
  • 所有项目

评论

支持 Markdown

第一次接触 MCP?

从协议是什么,到在客户端里装上第一个服务,这几条回答覆盖了大多数入门问题。

一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。