mcpField
C
customizations-in-the-agent-host

customizations-in-the-agent-host

github.com/microsoft開発194.4k47 回インストール今日更新
リポジトリ

customizations-in-the-agent-host について

プラグイン、MCP サーバー、エージェント、スキル、指示を含む Agent Host カスタマイズの有効化に関する開発者向けガイドです。スコープ別の判断、クライアントとホストの重ね合わせ、公開、プロバイダーごとの適用を説明します。

主な機能

  1. 01セッション、ワークスペース、全体の判断範囲
  2. 02クライアントの基準値とホストの上書きの分離
  3. 03安定した識別子による MCP サーバー設定の保存
  4. 04有効化の保留中はフェイルクローズ
  5. 05公開や引き渡し前の有効化状態の解決
  6. 06起動時とセッション中の適用制御の区別

活用シーン

  1. 01カスタマイズ有効化ロジックの変更
  2. 02保存されない MCP サーバー設定の調査
  3. 03公開前の有効化確認の徹底
  4. 04プロバイダー別の有効化制限の検証

MCP 対応

Agent Host が MCP サーバーの有効化を決定して公開し、各プロバイダーで起動時およびセッション中に適用する方法を説明します。

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/microsoft/vscode --skill customizations-in-the-agent-host

インストール後、Claude Code で/customizations-in-the-agent-hostを入力して読み込まれたか確認します。

詳細

エージェントホストのカスタマイズ

カスタマイズとは、エージェントホストのセッションがモデルに公開するプラグイン、MCP サーバー、エージェント、スキル、指示です。このスキルでは、有効化、つまりカスタマイズのオン/オフ状態がどのように決定、保存、公開、適用されるかを扱います。

以下を変更する前にお読みください。

  • src/vs/platform/agentHost/node/agentHostCustomizationEnablementService.ts
  • src/vs/platform/agentHost/node/shared/customizationEnablementGate.ts
  • src/vs/platform/agentHost/common/customizationEnablement.ts
  • node/copilot/、node/claude/、node/codex/ 以下のプロバイダーごとのカスタマイズパス

基本的な考え方

有効化はブール値ではなく、スコープ付き決定のリストです

プラグインまたは MCP サーバーは enablement?: CustomizationEnablement[] を持ちます。これは、より具体的なものから並べた決定のリストです。

種類所有者意味
Sessionエージェントホストこのセッションのみ
Workspaceエージェントホストこの作業ディレクトリ(uri を保持)
Globalクライアント。ただし、ホストが検出したものは除くすべての場所

isCustomizationEnabled() は最初のエントリを選びます。エントリがない場合は、次の外側のスコープから継承され、リストがない場合は有効になります。プラグインや MCP サーバーに並行する enabled ブール値を再び追加しないでください。二つ目の信頼できる情報源をなくすことが、この設計の目的です。(DirectoryCustomization は正当に通常の enabled を保持しますが、コンシューマーは同様に isCustomizationEnabled() を通じてその状態を導出します。)

Global の所有者

ホストは、クライアントから転送されたプラグイン内にある場合でも、ホストが検出したものについては Global を所有します。クライアントが Global を所有するのは、実際にバンドルしたものに限られます。判別に使うのは clientId ではなく、childEnablement です。プラグインの childEnablement レコードに子 MCP サーバーのキーがある場合、「クライアントがこの子をバンドルし、そのグローバルな決定を所有している」ことを意味します。それ以外は、ホストがプラグインディレクトリを解析して検出したものです。

オーバーレイモデル

これは最も重要な不変条件です。これを誤ると、ユーザーの決定をひそかに消去する、目的を損なう書き込みが発生しました。

クライアントの値がベースです。ホストの決定はその上に重ねられます。「異なる値のみを保存する」処理では、そのベースと比較します。ハードコードされた true と比較してはいけません。

具体的には、agentHostCustomizationEnablementService.ts では次のとおりです。

  • _clientGlobalEnablement は、セッションごとのインメモリのベースです。_setClientGlobal はここに書き込み、_persistent には決して触れてはいけません。
  • _persistent が保持するのは、ベースと異なるホスト所有の決定のみです。
  • 継承は、ホスト → ベース → DEFAULT_CUSTOMIZATION_ENABLED(_globalEnablement、_workspaceInheritedEnablement)の順で実行されます。
  • _setPersistentDecision は、継承された値と一致する決定を削除します。これにより、UI は「継承」アクションを三つ目に追加せずに継承状態へ戻せます。

これらを分離しておくことで、次のループを防げます。replaceEnablement → 同期的な再公開 → クライアントの古い [{global,true}] → _setClientGlobal → 「異なる値のみを保存」による、直前に書き込んだエントリの削除。

ID はツリー内の位置に依存してはいけません

MCP サーバーのストレージキーは、公開されたツリー内でどこに現れたかではなく、owningPluginUri から導出されます。トップレベル(mcpServers#azure)で検出されたサーバーと、プラグインの下(<pluginURI>#mcp=azure)で検出された同じサーバーが一つのキーになる必要があります。そうでないと、あるセッションで書き込んだ決定を次のセッションで確認できません。このターゲットを構築する際にサポートされる方法は、ゲート内の targetForMcpServer() と withOwningPluginUri() のみです。

pending はフェイルクローズにする。ただし、一時的な状態でなければなりません

セッションがまだ初期化されていない場合(reason: 'session')、または作業ディレクトリが不明な場合(reason: 'workingDirectory')、resolve() は {kind:'pending'} を返します。resolveCustomizationEnablement() はこれらを pendingCustomizationIds に記録し、isCustomizationSdkEligible() は SDK 境界のコンシューマーをフェイルクローズにします。

次の 2 つのルールに従ってください。

  1. pending のカスタマイズを、デフォルトで有効にして「修正」してはいけません。
  2. pending の判定をキャッシュしてはいけません。pending は一時的な状態です。再計算し、有効化サービスの状態が確定したら再公開してください。pendingEnablement: true をキャッシュすると、プラグインがひそかに永久に除外されます。

子 MCP サーバーが pending の場合、それを含むプラグインも pending になります。プラグインのディレクトリが原因で、SDK が子を独自に検出することがあるためです。

ほとんどのバグを防ぐルール

すべての公開、イベント発行、SDK への引き渡しの経路で、必ず解決処理を通してください。

この機能に関する本番環境のバグは 3 件あり、いずれも解決処理を通さない経路が原因でした(copilotAgent の 4 か所、mcpCustomizationController の 1 か所、および起動後の再調整)。カスタマイズを公開する箇所やプロバイダーに渡す箇所を新たに追加する場合は、resolveCustomizationEnablement() を経由させてください。保存済みのスナップショットを読み取ってはいけません。

その帰結として、一度だけ計算した状態は、入力が変わったときに再構築する必要があります。 クライアントごとの有効化マップを追加のみで更新すると、同じ URI を持つ新たに解析されたプラグインに古い判定が引き継がれます。

適用方法はプロバイダーごとに異なります

プロバイダー起動時セッション中
Copilotオープン/再開時の SessionConfig にある disabledMcpServersrpc.mcp.enable / rpc.mcp.disable
Claudeマテリアライズ時のセッションオプションライブ再調整
CodexMCP サーバーは thread/start/再開時にのみ提供不可能 — 仕様上の制限

適用時の注意事項:

  • 起動後の再調整は、起動から約 800 ms 後に実行されます。ここでのみ制御するものは、起動中にサーバーが開始し、認証リクエストを送信する可能性があります。起動時にも制御してください。
  • SDK の not_configured ステータスを enabled: true に変換してはいけません。このバグにより、正しく「この無効なサーバーをスキップする」という判断が、「有効にする」と誤認されました。
  • Copilot の起動時の制御では、クライアントのスナップショットだけでなく、ユーザーの mcp.config.list にある名前も含める必要があります。ホストが保持する前に、再調整と認証でも新たに検出された名前を解決します。その後の公開によって、継承された有効のデフォルト設定が、SDK の設定で無効にされたサーバーを有効にする明示的な意図に変わらないようにしてください。
  • ルート MCP カスタマイズ ID には、SDK のバックエンド ID ではなく、所有元である AHP セッション ID を使用します。復元時に、保持されたホスト公開済みのランタイムインベントリではなく、適用されたルート/プラグイン定義から MCP 名を導出してください。
  • 接続ライフサイクル(pending、connected、stopped)は、有効化の意図を示すものではありません。古い非同期スナップショットとホストが要求した変更を無視し、受動的な mcp.listConfigured の有効化状態と照合して、外部からの切り替えを確認してください。

クライアントが同梱する MCP サーバー(ホストが検出したのではなく、クライアントがバンドルしたもの)のグローバルな無効化は、ホストではなくクライアントにルーティングする必要があります。ホストはクライアント所有のグローバル設定の保存を正しく拒否するため、ホストにルーティングしても何も起きません。判別には isClientBundled を使用してください。mcpListWidget.ts の getBuiltinMcpServerEnablementActions を参照してください。

デバッグ手順

シグナルの優先順位を付けてください。シグナル同士は食い違い、モデルは嘘をつきます。

  1. 最優先 — 公開されたカスタマイズオブジェクトと、ホストに保存された判定です。
  2. ツールの公開状況を確定できる情報 — Agent Host.log の tool_search override … clientMatched=[…] です。
  3. 補助情報 — カスタマイズ UI です。
  4. 裏付けにしかならない情報 — モデルの発言です。サーバーが stopped のときに、モデルが「はい、azure のツールがあります」と回答したことがあります。これだけで結論を出さないでください。

実際の障害が起きる状況を再現する手順です。セッションを開始し、メッセージを送ってすべてを起動し、対象を無効にしてから、新しいセッションでそのツールが使えるかどうかを尋ねます。セッションをまたいで状態を保持すると、ID とオーバーレイのバグが表面化します。

エクスポートしたバンドルには agent-host-logs スキルを使い、実際のインスタンスの操作には launch スキルを使ってください。

既知の誤検知:

  • azure プラグインは MCP サーバーに加えて、24 個の azure-* スキルを提供します。サーバーを無効にしても、スキルが有効なままなのは正しい動作です。
  • sleepy は意図的に壊されたテストサーバーで、初期化できません。正常系の対照としては役に立ちません。

実際に時間を浪費する落とし穴

  • CustomizationType と CustomizationEnablementKind は const enum です。**型のみのインポートは、コンパイルも lint も問題なく通りますが、実行時にクラッシュします。**値としてインポートしてください。
  • 単体テストのランナーはプリコンパイル済みの出力を使うため、型エラーがあっても失敗しません。必ず別途 npm run typecheck-client を実行してください。
  • 統合テストのサーバーは --quiet を付けて起動します。agentHostServerMain.ts の if (!options.quiet) の外に移した配線は、挙動をひそかに変えてしまいます。setWorktreeIsolation を外に移したときは、quiet サーバーで isolation: 'folder' が解決され、無関係な 7 件のテストが失敗しました。
  • common/state/protocol/ 内のプロトコルファイルには、"DO NOT EDIT — auto-generated" のバナーがあります。これらを変更する場合は、agent-host-protocol リポジトリにも同じ変更をバックポートする必要があります。
  • Electron ランナーのセットアップ中断(EEXIST/EPERM/ENOENT/ReactiveObjC)は環境に起因します。rm -rf .build/electron && npm run electron を実行して復旧してください。

検証

npm run typecheck-client
./scripts/test.sh --grep "customizationEnablement"
./scripts/test-integration.sh --runGlob "**/agentHost/**/{sessionConfig,toolApproval,codexCustomizations}.integrationTest.js"
./scripts/test-integration.sh --run src/vs/platform/agentHost/test/node/claudeAgent.integrationTest.ts

エージェントセッションを直接構築するテストでは、サービスコレクションに IAgentHostCustomizationEnablementService が必要です。src/vs/platform/agentHost/test/node/testCustomizationEnablementService.ts の createNoopCustomizationEnablementService() を使ってください。

コメント