-
Claude에 도구 등록하는 방법 — input_schema 설계부터 defer_loading까지IT 2026. 6. 16. 22:00
지난 글에서 deferred loading으로 도구 100개를 컨텍스트 밖으로 밀어낼 수 있다고 했다. 이 글은 그 전 단계, 도구를 어떻게 정의하고 등록하는가를 다룬다. 도구 등록의 본질은 JSON 스키마다 — 세 가지 필수 필드가 각각 무엇을 결정하는지,
input_schema를 어떻게 설계해야 LLM이 파라미터를 혼동하지 않는지,description이 왜 단순한 메모가 아니라 BM25 검색 인덱스이기도 한지를 다이어그램으로 해부한다.도구 호출은 어떻게 작동하는가
LLM이 도구를 "직접 실행"한다는 인상이 있지만, 실제로는 다르다. LLM은 도구를 어떤 파라미터로 불러야 하는지를 JSON 형태로 출력할 뿐이다. 그 JSON을 받아 실제로 실행하는 것은 LLM 밖의 코드다. 결과가 다시 LLM에게 전달되면, LLM이 그 결과를 보고 최종 자연어 응답을 만든다.
다이어그램 설명. LLM은 두 번의 API 호출로 응답을 완성한다. 첫 번째 호출에서
tool_use블록을 출력하고, 두 번째 호출에서 실행 결과(tool_result)를 받아 자연어 응답을 생성한다. LLM이 직접 코드를 실행하는 게 아니라 항상 외부 실행기를 경유한다 — 이 구조 덕분에 도구 실행을 완전히 통제할 수 있다.도구 정의 객체 구조 — 세 가지 필수 필드
도구 하나는 JSON 객체 하나다. 세 가지 필수 필드가 있다.
다이어그램 설명.
name은 식별자이므로 영문 소문자와 밑줄로 구성한다.description은 LLM이 "이 도구가 지금 필요한가"를 판단할 때 읽는 문장이다 — 단순한 주석이 아니다.input_schema는 LLM이 호출 시 파라미터를 어떻게 채울지 알려주는 계약서다.MCP 서버가 Claude Code의
tools/list요청에 응답하는 JSON-RPC 메시지는 이렇다.{ "jsonrpc": "2.0", "id": 1, "result": { "tools": [ { "name": "sdk_build_app", "description": "SDK로 앱을 빌드한다. 디바이스 또는 에뮬레이터 배포용 패키지를 생성할 때 사용. arch(arm64/x86)와 configuration(Debug/Release)을 받는다.", "inputSchema": { "type": "object", "properties": { "project_dir": { "type": "string", "description": "빌드할 프로젝트 루트 경로 (절대경로)" }, "arch": { "type": "string", "enum": ["arm64", "x86"], "description": "타겟 아키텍처" }, "configuration": { "type": "string", "enum": ["Debug", "Release"], "description": "빌드 설정. 릴리스 배포라면 Release" } }, "required": ["project_dir"] } } ] } }JSON 설명.
jsonrpc: "2.0"·id·result는 JSON-RPC 2.0(클라이언트-서버 간 요청·응답을 JSON으로 주고받는 표준 프로토콜) 봉투(envelope)다. 도구 정의 자체는result.tools배열 안에 들어간다.inputSchema는 camelCase — MCP 프로토콜 표기법이다(직접 Anthropic API의input_schemasnake_case와 다르니 주의).enum으로 허용 값을 명시하면 LLM이 임의 변형을 쓰지 않고,required에 없는 파라미터는 LLM이 생략해도 도구가 호출된다.input_schema 설계 원칙
input_schema는 JSON Schema(데이터 구조의 형식과 제약을 JSON으로 기술하는 국제 표준) 형식을 따른다. 다섯 가지 원칙이 있다.
다이어그램 설명. 가장 효과가 큰 것은 ③번
enum이다. LLM은arch를 물어보면 "arm64인가 aarch64인가 ARM64인가"를 고민한다.enum: ["arm64", "x86"]을 보는 순간 고민이 사라진다. ④번도 중요하다 — "경로"라고만 쓰면 LLM이 상대경로를 쓸지 절대경로를 쓸지 모른다. "절대경로, 예: /home/user/project"라고 쓰면 틀릴 여지가 없다.파라미터 타입별로 자주 쓰는 패턴을 비교해 보면 이렇다.
다이어그램 설명. 가장 흔한 실수는
boolean파라미터에 설명을 안 쓰는 것이다."force": {"type": "boolean"}만 있으면 LLM은 True가 무엇을 강제하는지 모른다."True면 캐시를 무시하고 재빌드, False면 변경된 파일만 빌드"처럼 각 값의 효과를 명시해야 LLM이 상황에 맞게 선택한다.description 작성법 — LLM 선택 정확도를 결정하는 숨은 변수
description은 두 가지 독자를 동시에 위한 글이다. 하나는 사람(코드 리뷰어, 미래의 나), 다른 하나는 LLM이다. LLM은 description을 읽고 "지금 이 요청에 이 도구가 맞는가"를 판단한다. 그리고 도구가 deferred loading 상태라면, BM25 검색 엔진(키워드 기반 검색 알고리즘)이 이 텍스트에서 키워드를 뽑아 인덱싱한다. description이 곧 검색 인덱스다.
나쁜 예와 좋은 예를 비교해 보자.
▲ 나쁜 예 — 동사 하나짜리 description
▲ 좋은 예 — 서비스 접두사 + 동작 + 사용 맥락
다이어그램 설명. 좋은 description의 구성은 세 층이다. ① 무엇을 하나 ("앱을 빌드한다"), ② 언제 쓰나 ("디바이스 배포용 패키지 생성", "디바이스 없이 테스트할 때"), ③ 어떤 입력을 받나 ("arch·configuration 지정 가능"). ②번이 가장 중요하다 — LLM이 "지금 이게 맞는 상황인가"를 ②번 문장으로 판단하기 때문이다. 서비스 접두사(
sdk_)는 여러 MCP 서버가 붙을 때 이름 충돌을 막고 BM25 검색 범위를 좁혀 주는 역할도 한다.MCP 서버를 통한 등록 — 도구를 외부 프로세스로 분리
도구를 API 호출 코드 안에 하드코딩하는 방식은 도구가 10개 이하일 때는 괜찮다. 그 이상이 되거나, 도구 로직이 별도 서비스·언어·환경에 있다면 MCP(Model Context Protocol, AI 모델과 외부 도구를 잇기 위한 표준 규약) 서버가 맞다. MCP 서버는 독립 프로세스로 실행되면서 Claude Code나 API가 JSON-RPC로 질의하면 도구 목록과 실행 결과를 반환한다.
Claude Code에서 MCP 서버를 등록하는 방법은
~/.claude/settings.json에 한 블록을 추가하는 것이다. Claude Code는 이 설정을 읽어 MCP 서버에 연결하고, JSON-RPC로 도구 목록(tools/list)을 받아온다.deferred loading — JSON-RPC 어디에도 없는 이유
tools/list응답 JSON을 아무리 봐도defer_loading이 없다. 이유는 단순하다 — MCP JSON-RPC 스펙에 이 필드가 없기 때문이다. deferred loading은 MCP 서버가 아니라 클라이언트(Claude Code)가 결정한다. MCP 서버는 도구를 나열하기만 하고, Claude Code가settings.json의ENABLE_TOOL_SEARCH를 보고 deferred loading 여부를 자동으로 적용한다.다이어그램 설명. MCP 서버는 도구를 그냥 나열하기만 한다. Claude Code가
ENABLE_TOOL_SEARCH: "auto"를 보고 도구 정의 합계가 컨텍스트 창의 10%를 넘으면 자동으로 deferred loading으로 전환하고, Tool Search Tool을 컨텍스트에 추가한다.alwaysLoad: true로 지정된 도구만 항상 컨텍스트에 올라가고, 나머지는 Tool Search Tool이 JIT 로드한다.deferred loading 전략은
settings.json한 곳에서 모두 제어한다.{ "mcpServers": { "my-dev-tools": { "command": "node", "args": ["~/mcp-server/index.js"], "env": { "ENABLE_TOOL_SEARCH": "auto" } } }, "tools": [ { "name": "read_file", "alwaysLoad": true }, { "name": "list_files", "alwaysLoad": true } ] }ENABLE_TOOL_SEARCH는 세 가지 값을 받는다."auto"는 도구 정의 합계가 컨텍스트 창의 10%를 넘을 때 자동으로 deferred loading으로 전환한다(권장)."always"는 도구 수와 무관하게 항상 적용하고,"never"는 디버깅용으로 항상 전부 로드한다.alwaysLoad의 판단 기준은 하나다 — 거의 모든 요청에서 쓰이는 도구인가. 파일 읽기처럼 매 요청마다 필요한 것은true로, 특정 작업에서만 쓰는 것은 deferred로 두면 된다. Tool Search Tool은ENABLE_TOOL_SEARCH설정 시 Claude Code가 자동으로 추가한다.전체 등록 플로우 한눈에 보기
다이어그램 설명. ①~③은 도구 자체의 설계와 MCP 서버 등록, ④~⑤는 settings.json에서 deferred loading 전략 설정이다. MCP 서버에서는
defer_loading을 쓰지 않고, Claude Code가 settings.json을 읽어 자동으로 처리한다. 실전에서는 ⑥에서 LLM이 엉뚱한 도구를 선택하면 ①로 돌아가description과name을 다듬는다.도구 설계가 정확도를 결정한다
Anthropic이 공개한 수치에서 Claude Opus 4 기준 도구 선택 정확도가 Flat Loading(도구 전부 노출)에서 49%였다가 Deferred Loading으로 74%로 올랐다. 이 수치는 "deferred loading을 켜면 자동으로" 달성되는 게 아니다. BM25 검색이 맞는 도구를 찾아 올리려면, 그 도구의 이름과 description에 LLM이 검색할 만한 키워드가 실제로 있어야 한다. 도구 정의가 곧 검색 가능성이고, 검색 가능성이 곧 정확도다.
도구를 등록하는 것은 API 호출 하나가 아니라, LLM이 도구를 찾고·선택하고·올바르게 호출하는 전체 경로를 설계하는 일이다.
이 글은 생성형 AI의 도움을 받아 작성되었습니다. 원본 자료를 기반으로 AI가 초안을 생성하고, 작성자가 검토·편집하였습니다.
'IT' 카테고리의 다른 글
서브에이전트 제어 필드 4종 완전 해부 — permissionMode · memory · isolation · background (0) 2026.06.18 서브에이전트 정의 파일(.md) 완전 해부 — frontmatter 8개 필드와 시스템 프롬프트 (0) 2026.06.17 Claude Code 서브에이전트의 파일 구성 전체 지도 — 단순 .md부터 플러그인까지 (0) 2026.06.17 Claude Code에서 /agents로 서브에이전트를 만드는 가장 쉬운 방법 (0) 2026.06.17 BM25 — AI가 도구 100개 중 3개를 정확히 찾아내는 방법 (0) 2026.06.16 도구 100개, LLM에게 필요할 때만 꺼내는 법 — Anthropic Deferred Loading 해부 (0) 2026.06.16 MCP 서버는 원격에 둬야 할까, 로컬에 둬야 할까 — 전송 방식이 결정되는 한 줄 (0) 2026.06.15 GUI 전용 도구를 코딩 어시스턴트에 태우는 3단계 사다리 (1) 2026.06.15 MCP sampling/createMessage: AI 도구가 AI를 부르는 역방향 설계 (0) 2026.06.14 MCP Roots 완전 분해: 서버가 클라이언트에게 먼저 묻는 역방향 설계 (0) 2026.06.13