claude-code/docs/ko/level-2-systems/tool-system.md
JuYoung Jeong 3358348196 docs: add comprehensive Korean source code analysis (19 documents, 6,926 lines)
Add complete Korean-language technical documentation analyzing Claude Code CLI
internals, organized in a leveled guide structure (Level 1-3 + Appendix).

Level 1 (입문): architecture overview, request lifecycle, key concepts
Level 2 (시스템): QueryEngine, Tool system, Command system, Permission system,
  Agent coordinator, UI/Ink components
Level 3 (심화): MCP/LSP integration, IDE bridge, Plugin/Skill system,
  Context compression, OAuth/Auth, Telemetry
Appendix: Korean-English glossary, file map, design patterns

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-31 19:39:03 +09:00

634 lines
26 KiB
Markdown

# Tool 시스템: 도구 레지스트리 & 실행 파이프라인
## 1. 개요
Tool 시스템은 Claude Code의 핵심 확장 메커니즘이다. LLM(Large Language Model, 대규모 언어 모델)이 텍스트 생성 외에 실제 작업을 수행할 수 있도록 40개 이상의 도구를 등록, 검증, 실행하는 파이프라인 전체를 관할한다.
**역할 요약:**
- **등록**: 빌트인(built-in, 내장) 도구와 MCP(Model Context Protocol) 도구를 단일 풀(pool)로 조합
- **검증**: Zod v4 스키마 기반 입력 유효성 검사 및 권한 확인
- **실행**: `ToolUseContext`를 의존성 주입(dependency injection) 컨테이너로 활용하여 각 도구에 실행 환경 전달
- **결과 처리**: 결과 포맷팅, 대용량 결과의 디스크 저장, UI 렌더링
**파일 구조:**
| 파일 | 역할 |
|------|------|
| `src/Tool.ts` | 핵심 타입 및 인터페이스 정의 |
| `src/tools.ts` | 도구 레지스트리 및 조합 로직 |
| `src/tools/*/` | 개별 도구 구현체 (40개 이상) |
---
## 2. 아키텍처 다이어그램
```mermaid
graph TD
A[LLM tool_use 블록 반환] --> B[toolMatchesName으로 도구 탐색]
B --> C{도구 발견?}
C -- 아니오 --> D[에러 반환]
C -- 예 --> E[Zod 스키마 입력 검증]
E --> F{validateInput 통과?}
F -- 실패 --> G[ValidationResult 에러 반환]
F -- 통과 --> H[checkPermissions 호출]
H --> I{권한 결과}
I -- deny --> J[거부 메시지 반환]
I -- ask --> K[사용자에게 확인 요청]
I -- allow --> L[tool.call 실행]
K -- 승인 --> L
K -- 거부 --> J
L --> M[ToolResult 반환]
M --> N{결과 크기 > maxResultSizeChars?}
N -- 예 --> O[디스크에 저장, 프리뷰 반환]
N -- 아니오 --> P[mapToolResultToToolResultBlockParam]
O --> P
P --> Q[렌더링 및 메시지 히스토리 추가]
subgraph "도구 레지스트리 (tools.ts)"
R[getAllBaseTools] --> S[getTools]
S --> T[filterToolsByDenyRules]
T --> U[assembleToolPool]
U --> V[MCP 도구 병합]
end
subgraph "개별 도구 구현체 (src/tools/*/)"
W[BashTool]
X[FileReadTool]
Y[AgentTool]
Z[기타 40+ 도구]
end
```
---
## 3. 핵심 타입 및 인터페이스
`src/Tool.ts`는 792줄 규모의 타입 전용 파일로, 런타임 코드를 최소화하고 타입 시스템을 통해 도구 계약(contract)을 강제한다.
### 3.1 `ToolInputJSONSchema`
```typescript
export type ToolInputJSONSchema = {
[x: string]: unknown
type: 'object'
properties?: {
[x: string]: unknown
}
}
```
MCP 도구처럼 Zod 스키마 대신 JSON Schema를 직접 사용하는 도구를 위한 타입이다. 빌트인 도구는 `inputSchema: Input`(Zod 기반)을 사용하고, MCP 도구는 `inputJSONSchema`를 사용하는 두 경로가 공존한다.
### 3.2 `ToolPermissionContext`
```typescript
export type ToolPermissionContext = DeepImmutable<{
mode: PermissionMode
additionalWorkingDirectories: Map<string, AdditionalWorkingDirectory>
alwaysAllowRules: ToolPermissionRulesBySource
alwaysDenyRules: ToolPermissionRulesBySource
alwaysAskRules: ToolPermissionRulesBySource
isBypassPermissionsModeAvailable: boolean
isAutoModeAvailable?: boolean
strippedDangerousRules?: ToolPermissionRulesBySource
shouldAvoidPermissionPrompts?: boolean
awaitAutomatedChecksBeforeDialog?: boolean
prePlanMode?: PermissionMode
}>
```
`DeepImmutable<T>`로 감싸져 있어 런타임 중 권한 컨텍스트가 변경되지 않음을 타입 수준에서 보장한다. `alwaysAllowRules`, `alwaysDenyRules`, `alwaysAskRules`는 각각 소스(source)별로 분류된 규칙 맵으로, `.claude/settings.json`의 훅(hook) 설정이 여기에 반영된다.
### 3.3 `ToolUseContext`
전체 시스템에서 가장 복잡한 타입이다. 모든 `tool.call()`에 전달되는 의존성 주입 컨테이너 역할을 한다.
**주요 필드 분류:**
| 카테고리 | 필드 | 설명 |
|----------|------|------|
| 설정 | `options.tools`, `options.commands`, `options.mainLoopModel` | 런타임 설정 |
| 상태 관리 | `getAppState()`, `setAppState()` | React 상태 접근자 |
| 세션 인프라 | `setAppStateForTasks` | 서브에이전트에서도 루트 스토어에 접근 가능 |
| UI 콜백 | `setToolJSX`, `addNotification`, `appendSystemMessage` | UI 업데이트 함수 |
| 중단 제어 | `abortController` | 도구 실행 취소 신호 |
| 파일 캐시 | `readFileState: FileStateCache` | LRU 캐시 기반 파일 상태 |
| 컨텍스트 추적 | `queryTracking`, `toolDecisions` | 체인 추적 및 결정 기록 |
| 에이전트 식별 | `agentId`, `agentType` | 서브에이전트 구분 |
| 권한 관련 | `localDenialTracking`, `requireCanUseTool` | 비동기 서브에이전트용 거부 추적 |
| 프롬프트 캐시 | `renderedSystemPrompt`, `contentReplacementState` | 캐시 공유 최적화 |
`setAppStateForTasks`는 일반 `setAppState`와 다르게 비동기 서브에이전트에서도 루트 AppState에 접근할 수 있도록 설계된 점이 특징이다. 일반 `setAppState`는 비동기 에이전트에서 no-op(무작동)으로 작동하여 격리를 유지하지만, 세션 스코프 인프라(예: 백그라운드 태스크 등록)는 루트까지 전파되어야 하기 때문이다.
### 3.4 `ValidationResult`
```typescript
export type ValidationResult =
| { result: true }
| { result: false; message: string; errorCode: number }
```
도구 실행 전 사전 검증(pre-flight validation) 결과 타입이다. 실패 시 `message`가 LLM에게 반환되어 모델이 입력을 수정할 수 있도록 안내한다.
### 3.5 `Tool` 인터페이스
```typescript
export type Tool<
Input extends AnyObject = AnyObject,
Output = unknown,
P extends ToolProgressData = ToolProgressData,
> = {
name: string
aliases?: string[]
inputSchema: Input
inputJSONSchema?: ToolInputJSONSchema
maxResultSizeChars: number
call(args, context, canUseTool, parentMessage, onProgress?): Promise<ToolResult<Output>>
description(input, options): Promise<string>
checkPermissions(input, context): Promise<PermissionResult>
validateInput?(input, context): Promise<ValidationResult>
isConcurrencySafe(input): boolean
isReadOnly(input): boolean
isDestructive?(input): boolean
// ... UI 렌더링 메서드 다수
}
```
제네릭 파라미터 3개가 입력 스키마 타입(`Input`), 출력 타입(`Output`), 진행 상태 타입(`P`)을 각각 고정한다. UI 렌더링 관련 메서드(`renderToolResultMessage`, `renderToolUseMessage` 등)가 도구 인터페이스에 직접 포함된 점이 이 설계의 특징으로, 도구 로직과 렌더링 로직이 같은 모듈 내에 공존한다.
### 3.6 `buildTool` 팩토리 함수
```typescript
export function buildTool<D extends AnyToolDef>(def: D): BuiltTool<D> {
return {
...TOOL_DEFAULTS,
userFacingName: () => def.name,
...def,
} as BuiltTool<D>
}
```
모든 도구는 `buildTool`을 통해 생성되며, 다음 기본값이 주입된다:
| 메서드 | 기본값 | 설계 원칙 |
|--------|--------|-----------|
| `isEnabled` | `() => true` | 명시적 비활성화 없이는 활성 |
| `isConcurrencySafe` | `() => false` | 실패 안전(fail-closed): 기본적으로 동시 실행 불가 |
| `isReadOnly` | `() => false` | 기본적으로 쓰기 작업으로 간주 |
| `isDestructive` | `() => false` | 기본적으로 비파괴적 |
| `checkPermissions` | `allow` 반환 | 일반 권한 시스템에 위임 |
| `toAutoClassifierInput` | `() => ''` | 보안 분류기에서 제외 |
| `userFacingName` | `() => def.name` | 도구명을 그대로 표시 |
`BuiltTool<D>` 타입은 타입 수준 스프레드(spread)를 통해 런타임 `{...TOOL_DEFAULTS, ...def}` 의미론을 정확히 반영한다.
### 3.7 `Tools` 타입
```typescript
export type Tools = readonly Tool[]
```
`Tool[]` 대신 이 타입을 사용함으로써 코드베이스 전체에서 도구 집합이 조합, 전달, 필터링되는 지점을 타입 시스템으로 추적할 수 있다.
---
## 4. Tool 레지스트리 (`tools.ts`)
`src/tools.ts`는 도구 등록 방식을 세 가지 패턴으로 분류한다.
### 4.1 정적 임포트 (Static Import)
핵심 도구들은 ES 모듈 정적 임포트로 등록된다:
```typescript
import { AgentTool } from './tools/AgentTool/AgentTool.js'
import { BashTool } from './tools/BashTool/BashTool.js'
import { FileEditTool } from './tools/FileEditTool/FileEditTool.js'
// ...
```
번들러(bundler)가 빌드 시점에 포함 여부를 결정할 수 있어 트리 셰이킹(tree shaking)이 적용된다.
### 4.2 조건부 `require()` — 기능 플래그 기반
`bun:bundle``feature()` 함수를 활용한 dead code elimination(데드 코드 제거) 패턴이다:
```typescript
import { feature } from 'bun:bundle'
const SleepTool = feature('PROACTIVE') || feature('KAIROS')
? require('./tools/SleepTool/SleepTool.js').SleepTool
: null
const cronTools = feature('AGENT_TRIGGERS')
? [
require('./tools/ScheduleCronTool/CronCreateTool.js').CronCreateTool,
// ...
]
: []
```
`feature()` 플래그가 `false`인 경우, 번들러는 해당 `require()` 브랜치 전체를 빌드 산출물에서 제거한다. 이는 프로덕션 번들 크기를 최소화하는 동시에 기능별 점진적 배포를 가능하게 한다.
### 4.3 조건부 `require()` — 환경 변수 기반
`USER_TYPE` 환경 변수로 Ant(내부 Anthropic) 전용 도구를 분리한다:
```typescript
const REPLTool =
process.env.USER_TYPE === 'ant'
? require('./tools/REPLTool/REPLTool.js').REPLTool
: null
```
이 패턴을 사용하는 도구: `REPLTool`, `SuggestBackgroundPRTool`, `ConfigTool`, `TungstenTool`.
### 4.4 지연 `require()` — 순환 의존성 해결
`TeamCreateTool`, `TeamDeleteTool`, `SendMessageTool`은 순환 의존성(circular dependency)을 해결하기 위해 지연 로딩(lazy loading)된다:
```typescript
// Lazy require to break circular dependency: tools.ts -> TeamCreateTool/TeamDeleteTool -> ... -> tools.ts
const getTeamCreateTool = () =>
require('./tools/TeamCreateTool/TeamCreateTool.js')
.TeamCreateTool as typeof import('./tools/TeamCreateTool/TeamCreateTool.js').TeamCreateTool
```
이 팀 도구들은 `tools.ts`를 임포트하는 모듈 체인에 속하므로, 정적 임포트로 처리하면 모듈 초기화 시점에 순환이 발생한다. 지연 함수로 감싸면 실제 호출 시점까지 로딩을 미룰 수 있다.
### 4.5 `getAllBaseTools()` 함수
전체 도구 목록의 단일 진실 공급원(single source of truth)이다:
```typescript
export function getAllBaseTools(): Tools {
return [
AgentTool,
TaskOutputTool,
BashTool,
...(hasEmbeddedSearchTools() ? [] : [GlobTool, GrepTool]),
// ...
...(isTodoV2Enabled()
? [TaskCreateTool, TaskGetTool, TaskUpdateTool, TaskListTool]
: []),
// ...
...(isToolSearchEnabledOptimistic() ? [ToolSearchTool] : []),
]
}
```
주석에 따르면 이 목록은 Statsig 시스템 프롬프트 캐싱 설정(`claude_code_global_system_caching`)과 동기화를 유지해야 한다. 도구 순서가 바뀌면 프롬프트 캐시 키가 무효화될 수 있기 때문이다.
### 4.6 `getTools()` 함수
권한 컨텍스트를 적용하여 실제 사용 가능한 도구 목록을 반환한다:
```typescript
export const getTools = (permissionContext: ToolPermissionContext): Tools => {
// 1. CLAUDE_CODE_SIMPLE 환경 변수: Bash/Read/Edit만 반환
if (isEnvTruthy(process.env.CLAUDE_CODE_SIMPLE)) { ... }
// 2. 특수 도구 제외 (ListMcpResources 등 별도 경로로 추가)
const tools = getAllBaseTools().filter(tool => !specialTools.has(tool.name))
// 3. Deny 규칙으로 도구 필터링
let allowedTools = filterToolsByDenyRules(tools, permissionContext)
// 4. REPL 모드: 원시 도구 숨김 (REPL 내부 VM에서만 접근 가능)
if (isReplModeEnabled()) { ... }
// 5. isEnabled() 검사
return allowedTools.filter((_, i) => isEnabled[i])
}
```
### 4.7 `assembleToolPool()` 함수
빌트인 도구와 MCP 도구를 하나의 풀로 결합하는 최종 조합 함수다:
```typescript
export function assembleToolPool(
permissionContext: ToolPermissionContext,
mcpTools: Tools,
): Tools {
const builtInTools = getTools(permissionContext)
const allowedMcpTools = filterToolsByDenyRules(mcpTools, permissionContext)
// 빌트인 도구를 앞쪽 연속 블록으로 유지하여 프롬프트 캐시 안정성 확보
const byName = (a: Tool, b: Tool) => a.name.localeCompare(b.name)
return uniqBy(
[...builtInTools].sort(byName).concat(allowedMcpTools.sort(byName)),
'name',
)
}
```
정렬 시 빌트인 도구와 MCP 도구를 별도 파티션으로 유지하는 이유는 프롬프트 캐시 안정성(prompt cache stability) 때문이다. 서버 측 캐시 정책이 빌트인 도구 블록 끝에 캐시 브레이크포인트(cache breakpoint)를 두므로, 두 파티션을 평탄하게 섞으면 MCP 도구가 빌트인 목록 사이에 끼어들어 캐시 키가 무효화된다.
---
## 5. Tool 실행 파이프라인
LLM이 `tool_use` 블록을 반환한 시점부터 결과가 대화 히스토리에 기록되기까지의 전체 흐름이다.
### 단계 1: 도구 탐색
```typescript
export function toolMatchesName(
tool: { name: string; aliases?: string[] },
name: string,
): boolean {
return tool.name === name || (tool.aliases?.includes(name) ?? false)
}
export function findToolByName(tools: Tools, name: string): Tool | undefined {
return tools.find(t => toolMatchesName(t, name))
}
```
`aliases` 필드는 도구명이 변경될 때 하위 호환성(backward compatibility)을 유지하기 위한 장치다. 구 도구명으로 호출된 LLM 요청도 새 도구로 라우팅된다.
### 단계 2: 입력 검증
Zod v4 스키마로 입력을 파싱한다. 파싱 실패 시 구조화된 에러 메시지가 LLM에 반환되어 모델이 입력을 수정하도록 유도한다.
옵셔널 메서드 `validateInput`이 구현된 경우, Zod 파싱 통과 후 추가적인 도구별 검증이 실행된다:
```typescript
validateInput?(
input: z.infer<Input>,
context: ToolUseContext,
): Promise<ValidationResult>
```
### 단계 3: 권한 확인
```typescript
checkPermissions(
input: z.infer<Input>,
context: ToolUseContext,
): Promise<PermissionResult>
```
`PermissionResult``behavior` 필드에 따라 실행 경로가 분기된다:
| behavior | 동작 |
|----------|------|
| `allow` | 즉시 실행 |
| `deny` | 거부 메시지 반환, LLM에 전달 |
| `ask` | 사용자에게 확인 UI 표시 |
`buildTool`의 기본 구현은 `allow`를 반환하여 일반 권한 시스템(`permissions.ts`)에 위임한다. 도구별 특수 로직이 필요한 경우에만 이 메서드를 오버라이드한다.
### 단계 4: 실행
```typescript
call(
args: z.infer<Input>,
context: ToolUseContext,
canUseTool: CanUseToolFn,
parentMessage: AssistantMessage,
onProgress?: ToolCallProgress<P>,
): Promise<ToolResult<Output>>
```
`ToolUseContext`는 도구가 필요로 하는 모든 의존성(앱 상태, 파일 캐시, 중단 컨트롤러, UI 콜백 등)을 담은 컨테이너로 전달된다. `onProgress` 콜백을 통해 실행 중 진행 상태를 스트리밍할 수 있다.
### 단계 5: 결과 처리
```typescript
export type ToolResult<T> = {
data: T
newMessages?: (UserMessage | AssistantMessage | AttachmentMessage | SystemMessage)[]
contextModifier?: (context: ToolUseContext) => ToolUseContext
mcpMeta?: { _meta?: Record<string, unknown>; structuredContent?: Record<string, unknown> }
}
```
`contextModifier`는 동시 실행에 안전하지 않은 도구(`isConcurrencySafe = false`)에서만 사용 가능하며, 도구 결과에 따라 `ToolUseContext`를 변경할 수 있다. 예를 들어 `EnterPlanModeTool`은 이를 통해 권한 모드를 전환한다.
### 단계 6: 결과 직렬화 및 저장
```typescript
maxResultSizeChars: number
mapToolResultToToolResultBlockParam(
content: Output,
toolUseID: string,
): ToolResultBlockParam
```
결과 크기가 `maxResultSizeChars`를 초과하면 디스크에 저장하고 LLM에는 파일 경로와 프리뷰만 전달한다. `FileReadTool`의 경우 이 값이 `Infinity`로 설정되어 있는데, 파일 내용을 디스크에 다시 저장하면 `Read → 파일 → Read` 순환이 발생하고 이미 자체적으로 크기 제한을 적용하기 때문이다.
### 단계 7: 에러 처리
도구는 에러 UI를 커스터마이즈할 수 있다:
```typescript
renderToolUseErrorMessage?(
result: ToolResultBlockParam['content'],
options: { ... },
): React.ReactNode
```
이 메서드가 없으면 `<FallbackToolUseErrorMessage />`가 사용된다. 파일 탐색 도구처럼 "파일을 찾을 수 없습니다" 같은 도구별 에러 메시지가 필요한 경우에만 구현한다.
---
## 6. Tool 카테고리 분석
`src/tools/` 디렉토리의 모든 도구를 카테고리별로 분류한다.
### 파일 조작
| 도구 | 설명 | 읽기 전용 |
|------|------|-----------|
| `FileReadTool` | 파일 읽기, 이미지/PDF/노트북 지원 | O |
| `FileEditTool` | 파일 일부 편집 (문자열 대체) | X |
| `FileWriteTool` | 파일 생성 및 전체 덮어쓰기 | X |
| `GlobTool` | 파일 패턴 탐색 | O |
| `GrepTool` | 파일 내용 패턴 검색 (ripgrep 래핑) | O |
| `NotebookEditTool` | Jupyter 노트북 셀 편집 | X |
`GlobTool``GrepTool``hasEmbeddedSearchTools()``true`인 경우(Ant 내부 빌드) 제외된다. Ant 빌드에는 bfs/ugrep이 Bun 바이너리에 내장되어 있고, Bash 내에서 `find`/`grep` 명령이 이 빠른 도구로 앨리어스(alias)되기 때문이다.
### 실행
| 도구 | 설명 |
|------|------|
| `BashTool` | 셸 명령 실행 (샌드박스 지원) |
| `REPLTool` | 격리된 VM 환경에서 코드 실행 (Ant 전용) |
| `PowerShellTool` | Windows PowerShell 명령 실행 |
`BashTool`은 파이프라인 구성 명령을 분석하여 검색/읽기/목록 조회 명령인지 판단하고 UI에서 접을 수 있게(collapsible) 표시한다. 이를 위해 `BASH_SEARCH_COMMANDS`, `BASH_READ_COMMANDS`, `BASH_LIST_COMMANDS`, `BASH_SEMANTIC_NEUTRAL_COMMANDS` 집합을 사용한다.
### 에이전트
| 도구 | 설명 |
|------|------|
| `AgentTool` | 서브에이전트 생성 및 실행 |
| `SendMessageTool` | 팀 내 에이전트 간 메시지 전송 |
| `TeamCreateTool` | 에이전트 팀 생성 |
| `TeamDeleteTool` | 에이전트 팀 해체 |
`AgentTool`은 시스템에서 가장 복잡한 도구로, 로컬/원격 에이전트 실행, 워크트리(worktree) 생성, 포크(fork) 서브에이전트, 코디네이터 모드 등 다수의 실행 경로를 포함한다. `AgentTool`의 임포트 목록만 80줄에 달한다.
### 태스크 관리
| 도구 | 설명 |
|------|------|
| `TaskCreateTool` | 새 태스크 생성 |
| `TaskGetTool` | 태스크 상태 조회 |
| `TaskListTool` | 태스크 목록 조회 |
| `TaskUpdateTool` | 태스크 업데이트 |
| `TaskOutputTool` | 태스크 출력 스트리밍 |
| `TaskStopTool` | 태스크 중단 |
태스크 관련 도구 6개는 `isTodoV2Enabled()` 플래그로 일괄 활성화/비활성화된다.
### MCP / LSP
| 도구 | 설명 |
|------|------|
| `MCPTool` | MCP 서버 도구 래퍼 (동적) |
| `LSPTool` | Language Server Protocol 통합 |
| `McpAuthTool` | MCP 인증 처리 |
| `ListMcpResourcesTool` | MCP 서버 리소스 목록 |
| `ReadMcpResourceTool` | MCP 리소스 내용 읽기 |
`ListMcpResourcesTool``ReadMcpResourceTool``getTools()`에서 `specialTools` 집합으로 분리되어 별도 경로로 추가된다. `LSPTool``ENABLE_LSP_TOOL` 환경 변수로 명시적으로 활성화해야 한다.
### 워크플로우 제어
| 도구 | 설명 |
|------|------|
| `EnterPlanModeTool` | Plan 모드 진입 (쓰기 작업 차단) |
| `ExitPlanModeV2Tool` | Plan 모드 종료 |
| `EnterWorktreeTool` | Git 워크트리 모드 진입 |
| `ExitWorktreeTool` | Git 워크트리 모드 종료 |
| `SkillTool` | 스킬 파일 실행 |
| `ToolSearchTool` | 지연 로드된 도구 검색 |
`EnterWorktreeTool``ExitWorktreeTool``isWorktreeModeEnabled()` 조건으로 등록된다.
### 사용자 상호작용
| 도구 | 설명 |
|------|------|
| `AskUserQuestionTool` | 사용자에게 질문 및 응답 수집 |
| `BriefTool` | 간략한 상태 업데이트 표시 |
| `ConfigTool` | 설정 읽기/쓰기 (Ant 전용) |
| `TodoWriteTool` | 할 일 목록 업데이트 |
| `SleepTool` | 실행 일시 중지 (PROACTIVE/KAIROS) |
`TodoWriteTool``renderToolResultMessage`를 구현하지 않는 도구의 예시다. 이 도구는 결과를 투명 패널(todo panel)로 업데이트하므로 대화 트랜스크립트(transcript)에 별도 결과를 출력하지 않는다.
### 웹 접근
| 도구 | 설명 |
|------|------|
| `WebFetchTool` | URL에서 HTML/텍스트 가져오기 |
| `WebSearchTool` | 웹 검색 실행 |
### 기타 (실험적/내부)
| 도구 | 설명 | 조건 |
|------|------|------|
| `SyntheticOutputTool` | 합성 출력 생성 | 항상 특수 처리 |
| `RemoteTriggerTool` | 원격 트리거 실행 | `AGENT_TRIGGERS_REMOTE` 플래그 |
| `ScheduleCronTool` 계열 | 크론 작업 관리 | `AGENT_TRIGGERS` 플래그 |
| `SleepTool` | 에이전트 슬립 | `PROACTIVE` 또는 `KAIROS` 플래그 |
| `TungstenTool` | 내부 도구 | Ant 전용 |
| `TestingPermissionTool` | 권한 테스트 | `NODE_ENV === 'test'` |
---
## 7. 주요 설계 결정
### 7.1 Zod v4 기반 스키마 검증
빌드 시점과 런타임 모두에서 타입 안전성을 보장하기 위해 Zod v4를 채택했다. `z.infer<Input>`으로 입력 타입을 자동 추론하므로 별도의 타입 정의가 불필요하다. 또한 Zod 스키마는 Anthropic API가 요구하는 JSON Schema로 직렬화되어 LLM에 도구 명세로 전달된다.
`lazySchema` 패턴이 다수의 도구에서 사용된다:
```typescript
// FileReadTool.tsx에서
import { lazySchema } from '../../utils/lazySchema.js'
```
이는 스키마 생성 비용이 높거나 순환 의존성이 있는 경우, 실제 사용 시점까지 스키마 평가를 지연시키는 패턴이다.
### 7.2 조건부 도구 로딩과 번들 크기 최적화
`bun:bundle``feature()` 함수는 Bun의 번들러가 이해하는 특수 API다. `feature('FLAG_NAME')``false`로 평가되는 빌드에서는 해당 `require()` 브랜치가 번들에서 완전히 제거된다. 이를 통해:
1. 프로덕션 빌드 크기 최소화
2. 미완성 기능의 코드가 배포되지 않음 보장
3. A/B 테스트 및 단계적 기능 롤아웃 지원
런타임 기능 플래그(`isEnvTruthy`, `process.env.USER_TYPE`)와 빌드 타임 플래그(`feature()`)를 구분하여 사용하는 점이 특징이다.
### 7.3 `ToolUseContext`의 의존성 주입 패턴
모든 도구가 단일 `context: ToolUseContext` 인수를 통해 필요한 의존성에 접근한다. 이 패턴의 장점:
- **테스트 용이성**: 컨텍스트 객체를 모킹(mocking)하여 도구를 격리 테스트 가능
- **확장성**: 새 의존성을 추가할 때 함수 시그니처를 변경하지 않고 컨텍스트에 필드만 추가
- **서브에이전트 격리**: `setAppState`가 서브에이전트에서 no-op으로 작동하도록 컨텍스트를 교체하여 격리 달성
단점으로는 컨텍스트 타입이 매우 커진다는 점이 있다 — `ToolUseContext`는 약 50개의 필드를 포함한다.
### 7.4 프롬프트 캐시 안정성 설계
`assembleToolPool`이 빌트인/MCP 도구를 별도 파티션으로 정렬하는 이유는 Anthropic API의 프롬프트 캐싱 메커니즘과 직접 연관된다. 서버 측 캐시 정책은 빌트인 도구 목록 끝에 캐시 브레이크포인트를 배치하는데, 도구 순서가 변경되면 이 브레이크포인트 이후의 모든 토큰이 캐시 미스(cache miss)가 된다. 이 설계를 통해 MCP 서버 연결/해제가 빈번해도 빌트인 도구 캐시는 유지된다.
### 7.5 `shouldDefer` / `alwaysLoad` — 지연 도구 로딩
도구 수가 많아지면 LLM에 전달되는 프롬프트 크기가 크게 증가한다. `shouldDefer: true`인 도구는 초기 프롬프트에서 스키마가 생략되고(`defer_loading: true` 플래그로 API에 전달), `ToolSearchTool`을 통해 필요 시 온디맨드(on-demand)로 스키마를 가져올 수 있다:
```typescript
readonly shouldDefer?: boolean
readonly alwaysLoad?: boolean // 지연 도구 중에서도 항상 초기 로드
```
이를 통해 40개 이상의 도구를 보유하면서도 초기 컨텍스트 토큰 소비를 최소화한다.
---
## 8. 실제 도구 구현 패턴
### BashTool 구현 패턴
```typescript
// BashTool.tsx (단순화)
export const BashTool = buildTool({
name: BASH_TOOL_NAME,
// 1. Zod 스키마 정의
inputSchema: lazySchema(() => z.object({
command: z.string(),
timeout: z.number().optional(),
})),
// 2. 핵심 실행 로직
async call(args, context, canUseTool, parentMessage, onProgress) {
// ... 실행 로직
},
// 3. 검색/읽기 명령 판별
isSearchOrReadCommand(input) {
return isSearchOrReadBashCommand(input.command)
},
// 4. 읽기 전용 여부 (기본값 false 사용)
isReadOnly: () => false,
})
```
### FileReadTool 구현 패턴
`FileReadTool.tsx`는 빌드 시점에 수십 개의 유틸리티를 임포트하는 복잡한 도구다. PDF, 이미지, Jupyter 노트북, 일반 텍스트 등 다양한 파일 형식을 처리하며, `maxResultSizeChars: Infinity`를 통해 결과가 디스크에 저장되지 않도록 보장한다.
---
## 내비게이션
- 이전: [QueryEngine](query-engine.md)
- 다음: [권한 시스템](permission-system.md)
- 상위: [목차](../README.md)