mirror of
https://github.com/shareAI-lab/analysis_claude_code.git
synced 2026-06-21 04:33:36 +08:00
* 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:
282
s19_mcp_plugin/README.en.md
Normal file
282
s19_mcp_plugin/README.en.md
Normal 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 (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
282
s19_mcp_plugin/README.ja.md
Normal 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 API(issue 検索、ticket 作成)、独自のデプロイシステム(deploy トリガー、ログ閲覧)、チームの Notion ナレッジベース(ドキュメント検索、ページ作成)。各サービスのためにツールコードを書き直したくない。
|
||||
|
||||
標準プロトコルが必要 — 外部サービスがこのプロトコルを実装していれば、サービスが何の言語で書かれていても、Agent は直接そのツールを呼び出せる。
|
||||
|
||||
---
|
||||
|
||||
## ソリューション
|
||||
|
||||

|
||||
|
||||
MCP(Model 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 HTTP(POST/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
282
s19_mcp_plugin/README.md
Normal 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 API(查 issue、建 ticket)、自建的部署系统(触发 deploy、看日志)、团队的 Notion 知识库(搜文档、建页面)。你不想为每个服务重写一套工具代码。
|
||||
|
||||
你需要一个标准协议——外部服务只要实现它,Agent 就能直接调用,不管服务用什么语言写的。
|
||||
|
||||
---
|
||||
|
||||
## 解决方案
|
||||
|
||||

|
||||
|
||||
MCP(Model 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 HTTP(POST/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
1024
s19_mcp_plugin/code.py
Normal file
File diff suppressed because it is too large
Load Diff
112
s19_mcp_plugin/images/mcp-architecture.en.svg
Normal file
112
s19_mcp_plugin/images/mcp-architecture.en.svg
Normal 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 |
112
s19_mcp_plugin/images/mcp-architecture.ja.svg
Normal file
112
s19_mcp_plugin/images/mcp-architecture.ja.svg
Normal 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 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 ===== -->
|
||||
<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 + 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:tools、permissions、teams、worktree、MCP などを 1 つの while True ループに統合。</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 7.8 KiB |
112
s19_mcp_plugin/images/mcp-architecture.svg
Normal file
112
s19_mcp_plugin/images/mcp-architecture.svg
Normal 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 |
Reference in New Issue
Block a user