
Claude Code에서 MCP 서버를 여러 개 연결할 때 발생하는 '미로드 툴(Unloaded Tool)' 문제 — 지연 로드(Tool
요약
Claude Code에서 여러 MCP 서버를 연결할 때 발생하는 '미로드 툴(Unloaded Tool)' 문제와 그 해결책인 지연 로드(Lazy Load) 방식을 설명합니다. 툴 스키마가 시스템 프롬프트를 압박하는 것을 방지하기 위해 이름만 먼저 노출하고 필요할 때 스키마를 로드하는 메커니즘을 다룹니다.
핵심 포인트
- MCP 서버 다수 연결 시 툴 스키마가 컨텍스트를 과도하게 점유함
- Claude Code는 'ToolSearch'를 통한 지연 로드 방식을 채택함
- 스키마 미로드 상태에서 툴 호출 시 InputValidationError 발생
- 에이전트 설계 시 툴 이름 목록과 풀 스키마를 분리하는 설계가 중요함
Claude Code나 직접 만든 AI 에이전트에서 MCP (Model Context Protocol) 서버를 여러 개 동시에 연결하고 있으면, 어느 날 갑자기 "방금 전까지 사용 가능했던 툴을 호출할 수 없다"는 현상에 부딪힐 때가 있다.
이 기사는 다음을 전제로 한다.
- Claude Code나 MCP 대응 AI 에이전트를 평소에 사용하고 있다
- Notion / GitHub / 브라우저 조작 (Playwright) 등, 여러 개의 MCP 서버를 동시에 연결하고 있거나 연결하려고 한다
- "툴의 수가 늘어나면 컨텍스트를 압박한다"는 문제에 한 번이라도 직면했거나, 앞으로 직면할 것 같다
검증 환경: Claude Code (2026년 7월 시점의 최신 버전), Python 3.13.
- MCP 서버를 여러 개 연결하면, 툴 정의 (JSON Schema)의 합계 사이즈가 시스템 프롬프트 (System Prompt)를 압박하여 컨텍스트를 낭비한다
- 해결책은 "이름만 먼저 내보내기 → 사용 직전에 검색하여 스키마를 해결하는" 지연 로드 (Lazy Load) 방식
- 미로드된 툴을 갑자기 호출하면
InputValidationError계열의 에러로 중단된다. 검색 후 호출하는 2단계 과정을 철저히 지키면 방지할 수 있다. - 직접 만든 에이전트에 동일한 메커니즘을 넣을 경우, 캐시와 검색 쿼리의 입도 (Granularity) 설계에서 실수하기 쉽다
- 미로드된 툴을 갑자기 호출하면
MCP 서버를 5~6개 (Notion, Playwright, Figma, Gmail, Calendar, Drive 등) 동시에 연결한 상태에서 툴 목록을 보면 다음과 같은 표시가 나타난다.
The following deferred tools are now available via ToolSearch.
Their schemas are NOT loaded — calling them directly will fail with InputValidationError.
Use ToolSearch with query "select:<name>[,<name>...]" to load tool schemas before calling them:
...
여기서 툴 이름만 보고 스키마를 가져오지 않은 채 갑자기 호출하면 실패한다.
InputValidationError: tool "mcp__notion__API-query-data-source" schema not loaded
이것은 "고장 난" 것이 아니라 사양이며, 툴 이름 목록 (수백 바이트)과 풀 스키마 (Full Schema, 개당 수백~수천 토큰)를 분리함으로써 시스템 프롬프트의 비대화를 방지하고 있다. MCP 서버를 5개만 연결해도 툴 수는 100개를 훌쩍 넘으며, 전부 풀 스키마로 전달하면 수만 토큰 단위로 컨텍스트를 소비하게 된다.
Claude Code 측의 방식은 간단하다. 먼저 검색 툴로 스키마를 로드한 다음 본체를 호출한다.
1. ToolSearch(query="select:mcp__notion__API-query-data-source,mcp__notion__API-post-page")
→ 해당 툴의 풀 스키마가 대화에 전개됨
2. mcp__notion__API-query-data-source(...) 를 실제로 호출
자작 에이전트 기반에서 동일한 패턴을 구현한다면, Python으로 작성했을 때 다음과 같은 골격이 된다.
class LazyToolRegistry:
def __init__(self, all_tools: dict[str, dict]):
# all_tools: {tool_name: full_json_schema}
...
stub_list()는 툴 이름만 반환하므로 컨텍스트에 거의 영향을 주지 않는다. search()를 거치지 않으면 call()이 예외를 발생시킨다는 제약을 의도적으로 넣음으로써 "검색 없이 호출하는" 경로를 차단한다.
100개가 넘는 툴을 가진 구성에서 테스트해 본 결과, 모든 툴을 풀 스키마로 전달했을 때와 이름만 + 지연 로드 방식을 사용했을 때를 비교하면 초기 프롬프트의 토큰 수가 대략 1/10 ~ 1/20까지 줄어든다 (툴의 복잡도에 따라 다름). 내 환경에서도 MCP 서버 6개 구성에서 이 방식으로 전환한 이후, 세션 시작 직후의 컨텍스트 소비가 체감상 크게 줄었다.
search("notion")
ような 느슨한 쿼리(query)를 사용하면 Notion 관련 도구가 20개나 검색되어, 결국 전체 스키마(full schema)를 대량으로 로드하게 되어 본말전도되는 상황이 발생한다. select:<정확한 도구 이름>
과 같은 완전 일치 검색(exact match search)을 우선시하고, 키워드 검색은 "도구 이름을 모를 때 전용"으로 한정하면 불필요한 로드를 피할 수 있다.
세션 내에서 동일한 도구를 여러 번 호출하는 경우, 매번 search()를 실행하면 왕복 비용(round-trip cost)이 쌓이게 된다. _loaded에 한 번 들어간 도구는 다시 검색하지 않는다는 캐시(cache) 로직을 넣지 않으면, 의미 없는 검색 단계가 계속 늘어나게 된다.
비대화형(headless) 환경에서 구동 중일 때는 인증 대기 중인 MCP 서버의 도구를 지연 로드(lazy load)하더라도 영원히 호출할 수 없다.
The following MCP servers require authentication before their tools can be used:
<server name>
This session is non-interactive, so the OAuth flow cannot run here.
이는 지연 로드의 버그가 아니라 별개의 문제이므로, "도구를 호출할 수 없다"는 에러가 발생하면 우선 InputValidationError (미로드)인지 인증 에러인지 구분하는 것이 첫 번째 단계가 된다.
LLM의 시스템 프롬프트(system prompt)는 도구 정의를 포함하여 매 요청마다 전송되는 비용이 큰 영역이다. 도구 수가 적을 때는 신경 쓰이지 않지만, 실용적인 MCP 서버들을 조합하기 시작하면 순식간에 수십~수백 개의 도구로 늘어나며, 전체 스키마를 매번 전부 포함하는 설계에서는 본론에 사용할 수 있는 컨텍스트(context)가 눈에 띄게 줄어든다. 이름만 먼저 노출하고 온디맨드 해결(on-demand resolution)하는 방식은, 도구 수의 스케일에 관계없이 시스템 프롬프트의 크기를 거의 일정하게 유지하기 위한 구현 패턴으로서 매우 합리적이다.
- MCP 서버를 여러 개 연결하면 도구 정의의 총합 크기가 컨텍스트를 압박한다.
- "이름만 먼저 노출 → 사용 시점에 검색하여 스키마 로드"의 2단계 방식을 사용하면 해결된다.
- 미로드된 도구를 갑자기 호출하면
InputValidationError가 발생한다. 검색 단계를 거치는 것을 철저히 한다. - 검색 쿼리는 완전 일치 검색을 우선하며, 이미 로드된 도구는 캐시한다.
- OAuth 대기가 필요한 도구는 지연 로드와는 별개의 문제로 구분한다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Qiita AI의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기