/Part 2: 도구 시스템/Tool 인터페이스 설계
🔧
Part 2중급⏱ 약 50분 · 3개 섹션

Tool 인터페이스 설계

모든 도구의 청사진

Tool 인터페이스 설계 대표 이미지
이 챕터에서 배울 것
  • 모든 도구는 동일한 Tool 인터페이스를 구현
  • buildTool()은 안전한 기본값을 제공하는 팩토리 함수
  • isConcurrencySafe()의 기본값이 false인 이유: fail-closed 원칙
전체 아키텍처에서 현재 위치
⚙️코어 엔진
🔧도구 시스템
🛡️보안
🧠컨텍스트
🤝멀티 에이전트
🌐생태계
인프라
1

Tool 타입 정의 해부

Tool<Input, Output, P> 제네릭의 각 파라미터와 메서드를 분석합니다

제네릭 타입Zod 스키마LazySchemaToolResult|Tool.ts

Claude Code의 모든 도구는 Tool<Input, Output, P> 제네릭 인터페이스를 구현합니다. 이 인터페이스는 792줄의 src/Tool.ts에 정의되어 있으며, 50개 이상의 도구가 동일한 계약을 따릅니다.

핵심 타입 파라미터:

  • Input — Zod 스키마로 정의된 입력 타입 (strictObject로 알려지지 않은 필드 거부)
  • Output — 도구 실행 결과 타입
  • P extends ToolProgressData — 스트리밍 진행률 타입 (BashProgress, AgentToolProgress 등)
src/Tool.ts:362-450 — Tool 인터페이스 정의
1// src/Tool.ts — 핵심 Tool 인터페이스 (792줄 중 발췌)
2
3export type Tool<💡
4 Input extends AnyObject = AnyObject,
5 Output = unknown,
6 P extends ToolProgressData = ToolProgressData,
7> = {
8 // ─── 식별 ───
9 aliases?: string[] // 이름 변경 시 하위 호환 별칭
10 searchHint?: string // ToolSearch 키워드 매칭 (3-10 단어)
11
12 // ─── 핵심 메서드 ───
13 call(💡
14 args: z.infer<Input>,
15 context: ToolUseContext,
16 canUseTool: CanUseToolFn,
17 parentMessage: AssistantMessage,
18 onProgress?: ToolCallProgress<P>,
19 ): Promise<ToolResult<Output>>
20
21 description(input, options): Promise<string>
22 readonly inputSchema: Input // Zod 스키마
23 outputSchema?: z.ZodType<unknown>
24
25 // ─── 안전성 메서드 ───
26 isConcurrencySafe(input): boolean // 기본: false (fail-closed)
27 isEnabled(): boolean // 기본: true💡
28 isReadOnly(input): boolean // 기본: false
29 isDestructive?(input): boolean // 기본: false
30
31 // ─── 인터럽트 동작 ───
32 interruptBehavior?(): 'cancel' | 'block' // 기본: 'block'
33
34 // ─── 검색/읽기 분류 (UI 최적화) ───
35 isSearchOrReadCommand?(input): {
36 isSearch: boolean
37 isRead: boolean
38 isList?: boolean💡
39 }💡
40
41 // ─── 지연 로딩 ───
42 readonly shouldDefer?: boolean // ToolSearch로만 접근
43 readonly alwaysLoad?: boolean // 항상 모델에 전달
44
45 // ─── 권한 ───
46 checkPermissions(input, context): Promise<PermissionResult>
47 validateInput?(input, context): Promise<ValidationResult>
48 getPath?(input): string
49 preparePermissionMatcher?(input): Promise<(pattern: string) => boolean>
50
51 // ─── 렌더링 (UI.tsx) ───
52 renderToolUseMessage(input, options): ReactNode
53 renderToolResultMessage(result, options): ReactNode
54 renderToolUseProgressMessage?(progress, options): ReactNode
55 getToolUseSummary(input): string
56 userFacingName(input): string
57}
💡 fail-closed 원칙
isConcurrencySafe()의 기본값이 false인 이유: 새 도구를 추가할 때 개발자가 동시성 안전을 깜빡하고 선언하지 않으면, 시스템은 '안전하지 않다'고 가정합니다. 이것이 보안 시스템의 핵심 원칙인 fail-closed입니다. 반대로 fail-open이면 선언을 깜빡한 도구가 병렬 실행되어 레이스 컨디션이 발생할 수 있습니다.
2

buildTool() 팩토리 패턴

안전한 기본값 적용과 타입 추론

팩토리 패턴기본값 설계fail-closed타입 추론|Tool.tstools.ts

buildTool() 팩토리 함수는 안전한 기본값을 적용하여 도구를 생성합니다. 모든 도구는 이 함수를 통해 만들어집니다.

src/Tool.ts + src/tools/GrepTool/GrepTool.ts
1// src/Tool.ts:750-792 — TOOL_DEFAULTS와 buildTool()
2
3const TOOL_DEFAULTS = {
4 isEnabled: () => true,💡
5 isConcurrencySafe: (_input?: unknown) => false, // ← fail-closed
6 isReadOnly: (_input?: unknown) => false, // ← 쓰기 가정
7 isDestructive: (_input?: unknown) => false,
8 checkPermissions: (input, _ctx?) =>
9 Promise.resolve({ behavior: 'allow', updatedInput: input }), // ← 일반 권한에 위임
10 toAutoClassifierInput: (_input?: unknown) => '', // ← 분류기 건너뛰기
11 userFacingName: (_input?: unknown) => '',
12}
13
14export function buildTool<D extends AnyToolDef>(def: D): BuiltTool<D> {💡
15 return {
16 ...TOOL_DEFAULTS, // 1. 안전한 기본값 적용
17 userFacingName: () => def.name, // 2. 이름 기본값
18 ...def, // 3. 사용자 정의로 오버라이드
19 } as BuiltTool<D>
20}
21
22// ─── 사용 예시: GrepTool ───
23export const GrepTool = buildTool({
24 name: GREP_TOOL_NAME,
25 searchHint: 'search file contents regex ripgrep',
26 inputSchema: lazySchema(() => z.strictObject({💡
27 pattern: z.string().describe('정규식 패턴'),
28 path: z.string().optional(),
29 // ...
30 })),
31 isConcurrencySafe: () => true, // ← 읽기 전용이므로 명시적 선언
32 isReadOnly: () => true, // ← 읽기 전용
33 async call(input, context) {
34 const results = await ripGrep(input.pattern, resolvedPath);
35 return { output: results };
36 },
37})
3

도구 등록과 검색

getAllBaseTools(), assembleToolPool(), ToolSearch 메커니즘

도구 레지스트리피처 게이트MCP 도구 병합지연 로딩|tools.ts

src/tools.ts는 모든 도구의 단일 진실 소스(Single Source of Truth)입니다. getAllBaseTools() 함수가 전체 도구 목록을 반환하며, 피처 게이트와 환경변수에 따라 조건부로 도구를 포함합니다.

src/tools.ts — 도구 등록 시스템
1// src/tools.ts — 도구 등록의 3계층 구조
2
3// 1단계: 정적 import (항상 포함)
4import { BashTool } from './tools/BashTool/BashTool.js'
5import { FileEditTool } from './tools/FileEditTool/FileEditTool.js'
6import { FileReadTool } from './tools/FileReadTool/FileReadTool.js'
7import { GlobTool } from './tools/GlobTool/GlobTool.js'
8import { GrepTool } from './tools/GrepTool/GrepTool.js'
9
10// 2단계: 피처 게이트 (빌드 타임 dead code elimination)
11const SleepTool = feature('PROACTIVE') || feature('KAIROS')💡
12 ? require('./tools/SleepTool/SleepTool.js').SleepTool
13 : null
14const MonitorTool = feature('MONITOR_TOOL')
15 ? require('./tools/MonitorTool/MonitorTool.js').MonitorTool
16 : null
17
18// 3단계: 환경변수 기반 (런타임)
19const REPLTool = process.env.USER_TYPE === 'ant'
20 ? require('./tools/REPLTool/REPLTool.js').REPLTool
21 : null
22
23// ─── 전체 도구 목록 반환 ───
24export function getAllBaseTools(): Tools {
25 return [💡
26 AgentTool, BashTool, FileReadTool, FileEditTool,
27 FileWriteTool, GlobTool, GrepTool, WebFetchTool,
28 TodoWriteTool, WebSearchTool, AskUserQuestionTool,
29 SkillTool, EnterPlanModeTool, ExitPlanModeV2Tool,
30 NotebookEditTool, TaskStopTool, BriefTool,
31 // 피처 게이트 도구
32 ...(SleepTool ? [SleepTool] : []),
33 ...(MonitorTool ? [MonitorTool] : []),
34 ...(RemoteTriggerTool ? [RemoteTriggerTool] : []),
35 // 환경변수 도구
36 ...(process.env.USER_TYPE === 'ant' ? [ConfigTool, TungstenTool] : []),
37 ...(REPLTool ? [REPLTool] : []),
38 // ToolSearch (지연 로딩 활성화 시)
39 ...(isToolSearchEnabledOptimistic() ? [ToolSearchTool] : []),💡
40 ].filter(Boolean) as Tools
41}
42
43// ─── MCP 도구 병합 ───
44export function assembleToolPool(builtIn, mcpTools): Tools {💡
45 // 내장 도구가 MCP 도구보다 우선 (이름 충돌 시)
46 return uniqBy([...builtIn, ...mcpTools], t => t.name)
47}
⚠️ dead code elimination의 중요성
feature('PROACTIVE')가 false이면 SleepTool의 require()는 빌드 결과에 아예 포함되지 않습니다. 이것은 번들 크기를 줄이는 것뿐 아니라, 해당 도구의 의존성 전체가 제거되는 효과가 있습니다. 단순한 if문이 아니라 빌드 타임 최적화입니다.
?

이해도 확인 퀴즈

buildTool()에서 isConcurrencySafe의 기본값이 false인 이유는?

학습 체크리스트

이해한 항목을 체크하고 학습 진도를 확인하세요

0/5 완료0%

📋 Chapter 4 요약: Tool 인터페이스 설계

이 챕터에서 배운 핵심 개념을 정리합니다

1
Tool 제네릭
Tool<Input, Output, P>로 모든 도구를 타입 안전하게 정의합니다.
2
buildTool 팩토리
TOOL_DEFAULTS + 사용자 정의를 스프레드로 병합하여 안전한 기본값을 보장합니다.
3
fail-closed
명시적 선언 없이는 동시 실행 불가 — 안전 우선 원칙.
4
도구 등록
정적 import + 피처 게이트 + 환경변수로 3단계 조건부 등록.

다음 챕터 미리보기: 다음 챕터에서는 FileEdit, Grep, BashTool 등 실제 도구 구현을 분석합니다.