mirror of
https://github.com/shareAI-lab/analysis_claude_code.git
synced 2026-09-21 21:03:38 +08:00
feat: refresh course through workflow and goal loops
This commit is contained in:
@@ -1,47 +1,47 @@
|
||||
# s19: MCP Tools — 外接工具,标准协议
|
||||
# s19: MCP Tools — External Tools, Standard Protocol
|
||||
|
||||
[中文](README.md) · [English](README.en.md) · [日本語](README.ja.md)
|
||||
[English](README.md) · [中文](README.zh.md) · [日本語](README.ja.md)
|
||||
|
||||
s01 → ... → s17 → s18 → `s19` → [s20](../s20_comprehensive/)
|
||||
s01 → ... → s17 → s18 → `s19` → [s20](../s20_comprehensive/) → s21 → s22
|
||||
|
||||
> *"外接工具, 标准协议"* — 发现、组装、调用,Agent 不需要知道工具是谁写的。
|
||||
> *"External tools, standard protocol"* — Discover, assemble, invoke. Agent doesn't need to know who wrote them.
|
||||
>
|
||||
> **Harness 层**: 插件 — 外部能力通过标准协议接入。
|
||||
> **Harness layer**: Plugins — External capabilities via a standard protocol.
|
||||
|
||||
---
|
||||
|
||||
## 问题
|
||||
## The Problem
|
||||
|
||||
s01 到 s18,Agent 的所有工具都是手写的——bash、read、write、task、worktree。每个工具的输入验证、执行逻辑、错误处理,都是你一行行写的。
|
||||
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.
|
||||
|
||||
现在你有 3 个外部服务想接入:公司的 Jira API(查 issue、建 ticket)、自建的部署系统(触发 deploy、看日志)、团队的 Notion 知识库(搜文档、建页面)。你不想为每个服务重写一套工具代码。
|
||||
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.
|
||||
|
||||
你需要一个标准协议——外部服务只要实现它,Agent 就能直接调用,不管服务用什么语言写的。
|
||||
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)定义了 Agent 如何发现和调用外部工具。核心概念:
|
||||
MCP (Model Context Protocol) defines how agents discover and invoke external tools. Core concepts:
|
||||
|
||||
| 概念 | 作用 |
|
||||
| Concept | Purpose |
|
||||
|------|------|
|
||||
| MCPClient | Agent 端的客户端,连接 server、发现工具、调用工具 |
|
||||
| MCP Server | 外部服务,实现 `tools/list` + `tools/call` |
|
||||
| assemble_tool_pool | 把内置工具和 MCP 工具组装成一个工具池 |
|
||||
| mcp\_\_server\_\_tool 命名 | 避免不同 server 的工具名冲突 |
|
||||
| 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 |
|
||||
|
||||
沿用 s18 的教学版 worktree 隔离、自主认领、空闲轮询、协议系统。本章新增:`connect_mcp` 工具——连接外部服务,发现工具,加入工具池。
|
||||
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.
|
||||
|
||||
教学版用 mock handler 模拟外部 server。真实版会启动子进程,通过 stdin/stdout 发送 JSON-RPC 请求。mock 的好处是不依赖外部服务就能跑完整流程;代价是你看不到真正的网络通信和进程管理。
|
||||
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:发现 + 调用
|
||||
### MCPClient: Discovery + Invocation
|
||||
|
||||
```python
|
||||
class MCPClient:
|
||||
@@ -63,9 +63,9 @@ class MCPClient:
|
||||
return handler(**args)
|
||||
```
|
||||
|
||||
教学版用 Python 函数模拟 server 的工具实现。真实版通过 stdio JSON-RPC 与子进程通信。
|
||||
The tutorial uses Python functions to simulate server tool implementations. The real version communicates with subprocesses via stdio JSON-RPC.
|
||||
|
||||
### connect_mcp:连接 + 发现
|
||||
### connect_mcp: Connect + Discover
|
||||
|
||||
```python
|
||||
def connect_mcp(name: str) -> str:
|
||||
@@ -79,9 +79,9 @@ def connect_mcp(name: str) -> str:
|
||||
return f"Connected to '{name}'. Discovered: ..."
|
||||
```
|
||||
|
||||
连接后,server 提供的工具立即可用。
|
||||
After connecting, the server's tools are immediately available.
|
||||
|
||||
### normalize_mcp_name:名称规范化
|
||||
### normalize_mcp_name: Name Normalization
|
||||
|
||||
```python
|
||||
_DISALLOWED_CHARS = re.compile(r'[^a-zA-Z0-9_-]')
|
||||
@@ -90,9 +90,9 @@ def normalize_mcp_name(name: str) -> str:
|
||||
return _DISALLOWED_CHARS.sub('_', name)
|
||||
```
|
||||
|
||||
所有非 `[a-zA-Z0-9_-]` 的字符替换为 `_`。防止 server 名或工具名中包含特殊字符导致命名冲突或注入问题。
|
||||
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: Assemble Tool Pool
|
||||
|
||||
```python
|
||||
def assemble_tool_pool() -> tuple[list[dict], dict]:
|
||||
@@ -110,173 +110,173 @@ def assemble_tool_pool() -> tuple[list[dict], dict]:
|
||||
return tools, handlers
|
||||
```
|
||||
|
||||
前缀 `mcp__{server}__{tool}` 避免不同 server 的工具名冲突。名称经过 `normalize_mcp_name` 规范化。
|
||||
The prefix `mcp__{server}__{tool}` prevents tool name collisions across different servers. Names are normalized through `normalize_mcp_name`.
|
||||
|
||||
MCP 工具的 description 带 `(readOnly)` 或 `(destructive)` 标注——教学版用文本标注,真实 CC 用 tool annotations 结构体让权限系统判断。
|
||||
MCP tool descriptions include `(readOnly)` or `(destructive)` annotations — the tutorial uses text annotations, while real CC uses structured tool annotations for the permission system.
|
||||
|
||||
### 无缓存:工具池变了,prompt 也变
|
||||
### No Cache: Tool Pool Changes, Prompt Changes Too
|
||||
|
||||
s10-s18 的 agent_loop 用 prompt cache 避免重复序列化。s19 去掉了缓存:
|
||||
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() # 每次重新构建
|
||||
system = assemble_system_prompt(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() # 连接后重建
|
||||
tools, handlers = assemble_tool_pool() # Rebuild after connection
|
||||
system = assemble_system_prompt(context)
|
||||
```
|
||||
|
||||
原因:`connect_mcp` 之后工具池变化了——新增了 `mcp__docs__search` 等工具。缓存中的工具列表是旧的,继续用会导致模型调用不到新工具。教学版直接去掉缓存,代价是多花一点序列化时间。
|
||||
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 工具只有 Lead 可用
|
||||
### MCP Tools: Lead Only
|
||||
|
||||
教学版中,`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)。
|
||||
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).
|
||||
|
||||
这是教学简化。真实 CC 中,MCP 工具对主 agent 和子 agent 都可用——子 agent 继承父级的 MCP 配置。
|
||||
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.
|
||||
|
||||
---
|
||||
|
||||
## 相对 s18 的变更
|
||||
## Changes from s18
|
||||
|
||||
| 组件 | 之前 (s18) | 之后 (s19) |
|
||||
| Component | Before (s18) | After (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 |
|
||||
| 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
|
||||
```
|
||||
|
||||
试试这些 prompt:
|
||||
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?`
|
||||
|
||||
观察重点:连接 MCP server 后,工具名是否带 `mcp__docs__` 或 `mcp__deploy__` 前缀?两个 server 的工具是否同时可用?MCP 工具的 description 是否带 (readOnly)/(destructive) 标注?
|
||||
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
|
||||
|
||||
现在 Agent 可以通过标准协议接入外部工具了。但前面 19 章每章都只加一个机制,真实 Agent 不会这样拆开运行。
|
||||
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.
|
||||
|
||||
工具、权限、hooks、todo、任务图、记忆、压缩、后台、cron、团队、worktree、MCP 这些机制应该挂在同一个循环上,而不是散在 19 个 demo 里。
|
||||
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 → 把前 19 章的机制合回一个完整 harness。机制很多,循环一个。
|
||||
s20 Comprehensive Agent → Combine the first 19 chapters into one complete harness. Many mechanisms, one loop.
|
||||
|
||||
<details>
|
||||
<summary>深入 CC 源码</summary>
|
||||
<summary>Deep Dive into CC Source</summary>
|
||||
|
||||
> 以下基于 CC 源码 `services/mcp/client.ts`、`auth.ts`、`config.ts`、`channelNotification.ts` 的分析。
|
||||
> The following is based on analysis of CC source: `services/mcp/client.ts`, `auth.ts`, `config.ts`, `channelNotification.ts`.
|
||||
|
||||
### 一、6 种 Transport 类型
|
||||
### 1. Six Transport Types
|
||||
|
||||
教学版只展示了 stdio mock。CC 支持 6 种传输(`types.ts:23-25`):
|
||||
The tutorial only shows a stdio mock. CC supports 6 transport types (`types.ts:23-25`):
|
||||
|
||||
| Transport | 通信方式 |
|
||||
| Transport | Communication method |
|
||||
|-----------|---------|
|
||||
| `stdio` | 子进程 stdin/stdout(跨平台默认) |
|
||||
| `stdio` | Subprocess stdin/stdout (cross-platform default) |
|
||||
| `sse` | HTTP Server-Sent Events |
|
||||
| `http` | Streamable HTTP(POST/SSE 双向) |
|
||||
| `http` | Streamable HTTP (POST/SSE bidirectional) |
|
||||
| `ws` | WebSocket |
|
||||
| `sse-ide` | IDE 内嵌 SSE 传输 |
|
||||
| `sdk` | 进程内 SDK 传输 |
|
||||
| `sse-ide` | IDE-embedded SSE transport |
|
||||
| `sdk` | In-process SDK transport |
|
||||
|
||||
连接时本地(stdio)和远程(http/sse/ws)服务器分批并发:本地批量 3 个,远程批量 20 个。
|
||||
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`):
|
||||
`assembleToolPool()` (`tools.ts:345-364`):
|
||||
|
||||
```typescript
|
||||
// 去重时优先保留内置工具(name 相同时内置在前)
|
||||
// Dedup with priority: built-in tools win on name collision (sorted first)
|
||||
return uniqBy(
|
||||
[...builtInTools.sort(byName), ...filteredMcpTools.sort(byName)],
|
||||
'name',
|
||||
)
|
||||
```
|
||||
|
||||
内置工具和 MCP 工具分开排序,不是合起来排。原因是 CC 的 `claude_code_system_cache_policy` 在最后一个内置工具之后的某个位置放全局缓存断点——混排会破坏这个设计。
|
||||
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.
|
||||
|
||||
### 三、命名规则:`mcp__server__tool`
|
||||
### 3. Naming Convention: `mcp__server__tool`
|
||||
|
||||
`buildMcpToolName()`(`mcpStringUtils.ts:50-52`):
|
||||
`buildMcpToolName()` (`mcpStringUtils.ts:50-52`):
|
||||
|
||||
```
|
||||
mcp__<normalizedServerName>__<normalizedToolName>
|
||||
```
|
||||
|
||||
所有非 `[a-zA-Z0-9_-]` 字符替换为 `_`(`normalization.ts:17-23`)。教学版的 `normalize_mcp_name` 用同样的规则。
|
||||
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 对 MCP 工具有独立的权限系统。`checkPermissions()` 对 MCP 工具的检查逻辑不同于内置工具——MCP 工具可以声明自己的权限需求(readOnly、destructive 等),CC 根据声明决定是否需要用户确认。教学版只在 description 中用文本标注 `(readOnly)` / `(destructive)`,不做权限拦截。
|
||||
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 服务器配置来自多个来源。CC 的配置优先级从低到高:
|
||||
MCP server configuration comes from multiple sources. CC's priority from lowest to highest:
|
||||
|
||||
```
|
||||
claude.ai 连接器 < plugin < user settings.json < approved project .mcp.json < local settings.local.json
|
||||
claude.ai connectors < plugin < user settings.json < approved project .mcp.json < local settings.local.json
|
||||
```
|
||||
|
||||
`claude.ai` 连接器单独拉取、按内容签名去重,以最低优先级合并(`config.ts:1267-1289`)。企业 `managed-mcp.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.
|
||||
|
||||
教学版直接传 server name 给 `MOCK_SERVERS` 字典,不做配置合并。
|
||||
The tutorial passes server names directly to the `MOCK_SERVERS` dict, without config merging.
|
||||
|
||||
### 六、Channel 通知:服务器反向推消息
|
||||
### 6. Channel Notifications: Servers Push Messages Back
|
||||
|
||||
教学版只讲了 Agent → MCP Server 的单向调用。CC 还支持反向通知(`channelNotification.ts`):
|
||||
The tutorial only covers agent → MCP Server unidirectional calls. CC also supports reverse notifications (`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 秒内)
|
||||
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)
|
||||
|
||||
Server 还可以请求权限:`notifications/claude/channel/permission_request` → Agent 回复 `notifications/claude/channel/permission`。用户通过 5 字母短 ID 确认/拒绝。
|
||||
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.
|
||||
|
||||
### 七、OAuth 认证流程
|
||||
### 7. OAuth Authentication Flow
|
||||
|
||||
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'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 对 MCP 连接有精细的错误分类和重试(`client.ts:1266-1402`):
|
||||
- 终局性错误(ECONNRESET、ETIMEDOUT、EPIPE 等):连续 3 次 → 关闭 + 重连
|
||||
- 工具调用 401:令牌过期 → 抛出 `McpAuthError` → 触发重认证
|
||||
- 工具调用超时:`Promise.race` 超时(可配置,默认约 28 小时)
|
||||
- Stdio 断连:按 SIGINT → SIGTERM → SIGKILL 顺序杀进程
|
||||
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 → 1 种(mock stdio):概念量可控
|
||||
- Channel 反向通知 → 省略:教学版 Agent 是主动方
|
||||
- OAuth 流程 → 省略:教学版假设 server 不需要认证
|
||||
- 多层配置优先级 → 省略:教学版直接传 server name
|
||||
- 复杂的错误分类 → 省略:教学版用 try/except 兜底
|
||||
- MCP 工具只给 Lead → 省略子 agent 继承:简化代码结构
|
||||
- 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@v0, ja@v0 -->
|
||||
<!-- translation-sync: zh@v2, en@v2, ja@v0 -->
|
||||
|
||||
Reference in New Issue
Block a user