STUDY NOTES
第33回: Agent Client Protocol (ACP) で Zed エディタに deepagents を統合 — 連載の総まとめ¶
Agent Client Protocol (ACP) という新標準で、Zed エディタから自作 deepagents エージェントを呼び出す最小構成を作る。MCP(第19回)と双子の関係にある新プロトコル。
比喩: - MCP (Model Context Protocol) = 「LLM ホストが外部ツールを呼ぶ」プロトコル(Cursor → My MCP Server) - ACP (Agent Client Protocol) = 「エディタが外部エージェントを呼ぶ」プロトコル(Zed → My Agent Server)
どちらも JSON-RPC over stdio で動き、外部システムをサブプロセスとして立ち上げて疎結合に統合する LSP 系設計。MCP は「機能の提供」、ACP は「会話の提供」と思うと違いが分かりやすい。
サンプルコードはたった 28 行(server.ts)。deepagents-acp パッケージが全てを吸収しているので、最小限の boilerplate でエディタ統合が成立する。
| 観点 | 第32回 (deepagents + CopilotKit) | 第33回 (deepagents + ACP) |
|---|---|---|
| クライアント | Next.js React UI | Zed エディタ |
| プロトコル | HTTP (CopilotRuntime) | stdio + JSON-RPC (ACP) |
| エージェント側 | deepagents + LocalShellBackend |
deepagents + ACPFilesystemBackend |
| 起動 | mise run dev → localhost:3000 |
Zed が サブプロセスとして起動 |
| UI | CopilotChat | Zed のエージェントパネル |
| 行数 | 〜数百行 | 28 行のみ |
位置付け: 第19回 (MCP basics) → 第20回 (MCP × LangGraph) → 第32回 (deepagents × CopilotKit) と来た流れの 最終形。連載が示してきた「LLM アプリは独立サーバとして提供し、ホストが薄いプロトコルで統合する」設計思想を エディタ統合で完結する。
全体像¶
33/
├── server.ts ← ★ 28 行で完結。DeepAgentsServer をビルドして start
├── package.json ← @langchain/anthropic + deepagents-acp の 2 依存だけ
├── bun.lock
└── README.md ← Zed settings.json への登録方法
ランタイムフロー(Zed エディタからのメッセージ送信時):
sequenceDiagram
participant User
participant Zed as Zed Editor
participant Sub as bun run server.ts<br/>(subprocess)
participant Srv as DeepAgentsServer
participant Agent as deepagents エージェント<br/>(Claude Sonnet 4.6)
participant FS as ACPFilesystemBackend<br/>(virtualMode)
participant Files as ワークスペースファイル
Note over User,Zed: settings.json で<br/>"Deep Agents" を登録済み
User->>Zed: New Deep Agents Thread
Zed->>Sub: subprocess 起動 (stdio パイプ)
Sub->>Srv: server.start()
Sub-->>Zed: initialize (capabilities 交換)
User->>Zed: 「このバグ修正して」
Zed->>Sub: ACP message (JSON-RPC)
Sub->>Srv: route to agent
Srv->>Agent: invoke
loop ReAct ループ
Agent->>FS: readFile("./src/foo.ts")
FS->>Files: 実ファイル読み
Files-->>FS: 内容
FS-->>Agent: ファイル内容
Agent->>FS: writeFile("./src/foo.ts", patched)
FS->>Files: 書き込み
FS-->>Agent: OK
end
Agent-->>Srv: 最終応答
Srv-->>Sub: streaming chunks
Sub-->>Zed: ACP message (JSON-RPC)
Zed-->>User: エージェントパネルに表示
ACP の本質: Zed エディタは ACP プロトコルさえ守れば、どんなエージェント(Claude / GPT / Gemini / 自作)も統合できる。LSP が「言語サーバを切り替えれば任意の言語を扱える」のと同じく、エディタ × エージェントの組合せ爆発を防ぐ標準。
使用ライブラリ・原理¶
deepagents-acp パッケージ — ACP プロトコルを完全カプセル化¶
import { DeepAgentsServer, ACPFilesystemBackend } from "deepagents-acp";
const server = new DeepAgentsServer({
agents: {
name: "coding-assistant",
description: "Deep Agentsベースのコーディングアシスタント",
model: "claude-sonnet-4-6",
},
debug: true,
});
await server.start();
DeepAgentsServer がやってくれること:
- stdio リスナー起動 (Zed から JSON-RPC を受信)
- ACP プロトコルメッセージのパース (initialize, sessions/new, messages/send 等)
- 内部で
deepagentsのcreateDeepAgentを呼んで通常のエージェント構築 agentsconfig から model / system prompt を組み立て- ストリーミング応答を ACP 仕様に従って返す
ACPFilesystemBackend — Zed のファイルシステム API を経由したアクセス¶
const backend = new ACPFilesystemBackend(...);
backend.virtualMode = true;
backend.resolveAbsPath = function (filePath) {
return this.resolvePath(filePath);
};
特徴:
- ACP プロトコル経由で Zed が開いているワークスペース内のファイルを読み書き
virtualModeで workspace 外への到達を制限- Zed が「ユーザに見えるファイル変更」を可視化できる(diff 表示など)
- LocalShellBackend (32回) が「Node.js プロセスから直接 fs.readFile」だったのに対し、これは エディタ越し にアクセス
server.createBackend フックの上書き (workaround)¶
const originalCreateBackend = server.createBackend.bind(server);
server.createBackend = function (config) {
const backend = originalCreateBackend(config);
if (backend instanceof ACPFilesystemBackend) {
backend.virtualMode = true;
backend.resolveAbsPath = function (filePath) {
return this.resolvePath(filePath);
};
}
return backend;
};
これは ACPFilesystemBackend のバグ回避コード:
deepagents-acp 0.1.8のACPFilesystemBackendはvirtualMode: trueを public API で設定する手段がない(バグ)- かつ
resolveAbsPathメソッドが未実装で、仮想パスを実パスに変換できない - 対策として
server.createBackendをオーバーライドし、生成された backend に手動でvirtualMode = trueとresolveAbsPathを後付けインジェクション
将来の deepagents-acp リリースで修正されれば、この workaround は不要になる。
Agent Client Protocol (ACP) — Zed が標榜する「エージェント統合の LSP」¶
ACP の主要メッセージ:
| メッセージ | 役割 |
|---|---|
initialize |
Capabilities 交換 |
sessions/new |
新しい会話スレッド作成 |
messages/send |
ユーザメッセージ送信 |
messages/stream |
エージェントからのストリーミング応答 |
sessions/abort |
実行中の処理を中断 |
tools/call |
エージェント側から「Zed のツール」(ファイル読み書き等)を呼ぶ |
MCP との対比: - MCP: ホスト(Cursor)→ サーバ(自作ツール)の「機能呼び出し」 - ACP: ホスト(Zed)→ サーバ(自作エージェント)の「会話セッション」 - どちらも JSON-RPC 2.0 over stdio という同じトランスポートで動く
ファイル別の役割¶
| ファイル | 役割 |
|---|---|
server.ts |
唯一の実コード。DeepAgentsServer 設定 + バックエンドのバグ workaround + server.start() |
package.json |
@langchain/anthropic と deepagents-acp のみの 2 依存 |
bun.lock |
bun の依存ロック |
README.md |
mise install + Zed settings.json への登録方法 |
行レベルの工夫(中核ロジックの抜粋)¶
① DeepAgentsServer の最小設定 (server.ts:1-11)¶
import { DeepAgentsServer, ACPFilesystemBackend } from "deepagents-acp";
const server = new DeepAgentsServer({
agents: { // ①
name: "coding-assistant",
description: "Deep Agentsベースのコーディングアシスタント",
model: "claude-sonnet-4-6",
},
debug: true, // ②
});
| 行 | やってること | なぜそうする |
|---|---|---|
| ① | agents config に name/description/model を 1 つだけ指定 |
内部で createDeepAgent(model: ChatAnthropic(...)) 等を組み立ててくれる。LangChain の boilerplate が消える |
| ② | debug: true で詳細ログ |
開発中は ACP プロトコルの送受信を全部 stderr に出す。Zed のログパネルで監視できる |
② virtualMode 有効化の workaround (server.ts:15-25)¶
const originalCreateBackend = server.createBackend.bind(server); // ①
server.createBackend = function (config) {
const backend = originalCreateBackend(config); // ②
if (backend instanceof ACPFilesystemBackend) {
backend.virtualMode = true; // ③
backend.resolveAbsPath = function (filePath) { // ④
return this.resolvePath(filePath);
};
}
return backend;
};
| 行 | やってること | なぜそうする |
|---|---|---|
| ① | server.createBackend をオリジナル参照保持 |
純粋に「メソッド差し替え」する monkey patch パターン |
| ② | オリジナルを呼んで通常通り backend 生成 | バックエンドの初期化ロジックは触らない |
| ③ | virtualMode = true を後付け代入 |
deepagents-acp 0.1.8 には public API での設定経路がないため、direct assignment で強制有効化 |
| ④ | resolveAbsPath を resolvePath 経由でフォールバック実装 |
内部で resolveAbsPath が呼ばれるが未実装なので、resolvePath で代用 |
③ await server.start() (server.ts:27)¶
これだけで:
- stdio リスナー起動(Zed からの subprocess 起動を待ち受け)
- ACP プロトコル全メッセージのハンドラ登録
- プロセスが alive な限り会話セッション継続
プロセスは Zed が kill するまで生き続ける。subprocess なので、Zed を閉じれば自動終了。
④ Zed settings.json への登録 (README より)¶
{
"agent_servers": {
"Deep Agents": {
"type": "custom",
"command": "bun",
"args": [
"run",
"/absolute/path/to/softwaredesign-llm-application/33/server.ts"
],
"env": {
"ANTHROPIC_API_KEY": "sk-ant-..."
}
}
}
}
ポイント:
type: "custom"で「自作 ACP サーバ」と宣言(公式 Claude/Cursor agent 等とは別ジャンル)- 絶対パス必須 (ACP 仕様)。相対パスでは Zed が解決できない
envで APIキー注入。Zed は subprocess を.env抜きで起動するので、settings.json で直接渡す必要がある(ここに API キー生書きは要注意)
学んだこと(要点)¶
- ACP は MCP の双子。MCP が「機能提供」、ACP が「会話提供」。両方とも LSP 系設計 で、stdio + JSON-RPC で疎結合化
deepagents-acpパッケージが 28 行で全てを覆い隠す。LangChain + LangGraph + ACP の三層がDeepAgentsServer1 行で組み立つ- Zed エディタが ACP 公式ホスト。VS Code は MCP 寄り、Zed は ACP 寄りという棲み分け
ACPFilesystemBackendは Zed 越しにファイル操作するので、エディタ側で diff 表示・編集確認 UI が綺麗に出る- 公式ライブラリのバグ workaround を直接書くスタイルは production では避けたいが、サンプルなら可。
prototypechain やinstanceへの monkey patch で対応する - 「Zed の
settings.jsonに API キー直書き」がデフォルト: production 用途では Zed Secrets API 経由などの secure な仕組みが必要 - Zed が
subprocessとして agent を起動するので、.envファイルは無視される。全環境変数を settings.json か Zed の env 設定で渡す必要がある - 連載の総まとめとして、第19回 (MCP basics) → 第20-22回 (MCP + LangGraph) → 第29-31回 (Middleware) → 第32回 (CopilotKit) → 第33回 (ACP) のアーキテクチャ進化が美しい
拡張アイデア¶
- スキルファイルを追加 —
workspace/.agent/skills/に SKILL.md を置いて、エージェントに「コードレビュー」「リファクタリング案出し」「テスト生成」等のスキルを足す(第32回パターンと同じ) - 複数モデル切替 UI — Zed の
settings.jsonでclaude-sonnet-4-6をclaude-opus-4-7やclaude-haiku-4-5に切替して比較 - MCP との統合 — DeepAgents の
toolsに MCP ツール(mcp-useパッケージ等)を混ぜる。MCP × ACP の二重統合 - エージェント別 server を複数登録 —
coding-assistant/doc-writer/test-generatorなどを別 ACP サーバとして起動 → Zed で切替 virtualModeバグの upstream PR —deepagents-acpの GitHub に Issue/PR を立てて、virtualModeを public API で設定可能にする- HumanInTheLoop パターン — ファイル書き込み前に Zed 側で承認ダイアログ(第31回パターン)
- 会話のスレッド永続化 —
MemorySaverでなくSqliteSaverをDeepAgentsServerに注入し、Zed を再起動しても会話継続 - 他のエディタへの展開 — ACP は Zed 専用ではない(仕様は公開)。Neovim / Emacs / VS Code のプラグインを書いて同じ ACP サーバを統合
現代版に移植するなら¶
1. API キー設定は .env.op + op run に切り替える(CLAUDE.md ルール 8)¶
通常起動なら:
ただし Zed 経由起動には特殊な工夫が必要: Zed が subprocess を .env なしで起動するので、op run を間に挟む必要がある。Zed の settings.json を以下に書き換え:
{
"agent_servers": {
"Deep Agents": {
"type": "custom",
"command": "op",
"args": [
"run",
"--env-file=/absolute/path/to/softwaredesign-llm-application/33/.env.op",
"--",
"bun",
"run",
"/absolute/path/to/softwaredesign-llm-application/33/server.ts"
]
}
}
}
これで op run が API キーを展開してから bun run server.ts を起動 → settings.json に生キーが残らない。
2. claude-sonnet-4-6 の最新化チェック¶
2026 年現在 claude-sonnet-4-6 は十分新しい。claude-opus-4-7 に上げると推論力が向上するがコスト 5 倍。コーディングタスクでは sonnet-4-6 がコスパ最高なのでこのままで OK。
3. deepagents-acp バージョンアップ追従¶
0.1.8 から将来 virtualMode が public API 化されたら、server.ts:13-25 の workaround を削除して config 経由に書き換え:
const server = new DeepAgentsServer({
agents: {...},
backendConfig: {
type: "filesystem",
virtualMode: true, // 公式 API 化したら
},
});
4. await server.start() のエラーハンドル¶
現状は start() が throw すると bun プロセスが終了。Zed 側で再起動しないと回復しない。try-catch + 再接続ループ を入れると堅い:
while (true) {
try {
await server.start();
break;
} catch (e) {
console.error("[server] crashed:", e);
await new Promise((r) => setTimeout(r, 1000));
}
}
5. ログを構造化¶
debug: true で出る生ログを stderr に流すと Zed のログパネルがノイジー。pino 等で JSON 化して info 以上だけ stderr に出すと整理される。
既知の不具合・注意点¶
deepagents-acp 0.1.8のACPFilesystemBackendバグ: 上記の通りvirtualModeとresolveAbsPathがない。workaround を入れているが、ライブラリ更新時に削除を忘れる可能性- settings.json の絶対パス: クローン場所が変わると settings.json も更新必要。マシン間で
~/.config/zed/settings.jsonを同期している場合は注意 - 環境変数の二重管理:
.envと settings.json の両方に書く運用になりがち(うっかり)。1 つの真の出所(op)に集約推奨 agentsconfig の単数指定: README にはagentsを 1 つだけ書く例。複数 agents 登録時のフォーマットが docs 不明(要確認)- stdio バッファリング: Zed が subprocess の stdout を都度フラッシュ前提で待つので、Node.js のデフォルトバッファリングと相性が悪い場面あり。
debug: trueで発覚しがち - エージェントが任意のファイルを編集する: コーディングアシスタントなので 意図せず重要ファイルを書き換える リスク。git で常にコミットしてから依頼する慣習が必要
- ANTHROPIC_API_KEY を settings.json に生書きする推奨例: README が
"ANTHROPIC_API_KEY": "sk-ant-..."を例示。グローバルルール(生キー禁止)に反する のでop run経由に必ず置換すべき - Zed プロセスが死ぬと subprocess も孤児化: bun プロセスが孤児として残る場面あり。手動 kill か
process.on('disconnect')ハンドラ実装
記事参照¶
- Software Design 2026 年 6 月号(推定)連載第33回(最終回)「ACP で Zed × 自作エージェント」
- 関連: 第19回 STUDY_NOTES — MCP basics。プロトコル設計の妹分
- 関連: 第32回 STUDY_NOTES —
deepagentsを CopilotKit で React と統合 - 関連: 第20回 STUDY_NOTES — MCP × LangGraph。stdio + JSON-RPC の同じ流儀
- ACP 公式: https://agentclientprotocol.com/
deepagents-acp: https://github.com/langchain-ai/deepagentsjs/tree/main/libs/acp- Zed External Agents: https://zed.dev/docs/ai/external-agents
- 連載全体の振り返り: 第1回 (Chainlit) → 第33回 (ACP + Zed) の 3 年間で 「単発 LLM 呼び出し」から「プロトコル標準化された分散エージェント」まで一直線
作成: 2026-05-25 / 最終更新: 2026-06-22