Claude Code 表面上是一个运行在终端里的编码 Agent,网络上却不是“CLI 发一个请求,模型返回一个答案”这么简单。一次完整任务至少会同时经过三条相互独立的通信面:
- 模型通信面:Claude Code 向 Claude 模型服务持续发送上下文,并通过流式响应接收文本、thinking 和
tool_use; - 工具通信面:本地工具在进程内或子进程中执行,远程能力则可能通过 MCP 的 stdio、Streamable HTTP 或兼容性 SSE 接入;
- 执行出网面:Bash、测试程序、包管理器、浏览器和其他子进程从沙箱内访问文件系统与网络。
此外还有一条横跨三者的控制面:模型路由、认证、企业代理、LLM Gateway、权限规则、沙箱策略、取消信号与遥测。
这几条链路的故障语义不同。模型 SSE 中断,并不等于 MCP 断线;MCP 工具返回错误,也不等于 Bash 子进程没有执行;上下文超长,更不是网络不可达。只有先把边界拆开,才能理解 Claude Code 为什么能够连续工作,以及它在什么位置恢复、重试或停止。
资料与证据边界
本文使用两类资料:
- 官方契约:Anthropic 的 Claude Code、Claude API 和 MCP 官方文档,用于确认公开协议、配置项与用户可依赖的行为;
- 实现分析:
how-claude-code-works仓库对特定 Claude Code 构建快照的源码分析,用于解释QueryEngine、query()、StreamingToolExecutor、压缩流水线和错误扣留等内部结构。该仓库 README 明确声明它是独立研究与教育性分析,并非 Anthropic 官方设计说明。因此,文中凡涉及内部类名、精确调度顺序和恢复分支,都会标注为“仓库分析”,不把它们写成跨版本稳定的产品契约。1
核心结论
在进入细节前,可以先记住六点:
- Claude Code 的主模型流本质上是一次次 HTTPS/SSE 请求,而不是一个无限延续的单请求会话。
- 模型只生成工具调用意图,真正的文件写入、命令执行与外部 API 副作用由本地 Claude Code 或 MCP Server 完成。
- 每一轮工具执行都会把
tool_result写回消息历史,再发起下一次模型请求。 - 仓库分析显示,Claude Code 可以在整体模型响应尚未结束时,提前执行已经完整解析的工具调用;但这属于实现优化,不是 Messages API 的协议保证。
- MCP 远程 HTTP/SSE 连接与本地 stdio 进程采用不同恢复策略:前者自动退避重连,后者不会自动重启。
- 企业正向代理、LLM Gateway 与 Bash 沙箱代理是三层不同设施,不能互相替代。
1. Claude Code 网络拓扑

1.1 本地 CLI 进程
Claude Code 的 CLI 不是一个薄客户端,而是 Agent 的本地运行时。官方文档将 Claude Code 描述为可以直接读取代码库、编辑文件、运行命令并接入开发工具的 Agentic Coding Tool;终端只是入口,任务循环和工具副作用主要发生在本地。2
从网络职责看,本地进程至少包含以下逻辑模块:
| 模块 | 主要职责 | 是否直接产生网络流量 |
|---|---|---|
| 输入与界面层 | 接收用户消息、显示流式文本、权限弹窗、进度和结果 | 间接 |
| 会话层 | 保存消息历史、使用量、工作目录与任务状态 | 通常否 |
| 模型客户端 | 组装 Messages 请求,连接 Anthropic 或第三方模型上游 | 是 |
| Agent Loop | 判断继续、执行工具、回填结果或停止 | 间接 |
| MCP Client | 管理 MCP Server、能力发现、OAuth、工具调用 | 可能 |
| 工具执行器 | 调度 Read、Edit、Bash、MCP 等工具 | 可能 |
| 沙箱管理器 | 限制 Bash 及其子进程的文件与网络访问 | 是 |
| 权限与策略层 | allow/deny/ask、工作区信任、企业策略 | 通常否 |
how-claude-code-works 将会话外壳还原为 QueryEngine,将单次模型—工具循环还原为 query() 异步生成器。这个分层非常有解释力:
QueryEngine关注会话生命周期、消息持久化、预算、最终结果和权限拒绝;query()关注上下文压缩、模型流、工具执行以及本轮继续或终止。3
因此,“一次用户输入”与“一次模型 API 请求”不是同一件事。一个用户任务可能只对应一次 submitMessage(),却在 query() 内部发起多次模型请求。
1.2 Claude 模型服务
在直接 Anthropic API 路径上,Claude Code 使用 Messages API 的流式能力。请求设置 stream: true 后,服务端通过 SSE 连续发送结构化事件,包括:
- 文本块增量;
- thinking / reasoning 增量;
tool_use块及其参数增量;- usage、stop reason 与最终完成事件;
- 流内部错误。4
模型服务做的是推理与决策,不是本地副作用执行。例如,模型可以返回:
{ "type": "tool_use", "id": "toolu_xxx", "name": "Read", "input": { "file_path": "src/payment.ts" }}但它不会直接打开用户磁盘上的文件。Read 的实际执行发生在 Claude Code 本地工具系统;如果是 MCP 工具,则由对应 MCP Server 执行。
这条边界十分关键:
模型返回的是“调用什么工具、传什么参数”,本地运行时决定“是否允许、何时执行、执行结果如何回填”。
因此,网络层和权限层并不是模型服务的附属功能,而是 Agent Runtime 自己的责任。
1.3 Amazon Bedrock 等模型上游
Claude Code 不只支持直连 Anthropic API。官方文档还提供 Amazon Bedrock、Google Vertex AI 和 Microsoft Foundry 等第三方部署路径。不同上游共享相近的 Agent 语义,但认证、端点、模型标识和底层 API 不同。
| 上游 | 主要认证方式 | 模型标识 | 关键差异 |
|---|---|---|---|
| Anthropic API | Claude 账号或 ANTHROPIC_API_KEY | Anthropic 模型名或别名 | 直接使用 Anthropic Messages 协议 |
| Amazon Bedrock | AWS SDK 默认凭证链、IAM、SSO、Bedrock API Key | inference profile ARN / Bedrock model ID | Claude Code 使用 Bedrock Invoke API,不使用 Converse API |
| Google Vertex AI | Application Default Credentials、Service Account、Workload Identity | Vertex 版本名 | 受项目、区域、配额和 Model Garden 可用性约束 |
| Microsoft Foundry | API Key 或 Entra ID 默认凭证链 | deployment name | 认证与部署绑定在 Azure 资源上 |
| 企业 LLM Gateway | 企业自定义 token、mTLS 或代理认证 | 网关映射的模型名 | 由网关决定上游路由、审计和配额 |
Bedrock 路径依赖 AWS 默认凭证链,并允许通过 IAM、SSO Profile、环境变量或 Bedrock API Key 认证。官方文档还明确指出标准 Bedrock 路径使用 Invoke API。5
Vertex AI 路径则使用 Google Cloud 的标准凭证体系,端点与可用模型还受项目、区域和配额影响。6
这里要区分两个配置概念:
- 模型选择决定“由哪个模型回答”;
- Base URL / Provider 选择决定“请求发到哪里”。
官方模型配置文档明确指出,ANTHROPIC_BASE_URL 只改变请求端点,并不自动改变模型选择。7
1.4 企业 LLM Gateway
LLM Gateway 是位于 Claude Code 与模型上游之间的应用层代理。它一般承担:
- 集中保存 API Key 或云凭证;
- 按团队、项目或环境统计使用量;
- 实施预算、速率限制和模型白名单;
- 记录审计日志;
- 在 Anthropic、Bedrock、Vertex 等上游间路由;
- 对 Header、模型名和请求格式进行转换。
官方文档强调,Gateway 必须正确透传 Claude Code 使用的请求格式与能力 Header;否则新模型能力、beta 功能、工具搜索或 prompt caching 可能失效。8
LLM Gateway 只覆盖模型流量。它默认不会替 Claude Code 管理:
- 本地 Bash 子进程访问 GitHub 或 npm;
- MCP Server 的 HTTP 连接;
- 本地 stdio MCP 进程;
- WebFetch 与其他工具的独立网络路径。
把 LLM Gateway 当成“所有 Agent 网络流量的总入口”是常见误区。
1.5 本地和远程 MCP Server
MCP 是 Claude Code 的第二条网络主干。它把外部系统能力标准化为:
- Tools;
- Resources;
- Prompts;
- 通知与动态能力变化。
本地 MCP Server 常通过 stdio 运行,由 Claude Code 启动子进程;远程 MCP Server 则主要通过 Streamable HTTP 接入,旧式 HTTP+SSE 仍作为兼容路径存在。Claude Code 官方文档还支持 WebSocket 扩展,但 MCP 标准传输的核心仍是 stdio 与 Streamable HTTP。910
how-claude-code-works 的工具系统分析显示,MCP 工具最终会被桥接为与内置工具相同的统一 Tool 接口,进入同一套:
- 工具查找;
- Schema 验证;
- 权限检查;
- 执行;
- 结果格式化;
tool_result回填。
这使 MCP 不是“外挂脚本”,而是 Claude Code 工具池的一部分。11
1.6 Shell 与沙箱子进程
Shell 工具打开的是第三条网络面。一个 Bash 命令可能继续派生:
git;gh;npm/pnpm;curl;- 测试进程;
- 浏览器驱动;
- 容器或基础设施 CLI。
这些子进程可能访问与模型服务完全无关的主机。Claude Code 的 Bash 沙箱使用操作系统级机制限制文件系统与网络访问,并将约束施加到 Bash 及其所有子进程。macOS 使用 Seatbelt;Linux/WSL2 依赖 bubblewrap,并通过外部代理控制出网。12
2. 一次模型请求如何构造

Claude 模型在每次 API 请求之间不会自动记住上一轮。Claude Code 必须把当前推理需要的内容重新组装进请求。官方 prompt caching 文档明确说明:每个新 turn 都会再次发送系统提示词、项目上下文、历史消息、工具调用和结果;缓存只是让服务端避免重复计算相同前缀,并没有省略这些语义内容。13
一个高度简化的请求结构如下:
{ "model": "claude-...", "max_tokens": 16000, "system": [ {"type": "text", "text": "..."} ], "tools": [ { "name": "Read", "description": "...", "input_schema": {"type": "object", "properties": {}} } ], "messages": [ {"role": "user", "content": "..."}, {"role": "assistant", "content": []}, {"role": "user", "content": []} ], "stream": true}实际请求还可能包含 thinking、effort、cache control、service tier、beta headers 与 provider 特定配置。
2.1 System Prompt
System Prompt 定义 Claude Code 作为编码 Agent 的行为边界,包括:
- 核心操作原则;
- 工具使用规则;
- 安全约束;
- 当前环境信息;
- 输出风格;
- 用户追加的系统指令。
官方 context window 文档确认,系统指令、CLAUDE.md、auto memory、skills、MCP 工具名、文件内容和命令输出都会进入 Claude 可见上下文,但不同机制的加载时机和压缩后恢复方式不同。14
仓库分析进一步给出了一种具体快照布局:
- 稳定核心指令放在较前位置;
- 动态系统内容位于后部;
- CLAUDE.md、日期、记忆、技能或 MCP 增量可能以消息附件形式注入;
- Git 状态、平台、Shell 等环境信息被纳入请求上下文。15
这里最重要的工程约束不是“提示词应该写多长”,而是稳定前缀。Prompt cache 按精确前缀匹配,修改前部内容会使后续层全部失效。Claude API 的缓存层级是:
tools → system → messages因此:
- 工具定义变化会使 tools、system 和 messages 缓存全部失效;
- system 变化会使 system 与 messages 层失效;
- 只在消息尾部追加新 turn,通常可以继续命中前缀缓存。16
这也是 Claude Code 尽量保持工具顺序、系统提示词和会话启动上下文稳定的原因。
2.2 对话历史
对话历史不仅包含用户与模型的文本,还包括完整的 Agent 轨迹:
User message→ Assistant text / thinking / tool_use→ User tool_result→ Assistant next reasoning / tool_use→ User tool_result→ ...官方文档把会话上下文描述为:
- 对话历史;
- 已读取文件;
- 命令输出;
- CLAUDE.md;
- auto memory;
- skills;
- 系统指令。17
对于工具调用,Messages API 使用一条 assistant 消息承载 tool_use,随后由一条 user 消息承载与 tool_use_id 对应的 tool_result。工具结果之所以使用 user role,并不表示它是用户手工输入,而是协议把“客户端环境返回给模型的数据”放在 user turn 中。
当一次 assistant turn 生成多个并行工具调用时,推荐将所有对应 tool_result 放在同一条 user 消息中,否则会降低模型后续继续并行调用工具的概率。18
2.3 工具定义
工具定义至少包括:
- 唯一名称;
- 描述;
- JSON Schema;
- 可选的缓存或延迟加载标记。
模型依据工具定义生成 tool_use,Claude Code 再根据本地实现决定权限与执行方式。
仓库分析中的统一 Tool 接口还包含:
isConcurrencySafe(input);isReadOnly(input);isDestructive(input);validateInput();checkPermissions();call();- UI 渲染函数。11
特别需要注意:
只读与并发安全不是同一个概念。
只读操作通常适合并行,但并不自动意味着并发安全;真正决定调度的应是工具对当前输入的并发安全声明。
为了减少上下文占用并维持缓存稳定,Claude Code 默认延迟加载 MCP 工具定义。官方文档说明,开始时可以只加载工具名;当模型通过 Tool Search 发现某个工具后,再以 tool_reference 形式把完整定义追加到历史中,从而不破坏前部缓存。1419
仓库分析还指出,内置工具与 MCP 工具会分区排序:内置工具保持稳定前缀,MCP 工具放在后缀,以降低 MCP 连接变化对缓存键的影响。这个精确排序属于实现分析,但其背后的目标与官方 prompt caching 规则一致。11
2.4 上下文压缩结果
当上下文接近上限时,Claude Code 不会简单丢弃整个会话。官方公开行为是:
- 优先清理较旧的工具输出;
- 仍不够时,对对话历史生成结构化摘要;
- 重新注入需要长期保留的系统提示词、项目根 CLAUDE.md 与 auto memory;
- 某些按路径加载的规则或嵌套 CLAUDE.md,会在后续重新读取匹配文件时再次加载。1420
how-claude-code-works 对特定快照还原出更细的入口流水线,例如:
Tool Result Budget→ History Snip→ Microcompact→ Context Collapse→ Autocompact→ Build API Request这些阶段的共同目的,是在下一次网络请求发出前控制消息体积。315
这里必须严格区分:
| 问题 | 本质 | 典型处理 |
|---|---|---|
| Prompt Too Long | 请求语义内容超过上下文能力 | 裁剪、折叠、摘要、重新构造请求 |
| Request Too Large | HTTP 请求字节数超限 | 减少附件、消息或工具定义 |
| 网络超时 | 连接或读取阶段失败 | 退避重试、重新建连 |
| SSE 流中断 | 已开始响应但未完成 | 处理部分流,视内容类型决定恢复 |
| Max Output Tokens | 当前输出被截断 | 提高上限或生成续写 turn |
把 Prompt Too Long 当作普通网络错误反复重试,只会重复发送同一个过大的请求。
2.5 Base URL、认证和模型路由
Claude Code 的“发到哪里”“如何认证”和“选择哪个模型”是三个独立维度。
直接 Anthropic API
常见配置包括:
Amazon Bedrock
认证来自 AWS SDK 默认凭证链,可使用 Profile、SSO、环境变量、角色或 Bedrock API Key;模型名则是 Bedrock 的 inference profile 或 model ID。5
Google Vertex AI
认证来自 Google Cloud ADC,模型路由还依赖 Project、Region 与可用模型版本。6
Microsoft Foundry
可以使用 Foundry API Key 或 Microsoft Entra ID 默认凭证链,模型选择对应 Azure 中的 deployment name。22
企业 LLM Gateway
ANTHROPIC_BASE_URL 可把请求转发到企业网关,但网关仍需要理解并转发:
- Messages API 请求体;
- streaming 响应;
anthropic-version;- 必要的 beta headers;
- 模型别名或自定义映射;
- request/session/agent 追踪 Header。8
3. Claude API 流式响应处理
3.1 SSE 事件序列
Messages API 的流式响应不是“一个 JSON 被切成很多段”,而是由多个有名称的 SSE 事件组成。官方事件顺序为:
message_start;- 对每个内容块:
content_block_start;- 零个或多个
content_block_delta; content_block_stop;
- 一个或多个
message_delta; message_stop。4
中间还可能出现:
ping;error;- 新增的未知事件类型。
因此,客户端必须按事件类型和内容块 index 维护状态,而不能只把所有 data: 字段拼成字符串。
3.2 文本块增量
文本内容通过:
{ "type": "content_block_delta", "index": 0, "delta": { "type": "text_delta", "text": "..." }}持续到达。
CLI 可以即时渲染这些片段,改善首字延迟。但渲染层需要理解:
- 当前文本只是部分结果;
- 后面可能继续出现新的内容块;
- 当前请求可能最终以
tool_use而不是普通文本完成; - 流中错误可能发生在已经显示部分文本之后。
文本增量适合做“尽早展示”,不适合当作本轮已完成的事务提交信号。
3.3 Tool Use 块增量
工具调用的参数以 input_json_delta 形式到达,其中 partial_json 是 JSON 字符串片段:
{ "type": "content_block_delta", "index": 1, "delta": { "type": "input_json_delta", "partial_json": "{\"file_path\":\"src/pay" }}标准处理方式是:
- 按内容块 index 累积
partial_json; - 等待
content_block_stop; - 解析完整 JSON;
- 用工具 Schema 再次验证;
- 只有验证成功后,才进入权限与执行流水线。4
Claude API 还提供 fine-grained tool streaming,可减少服务端对工具参数的缓冲,从而更早返回大参数片段。但官方明确提醒,这种模式可能产生不完整或无效 JSON;若输出因 max_tokens 结束,工具参数甚至可能被截断在中间。23
因此:
“收到工具参数增量”不等于“工具可以执行”;完整内容块边界与 Schema 验证才是安全调度点。
3.4 流完成与流中错误
完成一条内容块和完成整个消息是两个不同事件:
content_block_stop:某一个 text、thinking 或 tool_use block 完成;message_stop:整个模型响应完成。
message_delta 会更新顶层字段,包括 stop reason 和累计 usage。一次典型工具调用响应的 stop reason 是 tool_use;普通答案可能是 end_turn;输出上限则可能是 max_tokens。
流中还可能出现:
event: errordata: {"type":"error","error":{"type":"overloaded_error", ...}}这类错误不是普通 HTTP 非 2xx 响应,而是已经建立 SSE 连接后由事件流传回的错误。4
3.5 HTTP 200 后为什么仍可能失败
HTTP 200 只表示:
- 请求已被接受;
- 响应 Header 已返回;
- SSE 流开始建立。
它不表示:
- 模型已经完成生成;
- 所有内容块已经闭合;
- 工具 JSON 一定完整;
- 服务端不会在后续过载;
- 中间代理不会断开长连接。
Anthropic 官方错误文档明确说明,SSE 可以在初始 HTTP 200 之后出现错误,此时不能沿用普通 HTTP 状态码错误处理。24
这会形成四种不同结果:
| 结果 | 是否有 HTTP 200 | 是否有部分内容 | 是否有 message_stop |
|---|---|---|---|
| 正常完成 | 是 | 是 | 是 |
| 流中服务错误 | 是 | 可能 | 否 |
| 网络中断 | 是 | 可能 | 否 |
| 客户端取消 | 是或否 | 可能 | 否 |
因此,Claude Code 必须把 message_stop 或等价的完整消息状态当作“本轮网络响应完成”的边界,而不是仅依赖 HTTP 200。
4. 流式响应如何驱动 Agent Loop

4.1 QueryEngine 与查询循环
how-claude-code-works 将 Claude Code 的主循环还原为双层结构:3
QueryEngine└── submitMessage() └── query() ├── 压缩与上下文构造 ├── 调用模型 ├── 消费流事件 ├── 调度工具 ├── 拼接 tool_result └── 继续下一轮或终止这个分层的价值在于区分两种生命周期:
| 维度 | QueryEngine | query() |
|---|---|---|
| 管理对象 | 一次用户交互与会话外壳 | 当前模型—工具循环 |
| 关注点 | 消息持久化、预算、最终结果、权限拒绝 | 压缩、模型流、工具执行、恢复 |
| 状态范围 | 会话级 | turn / loop 级 |
| 主要输出 | 面向 CLI/SDK 的消息和最终 Result | 流事件、assistant、tool result 等中间消息 |
这不是 Anthropic 对外保证的类结构,而是仓库对特定构建的实现分析。但它非常适合解释为什么 Claude Code 可以在一个用户任务内多次调用模型。
4.2 异步生成器消费事件
仓库分析中的 query() 是 async function*。异步生成器在 Agent Loop 中有三个优势:
线性控制流
恢复分支可以写成普通的:
更新状态→ continue而不必把每种情况拆成复杂回调。
自然背压
消费方只有处理完当前 yield 的事件,才继续拉取下一事件。相比无界 EventEmitter,这更容易避免 UI、日志和工具结果队列失控。
取消传播
生成器退出时可以清理底层流、取消 pending task 和释放资源。官方交互文档也把 Ctrl+C 定义为取消当前操作,把 Esc 定义为停止当前响应或工具调用。25
4.3 Tool Call 完成后的调度
模型流里可能同时出现文本块和多个工具块。安全的调度顺序应是:
input_json_delta→ 累积参数→ content_block_stop→ JSON 解析→ Schema 验证→ 查找工具→ 权限检查→ 入执行队列仓库分析中的 StreamingToolExecutor 为每个工具维护:
queued → executing → completed → yielded当一个 tool_use 块完整解析后,它就可以被加入队列,不必等待整个 assistant message 的 message_stop。3
这一步的关键是“完整块”,不是“看到工具名称”或“收到一部分 JSON”。
4.4 模型继续输出时提前执行工具
这是 Claude Code 网络层最有价值、也最需要准确表述的优化。
Messages API 只规定事件如何到达,不规定客户端何时执行工具。官方通用工具文档也明确表示,多个工具是并行还是串行,由客户端根据副作用和依赖自行决定。18
how-claude-code-works 对其分析快照的结论是:
当某个
tool_useblock 已完整解析,StreamingToolExecutor会立即尝试调度;模型可以继续输出后面的 token 或其他工具块。
时间线上表现为:
模型流: [ text ][ tool A 完成 ][ text ][ tool B 完成 ][ message_stop ]工具 A: [----------执行----------]工具 B: [---执行---]而不是:
模型流: [----------------完整响应----------------]工具 A: [执行]工具 B: [执行]这一优化将部分工具耗时覆盖在模型尾部生成时间里。但必须强调:
- 它是仓库还原出的内部实现,不是 Claude API 的公共兼容性承诺;
- 只有完整、验证通过且权限已允许的调用才能提前执行;
- 对写操作和存在顺序依赖的调用,调度器仍需要独占或延迟;
- fine-grained tool streaming 的原始 JSON 片段不能直接执行。
4.5 Tool Result 回填和下一轮请求
工具执行后,Claude Code 把结果转换为 tool_result 内容块:
{ "role": "user", "content": [ { "type": "tool_result", "tool_use_id": "toolu_xxx", "content": "..." } ]}然后发起下一次模型请求:
工具失败同样应该返回结构化结果,而不是让整个进程直接崩溃。仓库分析把这一原则概括为“错误是数据”:Schema 错误、权限拒绝、执行异常和 MCP 错误都会被封装为带错误标记的 tool_result,让模型决定是否修正参数、换工具或停止。11
5. Claude Code 的工具并发机制
5.1 只读工具并行
官方通用工具文档指出,彼此独立的只读操作通常适合并行,而有副作用、共享状态或顺序要求的工具更适合串行。API 本身不规定执行顺序。18
仓库分析显示,Claude Code 的实际门控字段是:
isConcurrencySafe(input)而不是单纯的 isReadOnly(input)。典型可并发操作包括:
- 读取不同文件;
- Grep / Glob 搜索;
- 无副作用的 MCP 查询;
- 彼此独立的状态读取。
调度器会将连续出现的并发安全调用组成一批并行执行,同时保留模型给出的整体顺序。
5.2 写操作串行
典型需要串行或独占的操作包括:
Edit/Write;- 会修改仓库状态的 Bash 命令;
- 创建分支、提交或 PR;
- 会改变共享服务状态的 MCP 工具;
- 对前一个工具结果存在依赖的调用。
原因并不仅是“写操作危险”,还包括:
- 两个编辑可能基于同一旧文件版本;
mkdir与后续写文件存在顺序关系;git checkout会改变后续所有命令的工作树视图;- PR 创建依赖 push 已经成功;
- 外部服务可能不支持幂等重试。
仓库分析中的 fail-closed 默认值是:新工具默认视为不可并发、非只读,只有工具显式声明安全时才进入并行批次。11
5.3 兄弟工具取消
这一节需要区分“通用协议”与“实现策略”。
官方工具协议要求:即使某个调用最终没有执行,也应为它返回一个 is_error: true 的 tool_result,解释未执行原因。18
仓库分析进一步指出,在其观察到的 Claude Code 实现中,某些 Bash 工具失败后会触发 siblingAbortController,取消正在并行执行的兄弟工具。设计动机是 Bash 调用之间经常存在隐式依赖:
mkdir build→ cd build→ cmake ..如果第一步失败,继续执行后两步没有意义。
但这个行为不能泛化为“任何工具失败都会取消所有并行工具”:
- 独立 Read/Grep 失败通常不需要取消其他读取;
- MCP 查询之间可能彼此独立;
- 是否级联取消取决于工具类型和调度批次;
- 这是版本相关内部策略,不是公开 API 合约。3
正确抽象应是:
并发批次需要显式的失败域,而不是一刀切地全取消或全继续。
5.4 流式窗口如何隐藏工具延迟
假设模型完整响应耗时为 ,一批只读工具耗时为 ,后续串行写工具耗时为 。
传统等待完整响应后执行:
若只读工具在模型尾部生成期间提前启动:
可节省的时间近似为:
这种优化最适合:
- 读取文件;
- 搜索仓库;
- 查询 issue;
- 无副作用的 MCP 数据获取。
不适合:
- 尚未完整解析的参数;
- 需要用户批准的危险命令;
- 有强顺序依赖的写操作;
- 可能被后续模型输出撤销或修正的副作用操作。
6. MCP 网络链路

6.1 本地 stdio MCP
stdio MCP Server 是 Claude Code 启动的本地子进程。协议消息通过标准输入和标准输出传输:
Claude Code ├── stdin ── JSON-RPC request ──> MCP Server ├── stdout <─ JSON-RPC response ── MCP Server └── stderr <─ 日志与诊断信息 ───── MCP Server特点:
- 不需要监听端口;
- 不暴露网络服务;
- 权限继承自本地进程环境;
- 进程崩溃即连接终止;
- stdout 必须保持协议纯净,普通日志应写 stderr。
Claude Code 官方文档明确说明,stdio server 是本地进程,断开后不会自动重连。9
“不会自动重连”的实际含义是:Claude Code 不会把一个已退出的本地 Server 当成远程网络抖动反复重启。需要修正命令、依赖或配置,再显式重新连接。
6.2 Remote Streamable HTTP
当前 MCP 标准推荐远程 Server 使用 Streamable HTTP。它通常在单一 MCP Endpoint 上承载 JSON-RPC 请求,并可以返回普通 JSON 或 SSE 流。10
从 Claude Code 视角,Remote Streamable HTTP 适合:
- 企业 SaaS;
- Issue Tracker;
- 代码托管平台;
- 监控系统;
- 数据库网关;
- 内部知识与权限服务。
相比 stdio,它增加了:
- DNS / TCP / TLS;
- HTTP 代理;
- OAuth 或 Header 认证;
- 服务端会话;
- 超时与重连;
- 负载均衡和多实例路由。
如果服务端启用 MCP Session,后续请求还需要携带对应 Session ID;服务端返回会话失效时,客户端需要重新初始化,而不是继续使用旧状态。
6.3 兼容性 SSE
旧式 HTTP+SSE MCP 传输曾使用:
- SSE 作为服务端到客户端通道;
- 独立 HTTP POST 作为客户端到服务端通道。
当前 MCP SDK 文档将它标为 deprecated,只建议用于旧客户端兼容;新的远程 Server 应优先使用 Streamable HTTP。26
Claude Code 仍支持添加 SSE Server,是为了兼容已有生态,而不是表示 SSE 仍是新 MCP 服务的首选架构。
需要避免把两种 SSE 混淆:
| SSE 用途 | 含义 |
|---|---|
| Claude Messages SSE | 模型服务向 Claude Code 推送生成事件 |
| 旧 MCP SSE | MCP Server 向 MCP Client 推送 JSON-RPC 消息 |
它们都是 SSE,但协议、端点、状态机和错误语义完全不同。
6.4 初始化与能力协商
MCP 建连后,不能立即随意调用工具。官方规范要求初始化阶段先完成:
- Client 发送
initialize:- 支持的 protocol version;
- client capabilities;
- clientInfo;
- Server 返回:
- 选定的 protocol version;
- server capabilities;
- serverInfo;
- 可选 instructions;
- Client 发送
notifications/initialized; - 进入正常 Operation;
- 再调用
tools/list、resources/list、prompts/list等。27
能力协商解决的是“双方能做什么”,不是单纯的握手形式。例如,Server 是否支持:
- tools;
- resources;
- prompts;
- logging;
- list change notifications;
- subscriptions。
Claude Code 还支持 MCP list_changed 通知。当 Server 的工具、Prompt 或 Resource 发生变化时,可以动态刷新能力,而不必重启整个会话。官方文档说明,刷新失败时,当前版本会保留上次成功发现的能力,等待后续刷新。9
6.5 OAuth 和请求头
远程 HTTP MCP 可以使用 OAuth。Claude Code 官方文档覆盖:
- 自动 OAuth 登录;
- Token 安全存储与自动刷新;
- 固定 callback port;
- Dynamic Client Registration;
- 预配置 client ID / secret;
- OAuth metadata discovery;
- scope pinning;
- 无浏览器 SSH 环境下复制回调 URL。28
如果企业认证不是 OAuth,例如:
- Kerberos;
- 短期内部 Token;
- 自定义 SSO;
- 动态服务身份;
可以使用 headersHelper 在连接时生成 Header。官方当前行为是:
- 每次新连接和重连都重新运行 helper;
- 动态 Header 覆盖同名静态 Header;
- 401/403 时可重新获取 Header、重连并重试一次;
- project/local 级 helper 只有在接受工作区信任后才执行。29
这说明 MCP 认证本身也有生命周期:
获取凭证→ 建连→ 初始化→ 调用工具→ 401/403→ 刷新 Header / OAuth→ 重连→ 重试6.6 MCP 连接中断与重连
Claude Code 官方文档给出了明确的远程 MCP 恢复策略:
- HTTP 或 SSE 在会话中断开:
- 最多重连 5 次;
- 初始等待 1 秒;
- 每次翻倍;
- 重连期间
/mcp显示 pending; - 失败后标记 failed,可手工重试;
- stdio Server:
- 不自动重连;
- 初始连接的瞬时错误:
- 最多重试 3 次;
- 认证错误与 not found:
- 不做盲重试,需要修改配置;
- 成功建连后的 capability discovery:
- 对瞬时网络和服务端错误最多重试 3 次。30
仓库分析中的 Pending / Connected / NeedsAuth / Failed / Disabled 状态与官方用户可见行为基本对应,但具体内部枚举仍应视为版本实现。11
7. 企业代理和工具出网
企业环境里最容易混淆的三种代理如下:
| 层 | 代理什么 | 主要作用 |
|---|---|---|
| LLM Gateway | Claude 模型请求 | 模型路由、预算、审计、凭证集中化 |
| Corporate Forward Proxy | Claude Code 进程发出的 HTTP(S) 流量 | 网络出口、认证、TLS Inspection、防火墙 |
| Sandbox Proxy | Bash 与子进程的出网 | 域名 allowlist、凭证注入、OS 级隔离 |
7.1 LLM Gateway
LLM Gateway 位于模型应用协议层。它知道:
- 模型名;
- Messages 请求;
- streaming;
- usage;
- provider;
- API key;
- token 与成本。
它适合解决模型治理,但不自动替代企业正向代理,也不自动控制子进程访问 github.com、registry.npmjs.org 或内网服务。
7.2 HTTP_PROXY、HTTPS_PROXY 和 NO_PROXY
Claude Code 当前官方网络配置支持:
export HTTPS_PROXY=https://proxy.example.com:8080export HTTP_PROXY=http://proxy.example.com:8080export NO_PROXY="localhost,192.168.1.1,example.com,.example.com"也支持 NO_PROXY="*" 绕过全部代理。31
工程上需要明确:
HTTPS_PROXY常是最主要配置;NO_PROXY应包含本地服务、内网 MCP Endpoint 和不应穿过企业代理的地址;- 带用户名密码的代理 URL 需要避免落入脚本和日志;
- NTLM、Kerberos 等高级认证更适合由企业代理助手或 Gateway 处理;
- 环境变量必须在 Claude Code 启动时可见。
不要假设主进程配置代理后,所有 Shell 工具都会得到同一网络行为。是否继承、是否被沙箱改写、目标是否在 allowlist,仍取决于 Bash 执行路径。
7.3 自定义 CA 与 TLS Inspection
企业 TLS Inspection 会由代理重新签发服务端证书。Claude Code 若不信任企业根 CA,会出现:
- unknown issuer;
- self-signed certificate;
- certificate verification failed。
当前原生 Claude Code 默认信任 bundled Mozilla CA 与操作系统 CA Store;也可以通过:
export CLAUDE_CODE_CERT_STORE="bundled,system"export NODE_EXTRA_CA_CERTS=/path/to/enterprise-ca.pem显式加入企业 CA。31
这里不应通过:
NODE_TLS_REJECT_UNAUTHORIZED=0关闭证书验证。那会把“信任指定企业 CA”变成“信任任何证书”,直接破坏 TLS 安全边界。
7.4 mTLS
如果企业 Gateway 要求客户端证书,Claude Code 支持:
export CLAUDE_CODE_CLIENT_CERT=/path/to/client-cert.pemexport CLAUDE_CODE_CLIENT_KEY=/path/to/client-key.pemexport CLAUDE_CODE_CLIENT_KEY_PASSPHRASE="..."mTLS 在普通服务端证书验证之外,再让服务端验证客户端身份。31
因此一次完整建连可能包含:
TCP→ TLS Server Certificate 验证→ Client Certificate 提交→ 企业 Gateway 身份映射→ HTTP Authentication→ Messages API 请求任意一层失败,都可能在用户侧只表现为“Unable to connect”。
7.5 沙箱 Domain Allowlist
Bash 沙箱的网络访问通过外部代理控制。官方文档说明:
- Bash 和所有子进程受相同 OS 级网络约束;
- 第一次访问新域名可触发批准;
allowedDomains控制允许访问的域名;deniedDomains可覆盖更宽的 allow wildcard;allowManagedDomainsOnly可阻止开发者扩大管理员策略;- 内置 WebFetch 的权限规则与 Bash sandbox 是不同路径。12
示意配置:
{ "sandbox": { "enabled": true, "network": { "allowedDomains": [ "github.com", "api.github.com", "registry.npmjs.org" ], "deniedDomains": [ "metadata.internal" ] } }}需要特别注意:
- 允许
github.com不一定自动允许api.github.com; - wildcard 范围过大会扩大数据外泄面;
- 沙箱内置代理默认不解密 TLS,因此能按域名控制,但不能检查加密内容;
- 若启用凭证 masking 与注入,则可能需要显式启用 TLS termination,由代理完成安全替换。12
7.6 Shell 子进程的网络权限
Bash 子进程最终有效权限由多层共同决定:
企业防火墙∩ Corporate Proxy∩ NO_PROXY∩ Sandbox allowedDomains∩ deniedDomains∩ 文件系统权限∩ 凭证可见性∩ Claude Code permission rules例如,gh pr create 成功需要同时满足:
- Bash 工具被允许执行;
- 命令可访问当前仓库和 Git 配置;
api.github.com在沙箱网络允许范围内;- GitHub Token 对子进程可见,或由 sandbox credential masking 安全注入;
- 企业代理信任 GitHub TLS;
- GitHub API 权限足以创建 PR。
任何一项失败,都可能表现为工具错误。故障定位不能只看“Claude Code 能否连模型”。
8. Claude Code 的故障恢复路径
8.1 模型请求失败
模型请求在收到响应 Header 前失败时,可按 HTTP 与 SDK 错误类型处理。
Anthropic 官方 SDK 默认会对以下瞬时错误自动重试两次,并使用短指数退避:
重试时应记录:
- request ID;
- provider;
- model;
- attempt;
- error type;
- retry-after;
- 是否经过 Gateway;
- connect / TLS / first-byte 时间。
以下错误不应盲重试:
- 400 参数错误;
- 401 无效认证;
- 403 权限不足;
- 413 请求过大;
- 模型在当前 provider 不存在;
- mTLS 证书错误。
8.2 SSE 流中断
SSE 中断发生时,模型可能已经输出:
- 部分文本;
- 完整文本块;
- thinking 块;
- 半截工具参数;
- 已完整的一个工具调用;
- 多个工具调用中的一部分。
官方流恢复建议指出:
- 文本块可以把已有部分作为上下文,构造 continuation request;
tool_use与 extended thinking 不能安全地做通用“半块续接”;- 应从最近完整文本边界恢复,而不是假设半截工具 JSON可继续。4
Claude Code 层还需要检查:
- 是否已经执行过某个完整 tool call;
- 是否已经把 tool result 写入本地会话;
- 重试会不会再次触发副作用;
- 前端已经展示的文本是否会重复;
- 当前请求是否有稳定的最终 assistant message。
仓库分析提到“可恢复错误扣留”:某些错误先保留在内部,不立刻向上层发射,只有恢复失败才暴露给 UI。这个机制适合避免前端看到“错误—恢复—继续”的噪声,但属于版本相关内部实现。3
8.3 Prompt Too Long 与网络错误的区别
二者的判定应尽量靠错误类型,而不是用户感觉“卡住了”。
Prompt Too Long
特征:
- 请求已到达服务端;
- 输入语义长度超限;
- 同样请求重试仍会失败;
- 正确动作是压缩、裁剪或改变请求结构。
仓库分析中的恢复顺序是:
尝试提交已有 Context Collapse→ 反应式全量压缩→ 重新构造请求→ 仍失败才返回错误网络错误
特征:
- DNS、TCP、TLS、代理或连接读取失败;
- 原请求本身可能合法;
- 可能通过重新建连或退避恢复;
- 不应自动改变上下文语义。
把网络错误错误地归类为 Prompt Too Long,会无谓损失上下文;把 Prompt Too Long 当网络错误重试,则会形成重复失败循环。
8.4 MCP 连接失败
MCP 故障要先区分阶段:
| 阶段 | 典型故障 | 处理 |
|---|---|---|
| 启动 | stdio command 不存在 | 修改本地配置,不自动重试 |
| 建连 | DNS / refused / timeout / 5xx | HTTP/SSE 瞬时重试 |
| 认证 | 401/403、OAuth scope 不足 | 刷新凭证或重新授权 |
| 初始化 | protocol version 不兼容 | 更新 Client/Server |
| 能力发现 | tools/list 暂时失败 | 短退避重试,保留旧能力 |
| 运行中 | Session 过期或流断开 | 重连并重新初始化 |
| 调用工具 | 业务错误 | 返回 tool_result(is_error=true) |
如果 GitHub MCP 断开,Claude Code 不应该假装“GitHub 工具不存在”。当前官方行为在启用 Tool Search 时会把连接失败信息传给 Claude,使模型可以明确告诉用户能力不可用或尝试替代路径。30
8.5 工具执行失败
工具失败分为四类:
- 输入错误:Schema 或业务验证失败;
- 权限错误:规则拒绝、用户拒绝或工作区不可信;
- 执行错误:文件不存在、命令非零退出、网络调用失败;
- 副作用不确定:远程服务可能已执行,但结果响应丢失。
前三类通常可以作为错误 Tool Result 回填,让模型调整。第四类最危险,例如:
gh pr create 已把请求发给 GitHub→ 本地连接超时→ Claude Code 没拿到 PR URL此时不能直接再次创建 PR。应先查询当前分支是否已有对应 PR,再决定是否补发。这属于副作用幂等问题,不能仅靠网络重试解决。
仓库分析还显示,大工具结果可能被截断或落盘,模型得到路径与摘要,再按需读取。这样可以防止一次工具输出把下一次模型请求挤爆。11
8.6 用户中断
官方交互文档支持:
Ctrl+C:取消当前操作;Esc:停止当前响应或工具调用,同时保留已经完成的工作;Ctrl+X Ctrl+K:终止后台子 Agent。25
仓库分析中的异步生成器与 AbortController 解释了取消如何向下传播:
用户中断→ QueryEngine 停止消费→ query() 生成器退出→ 模型流 abort→ pending tool / child process 取消→ 清理资源→ 保留已完成消息与文件变更但取消不是回滚:
- 已经写入的文件不会自动还原;
- 已经创建的 PR 不会自动删除;
- 已经发出的外部请求可能仍在服务端执行;
- 后台进程需要独立终止。
因此,用户中断语义应理解为“停止继续推进”,而不是“恢复到任务开始前”。
9. 完整案例时序

下面用一个完整任务串起所有网络边界:
读取 GitHub Issue,分析代码,修改文件,执行测试,创建 Pull Request,并返回结果。
9.1 正常路径
9.2 每一步的网络边界
| 阶段 | 网络或进程边界 | 主要状态 | 失败后能否直接重试 |
|---|---|---|---|
| 用户提交任务 | 终端输入 → Claude Code | Session / Turn | 可以 |
| Request 1 | CLI → 模型 Provider | Model request | 瞬时错误可以 |
| SSE 返回 | Provider → CLI | Stream / content blocks | 取决于已完成内容 |
| 读取 Issue | CLI → MCP → GitHub | MCP Session / Tool Call | 只读,通常可以 |
| 搜索文件 | 本地工具 | Tool Call | 通常可以 |
| 编辑文件 | 本地文件系统 | 副作用 | 应检查当前文件版本 |
| 执行测试 | Bash → 子进程 | Process / Tool Call | 通常可重跑,但有成本 |
| 创建 PR | MCP / gh → GitHub | 外部副作用 | 不能盲重试 |
| 最终回答 | CLI UI | Delivery | 可重新渲染,不应重跑任务 |
9.3 四个典型故障插入点
故障 A:模型 Request 2 收到 529
处理:
- SDK 按退避策略重试;
- 保留 Issue Tool Result;
- 不重新读取 Issue;
- 若重试失败,向用户暴露 Provider 错误和 request ID。
故障 B:模型已完整生成 Read,但后续 SSE 中断
处理:
- 检查该
tool_use是否已经完整解析; - 若内部实现已经执行 Read,记录 tool call ID 与结果;
- 不再次执行同一调用;
- 使用已完成消息边界或重建当前 turn;
- 不尝试续接半截 tool JSON。
故障 C:GitHub MCP 会话中断
处理:
- Remote HTTP/SSE 按官方策略退避重连;
- 重新初始化并发现能力;
- 对只读
get_issue可安全重试; - 若是
create_pull_request,先查询是否已经创建。
故障 D:测试进程无法访问依赖仓库
排查顺序:
- Bash 命令是否在沙箱内;
- 目标域名是否在
allowedDomains; - 企业代理变量是否传给进程;
NO_PROXY是否误配置;- 企业 CA 是否被信任;
- Token 是否被 deny、mask 或未继承;
- 是否需要用户批准 unsandboxed retry。
9.4 这条时序揭示了什么
完整任务不是一个长请求,而是多个事务边界组成的轨迹:
模型请求→ 工具决策→ 权限与调度→ 本地 / MCP 副作用→ 结果回填→ 新模型请求每个边界都需要自己的:
- ID;
- 超时;
- 取消语义;
- 重试策略;
- 日志;
- 幂等判断;
- 恢复状态。
将所有失败都归结为“Agent 出错”,会失去可诊断性;将所有恢复都归结为“再调用一次模型”,则会引入重复副作用。
结语:Claude Code 网络层的真正设计重点
Claude Code 的网络层并不以某一种协议取胜。它的核心是把不同性质的通信统一进一个可持续推进的 Agent Loop:
- Messages SSE 提供低延迟模型事件;
tool_use/tool_result把模型决策与真实执行分开;- QueryEngine 与
query()分离会话生命周期和当前循环; - StreamingToolExecutor 尝试重叠模型生成与安全工具执行;
isConcurrencySafe区分并行读取与串行副作用;- MCP 把外部系统接入统一工具流水线;
- Corporate Proxy、LLM Gateway 与 Bash Sandbox 分别治理不同网络面;
- Prompt compaction 解决语义容量问题,网络重试解决传输问题;
- 用户取消停止后续工作,但不假装回滚已经发生的副作用。
如果只在模型 API 外面包一层超时和重试,就还没有真正实现 Agent 网络层。生产级实现必须知道:当前断的是哪条链路、丢的是哪一段状态、已经发生了什么副作用,以及下一步能否安全重放。
参考资料
Anthropic 与 Claude Code 官方资料
MCP 官方规范
how-claude-code-works 实现分析
Footnotes
-
How Claude Code Works — README 与免责声明, Windy3f3f3f3f. ↩
-
Claude Code overview, Anthropic. ↩
-
Claude Code on Amazon Bedrock, Anthropic. ↩ ↩2
-
Claude Code on Google Vertex AI, Anthropic. ↩ ↩2
-
Model configuration, Anthropic. ↩ ↩2
-
LLM gateway configuration, Anthropic. ↩ ↩2
-
Connect Claude Code to tools via MCP, Anthropic. ↩ ↩2 ↩3
-
MCP Transports, Model Context Protocol. ↩ ↩2
-
Configure the sandboxed Bash tool, Anthropic. ↩ ↩2 ↩3
-
How Claude Code uses prompt caching, Anthropic. ↩
-
How Claude Code works — Context window, Anthropic. ↩ ↩2 ↩3
-
第 3 章:上下文工程,
how-claude-code-works. ↩ ↩2 -
Prompt caching, Anthropic. ↩
-
How Claude Code works, Anthropic. ↩
-
Parallel tool use, Anthropic. ↩ ↩2 ↩3 ↩4
-
Tool use with prompt caching, Anthropic. ↩
-
Explore the context window, Anthropic. ↩
-
Environment variables, Anthropic. ↩
-
Claude Code on Microsoft Foundry, Anthropic. ↩
-
Fine-grained tool streaming, Anthropic. ↩
-
Claude API errors, Anthropic. ↩ ↩2
-
Interactive mode and Keybindings, Anthropic. ↩ ↩2
-
MCP TypeScript SDK — Server transports, Model Context Protocol. ↩
-
MCP Lifecycle, Model Context Protocol. ↩
-
Claude Code MCP — Authenticate with remote MCP servers, Anthropic. ↩
-
Claude Code MCP — Dynamic headers, Anthropic. ↩
-
Claude Code MCP — Automatic reconnection, Anthropic. ↩ ↩2
-
Enterprise network configuration, Anthropic. ↩ ↩2 ↩3
-
TypeScript SDK — retries and request IDs, Anthropic. ↩