🔧
Part 2중급⏱ 약 60분 · 3개 섹션
도구 구현 패턴
실전 도구 해부

✦이 챕터에서 배울 것
전체 아키텍처에서 현재 위치
⚙️코어 엔진
→🔧도구 시스템
→🛡️보안
→🧠컨텍스트
→🤝멀티 에이전트
→🌐생태계
→⚡인프라
1
파일 도구 (Read, Edit, Write)
파일 시스템 조작 도구의 구현과 권한 체크
파일 읽기문자열 치환권한 체크결과 포맷팅|
tools/FileReadTool/FileReadTool.tstools/FileEditTool/FileEditTool.tstools/FileWriteTool/FileWriteTool.ts파일 조작 도구는 Claude Code의 가장 기본적인 능력입니다. FileReadTool, FileEditTool, FileWriteTool는 각각 읽기/편집/쓰기를 담당합니다.
src/tools/FileEditTool/FileEditTool.ts (핵심 구조)
1// src/tools/FileEditTool/FileEditTool.ts — 파일 편집 도구2import { buildTool } from '../../Tool.js'3import { checkWritePermissionForTool, matchingRuleForInput }4 from '../../utils/permissions/filesystem.js'5import { activateConditionalSkillsForPaths, addSkillDirectories }6 from '../../skills/loadSkillsDir.js'7import { getLspServerManager } from '../../services/lsp/manager.js'8import { notifyVscodeFileUpdated } from '../../services/mcp/vscodeSdkMcp.js'9 10// FileEditTool의 핵심 동작:11// 1. 파일 읽기 + 기존 내용 확인12// 2. old_string → new_string 문자열 치환13// 3. LSP 서버에 변경 알림 (didChange)14// 4. VSCode MCP에 변경 알림15// 5. 조건부 스킬 활성화 (파일 경로 기반)16// 6. 파일 히스토리 기록17 18export const FileEditTool = buildTool({19 name: FILE_EDIT_TOOL_NAME,20 inputSchema: lazySchema(() => z.strictObject({21 file_path: z.string().describe('수정할 파일의 절대 경로'),22 old_string: z.string().describe('교체할 기존 텍스트'),23 new_string: z.string().describe('새 텍스트'),24 replace_all: z.boolean().optional().describe('모든 매칭 교체 여부'),25 })),26 27 // 쓰기 도구이므로 권한 확인 필수28 async checkPermissions(input, context): Promise<PermissionResult> {💡29 return checkWritePermissionForTool(input.file_path, context)30 },💡31 getPath(input) { return input.file_path },32 33 // 권한 패턴 매칭: "FileEdit(src/**/*.ts)" 형태34 async preparePermissionMatcher(input) {35 return (pattern: string) => matchWildcardPattern(pattern, input.file_path)36 },37 38 async call(input, context) {39 // 파일 읽기 → old_string 검색 → 치환 → 저장40 const content = await readFile(input.file_path, 'utf-8')41 const newContent = content.replace(input.old_string, input.new_string)42 await writeTextContent(input.file_path, newContent)43 💡44 // LSP 서버에 변경 알림45 const lspManager = getLspServerManager()46 await lspManager?.changeFile(input.file_path, newContent)47 💡48 // VSCode에 변경 알림 (MCP 연동)49 notifyVscodeFileUpdated(input.file_path)50 💡51 // 조건부 스킬 활성화52 activateConditionalSkillsForPaths([input.file_path])53 54 return { output: { success: true, linesChanged } }55 },56})2
검색 도구 (Grep, Glob)
ripgrep 래퍼와 글로브 패턴 검색 구현
정규식 검색글로브 패턴읽기 전용 도구결과 잘라내기|
tools/GrepTool/GrepTool.tstools/GlobTool/GlobTool.tsGrepTool은 ripgrep을 래핑한 검색 도구입니다. 읽기 전용이므로 isConcurrencySafe: true, isReadOnly: true로 선언됩니다.
src/tools/GrepTool/GrepTool.ts
1// src/tools/GrepTool/GrepTool.ts — 검색 도구 구현2 3const inputSchema = lazySchema(() =>💡4 z.strictObject({5 pattern: z.string()6 .describe('정규식 패턴 (ripgrep 문법)'),7 path: z.string().optional()8 .describe('검색 경로 (기본: 현재 디렉토리)'),9 glob: z.string().optional()10 .describe('파일 필터 글로브 (예: "*.ts", "*.{ts,tsx}")'),11 output_mode: z.enum(['content', 'files_with_matches', 'count'])💡12 .optional()13 .describe('출력 모드'),14 '-B': semanticNumber(z.number().optional())15 .describe('매칭 전 N줄 표시'),16 '-A': semanticNumber(z.number().optional())17 .describe('매칭 후 N줄 표시'),18 '-C': semanticNumber(z.number().optional())19 .describe('매칭 전후 N줄 표시'),20 head_limit: semanticNumber(z.number().optional())21 .describe('결과 N개 제한 (기본: 250)'),💡22 '-i': semanticBoolean(z.boolean().optional())23 .describe('대소문자 무시'),24 multiline: semanticBoolean(z.boolean().optional())25 .describe('여러 줄 매칭'),26 })27)28 29export const GrepTool = buildTool({30 name: GREP_TOOL_NAME,31 searchHint: 'search file contents regex ripgrep',💡32 inputSchema,💡33 isConcurrencySafe: () => true, // 읽기 전용 → 병렬 안전34 isReadOnly: () => true,35 36 async checkPermissions(input, context) {37 return checkReadPermissionForTool(resolvedPath, context)38 },39 40 async call(input, context) {41 const results = await ripGrep(/* ... */)42 return { output: results }43 },44})3
도구 UI 렌더링 패턴
renderToolUseMessage, renderToolResultMessage 등 렌더링 프로토콜
호출 렌더링결과 렌더링진행률 스트리밍오류 표시|
tools/BashTool/UI.tsxtools/FileEditTool/UI.tsx각 도구의 UI.tsx는 도구 호출/결과/진행률/오류의 렌더링을 담당합니다. 이 분리 패턴으로 도구 로직과 표현이 독립적으로 변경됩니다.
도구 UI 렌더링 프로토콜 (6개 함수)
1// src/tools/BashTool/UI.tsx — BashTool 렌더링 프로토콜2 3// 1. 도구 호출 표시 (명령어 하이라이팅)4export function renderToolUseMessage(input, options): ReactNode {5 return <BashCommandDisplay command={input.command} timeout={input.timeout} />6}7 8// 2. 도구 결과 표시 (stdout/stderr 분리)9export function renderToolResultMessage(result, options): ReactNode {10 return <BashOutputDisplay stdout={result.stdout} stderr={result.stderr} />11}12 13// 3. 진행률 표시 (스트리밍 출력)14export function renderToolUseProgressMessage(progress, options): ReactNode {15 return <BashStreamingOutput lines={progress.lines} />16}17 18// 4. 오류 표시19export function renderToolUseErrorMessage(error, options): ReactNode {20 return <BashErrorDisplay error={error} />21}22 23// 5. 요약 텍스트 (컴팩트 뷰에서 사용)24export function getToolUseSummary(input): string {25 return truncate(input.command, TOOL_SUMMARY_MAX_LENGTH)26}27 28// 6. 사용자 표시 이름29export function userFacingName(input): string {30 return 'Bash'31}✅ 학습 체크리스트
이해한 항목을 체크하고 학습 진도를 확인하세요
0/3 완료0%
📋 Chapter 5 요약: 도구 구현 패턴
이 챕터에서 배운 핵심 개념을 정리합니다
1
일관된 디렉토리 구조
Tool.ts, prompt.ts, UI.tsx, constants.ts로 모든 도구가 동일한 패턴을 따릅니다.
2
읽기 전용 도구
isReadOnly: true + isConcurrencySafe: true → 안전하게 병렬 실행 가능.
3
UI 렌더링 분리
도구 로직(call)과 표현(UI.tsx)이 분리되어 독립적으로 변경 가능.
다음 챕터 미리보기: 다음 챕터에서는 가장 복잡한 도구인 BashTool(18파일, 131KB 파서)을 심층 분석합니다.