Claude Code 是什么?

想象一下,你有一个超级聪明的程序员朋友,他能听懂你的话, 帮你写代码、查文件、修bug,甚至能解释整个项目是怎么运作的。

Claude Code 就是这样的朋友——一个运行在终端里的 AI 编程助手。

💡 顿悟时刻: 你不需要记住所有编程知识,你只需要学会如何向 AI 提问。
$ 帮我查看这个项目的结构
正在分析项目...

就像和朋友聊天一样简单

当你输入命令时发生了什么?

让我们追踪一个完整的请求流程

👤

1. 你发送请求

"帮我把所有的 console.log 改成 logger"

🔍

2. 理解你的意图

Claude 分析你的需求,理解要做什么

🛠️

3. 规划行动步骤

搜索文件 → 找到 console.log → 替换成 logger

4. 执行工具调用

使用 Grep 搜索、Edit 修改文件

5. 返回结果

"已完成!修改了 12 个文件"

核心引擎:query 函数

在 Claude Code 的源码中,query 函数是整个系统的核心引擎。 让我们来看看它的一部分代码:

TypeScript 代码
export async function* query(
  params: QueryParams,
): AsyncGenerator<
  | StreamEvent
  | RequestStartEvent
  | Message,
  Terminal
> {
  const consumedCommandUuids: string[] = []
  const terminal = yield* queryLoop(params, consumedCommandUuids)
  return terminal
}
🌐 中文翻译

export async function* query

↓ 导出一个异步生成器函数,名为 "query"(查询)


params: QueryParams

↓ 接收参数:包含系统提示、用户消息、可用工具等信息


AsyncGenerator<...>

↓ 返回一个异步生成器,可以持续产生事件流


yield* queryLoop(...)

↓ 委托给主循环,开始处理请求

💡 顿悟时刻: 生成器函数就像流水线,不是一次性返回结果,而是持续不断地产出中间结果。 这就是为什么你能看到 Claude Code 的思考过程!

生活类比

普通函数 = 点外卖

你点餐 → 等待 → 收到外卖(一次性完成)


生成器函数 = 现场做饭

备菜 → 炒菜 → 调味 → 装盘(你可以看到每一步)

Claude Code 的"手":工具系统

AI 不只是聊天,它能真正操作你的电脑

⌨️

Bash

执行命令行操作

ls -la npm install
📖

Read

读取文件内容

读取 package.json 查看配置文件
✏️

Edit

修改文件

替换变量名 更新函数逻辑
🔎

Grep

搜索代码

查找函数定义 搜索关键字
📝

Write

创建新文件

生成新组件 写入配置
🌐

Glob

查找文件

找到所有 .ts 文件 匹配文件模式
💡 顿悟时刻: 工具系统就像给 AI 装上了"手"和"眼睛"。它不只是建议你做什么, 而是真正能执行操作!这就是为什么 Claude Code 是"编程助手"而不是单纯的"聊天机器人"。

内部的"群聊":组件如何协作

当一个请求进来时,各个组件之间在"聊什么"

📱 Claude Code 内部协作群 6 个组件在线
👤 用户
帮我把所有 console.log 改成 logger
🔍 Query 引擎
收到请求!我需要规划一下执行步骤
🛠️ 工具管理器
我可以用 Grep 搜索 console.log,然后用 Edit 替换
🔎 Grep 工具
正在搜索... 找到 15 个 console.log!
✏️ Edit 工具
开始替换... 已修改 12 个文件
🤖 Claude
完成!已将所有 console.log 替换为 logger
💡 顿悟时刻: Claude Code 不是一个整体在"思考",而是多个专门的组件在协作。 这种设计让系统更灵活、更强大——每个组件专注于自己最擅长的事情!

核心概念总结

恭喜你完成了第一模块!

Claude Code 是什么

一个能真正操作代码的 AI 编程助手,不只是聊天机器人

请求处理流程

用户请求 → 理解意图 → 规划步骤 → 执行工具 → 返回结果

生成器函数

像流水线一样持续产出结果,让你看到思考过程

工具系统

给 AI 装上"手"和"眼睛",能真正执行操作

📚 词汇表

AI 编程助手

使用人工智能技术帮助开发者编写、理解和改进代码的工具

异步生成器

一种可以逐步产生多个值的函数,像流水线一样持续产出结果

工具系统

一套预定义的操作接口,让 AI 能够执行具体的编程任务

Query 引擎

Claude Code 的核心处理逻辑,负责协调所有组件完成用户请求

流事件

在处理过程中持续产生的状态更新,让用户实时看到进度

🎯 模块 1 测验

检验你的理解程度!

1. Claude Code 和普通聊天机器人有什么区别?

✅ 正确!工具系统让 Claude Code 能执行实际操作,而不只是提供建议。

❌ 不太对。再想想看,是什么让 Claude Code 能够真正修改文件?

2. query 函数为什么要用生成器(Generator)?

✅ 完全正确!生成器像流水线一样持续产出事件,让你能实时看到 Claude 的思考过程。

❌ 不完全是。生成器的关键优势是什么?提示:想想"流"这个概念。

3. 当你发送"帮我查看项目结构"时,哪个工具会被使用?

✅ 对!Bash 工具可以执行命令行操作,比如 ls 来查看文件结构。

❌ 不太对。想想看,查看文件结构通常需要执行什么操作?

4. "工具系统"在 Claude Code 中扮演什么角色?

✅ 完全正确!工具系统让 AI 能真正操作文件、执行命令,而不只是聊天。

❌ 不太准确。工具的核心作用是什么?想想"手"和"眼睛"的比喻。

测验结果

0 / 4

模块 2:核心组件介绍

认识 Claude Code 的三大支柱:查询引擎、工具系统与状态管理

🏗️ 架构全景图

⚙️

查询引擎 (QueryEngine)

协调整个对话流程

调用
🔧

工具系统 (Tools)

执行具体操作

状态同步
💾

状态管理 (AppState)

维护应用状态

💡 设计理念

Claude Code 采用分层架构:查询引擎负责编排,工具系统负责执行,状态管理负责持久化。三者协同工作,形成完整的 AI 代码助手系统。

⚙️ 查询引擎 (QueryEngine)

查询引擎是 Claude Code 的核心协调器,负责管理整个对话生命周期。

src/QueryEngine.ts
export class QueryEngine {
  private config: QueryEngineConfig
  private mutableMessages: Message[]
  private abortController: AbortController
  private permissionDenials: SDKPermissionDenial[]
  private totalUsage: NonNullableUsage

  constructor(config: QueryEngineConfig) {
    this.config = config
    this.mutableMessages = config.initialMessages ?? []
    this.abortController = config.abortController ?? createAbortController()
    this.permissionDenials = []
    this.totalUsage = EMPTY_USAGE
  }

  async *submitMessage(
    prompt: string | ContentBlockParam[],
    options?: { uuid?: string; isMeta?: boolean },
  ): AsyncGenerator {
    // 实现消息提交逻辑
  }
}
查询生命周期
// 1. 初始化阶段
- 加载系统提示词
- 准备工具列表
- 设置权限上下文

// 2. 处理输入阶段
- 解析用户消息
- 处理斜杠命令
- 附加文件内容

// 3. API 调用阶段
- 调用 Claude API
- 流式接收响应
- 处理工具调用请求

// 4. 工具执行阶段
- 验证工具权限
- 执行工具操作
- 收集工具结果

// 5. 结果返回阶段
- 格式化响应消息
- 更新使用统计
- 返回结果给用户
使用示例
// 创建查询引擎实例
const engine = new QueryEngine({
  cwd: process.cwd(),
  tools: availableTools,
  commands: allCommands,
  mcpClients: mcpConnections,
  agents: agentDefinitions,
  canUseTool: checkToolPermissions,
  getAppState: () => store.getState(),
  setAppState: (fn) => store.setState(fn()),
  readFileCache: new FileStateCache(),
})

// 提交消息并获取响应
for await (const message of engine.submitMessage(userInput)) {
  if (message.type === 'assistant') {
    console.log('Assistant:', message.message)
  } else if (message.type === 'result') {
    console.log('Result:', message.result)
  }
}
⚠️ 重要提示

每个 QueryEngine 实例对应一个对话会话。多次调用 submitMessage() 会在同一会话中继续对话,状态(消息历史、文件缓存、使用统计等)会在多次调用之间持久化。

🔧 工具系统 (Tools)

工具系统定义了 Claude 可以执行的所有操作,从文件读写到 Bash 命令执行。

🎮 交互式工具探索

点击不同类别查看工具类型:

文件操作工具
// Read 工具定义
const ReadTool: Tool = {
  name: 'Read',
  description: '读取文件内容',
  inputSchema: z.object({
    file_path: z.string(),
    offset: z.number().optional(),
    limit: z.number().optional(),
  }),
  call: async (input, context) => {
    const content = await fs.readFile(input.file_path, 'utf-8')
    return { data: content }
  },
  isReadOnly: () => true,  // 只读操作
  isEnabled: () => true,
}
搜索工具
// Grep 工具定义
const GrepTool: Tool = {
  name: 'Grep',
  description: '在文件中搜索模式',
  inputSchema: z.object({
    pattern: z.string(),
    path: z.string().optional(),
    glob: z.string().optional(),
  }),
  call: async (input, context) => {
    const results = await searchFiles(input.pattern, input.path)
    return { data: results }
  },
  isReadOnly: () => true,
  isSearchOrReadCommand: () => ({ isSearch: true, isRead: false }),
}
Bash 工具
// Bash 工具定义
const BashTool: Tool = {
  name: 'Bash',
  description: '执行 Shell 命令',
  inputSchema: z.object({
    command: z.string(),
  }),
  call: async (input, context) => {
    const result = await execCommand(input.command)
    return { data: result.stdout }
  },
  isReadOnly: (input) => isReadOnlyCommand(input.command),
  isDestructive: (input) => isDestructiveCommand(input.command),
}
MCP 工具
// MCP 工具示例(来自外部服务器)
const MCPTool: Tool = {
  name: 'mcp__server__tool_name',
  description: 'MCP 服务器提供的工具',
  mcpInfo: {
    serverName: 'server',
    toolName: 'tool_name'
  },
  isMcp: true,
  call: async (input, context) => {
    // 通过 MCP 协议调用远程工具
    const result = await callMCPTool(
      context.mcpClients,
      'server',
      'tool_name',
      input
    )
    return { data: result }
  },
}

📋 工具接口核心方法

方法 职责 返回值
call() 执行工具逻辑 Promise<ToolResult>
description() 生成工具描述 Promise<string>
checkPermissions() 验证权限 Promise<PermissionResult>
isReadOnly() 判断是否只读 boolean
isDestructive() 判断是否破坏性 boolean

💾 状态管理 (AppState)

AppState 使用 React Context 和自定义 Store 管理全局应用状态。

AppState 接口定义
export type AppState = {
  // 对话状态
  messages: Message[]
  promptSuggestion: CompletionBoundary | null

  // 工具权限
  toolPermissionContext: ToolPermissionContext

  // 模型配置
  mainLoopModel: string
  customSystemPrompt?: string
  appendSystemPrompt?: string

  // UI 状态
  verbose: boolean
  fastMode: FastModeState
  theme: ThemeName

  // 文件历史
  fileHistory: FileHistoryState
  attribution: AttributionState

  // 推测状态(预测用户输入)
  speculation: SpeculationState
}
状态访问 Hooks
// 读取状态
function MyComponent() {
  const messages = useAppState(s => s.messages)
  const verbose = useAppState(s => s.verbose)
  const model = useAppState(s => s.mainLoopModel)

  return 
{/* 使用状态 */}
} // 更新状态 function AnotherComponent() { const setAppState = useSetAppState() const handleClick = () => { setAppState(prev => ({ ...prev, verbose: !prev.verbose, // 只更新需要的字段 })) } return }
状态更新最佳实践
// ✅ 推荐:使用函数式更新
setAppState(prev => ({
  ...prev,
  messages: [...prev.messages, newMessage],
}))

// ✅ 推荐:只更新需要的字段
setAppState(prev => {
  const updated = { ...prev, verbose: true }
  return updated
})

// ❌ 避免:直接修改状态
setAppState(prev => {
  prev.messages.push(newMessage) // 不要这样做!
  return prev
})

// ✅ 推荐:使用不可变更新
setAppState(prev => ({
  ...prev,
  messages: [...prev.messages, newMessage],
}))
✅ 性能优化

useAppState() 使用选择器模式,只有当选择的值真正改变时才会重新渲染。对于多个独立字段,多次调用 hook 比返回新对象更高效。

🤝 组件协作流程

让我们看看三大组件如何协同工作来处理一个用户请求:

1

用户输入

用户发送消息:"帮我找到所有 TypeScript 文件中的 TODO 注释"

2

QueryEngine 接收

submitMessage() 被调用,创建新的对话轮次

3

调用 Claude API

将消息历史和系统提示词发送给 API

4

API 返回工具调用

返回:Grep(pattern="TODO", glob="*.ts")

5

权限检查

canUseTool() 验证是否允许执行 Grep 工具

6

执行工具

Grep.call() 执行搜索,返回结果

7

更新状态

通过 setAppState() 添加新消息到历史

8

返回结果

将搜索结果返回给用户,完成对话轮次

📖 核心词汇表

Tool (工具)
Claude 可以执行的操作单元,如 Read、Bash、Grep 等
QueryEngine (查询引擎)
管理对话生命周期的核心协调器
AppState (应用状态)
存储全局应用状态的不可变数据结构
Message (消息)
对话中的单个消息单元,可以是用户、助手或系统消息
PermissionMode (权限模式)
控制工具权限检查的策略(default、auto、alwaysAllow)
AsyncGenerator
用于流式返回多个结果的异步迭代器

📝 知识检测

问题 1:QueryEngine 的主要职责是什么?

问题 2:Tool 接口中哪个方法是必须实现的?

问题 3:以下哪种方式更新 AppState 是正确的?

// A
setAppState(prev => {
  prev.messages.push(newMessage)
  return prev
})

// B
setAppState(prev => ({
  ...prev,
  messages: [...prev.messages, newMessage],
}))

问题 4:QueryEngine 每次调用 submitMessage() 会如何处理状态?

💻 实战练习

练习:创建自定义工具

中等

基于你学到的 Tool 接口知识,创建一个简单的自定义工具。这个工具应该能够统计指定目录下的文件数量。

  1. 定义工具的 name 和 description
  2. 使用 z.object() 定义 inputSchema
  3. 实现 call() 方法,使用 fs.readdir() 读取目录
  4. 实现 isReadOnly() 返回 true
  5. 使用 buildTool() 创建最终工具
查看参考答案
CountFilesTool.ts
import { buildTool } from './Tool.js'
import { z } from 'zod'
import { readdir } from 'fs/promises'
import { join } from 'path'

export const CountFilesTool = buildTool({
  name: 'CountFiles',
  description: '统计指定目录下的文件数量',
  inputSchema: z.object({
    directory: z.string().describe('要统计的目录路径'),
    pattern: z.string().optional().describe('可选的文件匹配模式'),
  }),

  async call(input, context) {
    try {
      const fullPath = join(context.options.cwd, input.directory)
      const files = await readdir(fullPath)

      let filteredFiles = files
      if (input.pattern) {
        const regex = new RegExp(input.pattern)
        filteredFiles = files.filter(f => regex.test(f))
      }

      return {
        data: {
          total: filteredFiles.length,
          directory: input.directory,
          files: filteredFiles,
        },
      }
    } catch (error) {
      return {
        data: {
          error: error.message,
          total: 0,
        },
      }
    }
  },

  isReadOnly: () => true,
  isEnabled: () => true,
})

🎯 模块总结

  • QueryEngine 是对话的核心协调器,管理整个查询生命周期
  • Tools 定义了 Claude 可以执行的所有操作,通过统一接口实现
  • AppState 使用不可变数据结构和 React Context 管理全局状态
  • 三者通过清晰的接口协作:QueryEngine 调用 Tools,Tools 更新 AppState
  • 理解组件协作流程对于调试和扩展功能至关重要

➡️ 下一步

完成本模块后,你已经了解了 Claude Code 的核心组件。在模块 3 中,我们将学习消息处理流程,了解用户输入如何被转换成 API 请求。

前往模块 3:消息处理流程 →

模块 3:工具系统如何工作

核心架构 预计时间:25 分钟

🎯 学习目标

  • 理解 40+ 工具如何注册、发现和执行
  • 掌握权限系统的保护机制
  • 追踪工具调用的完整生命周期
  • 理解 MCP 工具的集成方式

🍽️ 比喻:高级餐厅的点菜系统

Claude 点菜
🔐
权限检查
👨‍🍳
厨房执行
🍽️ 上菜(返回结果)

Claude Code 的工具系统就像一个高级餐厅:

  • 菜单(tools.ts):列出所有可用的"菜品"(工具)
  • 点菜(Claude):AI 根据需求选择合适的工具
  • 权限检查(门卫):确保操作安全,保护你的系统
  • 厨房执行(工具实现):实际执行操作的代码
  • 上菜(返回结果):将执行结果返回给 Claude

📦 核心概念 1:工具注册表(tools.ts)

src/tools.ts 是整个工具系统的"中央菜单",它管理着所有 40+ 内置工具的注册和发现。

src/tools.ts
// 工具注册:每个工具都被导入并注册
import { BashTool } from './tools/BashTool/BashTool.js'
import { FileReadTool } from './tools/FileReadTool/FileReadTool.js'
import { GrepTool } from './tools/GrepTool/GrepTool.js'
// ... 40+ 更多工具

/**
 * 获取所有基础工具(这是单一真实来源)
 * 注意:这个列表必须与 Statsig 配置保持同步
 * 以便系统提示可以被缓存
 */
export function getAllBaseTools(): Tools {
  return [
    AgentTool,
    TaskOutputTool,
    BashTool,
    GlobTool,
    GrepTool,
    FileReadTool,
    FileEditTool,
    FileWriteTool,
    // ... 更多工具
  ]
}

🔍 代码解读

  • 集中注册:所有工具在 getAllBaseTools() 中注册,这是工具的"单一真实来源"
  • 条件加载:某些工具只在特定环境下加载(如 USER_TYPE === 'ant'
  • 缓存同步:工具列表必须与远程配置同步,以优化性能
  • 类型安全:使用 TypeScript 的 Tools 类型确保所有工具符合接口
💡

为什么集中注册很重要?

集中注册让系统可以:

  1. 统一管理:在一个地方看到所有可用工具
  2. 权限控制:在工具暴露给 AI 之前进行过滤
  3. 性能优化:通过缓存工具列表减少 token 消耗
  4. 动态加载:根据环境和配置动态启用/禁用工具

🔧 核心概念 2:工具接口(Tool Interface)

每个工具都实现一个标准化的 Tool 接口,定义在 src/Tool.ts 中。这就像每个"菜品"都有标准的菜谱格式。

src/Tool.ts (简化版)
export type Tool<Input, Output, Progress> = {
  // 基本信息
  readonly name: string                      // 工具名称
  aliases?: string[]                         // 别名(向后兼容)
  searchHint?: string                        // 搜索提示(3-10词)

  // Schema 定义
  readonly inputSchema: Input                // 输入参数的 Zod schema
  readonly inputJSONSchema?: ToolInputJSONSchema  // JSON Schema 格式
  outputSchema?: z.ZodType<unknown>        // 输出 schema

  // 核心方法
  call(
    args: z.infer<Input>,
    context: ToolUseContext,
    canUseTool: CanUseToolFn,
    parentMessage: AssistantMessage,
    onProgress?: ToolCallProgress<Progress>,
  ): Promise<ToolResult<Output>>           // 执行工具

  // 描述和权限
  description(input: z.infer<Input>, options: ...): Promise<string>
  checkPermissions(input: z.infer<Input>, context: ...): Promise<PermissionResult>

  // 安全和并发
  isEnabled(): boolean                       // 是否启用
  isReadOnly(input: z.infer<Input>): boolean    // 是否只读
  isDestructive?(input: z.infer<Input>): boolean  // 是否破坏性
  isConcurrencySafe(input: z.infer<Input>): boolean  // 是否并发安全

  // UI 渲染
  renderToolUseMessage(input: ..., options: ...): React.ReactNode
  renderToolResultMessage?(content: ..., options: ...): React.ReactNode

  // 元数据
  maxResultSizeChars: number                // 结果大小限制(字符数)
  isMcp?: boolean                           // 是否 MCP 工具
}

🔍 关键方法解析

方法 作用 比喻
call() 执行工具的核心逻辑 厨师做菜
description() 生成工具的 AI 描述 菜单上的菜品说明
checkPermissions() 检查权限 门卫检查订单
renderToolUseMessage() 渲染工具调用 UI 厨房显示屏显示订单

🎬 交互演示:工具调用的生命周期

点击下方按钮,观察 Claude 如何调用工具的完整流程:

Claude Code 终端
系统就绪。等待用户请求...

🔄 工具调用生命周期

📝
1. 用户请求
用户发送消息
🤖
2. AI 决策
Claude 选择工具
🔐
3. 权限检查
验证操作安全
⚙️
4. 工具执行
调用 tool.call()
📤
5. 返回结果
结果传给 Claude
💬
6. AI 响应
生成最终回复

🔒 核心概念 3:权限系统

权限系统是 Claude Code 的安全网,确保 AI 不会执行危险操作。它就像餐厅的门卫,检查每个订单是否安全。

🛡️ 四层权限保护

📋
第 1 层:工具级过滤

在工具暴露给 AI 之前,根据拒绝规则过滤工具

filterToolsByDenyRules(tools, permissionContext)
第 2 层:输入验证

使用 Zod schema 验证输入参数的合法性

tool.validateInput?(input, context)
🔐
第 3 层:权限规则检查

检查用户的权限规则(alwaysAllow、alwaysDeny、alwaysAsk)

tool.checkPermissions(input, context)
🤖
第 4 层:AI 分类器(可选)

使用机器学习模型识别危险操作

classifyYoloAction(command, toolName)

📄 权限规则示例

settings.json 权限配置
{
  "permissions": {
    "alwaysAllow": [
      "Read *.md",           // 允许读取所有 Markdown 文件
      "Grep *.ts",           // 允许搜索 TypeScript 文件
      "Bash git status"      // 允许执行 git status
    ],
    "alwaysDeny": [
      "Bash rm -rf *",       // 禁止危险删除命令
      "Bash :(){ :|:& };:",  // 禁止 fork 炸弹
      "Write /etc/*"         // 禁止写入系统目录
    ],
    "alwaysAsk": [
      "Bash docker*",        // 执行 Docker 命令前询问
      "Write *prod*"         // 写入生产环境文件前询问
    ]
  }
}
⚠️

权限模式

Claude Code 支持三种权限模式:

  • default:正常权限检查,需要用户确认
  • auto:AI 自动决策,适合信任环境
  • bypass:跳过权限检查(仅限特殊情况)

🔌 核心概念 4:MCP 工具集成

MCP (Model Context Protocol) 工具是动态加载的外部工具,可以扩展 Claude Code 的能力。就像餐厅可以临时聘请"特邀厨师"。

🔄 MCP 工具加载流程

1
发现 MCP 服务器

从配置文件(.claude/mcp.json)读取 MCP 服务器列表

2
连接服务器

为每个 MCP 服务器建立连接,获取工具列表

3
转换工具格式

将 MCP 工具转换为 Claude Code 的 Tool 接口

4
合并到工具池

使用 assembleToolPool() 合并内置和 MCP 工具

💻 MCP 工具合并代码

src/tools.ts - assembleToolPool()
/**
 * 组合完整的工具池(内置 + MCP)
 * 这是内置工具和 MCP 工具合并的单一真实来源
 */
export function assembleToolPool(
  permissionContext: ToolPermissionContext,
  mcpTools: Tools,
): Tools {
  // 1. 获取内置工具(已根据权限过滤)
  const builtInTools = getTools(permissionContext)

  // 2. 过滤被拒绝的 MCP 工具
  const allowedMcpTools = filterToolsByDenyRules(
    mcpTools,
    permissionContext
  )

  // 3. 按名称排序(内置工具在前,MCP 工具在后)
  // 这样可以保持缓存稳定性
  const byName = (a: Tool, b: Tool) =>
    a.name.localeCompare(b.name)

  // 4. 去重(内置工具优先)
  return uniqBy(
    [...builtInTools].sort(byName)
      .concat(allowedMcpTools.sort(byName)),
    'name'
  )
}

MCP 工具的优势

  • 动态扩展:无需修改核心代码即可添加新工具
  • 权限继承:MCP 工具遵循相同的权限系统
  • 类型安全:自动转换为 TypeScript 类型
  • 缓存优化:支持工具延迟加载(defer_loading)

🔨 实际例子:Read 工具的完整实现

让我们看看一个真实工具的完整实现:

src/tools/FileReadTool/FileReadTool.ts (关键部分)
import { z } from 'zod/v4'
import { buildTool, type ToolDef } from '../../Tool.js'

// 1. 定义输入 Schema
const inputSchema = lazySchema(() =>
  z.strictObject({
    file_path: z.string().describe('要读取的文件的绝对路径'),
    offset: z.number().optional().describe('开始读取的行号'),
    limit: z.number().optional().describe('读取的最大行数'),
  })
)

// 2. 实现工具
export const FileReadTool: ToolDef<typeof inputSchema, string> = {
  name: 'Read',
  description: 'Read a file from the local filesystem',

  inputSchema,
  outputSchema: z.string(),

  // 3. 实现核心调用逻辑
  async call(args, context, canUseTool) {
    const { file_path, offset, limit } = args

    // 3.1 权限检查
    const permission = await checkReadPermissionForTool(
      file_path,
      context
    )
    if (permission.behavior === 'deny') {
      return permission
    }

    // 3.2 读取文件
    const content = await readFileInRange(
      file_path,
      offset || 0,
      limit || 2000
    )

    // 3.3 返回结果
    return {
      data: content,
      newMessages: []
    }
  },

  // 4. 实现权限检查
  async checkPermissions(args, context) {
    return await checkReadPermissionForTool(
      args.file_path,
      context
    )
  },

  // 5. 安全属性
  isReadOnly: () => true,           // 只读操作
  isDestructive: () => false,        // 非破坏性
  isConcurrencySafe: () => true,     // 并发安全

  // 6. UI 渲染
  renderToolUseMessage(input, options) {
    return <ReadToolUseMessage input={input} />
  },

  // 7. 元数据
  maxResultSizeChars: Infinity,        // 结果大小限制
  getActivityDescription(input) {
    return `Reading ${input.file_path}`
  }
}

// 8. 使用 buildTool 填充默认值
export default buildTool(FileReadTool)

🔍 实现解析

1
Schema 定义

使用 Zod 定义输入参数,提供类型安全和自动验证

2
工具定义对象

实现 ToolDef 接口,包含所有必需和可选方法

3
核心调用逻辑

call() 方法执行实际操作:权限检查 → 读取文件 → 返回结果

4
权限检查

checkPermissions() 实现工具特定的权限逻辑

5
安全属性

声明操作特性:只读、非破坏性、并发安全

6
UI 渲染

提供 React 组件渲染工具调用和结果的 UI

7
元数据

配置工具行为:结果大小限制、活动描述等

8
构建工具

使用 buildTool() 填充默认值,生成完整的 Tool 对象

📚 关键术语

Tool (工具)
实现 Tool 接口的对象,包含 call()description() 等方法
Tools (工具集)
工具的只读数组类型:readonly Tool[]
ToolDef (工具定义)
部分实现的工具接口,buildTool() 会填充默认值
ToolUseContext (工具上下文)
包含工具执行所需的所有上下文信息(状态、消息、权限等)
PermissionResult (权限结果)
权限检查的结果,包含 behavior(allow/deny/ask)和 updatedInput
MCP (Model Context Protocol)
用于扩展 AI 工具能力的开放协议,支持动态加载外部工具
canUseTool (可用工具函数)
用于在工具执行前检查权限和获取用户确认的函数
ToolResult (工具结果)
工具执行的返回值,包含 datanewMessages 等字段

📝 模块测验

测试你对工具系统的理解:

1. Claude Code 中所有工具的注册和发现发生在哪个文件中?
✅ 正确!src/tools.ts 是工具注册表,管理所有 40+ 内置工具。
2. 工具接口中哪个方法是实际执行工具操作的?
✅ 正确!call() 方法执行工具的核心逻辑,就像厨师实际做菜。
3. 权限系统的检查顺序是什么?
✅ 正确!权限检查按以下顺序进行:工具过滤 → 输入验证 → 权限规则 → AI分类器(可选)。
4. MCP 工具和内置工具的合并是通过哪个函数实现的?
✅ 正确!assembleToolPool() 负责合并内置工具和 MCP 工具,并进行去重和排序。
5. buildTool() 函数的主要作用是什么?
✅ 正确!buildTool() 填充工具定义中的默认值(如 isEnabledisReadOnly 等),生成完整的 Tool 对象。

🎓 模块总结

在本模块中,我们学习了:

  • 工具注册src/tools.ts 是所有工具的中央注册表
  • 工具接口Tool 接口定义了工具的标准结构
  • 权限系统:四层保护确保操作安全
  • MCP 集成:动态加载外部工具,扩展系统能力
  • 工具生命周期:从用户请求到最终响应的完整流程

🚀 下一步

在下一模块中,我们将探索消息系统,了解 Claude Code 如何管理对话历史和消息流。

前往模块 4:消息系统 →