Claude Code 源码分析第 49 课 · 第 07
第 49 课

入口点与 SDK

如何调用 Claude Code — CLI、MCP 服务器、无头、桥接、守护进程 — 以及外部开发人员如何通过代理 SDK 在其之上进行构建。

01 入口点层

The src/entrypoints/ 目录是外部世界和 Claude Code 内部之间的边界。 它包含五个表面级文件 - cli.tsx, init.ts, mcp.ts, agentSdkTypes.ts, 和 sandboxTypes.ts — 加上一个 sdk/ 保存可序列化合约类型的子目录。 main.tsx 位于上一层,是一个基于 Commander 的大型 CLI 处理程序,几乎每个交互式和无头路径都流经该处理程序。

架构笔记
cli.tsx 是一个薄引导程序,可以进行模式匹配 process.argv 和快速路径特殊命令 before 加载 200+ 模块导入图 main.tsx。 这保持 --version 和守护进程工作人员几乎即时启动。
flowchart TD A["claude binary\n(Bun single-file exe)"] --> B["cli.tsx\nbootstrap dispatcher"] B -->|"--version / -v"| V["print version\nzero imports"] B -->|"--daemon-worker"| DW["daemon/workerRegistry.js"] B -->|"remote-control / rc / bridge"| BR["bridge/bridgeMain.js"] B -->|"daemon subcommand"| DA["daemon/main.js"] B -->|"ps / logs / attach / kill\n--bg / --background"| BG["cli/bg.js"] B -->|"environment-runner"| ER["environment-runner/main.js"] B -->|"self-hosted-runner"| SH["self-hosted-runner/main.js"] B -->|"--claude-in-chrome-mcp"| CC["claudeInChrome/mcpServer.js"] B -->|"--computer-use-mcp"| CU["computerUse/mcpServer.js"] B -->|"everything else"| M["main.tsx\nCommander CLI"] M -->|"--print / -p\nheadless"| HL["runHeadless()"] M -->|"interactive REPL"| RP["launchRepl()"] M -->|"--mcp"| MC["entrypoints/mcp.ts"] M -->|"SDK transport"| SDK["Agent SDK\nprocess transport"] style A fill:#22201d,stroke:#7d9ab8 style B fill:#1a1816,stroke:#b8965e style M fill:#1a1816,stroke:#b8965e style SDK fill:#22201d,stroke:#6e9468

02 cli.tsx — Bootstrap 调度程序

src/entrypoints/cli.tsx 是实际的二进制入口点。 它在任何其他模块评估之前运行。 其设计理念: 为每个快速路径加载尽可能少的负载.

快速路径(按检测顺序)

快速路径1

--version / -v

零进口。 印刷 MACRO.VERSION 在构建时内联并立即退出。

快速路径2

--dump-system-prompt

内部评估工具。 仅加载配置+模型+提示模块来渲染和打印系统提示。

快速路径3

Chrome / 计算机使用 MCP

--claude-in-chrome-mcp and --computer-use-mcp 启动独立的 MCP 服务器,而不使用完整的 CLI 堆栈。

快速路径4

--daemon-worker=<kind>

由守护进程主管产生。 仅加载工作注册表——这一层没有配置、没有身份验证、没有遥测。

快速通道5

桥接/远程控制

remote-control, rc, sync, bridge — 连接本地计算机作为 claude.ai 的远程控制环境。

快速通道6

daemon subcommand

长期运行的主管进程。 设置接收器然后委托给 daemon/main.js.

快速通道 7

背景会议

ps, logs, attach, kill, --bg — 会话注册表管理,无需加载交互式 UI。

Fallthrough

完整 CLI (main.tsx)

其他一切。 加载完整的基于 Commander 的 CLI 处理程序以及所有 200 多个模块导入。

// From cli.tsx — each fast-path is gated by a build-time feature() flag
if (feature('BRIDGE_MODE') && (args[0] === 'remote-control' || args[0] === 'rc'
    || args[0] === 'remote' || args[0] === 'sync' || args[0] === 'bridge')) {
  // Auth check → GrowthBook gate → policy limits → bridgeMain()
  const { bridgeMain } = await import('../bridge/bridgeMain.js');
  await bridgeMain(args.slice(1));
  return;
}
设计模式
每个快速路径还会检查 feature() flag — Bun 构建时死代码消除门。 这意味着外部发行版本中完全不存在不受支持的功能,而不仅仅是在运行时进行限制。

03 init.ts — 共享初始化

src/entrypoints/init.ts 是一个已记忆的 init() 由所有重要入口点共享的函数。 这是 不要求快速路径。 它执行第一次 API 调用之前必须发生的所有一次性设置。

init() 做了什么(按顺序)
  1. enableConfigs() — 验证并激活设置系统
  2. applySafeConfigEnvironmentVariables() — 在信任对话框之前应用安全环境变量
  3. applyExtraCACertsFromConfig() — 在第一个 TLS 连接之前设置 TLS CA 证书
  4. setupGracefulShutdown() — 注册 SIGTERM/SIGINT 处理程序以进行退出时刷新
  5. initialize1PEventLogging() — 延迟加载 OpenTelemetry 分析(延迟 ~400KB)
  6. populateOAuthAccountInfoIfNeeded() — 填充钥匙串中缺失的 OAuth 缓存
  7. initJetBrainsDetection() — 异步检测 IDE 主机
  8. initializeRemoteManagedSettingsLoadingPromise() — 设置企业策略加载
  9. 配置GlobalMTLS() / 配置GlobalAgents() — TLS + 代理
  10. preconnectAnthropicApi() — 与 CLI 解析并行地预热 TCP+TLS (~150ms)
  11. initUpstreamProxy() — 用于组织注入凭证的 CCR 上游代理 (CLAUDE_CODE_REMOTE)
  12. registerCleanup(shutdownLspServerManager) — 退出时 LSP 拆除
  13. ensureScratchpadDir() — 如果启用则创建暂存目录

一个单独的 initializeTelemetryAfterTrust() 仅在用户接受信任对话框后才调用该函数。 这将独立于同意的设置与同意门控遥测分开,并在初始化 OpenTelemetry 导出器之前等待远程托管设置加载。

04 mcp.ts — Claude Code 作为 MCP 服务器

当调用时 claude --mcp, Claude Code 作为标准运行 模型上下文协议服务器 通过工作室。 这会将 Claude Code 的所有工具(Bash、Edit、Read、WebFetch 等)公开给 other MCP 客户端 — 编辑器、代理或自动化脚本可以直接调用它们,而无需生成完整的 REPL。

// src/entrypoints/mcp.ts
const server = new Server(
  { name: 'claude/tengu', version: MACRO.VERSION },
  { capabilities: { tools: {} } }
)

// ListTools: enumerate every Claude Code tool with its Zod-derived JSON schema
server.setRequestHandler(ListToolsRequestSchema, async () => ({
  tools: await Promise.all(tools.map(async tool => ({
    ...tool,
    description: await tool.prompt(...),
    inputSchema: zodToJsonSchema(tool.inputSchema),
  })))
}))

// CallTool: validate input, check permissions, execute, return result
server.setRequestHandler(CallToolRequestSchema, async ({ params }) => {
  const tool = findToolByName(tools, params.name)
  return await tool.call(params.arguments, toolUseContext, ...)
})
关键细节
MCP 服务器入口点强制 isNonInteractiveSession: true 并禁止思考(thinkingConfig: { type: 'disabled' })。 它还仅暴露 review 斜杠命令,因为斜杠命令在此上下文中没有意义。

05 agentSdkTypes.ts — 公共 SDK 合约

src/entrypoints/agentSdkTypes.ts 是 Agent SDK 包的主要导出。 它从三个子模块重新导出完整的公共 API,并声明 SDK 消费者调用的顶级函数。 所有函数体都会抛出异常 'not implemented' 在此文件中 - 实际实现由 SDK 传输层在运行时注入。

模块结构

ModulePurposeExamples
sdk/coreTypes.ts 从 Zod 模式生成的可序列化、传输安全的类型 SDKMessage, SDKUserMessage, ModelUsage, PermissionResult, HookInput
sdk/runtimeTypes.ts 具有回调和方法接口的不可序列化类型 SDKSession, Options, Query, SdkMcpToolDefinition
sdk/controlTypes.ts SDK 构建器的控制协议(桥接子路径消费者) SDKControlRequest, SDKControlResponse
sdk/settingsTypes.generated.ts 从设置 JSON 架构生成的完整设置类型 Settings

顶级 SDK 函数

// V1 API (stable) — headless one-shot query
export function query(params: {
  prompt: string | AsyncIterable<SDKUserMessage>
  options?: Options
}): Query

// V2 API (@alpha) — persistent multi-turn sessions
export function unstable_v2_createSession(options: SDKSessionOptions): SDKSession
export function unstable_v2_resumeSession(sessionId: string, options: SDKSessionOptions): SDKSession
export async function unstable_v2_prompt(message: string, options: SDKSessionOptions): Promise<SDKResultMessage>

// Session management
export async function listSessions(options?: ListSessionsOptions): Promise<SDKSessionInfo[]>
export async function getSessionInfo(sessionId: string): Promise<SDKSessionInfo | undefined>
export async function getSessionMessages(sessionId: string): Promise<SessionMessage[]>
export async function renameSession(sessionId: string, title: string): Promise<void>
export async function tagSession(sessionId: string, tag: string | null): Promise<void>
export async function forkSession(sessionId: string, options?: ForkSessionOptions): Promise<ForkSessionResult>

// In-process MCP server
export function createSdkMcpServer(options: { name: string; tools: SdkMcpToolDefinition[] }): McpSdkServerConfigWithInstance
export function tool<S>(name, description, schema, handler): SdkMcpToolDefinition<S>

06 控制协议(SDK 构建器)

外部 SDK 实现(如 Python claude-code-sdk) 通过基于 JSON 的方式与 Claude Code 进程进行通信 控制协议 分层在 stdio 上。 模式定义在 sdk/controlSchemas.ts.

控制请求子类型

SubtypeDirectionPurpose
initializeSDK → CLI启动会话 — 通过钩子、MCP 服务器、代理、系统提示覆盖
interruptSDK → CLI取消当前正在运行的回合
can_use_toolCLI → SDK请求工具使用许可; SDK 主机响应允许/拒绝
set_permission_modeSDK → CLI在会话中更改权限模式(默认/acceptEdits/bypassPermissions/plan/dontAsk)
set_modelSDK → CLI为后续回合切换模型
set_max_thinking_tokensSDK → CLI调整扩展思维预算
mcp_statusSDK → CLI查询当前 MCP 服务器连接状态
get_context_usageSDK → CLI按类别检查上下文窗口利用率
// Initialize request — sent by SDK to start a session
{
  subtype: "initialize",
  hooks: {
    "PreToolUse": [{ hookCallbackIds: ["my-hook"], matcher: "Bash" }]
  },
  sdkMcpServers: ["my-server"],
  systemPrompt: "You are a coding assistant.",
  agents: {
    "reviewer": { description: "Reviews code changes", ... }
  }
}

// Initialize response — returned by CLI
{
  commands: [...],    // available slash commands
  agents: [...],      // available agent types
  models: [...],      // available models
  account: {...},     // account info
  pid: 12345          // @internal CLI PID for tmux socket isolation
}

07 挂钩事件 — SDK 观察者模式

SDK 公开了丰富的钩子系统,使外部主机可以观察和拦截 Claude Code 的生命​​周期。 有 26 个命名的钩子事件,定义在 sdk/coreTypes.ts:

export const HOOK_EVENTS = [
  // Tool execution
  'PreToolUse', 'PostToolUse', 'PostToolUseFailure',
  // Permission flow
  'PermissionRequest', 'PermissionDenied',
  // Session lifecycle
  'SessionStart', 'SessionEnd', 'Setup',
  // Turn lifecycle
  'Stop', 'StopFailure',
  // Context management
  'PreCompact', 'PostCompact',
  // Agent/swarm lifecycle
  'SubagentStart', 'SubagentStop', 'TeammateIdle',
  'TaskCreated', 'TaskCompleted',
  // Notifications and user input
  'Notification', 'UserPromptSubmit',
  // Config changes
  'ConfigChange', 'InstructionsLoaded', 'CwdChanged', 'FileChanged',
  // Elicitation
  'Elicitation', 'ElicitationResult',
  // Worktree
  'WorktreeCreate', 'WorktreeRemove',
] as const

每个钩子都会发射 BaseHookInput 其中包括 session_id, transcript_path, cwd, agent_id (如果在子代理内部),以及 agent_type。 每个事件都会在顶部添加自己的特定字段。

子代理检测
Use agent_id (仅存在于子代理内部)- 不 agent_type — 区分子代理钩子触发和主线程触发。 主线程可以有一个 agent_type 当开始于 --agent 但永远不会有 agent_id.

08 守护进程和桥接模式 (@internal)

对于高级主机架构(桌面应用程序、CI 系统、claude.ai 集成), agentSdkTypes.ts 还出口 @internal primitives:

计划任务/Cron

// Watch .claude/scheduled_tasks.json and yield fire/missed events
export function watchScheduledTasks(opts: {
  dir: string
  signal: AbortSignal
  getJitterConfig?: () => CronJitterConfig
}): ScheduledTasksHandle

// ScheduledTasksHandle — drain with for await
{
  events(): AsyncGenerator<ScheduledTaskEvent>  // fire | missed
  getNextFireTime(): number | null               // epoch ms of next scheduled run
}

这使得守护进程拥有父进程中的调度程序。 当任务触发时,守护进程会生成一个 query() 子进程——如果它崩溃了,守护进程可以在计划继续进行的同时重新生成它。

远程控制/桥接器

// Connect the local machine as a claude.ai remote-control environment
export async function connectRemoteControl(opts: ConnectRemoteControlOptions):
  Promise<RemoteControlHandle | null>

// RemoteControlHandle — two-way bridge over WebSocket
{
  sessionUrl: string
  environmentId: string
  bridgeSessionId: string
  write(msg: SDKMessage): void         // pipe query() yields in
  sendResult(): void                     // signal turn complete
  inboundPrompts(): AsyncGenerator<...>  // read user messages from claude.ai
  controlRequests(): AsyncGenerator<...> // interrupt, set_model, etc.
  permissionResponses(): AsyncGenerator<...>
  onStateChange(cb): void               // ready | connected | reconnecting | failed
  teardown(): Promise<void>
}
架构差异
守护进程模式:WebSocket 存在于父进程中。 如果代理子进程崩溃,守护进程会重新生成它,同时 claude.ai 保持相同的会话。 query.enableRemoteControl:WebSocket 存在于子进程中并随之消亡。 使用守护进程模式实现生产级可靠性。

09 sandboxTypes.ts — 进程隔离配置

src/entrypoints/sandboxTypes.ts 是沙箱配置类型的单一事实来源。 SDK 和设置验证系统都从此处导入。 它是通过导出的 agentSdkTypes.ts 因此 SDK 消费者可以配置沙箱。

// SandboxSettings — full configuration for process-level isolation
{
  enabled: boolean
  failIfUnavailable: boolean    // hard-gate for managed deployments
  autoAllowBashIfSandboxed: boolean
  allowUnsandboxedCommands: boolean
  network: {
    allowedDomains: string[]
    allowManagedDomainsOnly: boolean  // enterprise: only managed domains
    allowUnixSockets: string[]        // macOS only
    allowLocalBinding: boolean
    httpProxyPort: number
    socksProxyPort: number
  }
  filesystem: {
    allowWrite: string[]      // merged with Edit() allow rules
    denyWrite: string[]       // merged with Edit() deny rules
    denyRead: string[]
    allowRead: string[]       // re-allow within denyRead regions
    allowManagedReadPathsOnly: boolean
  }
}

10 main.tsx — 调用模式

Once cli.tsx 落入 main.tsx,基于 Commander 的 CLI 处理其余的调用模式。 关键:

标志/模式Behavior
-p / --print <prompt> 无头模式 — 以非交互方式运行单个提示,将结果流式传输到标准输出,然后退出。 由脚本和代理 SDK 使用。
--mcp 通过 stdio 将 Claude Code 作为 MCP 服务器启动。 代表们 entrypoints/mcp.ts.
(无旗帜) 交互式 REPL — 渲染完整的 Ink TUI。 通话 launchRepl().
--resume [sessionId] 通过 UUID 恢复之前的会话,或显示恢复选择器 TUI。
--continue 继续最近的会话而不显示选择器。
--dangerously-skip-permissions 绕过所有权限检查(需要在设置中明确选择加入)。 由 CI 环境使用。
--allowedTools 会话的逗号分隔工具允许列表。
--sdk-transport=process SDK 进程传输模式 — 通过 stdin/stdout 控制协议连接代理 SDK。

11 完整的调用流程

sequenceDiagram participant Dev as External Developer participant SDK as Agent SDK (Python/TS) participant CLI as Claude Code Process participant API as Anthropic API Dev->>SDK: query("fix this bug", { cwd: "/project" }) SDK->>CLI: spawn claude --sdk-transport=process CLI->>CLI: cli.tsx → main.tsx init() CLI->>SDK: control: initialize response\n(commands, models, account) SDK->>CLI: user message (stdin) CLI->>API: streaming API request API-->>CLI: assistant tokens CLI-->>SDK: SDKAssistantMessage stream CLI->>SDK: control: can_use_tool? (Bash) SDK-->>CLI: allow CLI->>CLI: execute tool CLI-->>SDK: SDKToolResultMessage CLI-->>SDK: SDKResultMessage (final) SDK-->>Dev: async generator yields messages

要点

  • cli.tsx 是一个纯粹的调度程序 - 它在加载完整的 CLI 之前快速路径 8 个以上的特殊命令,保持版本检查、守护进程工作人员和桥接模式的快速启动。
  • Build-time feature() 标志执行死代码消除——桥接模式、守护进程、后台会话和其他功能在外部构建中完全不存在,除非启用。
  • init.ts 被所有入口点记忆和共享——它在第一次 API 调用之前协调 TLS 证书、代理、OAuth、遥测和清理处理程序。
  • Claude Code 可以 be MCP 服务器(--mcp 模式通过 mcp.ts)同时也 consuming MCP 服务器 — 边界是对称的。
  • 代理 SDK 公共 API (agentSdkTypes.ts) 是一个存根文件——所有函数体都会抛出异常; 真正的实现是在运行时由 SDK 传输注入的。
  • 26 个生命周期挂钩事件让 SDK 主机观察并拦截工具执行、权限决策、会话生命周期、压缩和工作树操作。
  • 控制协议将 SDK 消费者(谁调用 query())来自 SDK 构建者(实现进程传输并直接讲控制协议)。
  • sandboxTypes.ts 是进程隔离配置的单一事实来源——由 SDK 导出和设置验证系统使用。

检查你的理解情况

问题1
哪个文件处理第一次调度 claude --version 为什么它加载零个模块?
问题2
在代理 SDK 中,所有顶级函数 agentSdkTypes.ts throw 'not implemented'。 什么机制实际上提供了真正的实现?
问题3
守护进程模式远程控制和守护进程模式远程控制之间的主要架构区别是什么? query.enableRemoteControl?
问题4
怎么样 feature() cli.tsx 与运行时 GrowthBook 门不同,例如 isBridgeEnabled()?