conductor について
conductorは、Claude Code向けのMCPサーバーで、結果を返す前に入力を検証し、ツール出力を確認します。機械的な処理には決定論的なコードを使い、判断を要する処理には隔離された `claude -p` を使い、検証に失敗した場合は再試行またはフォールバックします。
主な機能
- 01スキーマに基づく入力検証
- 02必須チェックによる出力検証
- 03検証フィードバックを使ったモデル呼び出しの再試行
- 04失敗時に宣言済みフォールバックを使用
- 05モデル呼び出しなしの決定論的処理
- 06検証済みコマンドのパイプライン実行
活用シーン
- 01テキストから連絡先情報を抽出して検証
- 02PIIをマスキングし、除去を確認
- 03複数段階の変換を各段階で検証
- 04スラッグ生成やエンコードなどの純粋なツールを利用
MCP 対応
Claude Codeを含むMCPクライアントに、検証付きの23個のツールを提供するMCPサーバーです。Claude Codeプラグインとして導入でき、他のMCPクライアントからはstdioで実行できます。
設定
ローカルサーバーの設定はどのクライアントでも共通です。コピーしてクライアントの MCP 設定ファイルに貼り付けてください。
ツール
23掲載時のツール一覧 · 1 日前に更新
| パラメーター | 型 | 必須 | 説明 |
|---|---|---|---|
| text | string | はい |
詳細
conductor
Claude Code 向けの検証済みツール呼び出し:23 個のツールが出力を返す前にコードでチェックする MCP サーバー(プラグインとしてインストール可能)です。
「メールアドレスを抽出して」や「電話番号を伏せて」とモデルに依頼すると、たいていは正しい結果が返ってきますが、間違いを知らせるものはありません。conductor では、各タスクに入力スキーマ、出力スキーマ、必須の検証器からなる契約を設けています。スラッグ、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 で使用できるスラッグに変換します。 | 空でないこと。[a-z0-9] の連続を単一のハイフンでつないだ形式であること |
word_count | text | 文字列の単語数と文字数を数えます。単語はトリミングしたテキストを空白で分割したトークンで、文字数は入力の生の長さです。 | 両方の数を再計算します |
normalize_phone | phone | 数字以外の文字を取り除き、電話番号文字列を標準的な「[+]数字」形式に正規化します。 | 再計算します。任意の + の後に6〜15桁の数字が続くこと |
truncate | text, max | テキストを最大 max 文字に切り詰め、切り詰める必要がある場合は1文字の省略記号を付けます。 | 長さが max を超えないこと。再計算します |
dedupe_lines | text | テキストを改行で分割し、各行の最初の出現を順序を保って残します。 | 重複がないこと。各行が入力に含まれていること。再計算します |
extract_urls | text | テキストからすべての http(s) URL を抽出し、最初に出現した順序を保って重複を除去します。 | http(s) の形式であること。各 URL が入力に含まれていること |
template_fill | template, data | {{key}} トークンをすべて String(data[key]) に置き換えてテンプレートを埋めます。{{key}} に対応する値が data にない場合は、該当するキー名を示して失敗します。 | 再計算します。{{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 | テキスト全体の感情を、ポジティブ、ネガティブ、ニュートラルのいずれかに分類します。 | ラベルが3種類のいずれかであること、信頼度が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 | 指定されたカテゴリのリストから、テキストに最も適したカテゴリを1つ選びます。 | 指定されたカテゴリのいずれか1つであること |
translate | text, target_language | テキストを指定された対象言語に翻訳します。 | 空でないこと、入力の長さの5倍+20以下であること |
detect_language | text | テキストの自然言語を検出します。 | 空でない言語名と、小文字2文字のISO 639-1コードであること |
?で終わる入力は任意です。最初の3列はコマンドマニフェストに基づいています
(内容にずれがあるとnpm testは失敗します)。完全なJSON SchemaはMCPサーバーが公開するものです。
仕組み
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)。まず機械的なチェック(スキーマ、正規表現、再計算、グラウンディング)を行います。コードでは表現できない基準には、src/verify.tsのllmJudgeを使用します。 - 事前条件ゲート。 コマンドの
inputSchemaに違反する入力は、問題点を示すメッセージとともに、処理の実行前に拒否されます。 - 決定性の段階。
pureコマンドは決定的なコードです。一度だけ実行され、キャッシュされ、無料で、モデルに送信されることはありません。stochasticコマンドはモデルによって実行され、フィードバックを受けて再試行されます。effectfulは、人間によるゲートが必要になるコマンド用に確保されています。 - 自由形式へのフォールバック。 モデルを使うすべての段階は、通常の Claude で終わります。Claude にも同じ出力スキーマと検証機能が適用されるため、検証済みのコマンドが適用される場合に性能が向上するだけで、オーケストレーターが通常の呼び出しより劣ることはありません。
- 隔離された呼び出し。 すべてのモデル呼び出し(
src/cc.ts)では、システムプロンプトを置き換え(--system-prompt)、設定を読み込まず(--setting-sources "")、ツールを有効にせず(--tools "")、セッションを保存せず、--json-schemaで回答を制約します。これは重要です。単純な呼び出しで、入力に含まれるメールアドレスではなく、オペレーター自身のメールアドレスが返されたことがあります。グローバルな CLAUDE.md、ID、フックなどの周辺コンテキストが漏れていたためです。 - 合成。
runPipeline(src/pipeline.ts)はコマンドを連結します。各ステップは次のステップの実行前に検証されるため、エラーが気付かれないまま連鎖することはありません。 - テレメトリとルーティング。 すべての試行が JSONL の1行として記録されます。
npm run scoresは CLI と評価ログからコマンドごとの信頼性を出力し、route()(src/router.ts)はその割合を小型モデルに提示します。このモデルは、コマンド名の列挙型から選択する必要があります。
設定
| 設定 | デフォルト | 効果 |
|---|---|---|
CONDUCTOR_HOME 環境変数 | ~/.conductor | MCP サーバーが telemetry.jsonl を追記する場所 |
マニフェスト内の model | sonnet | そのコマンドの呼び出しに使うモデル(ルーターと llmJudge は haiku を使用) |
マニフェスト内の maxAttempts | 2 | 次のフォールバック段階へ進む前に、フィードバックを受けて試行する回数(pure コマンドは1回だけ実行) |
モデルを使う呼び出しでは、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を参照してください)。 -
{ "input": ..., "expect"?: ... }のリストであるcommands/<name>/tests/cases.jsonを追加します。 -
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
制限事項と FAQ
- モデルを使用するツールには、ログイン済みの
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 - 何かにお金を払う前に、エージェントがより安く、誠実な方法を見つけます
- flipback - 毎ターンに自動で
/goalを適用し、途中で止まった Claude Code を作業に戻します - recall - MCP を介して、あらゆるエージェントで Claude Code のメモリを利用できます
- すべてのプロジェクト
コメント
AI アプリケーションが外部のツールやデータソースに統一的な方法で接続するためのオープンなプロトコルです。クライアントはモデルとの対話を担い、MCP サーバーはツール・リソース・プロンプトテンプレートを提供します。標準メッセージでやり取りするため、1 つのサーバーを複数のクライアントで再利用できます。