Follow up PR #265: refine chapters, diagrams, and add S20 (#283)

* feat: s01-s14 docs quality overhaul — tool pipeline, single-agent, knowledge & resilience

Rewrite code.py and README (zh/en/ja) for s01-s14, each chapter building
incrementally on the previous. Key fixes across chapters:

- s01-s04: agent loop, tool dispatch, permission pipeline, hooks
- s05-s08: todo write, subagent, skill loading, context compact
- s09-s11: memory system, system prompt assembly, error recovery
- s12-s14: task graph, background tasks, cron scheduler

All chapters CC source-verified. Code inherits fixes forward (PROMPT_SECTIONS,
json.dumps cache, real-state context, can_start dep protection, etc.).

* feat: s15-s19 docs quality overhaul — multi-agent platform: teams, protocols, autonomy, worktree, MCP tools

Rewrite code.py and README (zh/en/ja) for s15-s19, the multi-agent platform
chapters. Each chapter inherits all previous fixes and adds one mechanism:

- s15: agent teams (TeamCreate, teammate threads, shared task list)
- s16: team protocols (plan approval, shutdown handshake, consume_inbox)
- s17: autonomous agents (idle polling, auto-claim, consume_lead_inbox)
- s18: worktree isolation (git worktree, bind_task, cwd switching, safety)
- s19: MCP tools (MCPClient, normalize_mcp_name, assemble_tool_pool, no cache)

All appendix source code references verified against CC source. Config priority
corrected: claude.ai < plugin < user < project < local.

* fix: 5 regressions across s05-s19 — glob safety, todo validation, memory extraction, protocol types, dep crash

- s05-s09: glob results now filter with is_relative_to(WORKDIR) (inherited from s02)
- s06-s08: todo_write validates content/status required fields (inherited from s05)
- s09: extract_memories uses pre-compression snapshot instead of compacted messages
- s16: submit_plan docstring clarifies protocol-only (not code-level gate)
- s17-s19: match_response restores type mismatch validation (from s16)
- s17-s19: claim_task deps list handles missing dep files without crashing

* fix: s12 Todo V2 logic reversal, s14/s15 cron range validation, s18/s19 worktree name validation

- s12 README (zh/en/ja): fix Todo V2 direction — interactive defaults to Task,
  non-interactive/SDK defaults to TodoWrite. Fix env var name to
  CLAUDE_CODE_ENABLE_TASKS (not TODO_V2).
- s14/s15: add _validate_cron_field with per-field range checks (minute 0-59,
  hour 0-23, dom 1-31, month 1-12, dow 0-6), step > 0, range lo <= hi.
  Replace old try/except validation that only caught exceptions.
- s18/s19: add validate_worktree_name() to remove_worktree and keep_worktree,
  not just create_worktree.

* fix: align s16-s19 teaching tool consistency

* fix pr265 chapter diagrams

* Add comprehensive s20 harness chapter

* Fix chapter smoke test regressions

* Clarify README tutorial track transition

---------

Co-authored-by: Haoran <bill-billion@outlook.com>
This commit is contained in:
gui-yue
2026-05-20 21:45:38 +08:00
committed by GitHub
parent c354cf7721
commit 1baf1aca5a
174 changed files with 35833 additions and 353 deletions

282
s19_mcp_plugin/README.en.md Normal file
View File

@@ -0,0 +1,282 @@
# s19: MCP Tools — External Tools, Standard Protocol
[中文](README.md) · [English](README.en.md) · [日本語](README.ja.md)
s01 → ... → s17 → s18 → `s19` → [s20](../s20_comprehensive/)
> *"External tools, standard protocol"* — Discover, assemble, invoke. Agent doesn't need to know who wrote them.
>
> **Harness layer**: Plugins — External capabilities via a standard protocol.
---
## The Problem
From s01 through s18, every tool the agent uses was hand-written — bash, read, write, task, worktree. Input validation, execution logic, error handling — all written line by line.
Now you have 3 external services to integrate: the company's Jira API (query issues, create tickets), an in-house deployment system (trigger deploys, view logs), and the team's Notion knowledge base (search docs, create pages). You don't want to rewrite tool code for every service.
You need a standard protocol — as long as an external service implements it, the agent can call its tools directly, regardless of what language the service is written in.
---
## The Solution
![MCP Architecture](images/mcp-architecture.en.svg)
MCP (Model Context Protocol) defines how agents discover and invoke external tools. Core concepts:
| Concept | Purpose |
|------|------|
| MCPClient | The agent-side client — connects to servers, discovers tools, invokes tools |
| MCP Server | The external service — implements `tools/list` + `tools/call` |
| assemble_tool_pool | Assembles built-in tools and MCP tools into one tool pool |
| mcp\_\_server\_\_tool naming | Prevents tool name collisions across different servers |
Carries forward s18's teaching-version worktree isolation, autonomous claiming, idle polling, and protocol system. This chapter adds: the `connect_mcp` tool — connect to external services, discover tools, add them to the tool pool.
The tutorial uses mock handlers to simulate external servers. The real version would spawn subprocesses and communicate via stdin/stdout JSON-RPC. Mocks let you run the full flow without external dependencies; the tradeoff is you don't see real network communication or process management.
---
## How It Works
### MCPClient: Discovery + Invocation
```python
class MCPClient:
def __init__(self, name: str):
self.name = name
self.tools: list[dict] = []
self._handlers: dict[str, callable] = {}
def register(self, tool_defs, handlers):
"""Simulates tools/list discovery."""
self.tools = tool_defs
self._handlers = handlers
def call_tool(self, tool_name: str, args: dict) -> str:
"""Simulates tools/call."""
handler = self._handlers.get(tool_name)
if not handler:
return f"MCP error: unknown tool '{tool_name}'"
return handler(**args)
```
The tutorial uses Python functions to simulate server tool implementations. The real version communicates with subprocesses via stdio JSON-RPC.
### connect_mcp: Connect + Discover
```python
def connect_mcp(name: str) -> str:
if name in mcp_clients:
return f"MCP server '{name}' already connected"
factory = MOCK_SERVERS.get(name)
if not factory:
return f"Unknown server '{name}'. Available: ..."
mcp_client = factory()
mcp_clients[name] = mcp_client
return f"Connected to '{name}'. Discovered: ..."
```
After connecting, the server's tools are immediately available.
### normalize_mcp_name: Name Normalization
```python
_DISALLOWED_CHARS = re.compile(r'[^a-zA-Z0-9_-]')
def normalize_mcp_name(name: str) -> str:
return _DISALLOWED_CHARS.sub('_', name)
```
All non-`[a-zA-Z0-9_-]` characters are replaced with `_`. Prevents special characters in server or tool names from causing naming conflicts or injection issues.
### assemble_tool_pool: Assemble Tool Pool
```python
def assemble_tool_pool() -> tuple[list[dict], dict]:
tools = list(BUILTIN_TOOLS)
handlers = dict(BUILTIN_HANDLERS)
for server_name, mcp_client in mcp_clients.items():
safe_server = normalize_mcp_name(server_name)
for tool_def in mcp_client.tools:
safe_tool = normalize_mcp_name(tool_def["name"])
prefixed = f"mcp__{safe_server}__{safe_tool}"
tools.append(...)
handlers[prefixed] = (
lambda *, c=mcp_client, t=tool_def["name"], **kw:
c.call_tool(t, kw))
return tools, handlers
```
The prefix `mcp__{server}__{tool}` prevents tool name collisions across different servers. Names are normalized through `normalize_mcp_name`.
MCP tool descriptions include `(readOnly)` or `(destructive)` annotations — the tutorial uses text annotations, while real CC uses structured tool annotations for the permission system.
### No Cache: Tool Pool Changes, Prompt Changes Too
s10-s18's agent_loop used prompt caching to avoid re-serialization. s19 removes the cache:
```python
def agent_loop(messages, context):
tools, handlers = assemble_tool_pool() # Rebuild every time
system = assemble_system_prompt(context) # Regenerate every time
...
if any(b.name == "connect_mcp" ...):
tools, handlers = assemble_tool_pool() # Rebuild after connection
system = assemble_system_prompt(context)
```
Reason: after `connect_mcp`, the tool pool changes — new tools like `mcp__docs__search` are added. The cached tool list is stale; continuing to use it means the model can't call the new tools. The tutorial simply removes caching, at the cost of slightly more serialization time.
### MCP Tools: Lead Only
In the tutorial, `connect_mcp` is a Lead tool, and `assemble_tool_pool` only serves the Lead's agent_loop. Teammates still use a fixed 8-tool subset (bash, read_file, write_file, send_message, submit_plan, list_tasks, claim_task, complete_task).
This is a teaching simplification. In real CC, MCP tools are available to both the main agent and sub-agents — sub-agents inherit the parent's MCP configuration.
---
## Changes from s18
| Component | Before (s18) | After (s19) |
|------|-----------|-----------|
| Tool source | All hand-written built-in | Hand-written + MCP external tools with dynamic discovery |
| Tool pool | Fixed BUILTIN_TOOLS | assemble_tool_pool dynamically assembles mcp\_\_ prefixed tools |
| Name safety | None | normalize_mcp_name normalization |
| New type | — | MCPClient class (simulates tools/list + tools/call) |
| Namespace | — | mcp\_\_server\_\_tool prevents collisions |
| Tool descriptions | No annotations | (readOnly)/(destructive) annotations |
| Prompt cache | Yes (since s10) | Removed — tool pool is dynamic, cache goes stale |
| Lead tools | 17 (s18) | 18 (+connect_mcp) |
| Teammate tools | 8 (s18) | 8 (unchanged, MCP tools are Lead-only) |
| Extension method | Write code to add tools | Standard protocol, implement servers in any language |
---
## Try It Out
```sh
cd learn-claude-code
python s19_mcp_plugin/code.py
```
Try these prompts:
1. `Connect to the docs MCP server and search for something`
2. `Connect to the deploy server and trigger a deployment`
3. `Connect both servers — what tools are now available?`
What to observe: After connecting to an MCP server, do tool names have `mcp__docs__` or `mcp__deploy__` prefixes? Are both servers' tools available simultaneously? Do MCP tool descriptions include (readOnly)/(destructive) annotations?
---
## What's Next
The Agent can now connect external tools through a standard protocol. But the first 19 chapters each add one mechanism in isolation; a real Agent does not run as 19 separate demos.
Tools, permissions, hooks, todo, task graph, memory, compact, background work, cron, teams, worktrees, and MCP should all attach to the same loop, not live in separate examples.
s20 Comprehensive Agent → Combine the first 19 chapters into one complete harness. Many mechanisms, one loop.
<details>
<summary>Deep Dive into CC Source</summary>
> The following is based on analysis of CC source: `services/mcp/client.ts`, `auth.ts`, `config.ts`, `channelNotification.ts`.
### 1. Six Transport Types
The tutorial only shows a stdio mock. CC supports 6 transport types (`types.ts:23-25`):
| Transport | Communication method |
|-----------|---------|
| `stdio` | Subprocess stdin/stdout (cross-platform default) |
| `sse` | HTTP Server-Sent Events |
| `http` | Streamable HTTP (POST/SSE bidirectional) |
| `ws` | WebSocket |
| `sse-ide` | IDE-embedded SSE transport |
| `sdk` | In-process SDK transport |
On connection, local (stdio) and remote (http/sse/ws) servers are batched concurrently: local batch of 3, remote batch of 20.
### 2. Tool Pool Merging Algorithm
`assembleToolPool()` (`tools.ts:345-364`):
```typescript
// Dedup with priority: built-in tools win on name collision (sorted first)
return uniqBy(
[...builtInTools.sort(byName), ...filteredMcpTools.sort(byName)],
'name',
)
```
Built-in and MCP tools are sorted separately, not together. The reason is CC's `claude_code_system_cache_policy` places a global cache breakpoint after the last built-in tool at a specific position — mixing the sort would break this design.
### 3. Naming Convention: `mcp__server__tool`
`buildMcpToolName()` (`mcpStringUtils.ts:50-52`):
```
mcp__<normalizedServerName>__<normalizedToolName>
```
All non-`[a-zA-Z0-9_-]` characters are replaced with `_` (`normalization.ts:17-23`). The tutorial's `normalize_mcp_name` uses the same rule.
### 4. Permission Checks
CC has a separate permission system for MCP tools. `checkPermissions()` applies different logic for MCP tools than for built-in tools — MCP tools can declare their own permission requirements (readOnly, destructive, etc.), and CC decides whether user confirmation is needed based on the declaration. The tutorial only uses text annotations `(readOnly)` / `(destructive)` in descriptions, without permission enforcement.
### 5. Configuration Sources and Priority
MCP server configuration comes from multiple sources. CC's priority from lowest to highest:
```
claude.ai connectors < plugin < user settings.json < approved project .mcp.json < local settings.local.json
```
`claude.ai` connectors are fetched separately, deduplicated by content signature, and merged at the lowest precedence (`config.ts:1267-1289`). When enterprise `managed-mcp.json` exists, all other configurations are excluded.
The tutorial passes server names directly to the `MOCK_SERVERS` dict, without config merging.
### 6. Channel Notifications: Servers Push Messages Back
The tutorial only covers agent → MCP Server unidirectional calls. CC also supports reverse notifications (`channelNotification.ts`):
1. Server declares `capabilities.experimental['claude/channel']`
2. Server sends messages to agent via MCP notification `notifications/claude/channel`
3. Messages are wrapped in `<channel source="serverName">...</channel>` XML tags
4. Agent is woken up by SleepTool (within 1 second)
Servers can also request permissions: `notifications/claude/channel/permission_request` → Agent replies `notifications/claude/channel/permission`. Users confirm/deny via a 5-letter short ID.
### 7. OAuth Authentication Flow
CC's MCP authentication (`auth.ts`) supports a full OAuth 2.0 + PKCE flow:
- OAuth metadata discovery via public client + PKCE (RFC 8414 / RFC 9728)
- Local callback server receives authorization code
- Tokens persisted via `getSecureStorage()` (macOS Keychain / Linux encrypted file / Windows Credential Manager)
- Auto-refresh 5 minutes before expiry
- Cross-application access (XAA): browser gets id_token → RFC 8693 + RFC 7523 exchange → no repeated browser popups
### 8. Connection Lifecycle Error Handling
CC has fine-grained error classification and retry for MCP connections (`client.ts:1266-1402`):
- Terminal errors (ECONNRESET, ETIMEDOUT, EPIPE, etc.): 3 consecutive failures → close + reconnect
- Tool call 401: Token expired → throw `McpAuthError` → trigger re-authentication
- Tool call timeout: `Promise.race` timeout (configurable, default ~28 hours)
- Stdio disconnect: Kill process in SIGINT → SIGTERM → SIGKILL order
### The Tutorial's Simplifications
- 6 transport types → 1 (mock stdio): Manageable concept count
- Channel reverse notifications → omitted: Tutorial agent is always the initiator
- OAuth flow → omitted: Tutorial assumes servers need no auth
- Multi-layer config priority → omitted: Tutorial passes server name directly
- Complex error classification → omitted: Tutorial uses try/except as fallback
- MCP tools Lead-only → omitted sub-agent inheritance: Simplifies code structure
</details>
<!-- translation-sync: zh@v2, en@v2, ja@v0 -->

282
s19_mcp_plugin/README.ja.md Normal file
View File

@@ -0,0 +1,282 @@
# s19: MCP Tools — 外部ツール、標準プロトコル
[中文](README.md) · [English](README.en.md) · [日本語](README.ja.md)
s01 → ... → s17 → s18 → `s19` → [s20](../s20_comprehensive/)
> *"外部ツール、標準プロトコル"* — 発見、組み立て、呼び出し。Agent はツールを誰が書いたか知る必要がない。
>
> **Harness 層**: プラグイン — 外部能力を標準プロトコルで接続。
---
## 課題
s01 から s18 まで、Agent の全ツールは手書き — bash、read、write、task、worktree。入力検証、実行ロジック、エラーハンドリング、全て一行ずつ書いた。
今、統合したい外部サービスが 3 つある:社内の Jira APIissue 検索、ticket 作成、独自のデプロイシステムdeploy トリガー、ログ閲覧)、チームの Notion ナレッジベース(ドキュメント検索、ページ作成)。各サービスのためにツールコードを書き直したくない。
標準プロトコルが必要 — 外部サービスがこのプロトコルを実装していれば、サービスが何の言語で書かれていても、Agent は直接そのツールを呼び出せる。
---
## ソリューション
![MCP Architecture](images/mcp-architecture.ja.svg)
MCPModel Context Protocolは、Agent が外部ツールを発見・呼び出しする方法を定義。核心概念:
| 概念 | 目的 |
|------|------|
| MCPClient | Agent 側のクライアント — server に接続、ツールを発見、ツールを呼び出し |
| MCP Server | 外部サービス側 — `tools/list` + `tools/call` を実装 |
| assemble_tool_pool | 組み込みツールと MCP ツールを一つのツールプールに組み立てる |
| mcp\_\_server\_\_tool 命名 | 異なる server 間のツール名衝突を防止 |
s18 の教学版 worktree 隔離、自動認領、空き時ポーリング、プロトコルシステムを踏襲。本章の追加:`connect_mcp` ツール — 外部サービスに接続、ツールを発見、ツールプールに追加。
教学版は mock handler で外部 server をシミュレート。実際の版はサブプロセスを起動し、stdin/stdout で JSON-RPC リクエストを送信。mock の利点は外部サービスなしで完全なフローを実行できること;代償は実際のネットワーク通信やプロセス管理が見えないこと。
---
## 仕組み
### MCPClient発見 + 呼び出し
```python
class MCPClient:
def __init__(self, name: str):
self.name = name
self.tools: list[dict] = []
self._handlers: dict[str, callable] = {}
def register(self, tool_defs, handlers):
"""Simulates tools/list discovery."""
self.tools = tool_defs
self._handlers = handlers
def call_tool(self, tool_name: str, args: dict) -> str:
"""Simulates tools/call."""
handler = self._handlers.get(tool_name)
if not handler:
return f"MCP error: unknown tool '{tool_name}'"
return handler(**args)
```
教学版は Python 関数で server のツール実装をシミュレート。実際の版は stdio JSON-RPC でサブプロセスと通信。
### connect_mcp接続 + 発見
```python
def connect_mcp(name: str) -> str:
if name in mcp_clients:
return f"MCP server '{name}' already connected"
factory = MOCK_SERVERS.get(name)
if not factory:
return f"Unknown server '{name}'. Available: ..."
mcp_client = factory()
mcp_clients[name] = mcp_client
return f"Connected to '{name}'. Discovered: ..."
```
接続後、server が提供するツールが即座に利用可能。
### normalize_mcp_name名前の正規化
```python
_DISALLOWED_CHARS = re.compile(r'[^a-zA-Z0-9_-]')
def normalize_mcp_name(name: str) -> str:
return _DISALLOWED_CHARS.sub('_', name)
```
`[a-zA-Z0-9_-]` 以外の全文字を `_` に置換。server 名やツール名の特殊文字による名前衝突やインジェクション問題を防止。
### assemble_tool_poolツールプールの組み立て
```python
def assemble_tool_pool() -> tuple[list[dict], dict]:
tools = list(BUILTIN_TOOLS)
handlers = dict(BUILTIN_HANDLERS)
for server_name, mcp_client in mcp_clients.items():
safe_server = normalize_mcp_name(server_name)
for tool_def in mcp_client.tools:
safe_tool = normalize_mcp_name(tool_def["name"])
prefixed = f"mcp__{safe_server}__{safe_tool}"
tools.append(...)
handlers[prefixed] = (
lambda *, c=mcp_client, t=tool_def["name"], **kw:
c.call_tool(t, kw))
return tools, handlers
```
プレフィックス `mcp__{server}__{tool}` で異なる server 間のツール名衝突を防止。名前は `normalize_mcp_name` で正規化。
MCP ツールの description に `(readOnly)` または `(destructive)` アノテーションを付与 — 教学版はテキストアノテーション、実際の CC は tool annotations 構造体で権限システムが判断。
### キャッシュなし:ツールプールが変われば、プロンプトも変わる
s10-s18 の agent_loop は prompt cache で再シリアライズを回避。s19 はキャッシュを削除:
```python
def agent_loop(messages, context):
tools, handlers = assemble_tool_pool() # 毎回再構築
system = assemble_system_prompt(context) # 毎回再生成
...
if any(b.name == "connect_mcp" ...):
tools, handlers = assemble_tool_pool() # 接続後に再構築
system = assemble_system_prompt(context)
```
理由:`connect_mcp` 後にツールプールが変化 — `mcp__docs__search` などの新ツールが追加される。キャッシュ内のツールリストは古く、使い続けるとモデルが新ツールを呼び出せない。教学版はキャッシュを単に削除、代償はシリアライズ時間の若干の増加。
### MCP ツールは Lead のみ利用可能
教学版では、`connect_mcp` は Lead ツール、`assemble_tool_pool` も Lead の agent_loop のみにサービスを提供。チームメイトは引き続き固定の 8 ツールサブセットbash、read_file、write_file、send_message、submit_plan、list_tasks、claim_task、complete_taskを使用。
これは教学簡略化。実際の CC では、MCP ツールはメイン agent とサブ agent の両方で利用可能 — サブ agent は親の MCP 設定を継承。
---
## s18 からの変更
| コンポーネント | 変更前 (s18) | 変更後 (s19) |
|--------------|------------|------------|
| ツールソース | 全て手書き builtin | 手書き + MCP 外部ツール動的発見 |
| ツールプール | 固定 BUILTIN_TOOLS | assemble_tool_pool が動的に mcp\_\_ プレフィックスツールを組み立てる |
| 名前の安全性 | なし | normalize_mcp_name 正規化 |
| 新規タイプ | — | MCPClient クラスtools/list + tools/call をシミュレート) |
| 名前空間 | — | mcp\_\_server\_\_tool 衝突防止 |
| ツール説明 | アノテーションなし | (readOnly)/(destructive) アノテーション |
| プロンプトキャッシュ | ありs10 から) | 削除 — ツールプールが動的、キャッシュが陳腐化 |
| Lead ツール | 17 (s18) | 18 (+connect_mcp) |
| チームメイトツール | 8 (s18) | 8変更なし、MCP ツールは Lead のみ) |
| 拡張方法 | ツール追加のコードを書く | 標準プロトコル、任意言語で server を実装 |
---
## 試してみる
```sh
cd learn-claude-code
python s19_mcp_plugin/code.py
```
以下のプロンプトを試してください:
1. `Connect to the docs MCP server and search for something`
2. `Connect to the deploy server and trigger a deployment`
3. `Connect both servers — what tools are now available?`
観察ポイントMCP server 接続後、ツール名に `mcp__docs__``mcp__deploy__` プレフィックスが付いているか?両方の server のツールが同時に利用可能かMCP ツールの description に (readOnly)/(destructive) アノテーションが付いているか?
---
## 次の章
Agent は標準プロトコルで外部ツールに接続できるようになりました。しかし前 19 章は各章で 1 つの仕組みだけを追加しています。実際の Agent は 19 個の demo に分かれて動くわけではありません。
tools、permissions、hooks、todo、task graph、memory、compact、background work、cron、teams、worktree、MCP は、別々の例ではなく同じ loop に接続されるべきです。
s20 Comprehensive Agent → 前 19 章の仕組みを 1 つの完全な harness に統合。仕組みは多く、loop は 1 つ。
<details>
<summary>CC ソースコード深掘り</summary>
> 以下は CC ソースコード `services/mcp/client.ts`、`auth.ts`、`config.ts`、`channelNotification.ts` の分析に基づく。
### 一、6 種の Transport タイプ
教学版は stdio mock のみ。CC は 6 種のトランスポートをサポート(`types.ts:23-25`
| Transport | 通信方式 |
|-----------|---------|
| `stdio` | サブプロセス stdin/stdoutクロスプラットフォームデフォルト |
| `sse` | HTTP Server-Sent Events |
| `http` | Streamable HTTPPOST/SSE 双方向) |
| `ws` | WebSocket |
| `sse-ide` | IDE 内蔵 SSE トランスポート |
| `sdk` | プロセス内 SDK トランスポート |
接続時、ローカルstdioとリモートhttp/sse/wsサーバーをバッチで並行処理ローカルは 3 つずつ、リモートは 20 つずつ。
### 二、ツールプール組み立てアルゴリズム
`assembleToolPool()``tools.ts:345-364`
```typescript
// 重複排除時に組み込みツールを優先name が同じ場合、組み込みが先)
return uniqBy(
[...builtInTools.sort(byName), ...filteredMcpTools.sort(byName)],
'name',
)
```
組み込みツールと MCP ツールは別々にソート、混ぜてソートしない。理由は CC の `claude_code_system_cache_policy` が最後の組み込みツールの後の特定位置にグローバルキャッシュブレークポイントを置く設計のため — ソートを混ぜるとこの設計が壊れる。
### 三、命名規則:`mcp__server__tool`
`buildMcpToolName()``mcpStringUtils.ts:50-52`
```
mcp__<normalizedServerName>__<normalizedToolName>
```
`[a-zA-Z0-9_-]` 以外の全文字を `_` に置換(`normalization.ts:17-23`)。教学版の `normalize_mcp_name` も同じルールを使用。
### 四、権限チェック
CC は MCP ツールに対して独立した権限システムを持つ。`checkPermissions()` は MCP ツールに対して組み込みツールとは異なるロジックを適用 — MCP ツールは独自の権限要件readOnly、destructive 等を宣言でき、CC は宣言に基づいてユーザー確認が必要かを判断。教学版は description 内のテキストアノテーション `(readOnly)` / `(destructive)` のみで、権限インターセプトは行わない。
### 五、設定ソースと優先度
MCP サーバー設定は複数のソースから。CC の優先度は低い順に:
```
claude.ai コネクタ < プラグイン < ユーザー settings.json < 承認済みプロジェクト .mcp.json < ローカル settings.local.json
```
`claude.ai` コネクタは個別に取得、コンテンツ署名で重複排除し、最低優先度で統合(`config.ts:1267-1289`)。企業 `managed-mcp.json` が存在する場合、他の全設定を完全に除外。
教学版は server 名を直接 `MOCK_SERVERS` 辞書に渡し、設定マージは行わない。
### 六、Channel 通知:サーバーからの逆方向メッセージ
教学版は Agent → MCP Server の一方向呼び出しのみ。CC は逆方向通知もサポート(`channelNotification.ts`
1. Server が `capabilities.experimental['claude/channel']` を宣言
2. Server が MCP 通知 `notifications/claude/channel` で Agent にメッセージを送信
3. メッセージは `<channel source="serverName">...</channel>` XML タグでラップ
4. Agent は SleepTool で起床1 秒以内)
Server は権限リクエストも可能:`notifications/claude/channel/permission_request` → Agent が `notifications/claude/channel/permission` で応答。ユーザーは 5 文字の短い ID で確認/拒否。
### 七、OAuth 認証フロー
CC の MCP 認証(`auth.ts`)は完全な OAuth 2.0 + PKCE フローをサポート:
- 公開クライアント + PKCE で OAuth メタデータを発見RFC 8414 / RFC 9728
- ローカルコールバックサーバーが認可コードを受信
- トークンは `getSecureStorage()` で永続化macOS Keychain / Linux 暗号化ファイル / Windows 資格情報マネージャー)
- 有効期限 5 分前に自動リフレッシュ
- クロスアプリケーションアクセスXAAブラウザが id_token を取得 → RFC 8693 + RFC 7523 交換 → 繰り返しブラウザポップアップ不要
### 八、接続ライフサイクルのエラーハンドリング
CC は MCP 接続にきめ細かいエラー分類とリトライを行う(`client.ts:1266-1402`
- 終局エラーECONNRESET、ETIMEDOUT、EPIPE 等):連続 3 回 → クローズ + 再接続
- ツール呼び出し 401トークン期限切れ → `McpAuthError` スロー → 再認証トリガー
- ツール呼び出しタイムアウト:`Promise.race` タイムアウト(設定可能、デフォルト約 28 時間)
- Stdio 切断SIGINT → SIGTERM → SIGKILL の順でプロセスを kill
### 教学版の簡略化
- 6 種のトランスポート → 1 種mock stdio概念量を管理可能に
- Channel 逆方向通知 → 省略:教学版 Agent は常にイニシエータ
- OAuth フロー → 省略:教学版は server が認証不要と仮定
- 多層設定優先度 → 省略:教学版は直接 server 名を渡す
- 複雑なエラー分類 → 省略:教学版は try/except でフォールバック
- MCP ツールは Lead のみ → サブ agent 継承を省略:コード構造を簡略化
</details>
<!-- translation-sync: zh@v2, en@v2, ja@v2 -->

282
s19_mcp_plugin/README.md Normal file
View File

@@ -0,0 +1,282 @@
# s19: MCP Tools — 外接工具,标准协议
[中文](README.md) · [English](README.en.md) · [日本語](README.ja.md)
s01 → ... → s17 → s18 → `s19` → [s20](../s20_comprehensive/)
> *"外接工具, 标准协议"* — 发现、组装、调用Agent 不需要知道工具是谁写的。
>
> **Harness 层**: 插件 — 外部能力通过标准协议接入。
---
## 问题
s01 到 s18Agent 的所有工具都是手写的——bash、read、write、task、worktree。每个工具的输入验证、执行逻辑、错误处理都是你一行行写的。
现在你有 3 个外部服务想接入:公司的 Jira API查 issue、建 ticket、自建的部署系统触发 deploy、看日志、团队的 Notion 知识库(搜文档、建页面)。你不想为每个服务重写一套工具代码。
你需要一个标准协议——外部服务只要实现它Agent 就能直接调用,不管服务用什么语言写的。
---
## 解决方案
![MCP Architecture](images/mcp-architecture.svg)
MCPModel Context Protocol定义了 Agent 如何发现和调用外部工具。核心概念:
| 概念 | 作用 |
|------|------|
| MCPClient | Agent 端的客户端,连接 server、发现工具、调用工具 |
| MCP Server | 外部服务,实现 `tools/list` + `tools/call` |
| assemble_tool_pool | 把内置工具和 MCP 工具组装成一个工具池 |
| mcp\_\_server\_\_tool 命名 | 避免不同 server 的工具名冲突 |
沿用 s18 的教学版 worktree 隔离、自主认领、空闲轮询、协议系统。本章新增:`connect_mcp` 工具——连接外部服务,发现工具,加入工具池。
教学版用 mock handler 模拟外部 server。真实版会启动子进程通过 stdin/stdout 发送 JSON-RPC 请求。mock 的好处是不依赖外部服务就能跑完整流程;代价是你看不到真正的网络通信和进程管理。
---
## 工作原理
### MCPClient发现 + 调用
```python
class MCPClient:
def __init__(self, name: str):
self.name = name
self.tools: list[dict] = []
self._handlers: dict[str, callable] = {}
def register(self, tool_defs, handlers):
"""Simulates tools/list discovery."""
self.tools = tool_defs
self._handlers = handlers
def call_tool(self, tool_name: str, args: dict) -> str:
"""Simulates tools/call."""
handler = self._handlers.get(tool_name)
if not handler:
return f"MCP error: unknown tool '{tool_name}'"
return handler(**args)
```
教学版用 Python 函数模拟 server 的工具实现。真实版通过 stdio JSON-RPC 与子进程通信。
### connect_mcp连接 + 发现
```python
def connect_mcp(name: str) -> str:
if name in mcp_clients:
return f"MCP server '{name}' already connected"
factory = MOCK_SERVERS.get(name)
if not factory:
return f"Unknown server '{name}'. Available: ..."
mcp_client = factory()
mcp_clients[name] = mcp_client
return f"Connected to '{name}'. Discovered: ..."
```
连接后server 提供的工具立即可用。
### normalize_mcp_name名称规范化
```python
_DISALLOWED_CHARS = re.compile(r'[^a-zA-Z0-9_-]')
def normalize_mcp_name(name: str) -> str:
return _DISALLOWED_CHARS.sub('_', name)
```
所有非 `[a-zA-Z0-9_-]` 的字符替换为 `_`。防止 server 名或工具名中包含特殊字符导致命名冲突或注入问题。
### assemble_tool_pool组装工具池
```python
def assemble_tool_pool() -> tuple[list[dict], dict]:
tools = list(BUILTIN_TOOLS)
handlers = dict(BUILTIN_HANDLERS)
for server_name, mcp_client in mcp_clients.items():
safe_server = normalize_mcp_name(server_name)
for tool_def in mcp_client.tools:
safe_tool = normalize_mcp_name(tool_def["name"])
prefixed = f"mcp__{safe_server}__{safe_tool}"
tools.append(...)
handlers[prefixed] = (
lambda *, c=mcp_client, t=tool_def["name"], **kw:
c.call_tool(t, kw))
return tools, handlers
```
前缀 `mcp__{server}__{tool}` 避免不同 server 的工具名冲突。名称经过 `normalize_mcp_name` 规范化。
MCP 工具的 description 带 `(readOnly)``(destructive)` 标注——教学版用文本标注,真实 CC 用 tool annotations 结构体让权限系统判断。
### 无缓存工具池变了prompt 也变
s10-s18 的 agent_loop 用 prompt cache 避免重复序列化。s19 去掉了缓存:
```python
def agent_loop(messages, context):
tools, handlers = assemble_tool_pool() # 每次重新构建
system = assemble_system_prompt(context) # 每次重新生成
...
if any(b.name == "connect_mcp" ...):
tools, handlers = assemble_tool_pool() # 连接后重建
system = assemble_system_prompt(context)
```
原因:`connect_mcp` 之后工具池变化了——新增了 `mcp__docs__search` 等工具。缓存中的工具列表是旧的,继续用会导致模型调用不到新工具。教学版直接去掉缓存,代价是多花一点序列化时间。
### MCP 工具只有 Lead 可用
教学版中,`connect_mcp` 是 Lead 工具,`assemble_tool_pool` 也只服务于 Lead 的 agent_loop。Teammate 仍使用固定的 8 个子集工具bash、read_file、write_file、send_message、submit_plan、list_tasks、claim_task、complete_task
这是教学简化。真实 CC 中MCP 工具对主 agent 和子 agent 都可用——子 agent 继承父级的 MCP 配置。
---
## 相对 s18 的变更
| 组件 | 之前 (s18) | 之后 (s19) |
|------|-----------|-----------|
| 工具来源 | 全部手写 builtin | 手写 + MCP 外部工具动态发现 |
| 工具池 | 固定 BUILTIN_TOOLS | assemble_tool_pool 动态组装 mcp\_\_ 前缀工具 |
| 名称安全 | 无 | normalize_mcp_name 规范化 |
| 新类型 | — | MCPClient 类(模拟 tools/list + tools/call |
| 命名空间 | — | mcp\_\_server\_\_tool 避免冲突 |
| 工具描述 | 无标注 | (readOnly)/(destructive) 标注 |
| prompt 缓存 | 有s10 起) | 去掉——工具池动态变化后缓存失效 |
| Lead 工具 | 17 (s18) | 18 (+connect_mcp) |
| Teammate 工具 | 8 (s18) | 8不变MCP 工具仅 Lead 可用) |
| 扩展方式 | 写代码加工具 | 标准协议,任意语言实现 server |
---
## 试一下
```sh
cd learn-claude-code
python s19_mcp_plugin/code.py
```
试试这些 prompt
1. `Connect to the docs MCP server and search for something`
2. `Connect to the deploy server and trigger a deployment`
3. `Connect both servers — what tools are now available?`
观察重点:连接 MCP server 后,工具名是否带 `mcp__docs__``mcp__deploy__` 前缀?两个 server 的工具是否同时可用MCP 工具的 description 是否带 (readOnly)/(destructive) 标注?
---
## 接下来
现在 Agent 可以通过标准协议接入外部工具了。但前面 19 章每章都只加一个机制,真实 Agent 不会这样拆开运行。
工具、权限、hooks、todo、任务图、记忆、压缩、后台、cron、团队、worktree、MCP 这些机制应该挂在同一个循环上,而不是散在 19 个 demo 里。
s20 Comprehensive Agent → 把前 19 章的机制合回一个完整 harness。机制很多循环一个。
<details>
<summary>深入 CC 源码</summary>
> 以下基于 CC 源码 `services/mcp/client.ts`、`auth.ts`、`config.ts`、`channelNotification.ts` 的分析。
### 一、6 种 Transport 类型
教学版只展示了 stdio mock。CC 支持 6 种传输(`types.ts:23-25`
| Transport | 通信方式 |
|-----------|---------|
| `stdio` | 子进程 stdin/stdout跨平台默认 |
| `sse` | HTTP Server-Sent Events |
| `http` | Streamable HTTPPOST/SSE 双向) |
| `ws` | WebSocket |
| `sse-ide` | IDE 内嵌 SSE 传输 |
| `sdk` | 进程内 SDK 传输 |
连接时本地stdio和远程http/sse/ws服务器分批并发本地批量 3 个,远程批量 20 个。
### 二、工具池组装算法
`assembleToolPool()``tools.ts:345-364`
```typescript
// 去重时优先保留内置工具name 相同时内置在前)
return uniqBy(
[...builtInTools.sort(byName), ...filteredMcpTools.sort(byName)],
'name',
)
```
内置工具和 MCP 工具分开排序,不是合起来排。原因是 CC 的 `claude_code_system_cache_policy` 在最后一个内置工具之后的某个位置放全局缓存断点——混排会破坏这个设计。
### 三、命名规则:`mcp__server__tool`
`buildMcpToolName()``mcpStringUtils.ts:50-52`
```
mcp__<normalizedServerName>__<normalizedToolName>
```
所有非 `[a-zA-Z0-9_-]` 字符替换为 `_``normalization.ts:17-23`)。教学版的 `normalize_mcp_name` 用同样的规则。
### 四、权限检查
CC 对 MCP 工具有独立的权限系统。`checkPermissions()` 对 MCP 工具的检查逻辑不同于内置工具——MCP 工具可以声明自己的权限需求readOnly、destructive 等CC 根据声明决定是否需要用户确认。教学版只在 description 中用文本标注 `(readOnly)` / `(destructive)`,不做权限拦截。
### 五、配置来源与优先级
MCP 服务器配置来自多个来源。CC 的配置优先级从低到高:
```
claude.ai 连接器 < plugin < user settings.json < approved project .mcp.json < local settings.local.json
```
`claude.ai` 连接器单独拉取、按内容签名去重,以最低优先级合并(`config.ts:1267-1289`)。企业 `managed-mcp.json` 存在时完全排除其他配置。
教学版直接传 server name 给 `MOCK_SERVERS` 字典,不做配置合并。
### 六、Channel 通知:服务器反向推消息
教学版只讲了 Agent → MCP Server 的单向调用。CC 还支持反向通知(`channelNotification.ts`
1. Server 声明 `capabilities.experimental['claude/channel']`
2. Server 通过 MCP 通知 `notifications/claude/channel` 给 Agent 发消息
3. 消息包装在 `<channel source="serverName">...</channel>` XML 标签中
4. Agent 被 SleepTool 唤醒1 秒内)
Server 还可以请求权限:`notifications/claude/channel/permission_request` → Agent 回复 `notifications/claude/channel/permission`。用户通过 5 字母短 ID 确认/拒绝。
### 七、OAuth 认证流程
CC 的 MCP 认证(`auth.ts`)支持完整的 OAuth 2.0 + PKCE 流程:
- 通过公钥客户端 + PKCE 发现 OAuth 元数据RFC 8414 / RFC 9728
- 本地回调服务器接收授权码
- 令牌通过 `getSecureStorage()` 持久化macOS Keychain / Linux 加密文件 / Windows 凭据管理器)
- 过期前 5 分钟自动刷新
- 支持跨应用访问XAA浏览器获取 id_token → RFC 8693 + RFC 7523 交换 → 无需反复弹浏览器
### 八、连接生命周期的错误处理
CC 对 MCP 连接有精细的错误分类和重试(`client.ts:1266-1402`
- 终局性错误ECONNRESET、ETIMEDOUT、EPIPE 等):连续 3 次 → 关闭 + 重连
- 工具调用 401令牌过期 → 抛出 `McpAuthError` → 触发重认证
- 工具调用超时:`Promise.race` 超时(可配置,默认约 28 小时)
- Stdio 断连:按 SIGINT → SIGTERM → SIGKILL 顺序杀进程
### 教学版的简化
- 6 种 transport → 1 种mock stdio概念量可控
- Channel 反向通知 → 省略:教学版 Agent 是主动方
- OAuth 流程 → 省略:教学版假设 server 不需要认证
- 多层配置优先级 → 省略:教学版直接传 server name
- 复杂的错误分类 → 省略:教学版用 try/except 兜底
- MCP 工具只给 Lead → 省略子 agent 继承:简化代码结构
</details>
<!-- translation-sync: zh@v2, en@v0, ja@v0 -->

1024
s19_mcp_plugin/code.py Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,112 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 760 460" font-family="system-ui, -apple-system, sans-serif">
<defs>
<linearGradient id="header" x1="0" y1="0" x2="1" y2="0">
<stop offset="0%" stop-color="#1e3a5f"/><stop offset="100%" stop-color="#dc2626"/>
</linearGradient>
<marker id="arrow" viewBox="0 0 10 10" refX="10" refY="5" markerWidth="6" markerHeight="6" orient="auto-start-reverse">
<path d="M 0 0 L 10 5 L 0 10 z" fill="#555"/>
</marker>
<marker id="arrow-rose" viewBox="0 0 10 10" refX="10" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
<path d="M 0 0 L 10 5 L 0 10 z" fill="#dc2626"/>
</marker>
<marker id="arrow-rose-left" viewBox="0 0 10 10" refX="10" refY="5" markerWidth="6" markerHeight="6" orient="auto-start-reverse">
<path d="M 10 0 L 0 5 L 10 10 z" fill="#dc2626"/>
</marker>
</defs>
<rect width="760" height="460" fill="#fafbfc" rx="8"/>
<!-- Title -->
<rect x="0" y="0" width="760" height="44" fill="url(#header)" rx="8"/>
<rect x="0" y="36" width="760" height="8" fill="url(#header)"/>
<text x="380" y="28" fill="#fff" font-size="15" font-weight="700" text-anchor="middle">MCP Plugin — Standard Protocol + External Tool Integration + Tool Pool Assembly</text>
<!-- Legend -->
<rect x="40" y="56" width="12" height="10" rx="2" fill="#f0f4ff" stroke="#2563eb" stroke-width="1"/>
<text x="58" y="66" fill="#2563eb" font-size="10" font-weight="600">s18 Preserved</text>
<rect x="160" y="56" width="12" height="10" rx="2" fill="#fff1f2" stroke="#dc2626" stroke-width="1"/>
<text x="178" y="66" fill="#dc2626" font-size="10" font-weight="600">s19 New</text>
<!-- ===== Row 1: Lead Loop (s18 preserved) ===== -->
<rect x="20" y="90" width="70" height="40" rx="8" fill="#eef2ff" stroke="#4f46e5" stroke-width="1.5"/>
<text x="55" y="114" fill="#4f46e5" font-size="8" font-weight="600" text-anchor="middle">turn</text>
<line x1="90" y1="110" x2="104" y2="110" stroke="#555" stroke-width="1.5" marker-end="url(#arrow)"/>
<rect x="107" y="90" width="70" height="40" rx="8" fill="#f0f4ff" stroke="#2563eb" stroke-width="1.5"/>
<text x="142" y="114" fill="#1e3a5f" font-size="10" font-weight="600" text-anchor="middle">messages</text>
<line x1="177" y1="110" x2="191" y2="110" stroke="#555" stroke-width="1.5" marker-end="url(#arrow)"/>
<rect x="194" y="86" width="80" height="48" rx="8" fill="#f0f4ff" stroke="#2563eb" stroke-width="1.5"/>
<text x="234" y="114" fill="#1e3a5f" font-size="9" font-weight="600" text-anchor="middle">prompt</text>
<line x1="274" y1="110" x2="288" y2="110" stroke="#555" stroke-width="1.5" marker-end="url(#arrow)"/>
<rect x="291" y="86" width="70" height="48" rx="8" fill="#f0f4ff" stroke="#2563eb" stroke-width="1.5"/>
<text x="326" y="114" fill="#1e3a5f" font-size="9" font-weight="600" text-anchor="middle">LLM</text>
<line x1="361" y1="110" x2="375" y2="110" stroke="#555" stroke-width="1.5" marker-end="url(#arrow)"/>
<rect x="378" y="76" width="356" height="72" rx="8" fill="#f0f4ff" stroke="#2563eb" stroke-width="1.5"/>
<text x="556" y="94" fill="#1e3a5f" font-size="10" font-weight="600" text-anchor="middle">TOOL DISPATCH (Lead 18 tools)</text>
<text x="394" y="109" fill="#2563eb" font-size="7.5">bash · read · write · task(4) · send · inbox</text>
<text x="394" y="121" fill="#7c3aed" font-size="7.5" font-weight="700">request_shutdown · request_plan · review_plan</text>
<text x="394" y="133" fill="#b45309" font-size="7.5" font-weight="700">create_worktree · remove_worktree · keep_worktree</text>
<text x="394" y="145" fill="#dc2626" font-size="7.5" font-weight="700">★ connect_mcp + dynamic mcp__server__tool tools</text>
<!-- Loop back -->
<path d="M 734 110 L 748 110 L 748 150 L 55 150 L 55 130" fill="none" stroke="#94a3b8" stroke-width="1" marker-end="url(#arrow)" stroke-dasharray="5,4"/>
<!-- ===== Row 2: MCP Architecture (s19 new) ===== -->
<rect x="30" y="172" width="700" height="215" rx="8" fill="#fff1f2" stroke="#dc2626" stroke-width="2"/>
<text x="380" y="194" fill="#991b1b" font-size="11" font-weight="700" text-anchor="middle">MCP Architecture (s19 new: standard protocol + external tools dynamic integration)</text>
<!-- Agent Side -->
<rect x="50" y="210" width="255" height="140" rx="6" fill="#fff" stroke="#dc2626" stroke-width="1.5"/>
<text x="177" y="230" fill="#991b1b" font-size="10" font-weight="700" text-anchor="middle">Agent Side (MCPClient)</text>
<rect x="65" y="240" width="225" height="24" rx="4" fill="#fef2f2" stroke="#fca5a5" stroke-width="0.5"/>
<text x="177" y="256" fill="#475569" font-size="8" text-anchor="middle">connect_mcp → discover → register tools</text>
<rect x="65" y="272" width="225" height="24" rx="4" fill="#fef2f2" stroke="#fca5a5" stroke-width="0.5"/>
<text x="177" y="288" fill="#475569" font-size="8" text-anchor="middle">assemble_tool_pool assembles builtin + mcp</text>
<rect x="65" y="304" width="225" height="24" rx="4" fill="#fef2f2" stroke="#fca5a5" stroke-width="0.5"/>
<text x="177" y="320" fill="#475569" font-size="8" text-anchor="middle">call_tool("mcp__docs__search", ...)</text>
<!-- Communication arrows -->
<line x1="305" y1="262" x2="435" y2="262" stroke="#dc2626" stroke-width="1.5" marker-end="url(#arrow-rose)"/>
<text x="370" y="256" fill="#dc2626" font-size="7" font-weight="600" text-anchor="middle">tools/list</text>
<line x1="435" y1="296" x2="305" y2="296" stroke="#dc2626" stroke-width="1.5" marker-end="url(#arrow-rose)"/>
<text x="370" y="312" fill="#dc2626" font-size="7" font-weight="600" text-anchor="middle">tools/call + response</text>
<!-- MCP Servers -->
<rect x="438" y="210" width="275" height="140" rx="6" fill="#fff" stroke="#ca8a04" stroke-width="1.5"/>
<text x="575" y="230" fill="#854d0e" font-size="10" font-weight="700" text-anchor="middle">MCP Servers (External Services)</text>
<rect x="453" y="240" width="245" height="28" rx="4" fill="#fefce8" stroke="#facc15" stroke-width="0.5"/>
<text x="575" y="258" fill="#854d0e" font-size="9" font-weight="600" text-anchor="middle">docs server: search · get_version</text>
<rect x="453" y="276" width="245" height="28" rx="4" fill="#fefce8" stroke="#facc15" stroke-width="0.5"/>
<text x="575" y="294" fill="#854d0e" font-size="9" font-weight="600" text-anchor="middle">deploy server: trigger · status</text>
<rect x="453" y="312" width="245" height="28" rx="4" fill="#fefce8" stroke="#facc15" stroke-width="0.5"/>
<text x="575" y="330" fill="#854d0e" font-size="9" font-weight="600" text-anchor="middle">Any language, just needs stdio JSON-RPC</text>
<!-- Naming convention -->
<rect x="50" y="360" width="660" height="20" rx="4" fill="#fef3c7" stroke="#d97706" stroke-width="1"/>
<text x="380" y="374" fill="#92400e" font-size="8" text-anchor="middle">Tool naming: mcp__{server}__{tool} → e.g. mcp__docs__search · mcp__deploy__trigger · prevents name collisions across servers</text>
<!-- ===== Row 3: Bottom notes ===== -->
<rect x="30" y="400" width="700" height="22" rx="4" fill="#f8fafc" stroke="#e2e8f0" stroke-width="1"/>
<rect x="50" y="408" width="12" height="10" rx="2" fill="#f0f4ff" stroke="#2563eb" stroke-width="1"/>
<text x="70" y="418" fill="#475569" font-size="10">s18: worktree + events + protocols (Lead 17)</text>
<rect x="420" y="408" width="12" height="10" rx="2" fill="#fff1f2" stroke="#dc2626" stroke-width="1"/>
<text x="440" y="418" fill="#475569" font-size="10">s19: MCP + dynamic tools (Lead 18)</text>
<!-- ===== Final note ===== -->
<rect x="30" y="430" width="700" height="22" rx="4" fill="#fff1f2" stroke="#dc2626" stroke-width="1"/>
<text x="380" y="444" fill="#991b1b" font-size="9" font-weight="600" text-anchor="middle">Next: s20 combines tools, permissions, teams, worktrees, MCP, and more into one while True loop.</text>
</svg>

After

Width:  |  Height:  |  Size: 7.8 KiB

View File

@@ -0,0 +1,112 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 760 460" font-family="system-ui, -apple-system, sans-serif">
<defs>
<linearGradient id="header" x1="0" y1="0" x2="1" y2="0">
<stop offset="0%" stop-color="#1e3a5f"/><stop offset="100%" stop-color="#dc2626"/>
</linearGradient>
<marker id="arrow" viewBox="0 0 10 10" refX="10" refY="5" markerWidth="6" markerHeight="6" orient="auto-start-reverse">
<path d="M 0 0 L 10 5 L 0 10 z" fill="#555"/>
</marker>
<marker id="arrow-rose" viewBox="0 0 10 10" refX="10" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
<path d="M 0 0 L 10 5 L 0 10 z" fill="#dc2626"/>
</marker>
<marker id="arrow-rose-left" viewBox="0 0 10 10" refX="10" refY="5" markerWidth="6" markerHeight="6" orient="auto-start-reverse">
<path d="M 10 0 L 0 5 L 10 10 z" fill="#dc2626"/>
</marker>
</defs>
<rect width="760" height="460" fill="#fafbfc" rx="8"/>
<!-- Title -->
<rect x="0" y="0" width="760" height="44" fill="url(#header)" rx="8"/>
<rect x="0" y="36" width="760" height="8" fill="url(#header)"/>
<text x="380" y="28" fill="#fff" font-size="14" font-weight="700" text-anchor="middle">MCP Plugin — 標準プロトコル + 外部ツール接続 + ツールプール組み立て</text>
<!-- Legend -->
<rect x="40" y="56" width="12" height="10" rx="2" fill="#f0f4ff" stroke="#2563eb" stroke-width="1"/>
<text x="58" y="66" fill="#2563eb" font-size="10" font-weight="600">s18 保持</text>
<rect x="130" y="56" width="12" height="10" rx="2" fill="#fff1f2" stroke="#dc2626" stroke-width="1"/>
<text x="148" y="66" fill="#dc2626" font-size="10" font-weight="600">s19 新規</text>
<!-- ===== Row 1: Lead Loop ===== -->
<rect x="20" y="90" width="70" height="40" rx="8" fill="#eef2ff" stroke="#4f46e5" stroke-width="1.5"/>
<text x="55" y="114" fill="#4f46e5" font-size="8" font-weight="600" text-anchor="middle">turn</text>
<line x1="90" y1="110" x2="104" y2="110" stroke="#555" stroke-width="1.5" marker-end="url(#arrow)"/>
<rect x="107" y="90" width="70" height="40" rx="8" fill="#f0f4ff" stroke="#2563eb" stroke-width="1.5"/>
<text x="142" y="114" fill="#1e3a5f" font-size="10" font-weight="600" text-anchor="middle">messages</text>
<line x1="177" y1="110" x2="191" y2="110" stroke="#555" stroke-width="1.5" marker-end="url(#arrow)"/>
<rect x="194" y="86" width="80" height="48" rx="8" fill="#f0f4ff" stroke="#2563eb" stroke-width="1.5"/>
<text x="234" y="114" fill="#1e3a5f" font-size="9" font-weight="600" text-anchor="middle">prompt</text>
<line x1="274" y1="110" x2="288" y2="110" stroke="#555" stroke-width="1.5" marker-end="url(#arrow)"/>
<rect x="291" y="86" width="70" height="48" rx="8" fill="#f0f4ff" stroke="#2563eb" stroke-width="1.5"/>
<text x="326" y="114" fill="#1e3a5f" font-size="9" font-weight="600" text-anchor="middle">LLM</text>
<line x1="361" y1="110" x2="375" y2="110" stroke="#555" stroke-width="1.5" marker-end="url(#arrow)"/>
<rect x="378" y="76" width="356" height="72" rx="8" fill="#f0f4ff" stroke="#2563eb" stroke-width="1.5"/>
<text x="556" y="94" fill="#1e3a5f" font-size="10" font-weight="600" text-anchor="middle">TOOL DISPATCHLead 18 tools</text>
<text x="394" y="109" fill="#2563eb" font-size="7.5">bash · read · write · task(4) · send · inbox</text>
<text x="394" y="121" fill="#7c3aed" font-size="7.5" font-weight="700">request_shutdown · request_plan · review_plan</text>
<text x="394" y="133" fill="#b45309" font-size="7.5" font-weight="700">create_worktree · remove_worktree · keep_worktree</text>
<text x="394" y="145" fill="#dc2626" font-size="7.5" font-weight="700">★ connect_mcp + 動的 mcp__server__tool ツール</text>
<!-- Loop back -->
<path d="M 734 110 L 748 110 L 748 150 L 55 150 L 55 130" fill="none" stroke="#94a3b8" stroke-width="1" marker-end="url(#arrow)" stroke-dasharray="5,4"/>
<!-- ===== Row 2: MCP Architecture ===== -->
<rect x="30" y="172" width="700" height="215" rx="8" fill="#fff1f2" stroke="#dc2626" stroke-width="2"/>
<text x="380" y="194" fill="#991b1b" font-size="11" font-weight="700" text-anchor="middle">MCP アーキテクチャs19 新規:標準プロトコル + 外部ツール動的統合)</text>
<!-- Agent Side -->
<rect x="50" y="210" width="255" height="140" rx="6" fill="#fff" stroke="#dc2626" stroke-width="1.5"/>
<text x="177" y="230" fill="#991b1b" font-size="10" font-weight="700" text-anchor="middle">Agent 側MCPClient</text>
<rect x="65" y="240" width="225" height="24" rx="4" fill="#fef2f2" stroke="#fca5a5" stroke-width="0.5"/>
<text x="177" y="256" fill="#475569" font-size="8" text-anchor="middle">connect_mcp → discover → ツール登録</text>
<rect x="65" y="272" width="225" height="24" rx="4" fill="#fef2f2" stroke="#fca5a5" stroke-width="0.5"/>
<text x="177" y="288" fill="#475569" font-size="8" text-anchor="middle">assemble_tool_pool builtin + mcp 組み立て</text>
<rect x="65" y="304" width="225" height="24" rx="4" fill="#fef2f2" stroke="#fca5a5" stroke-width="0.5"/>
<text x="177" y="320" fill="#475569" font-size="8" text-anchor="middle">call_tool("mcp__docs__search", ...)</text>
<!-- Communication arrows -->
<line x1="305" y1="262" x2="435" y2="262" stroke="#dc2626" stroke-width="1.5" marker-end="url(#arrow-rose)"/>
<text x="370" y="256" fill="#dc2626" font-size="7" font-weight="600" text-anchor="middle">tools/list</text>
<line x1="435" y1="296" x2="305" y2="296" stroke="#dc2626" stroke-width="1.5" marker-end="url(#arrow-rose)"/>
<text x="370" y="312" fill="#dc2626" font-size="7" font-weight="600" text-anchor="middle">tools/call + response</text>
<!-- MCP Servers -->
<rect x="438" y="210" width="275" height="140" rx="6" fill="#fff" stroke="#ca8a04" stroke-width="1.5"/>
<text x="575" y="230" fill="#854d0e" font-size="10" font-weight="700" text-anchor="middle">MCP Servers外部サービス</text>
<rect x="453" y="240" width="245" height="28" rx="4" fill="#fefce8" stroke="#facc15" stroke-width="0.5"/>
<text x="575" y="258" fill="#854d0e" font-size="9" font-weight="600" text-anchor="middle">docs server: search · get_version</text>
<rect x="453" y="276" width="245" height="28" rx="4" fill="#fefce8" stroke="#facc15" stroke-width="0.5"/>
<text x="575" y="294" fill="#854d0e" font-size="9" font-weight="600" text-anchor="middle">deploy server: trigger · status</text>
<rect x="453" y="312" width="245" height="28" rx="4" fill="#fefce8" stroke="#facc15" stroke-width="0.5"/>
<text x="575" y="330" fill="#854d0e" font-size="9" font-weight="600" text-anchor="middle">任意言語実装、stdio JSON-RPC のみ必要</text>
<!-- Naming convention -->
<rect x="50" y="360" width="660" height="20" rx="4" fill="#fef3c7" stroke="#d97706" stroke-width="1"/>
<text x="380" y="374" fill="#92400e" font-size="8" text-anchor="middle">ツール命名: mcp__{server}__{tool} → 例: mcp__docs__search · mcp__deploy__trigger · サーバー間の名前衝突を防止</text>
<!-- ===== Row 3: Bottom notes ===== -->
<rect x="30" y="400" width="700" height="22" rx="4" fill="#f8fafc" stroke="#e2e8f0" stroke-width="1"/>
<rect x="50" y="408" width="12" height="10" rx="2" fill="#f0f4ff" stroke="#2563eb" stroke-width="1"/>
<text x="70" y="418" fill="#475569" font-size="10">s18: worktree + events + protocolsLead 17</text>
<rect x="420" y="408" width="12" height="10" rx="2" fill="#fff1f2" stroke="#dc2626" stroke-width="1"/>
<text x="440" y="418" fill="#475569" font-size="10">s19: MCP + dynamic toolsLead 18</text>
<!-- ===== Final note ===== -->
<rect x="30" y="430" width="700" height="22" rx="4" fill="#fff1f2" stroke="#dc2626" stroke-width="1"/>
<text x="380" y="444" fill="#991b1b" font-size="9" font-weight="600" text-anchor="middle">次の s20tools、permissions、teams、worktree、MCP などを 1 つの while True ループに統合。</text>
</svg>

After

Width:  |  Height:  |  Size: 7.8 KiB

View File

@@ -0,0 +1,112 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 760 460" font-family="system-ui, -apple-system, sans-serif">
<defs>
<linearGradient id="header" x1="0" y1="0" x2="1" y2="0">
<stop offset="0%" stop-color="#1e3a5f"/><stop offset="100%" stop-color="#dc2626"/>
</linearGradient>
<marker id="arrow" viewBox="0 0 10 10" refX="10" refY="5" markerWidth="6" markerHeight="6" orient="auto-start-reverse">
<path d="M 0 0 L 10 5 L 0 10 z" fill="#555"/>
</marker>
<marker id="arrow-rose" viewBox="0 0 10 10" refX="10" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
<path d="M 0 0 L 10 5 L 0 10 z" fill="#dc2626"/>
</marker>
<marker id="arrow-rose-left" viewBox="0 0 10 10" refX="10" refY="5" markerWidth="6" markerHeight="6" orient="auto-start-reverse">
<path d="M 10 0 L 0 5 L 10 10 z" fill="#dc2626"/>
</marker>
</defs>
<rect width="760" height="460" fill="#fafbfc" rx="8"/>
<!-- Title -->
<rect x="0" y="0" width="760" height="44" fill="url(#header)" rx="8"/>
<rect x="0" y="36" width="760" height="8" fill="url(#header)"/>
<text x="380" y="28" fill="#fff" font-size="15" font-weight="700" text-anchor="middle">MCP Plugin — 标准协议 + 外部工具接入 + 工具池组装</text>
<!-- Legend -->
<rect x="40" y="56" width="12" height="10" rx="2" fill="#f0f4ff" stroke="#2563eb" stroke-width="1"/>
<text x="58" y="66" fill="#2563eb" font-size="10" font-weight="600">s18 保留</text>
<rect x="140" y="56" width="12" height="10" rx="2" fill="#fff1f2" stroke="#dc2626" stroke-width="1"/>
<text x="158" y="66" fill="#dc2626" font-size="10" font-weight="600">s19 新增</text>
<!-- ===== Row 1: Lead Loop (s18 preserved) ===== -->
<rect x="20" y="90" width="70" height="40" rx="8" fill="#eef2ff" stroke="#4f46e5" stroke-width="1.5"/>
<text x="55" y="114" fill="#4f46e5" font-size="8" font-weight="600" text-anchor="middle">turn</text>
<line x1="90" y1="110" x2="104" y2="110" stroke="#555" stroke-width="1.5" marker-end="url(#arrow)"/>
<rect x="107" y="90" width="70" height="40" rx="8" fill="#f0f4ff" stroke="#2563eb" stroke-width="1.5"/>
<text x="142" y="114" fill="#1e3a5f" font-size="10" font-weight="600" text-anchor="middle">messages</text>
<line x1="177" y1="110" x2="191" y2="110" stroke="#555" stroke-width="1.5" marker-end="url(#arrow)"/>
<rect x="194" y="86" width="80" height="48" rx="8" fill="#f0f4ff" stroke="#2563eb" stroke-width="1.5"/>
<text x="234" y="114" fill="#1e3a5f" font-size="9" font-weight="600" text-anchor="middle">prompt</text>
<line x1="274" y1="110" x2="288" y2="110" stroke="#555" stroke-width="1.5" marker-end="url(#arrow)"/>
<rect x="291" y="86" width="70" height="48" rx="8" fill="#f0f4ff" stroke="#2563eb" stroke-width="1.5"/>
<text x="326" y="114" fill="#1e3a5f" font-size="9" font-weight="600" text-anchor="middle">LLM</text>
<line x1="361" y1="110" x2="375" y2="110" stroke="#555" stroke-width="1.5" marker-end="url(#arrow)"/>
<rect x="378" y="76" width="356" height="72" rx="8" fill="#f0f4ff" stroke="#2563eb" stroke-width="1.5"/>
<text x="556" y="94" fill="#1e3a5f" font-size="10" font-weight="600" text-anchor="middle">TOOL DISPATCH (Lead 18 tools)</text>
<text x="394" y="109" fill="#2563eb" font-size="7.5">bash · read · write · task(4) · send · inbox</text>
<text x="394" y="121" fill="#7c3aed" font-size="7.5" font-weight="700">request_shutdown · request_plan · review_plan</text>
<text x="394" y="133" fill="#b45309" font-size="7.5" font-weight="700">create_worktree · remove_worktree · keep_worktree</text>
<text x="394" y="145" fill="#dc2626" font-size="7.5" font-weight="700">★ connect_mcp + 动态 mcp__server__tool 工具</text>
<!-- Loop back -->
<path d="M 734 110 L 748 110 L 748 150 L 55 150 L 55 130" fill="none" stroke="#94a3b8" stroke-width="1" marker-end="url(#arrow)" stroke-dasharray="5,4"/>
<!-- ===== Row 2: MCP Architecture (s19 new) ===== -->
<rect x="30" y="172" width="700" height="215" rx="8" fill="#fff1f2" stroke="#dc2626" stroke-width="2"/>
<text x="380" y="194" fill="#991b1b" font-size="11" font-weight="700" text-anchor="middle">MCP 架构s19 新增:标准协议 + 外部工具动态接入)</text>
<!-- Agent Side -->
<rect x="50" y="210" width="255" height="140" rx="6" fill="#fff" stroke="#dc2626" stroke-width="1.5"/>
<text x="177" y="230" fill="#991b1b" font-size="10" font-weight="700" text-anchor="middle">Agent Side (MCPClient)</text>
<rect x="65" y="240" width="225" height="24" rx="4" fill="#fef2f2" stroke="#fca5a5" stroke-width="0.5"/>
<text x="177" y="256" fill="#475569" font-size="8" text-anchor="middle">connect_mcp → discover → 注册工具</text>
<rect x="65" y="272" width="225" height="24" rx="4" fill="#fef2f2" stroke="#fca5a5" stroke-width="0.5"/>
<text x="177" y="288" fill="#475569" font-size="8" text-anchor="middle">assemble_tool_pool 组装 builtin + mcp</text>
<rect x="65" y="304" width="225" height="24" rx="4" fill="#fef2f2" stroke="#fca5a5" stroke-width="0.5"/>
<text x="177" y="320" fill="#475569" font-size="8" text-anchor="middle">call_tool("mcp__docs__search", ...)</text>
<!-- Communication arrows -->
<line x1="305" y1="262" x2="435" y2="262" stroke="#dc2626" stroke-width="1.5" marker-end="url(#arrow-rose)"/>
<text x="370" y="256" fill="#dc2626" font-size="7" font-weight="600" text-anchor="middle">tools/list</text>
<line x1="435" y1="296" x2="305" y2="296" stroke="#dc2626" stroke-width="1.5" marker-end="url(#arrow-rose)"/>
<text x="370" y="312" fill="#dc2626" font-size="7" font-weight="600" text-anchor="middle">tools/call + response</text>
<!-- MCP Servers -->
<rect x="438" y="210" width="275" height="140" rx="6" fill="#fff" stroke="#ca8a04" stroke-width="1.5"/>
<text x="575" y="230" fill="#854d0e" font-size="10" font-weight="700" text-anchor="middle">MCP Servers (外部服务)</text>
<rect x="453" y="240" width="245" height="28" rx="4" fill="#fefce8" stroke="#facc15" stroke-width="0.5"/>
<text x="575" y="258" fill="#854d0e" font-size="9" font-weight="600" text-anchor="middle">docs server: search · get_version</text>
<rect x="453" y="276" width="245" height="28" rx="4" fill="#fefce8" stroke="#facc15" stroke-width="0.5"/>
<text x="575" y="294" fill="#854d0e" font-size="9" font-weight="600" text-anchor="middle">deploy server: trigger · status</text>
<rect x="453" y="312" width="245" height="28" rx="4" fill="#fefce8" stroke="#facc15" stroke-width="0.5"/>
<text x="575" y="330" fill="#854d0e" font-size="9" font-weight="600" text-anchor="middle">任意语言实现,只需 stdio JSON-RPC</text>
<!-- Naming convention -->
<rect x="50" y="360" width="660" height="20" rx="4" fill="#fef3c7" stroke="#d97706" stroke-width="1"/>
<text x="380" y="374" fill="#92400e" font-size="8" text-anchor="middle">工具命名: mcp__{server}__{tool} → 例: mcp__docs__search · mcp__deploy__trigger · 避免不同 server 的工具名冲突</text>
<!-- ===== Row 3: Bottom notes ===== -->
<rect x="30" y="400" width="700" height="22" rx="4" fill="#f8fafc" stroke="#e2e8f0" stroke-width="1"/>
<rect x="50" y="408" width="12" height="10" rx="2" fill="#f0f4ff" stroke="#2563eb" stroke-width="1"/>
<text x="70" y="418" fill="#475569" font-size="10">s18: worktree + events + protocols (Lead 17)</text>
<rect x="420" y="408" width="12" height="10" rx="2" fill="#fff1f2" stroke="#dc2626" stroke-width="1"/>
<text x="440" y="418" fill="#475569" font-size="10">s19: MCP + dynamic tools (Lead 18)</text>
<!-- ===== Final note ===== -->
<rect x="30" y="430" width="700" height="22" rx="4" fill="#fff1f2" stroke="#dc2626" stroke-width="1"/>
<text x="380" y="444" fill="#991b1b" font-size="9" font-weight="600" text-anchor="middle">下一章 s20把工具、权限、团队、worktree、MCP 等机制合回同一个 while True 循环。</text>
</svg>

After

Width:  |  Height:  |  Size: 7.7 KiB