Claude가 도구를 직접 탐색하고 학습해서 실행하는 기능이 추가되었습니다. 이를 통해 실제 환경에서 동작하는 에이전트를 구축할 수 있습니다. 그 방법을 소개합니다.
AI 에이전트의 미래는 수백, 수천 개의 도구를 자유자재로 넘나드는 모델에 있습니다. git 작업, 파일 조작, 패키지 관리, 테스트 프레임워크, 배포 파이프라인을 통합하는 IDE 어시스턴트를 떠올려 보세요. 혹은 Slack, GitHub, Google Drive, Jira, 사내 데이터베이스, 수십 개의 MCP 서버에 동시에 연결되는 운영 코디네이터도 마찬가지입니다.
효과적인 에이전트를 구축하려면, 모든 도구 정의를 사전에 컨텍스트에 욱여넣지 않고도 무제한에 가까운 도구 라이브러리를 다룰 수 있어야 합니다. MCP에서 코드 실행을 활용하는 방법을 다룬 이전 블로그 글에서, 도구 결과와 정의만으로 에이전트가 요청을 읽기도 전에 50,000토큰 이상을 소비할 수 있다는 점을 살펴보았습니다. 에이전트는 도구를 필요할 때 동적으로 탐색하고 로드하여, 현재 작업에 관련된 도구만 유지해야 합니다.
또한 에이전트에게는 코드에서 직접 도구를 호출하는 능력도 필요합니다. 자연어 기반 도구 호출 방식에서는 호출 한 번마다 전체 추론 패스가 필요하고, 중간 결과가 유용하든 아니든 컨텍스트에 계속 쌓입니다. 반복문, 조건문, 데이터 변환 같은 오케스트레이션 로직에는 코드가 자연스러운 선택입니다. 에이전트는 당면한 작업에 따라 코드 실행과 추론을 유연하게 오갈 수 있어야 합니다.
마지막으로, 에이전트는 스키마 정의뿐 아니라 실제 사용 예시를 통해 올바른 도구 활용법을 학습할 수 있어야 합니다. JSON 스키마는 구조적으로 유효한 것을 정의할 뿐, 선택적 매개변수를 언제 포함해야 하는지, 어떤 조합이 적절한지, API가 기대하는 관례가 무엇인지 같은 사용 패턴까지 표현하지는 못합니다.
오늘, 이 모든 것을 가능하게 하는 세 가지 기능을 출시합니다:
내부 테스트에서 이 기능들은 기존 도구 사용 패턴으로는 불가능했던 것들을 만들 수 있게 해주었습니다. 예를 들어, Claude for Excel은 Programmatic Tool Calling을 활용하여 수천 행 규모의 스프레드시트를 모델의 컨텍스트 윈도우에 과부하를 주지 않으면서 읽고 수정합니다.
Anthropic의 경험에 비추어 볼 때, 이 기능들은 Claude로 구축할 수 있는 것의 새로운 가능성을 열어줄 것이라고 확신합니다.
MCP 도구 정의는 중요한 컨텍스트를 제공하지만, 연결되는 서버가 늘어날수록 토큰도 함께 불어납니다. 서버 5개를 연결한 경우를 살펴보겠습니다:
대화가 시작되기도 전에 도구 58개가 약 55K 토큰을 차지하는 셈입니다. 여기에 Jira(단독으로 ~17K 토큰)같은 서버를 추가하면 순식간에 100K 토큰 이상의 오버헤드에 도달합니다. Anthropic 내부에서는 최적화 전 도구 정의가 134K 토큰을 소비하는 경우도 있었습니다.
하지만 토큰 비용만이 문제는 아닙니다. 가장 흔한 실패 유형은 잘못된 도구 선택과 부정확한 매개변수 전달이며, 특히 notification-send-user과 notification-send-channel처럼 이름이 유사한 도구가 있을 때 두드러집니다.
모든 도구 정의를 사전에 로드하는 대신, Tool Search Tool은 필요할 때 도구를 동적으로 탐색합니다. Claude는 현재 작업에 실제로 필요한 도구만 확인합니다.

기존 방식:
Tool Search Tool 사용 시:
전체 도구 라이브러리에 대한 접근을 유지하면서도 토큰 사용량을 85% 줄이는 결과입니다. 내부 테스트에서는 대규모 도구 라이브러리를 다루는 MCP 평가에서 상당한 정확도 향상이 확인되었습니다. Opus 4는 49%에서 74%로, Opus 4.5는 79.5%에서 88.1%로 개선되었습니다.
Tool Search Tool은 모든 정의를 사전에 로드하는 대신, Claude가 도구를 동적으로 탐색할 수 있게 해줍니다. 모든 도구 정의를 API에 전달하되, defer_loading: true를 지정하면 해당 도구가 필요할 때만 탐색 가능하도록 설정됩니다. 지연 로드(deferred) 도구는 초기에 Claude의 컨텍스트에 로드되지 않습니다. Claude는 Tool Search Tool 자체와 defer_loading: false로 지정된 도구(가장 중요하고 자주 쓰는 도구)만 볼 수 있습니다.
Claude가 특정 기능이 필요하면, 관련 도구를 검색합니다. Tool Search Tool은 일치하는 도구의 참조를 반환하고, 이 참조가 Claude 컨텍스트에서 전체 정의로 확장됩니다.
예를 들어, Claude가 GitHub와 상호작용해야 할 경우, "github"를 검색하면 github.createPullRequest와 github.listIssues만 로드됩니다. Slack, Jira, Google Drive의 나머지 50개 이상의 도구는 로드되지 않습니다.
이렇게 하면 Claude가 전체 도구 라이브러리에 접근하면서도, 실제로 필요한 도구에 대해서만 토큰 비용을 지불하게 됩니다.
프롬프트 캐싱 참고사항: Tool Search Tool은 프롬프트 캐싱을 방해하지 않습니다. 지연 로드 도구는 초기 프롬프트에서 완전히 제외되고, Claude가 검색한 이후에만 컨텍스트에 추가되므로 시스템 프롬프트와 핵심 도구 정의는 그대로 캐싱 가능합니다.
구현 방법:
{
"tools": [
// Include a tool search tool (regex, BM25, or custom)
{"type": "tool_search_tool_regex_20251119", "name": "tool_search_tool_regex"},
// Mark tools for on-demand discovery
{
"name": "github.createPullRequest",
"description": "Create a pull request",
"input_schema": {...},
"defer_loading": true
}
// ... hundreds more deferred tools with defer_loading: true
]
}
MCP 서버의 경우, 특정 고빈도 도구는 로드된 상태로 유지하면서 서버 전체를 지연 로드할 수 있습니다:
{
"type": "mcp_toolset",
"mcp_server_name": "google-drive",
"default_config": {"defer_loading": true}, # defer loading the entire server
"configs": {
"search_files": {
"defer_loading": false
} // Keep most used tool loaded
}
}Claude 개발자 플랫폼은 정규식 기반 및 BM25 기반 검색 도구를 기본 제공하며, 임베딩이나 기타 전략을 활용한 커스텀 검색 도구를 직접 구현할 수도 있습니다.
모든 아키텍처 결정이 그렇듯, Tool Search Tool 활성화에도 트레이드오프가 따릅니다. 도구 호출 전에 검색 단계가 추가되므로, 컨텍스트 절약과 정확도 향상이 추가 지연 시간보다 클 때 가장 높은 ROI를 기대할 수 있습니다.
권장 상황:
효과가 적은 상황:
기존의 도구 호출 방식은 워크플로가 복잡해질수록 두 가지 근본적인 문제를 야기합니다:
프로그래매틱 도구 호출(Programmatic Tool Calling)을 사용하면 Claude가 개별 API 왕복 요청 대신 코드를 통해 도구를 오케스트레이션할 수 있습니다. 도구를 하나씩 요청하고 매번 결과를 컨텍스트에 반환받는 방식이 아니라, Claude가 여러 도구를 호출하고 출력을 처리하며 실제로 컨텍스트 윈도우에 들어갈 정보를 제어하는 코드를 직접 작성합니다.
Claude는 코드 작성에 뛰어납니다. 오케스트레이션 로직을 자연어 도구 호출이 아닌 Python으로 표현하게 하면, 더 안정적이고 정밀한 제어 흐름을 얻을 수 있습니다. 반복문, 조건문, 데이터 변환, 에러 처리가 Claude의 추론에 암묵적으로 의존하지 않고, 코드에 명시적으로 드러나기 때문입니다.
흔히 볼 수 있는 업무 시나리오를 생각해 보겠습니다. "3분기 출장 예산을 초과한 팀원이 누구인가?"
사용 가능한 도구는 세 가지입니다:
get_team_members(department) - 팀원 목록과 ID, 직급을 반환get_expenses(user_id, quarter) - 특정 사용자의 경비 항목을 반환get_budget_by_level(level) - 직급별 예산 한도를 반환기존 방식:
프로그래매틱 도구 호출 방식:
각 도구의 결과가 Claude에게 반환되는 대신, Claude가 전체 워크플로를 오케스트레이션하는 Python 스크립트를 작성합니다. 이 스크립트는 코드 실행 도구(샌드박스 환경)에서 실행되며, 외부 도구의 결과가 필요한 시점에 일시 중단됩니다. API를 통해 도구 결과를 반환하면 모델이 아닌 스크립트가 이를 처리하고, 스크립트가 계속 실행됩니다. Claude는 최종 출력만 확인합니다.

예산 준수 확인 작업에서 Claude가 작성하는 오케스트레이션 코드는 다음과 같습니다:
team = await get_team_members("engineering")
# Fetch budgets for each unique level
levels = list(set(m["level"] for m in team))
budget_results = await asyncio.gather(*[
get_budget_by_level(level) for level in levels
])
# Create a lookup dictionary: {"junior": budget1, "senior": budget2, ...}
budgets = {level: budget for level, budget in zip(levels, budget_results)}
# Fetch all expenses in parallel
expenses = await asyncio.gather(*[
get_expenses(m["id"], "Q3") for m in team
])
# Find employees who exceeded their travel budget
exceeded = []
for member, exp in zip(team, expenses):
budget = budgets[member["level"]]
total = sum(e["amount"] for e in exp)
if total > budget["travel_limit"]:
exceeded.append({
"name": member["name"],
"spent": total,
"limit": budget["travel_limit"]
})
print(json.dumps(exceeded))Claude의 컨텍스트에는 최종 결과, 즉 예산을 초과한 2~3명의 정보만 전달됩니다. 2,000개 이상의 경비 항목, 중간 합산 결과, 예산 조회 데이터는 Claude의 컨텍스트에 영향을 주지 않으므로, 200KB에 달하던 원시 경비 데이터가 단 1KB의 결과로 줄어듭니다.
효율성 개선 효과는 상당합니다:
실제 프로덕션 워크플로는 지저분한 데이터, 조건부 로직, 확장이 필요한 연산으로 가득합니다. 프로그래매틱 도구 호출을 활용하면 Claude가 이러한 복잡성을 프로그래밍 방식으로 처리하면서, 원시 데이터 가공이 아닌 실행 가능한 결과에 집중할 수 있습니다.
도구 목록에 code_execution을 추가하고, allowed_callers를 설정하여 프로그래밍 방식 실행에 사용할 도구를 지정합니다:
{
"tools": [
{
"type": "code_execution_20250825",
"name": "code_execution"
},
{
"name": "get_team_members",
"description": "Get all members of a department...",
"input_schema": {...},
"allowed_callers": ["code_execution_20250825"] # opt-in to programmatic tool calling
},
{
"name": "get_expenses",
...
},
{
"name": "get_budget_by_level",
...
}
]
}API가 이 도구 정의를 Claude가 호출할 수 있는 Python 함수로 변환합니다.
도구를 하나씩 요청하는 대신, Claude가 Python 코드를 생성합니다:
{
"type": "server_tool_use",
"id": "srvtoolu_abc",
"name": "code_execution",
"input": {
"code": "team = get_team_members('engineering')\n..." # the code example above
}
}코드에서 get_expenses()를 호출하면, caller 필드가 포함된 도구 요청을 받게 됩니다:
{
"type": "tool_use",
"id": "toolu_xyz",
"name": "get_expenses",
"input": {"user_id": "emp_123", "quarter": "Q3"},
"caller": {
"type": "code_execution_20250825",
"tool_id": "srvtoolu_abc"
}
}사용자가 결과를 제공하면, 이 결과는 Claude의 컨텍스트가 아닌 코드 실행 환경에서 처리됩니다. 이러한 요청-응답 사이클은 코드 내 각 도구 호출마다 반복됩니다.
코드 실행이 완료되면, 코드의 결과만 Claude에 반환됩니다:
{
"type": "code_execution_tool_result",
"tool_use_id": "srvtoolu_abc",
"content": {
"stdout": "[{\"name\": \"Alice\", \"spent\": 12500, \"limit\": 10000}...]"
}
}Claude가 보는 것은 이 결과뿐이며, 중간에 처리된 2,000건 이상의 경비 항목은 전혀 노출되지 않습니다.
프로그래밍 방식 도구 호출(Programmatic Tool Calling)은 워크플로에 코드 실행 단계를 추가합니다. 이 추가적인 오버헤드는 토큰 절감, 지연 시간 개선, 정확도 향상 폭이 클 때 충분히 그 값어치를 합니다.
효과가 큰 경우:
효과가 적은 경우:
JSON Schema는 타입, 필수 필드, 허용된 enum 등 구조를 정의하는 데는 탁월하지만, 사용 패턴은 표현할 수 없습니다. 선택적 매개변수를 언제 포함해야 하는지, 어떤 조합이 적절한지, API가 기대하는 규칙은 무엇인지 등은 스키마만으로는 알 수 없습니다.
지원 티켓 API를 예로 들어 보겠습니다:
{
"name": "create_ticket",
"input_schema": {
"properties": {
"title": {"type": "string"},
"priority": {"enum": ["low", "medium", "high", "critical"]},
"labels": {"type": "array", "items": {"type": "string"}},
"reporter": {
"type": "object",
"properties": {
"id": {"type": "string"},
"name": {"type": "string"},
"contact": {
"type": "object",
"properties": {
"email": {"type": "string"},
"phone": {"type": "string"}
}
}
}
},
"due_date": {"type": "string"},
"escalation": {
"type": "object",
"properties": {
"level": {"type": "integer"},
"notify_manager": {"type": "boolean"},
"sla_hours": {"type": "integer"}
}
}
},
"required": ["title"]
}
}스키마는 유효한 값이 무엇인지 정의하지만, 다음과 같은 핵심적인 질문에는 답하지 못합니다:
due_date의 형식이 "2024-11-06", "Nov 6, 2024", "2024-11-06T00:00:00Z" 중 어느 것이어야 할까요?reporter.id는 UUID인가요, "USR-12345" 형식인가요, 아니면 단순히 "12345"인가요?reporter.contact를 언제 채워야 할까요?escalation.level와 escalation.sla_hours는 우선순위와 어떤 관계가 있을까요?이러한 모호성은 잘못된 도구 호출과 일관되지 않은 매개변수 사용으로 이어질 수 있습니다.
도구 사용 예시(Tool Use Examples)를 활용하면 도구 정의에 샘플 호출을 직접 포함할 수 있습니다. 스키마에만 의존하는 대신, 구체적인 사용 패턴을 Claude에 직접 보여주는 방식입니다:
{
"name": "create_ticket",
"input_schema": { /* same schema as above */ },
"input_examples": [
{
"title": "Login page returns 500 error",
"priority": "critical",
"labels": ["bug", "authentication", "production"],
"reporter": {
"id": "USR-12345",
"name": "Jane Smith",
"contact": {
"email": "[email protected]",
"phone": "+1-555-0123"
}
},
"due_date": "2024-11-06",
"escalation": {
"level": 2,
"notify_manager": true,
"sla_hours": 4
}
},
{
"title": "Add dark mode support",
"labels": ["feature-request", "ui"],
"reporter": {
"id": "USR-67890",
"name": "Alex Chen"
}
},
{
"title": "Update API documentation"
}
]
}이 세 가지 예시만으로 Claude는 다음을 학습합니다:
내부 테스트 결과, 도구 사용 예시를 적용하자 복잡한 매개변수 처리의 정확도가 72%에서 90%로 향상되었습니다.
도구 사용 예시는 도구 정의에 토큰을 추가하므로, 정확도 향상이 추가 비용을 상쇄할 만큼 클 때 가장 효과적입니다.
효과가 큰 경우:
create_ticket vs create_incident)효과가 적은 경우:
실제 환경에서 동작하는 에이전트를 구축하려면 규모, 복잡성, 정밀도를 동시에 다룰 수 있어야 합니다. 앞서 소개한 세 가지 기능은 도구 사용 워크플로의 서로 다른 병목 지점을 해결하도록 설계되었으며, 함께 사용할 때 시너지를 발휘합니다. 효과적으로 조합하는 방법을 알아보겠습니다.
모든 에이전트가 하나의 작업에 세 가지 기능을 전부 사용할 필요는 없습니다. 가장 큰 병목 지점부터 해결하세요:
이렇게 집중적으로 접근하면, 처음부터 불필요한 복잡성을 추가하지 않고도 에이전트 성능을 제약하는 핵심 병목을 정확히 해소할 수 있습니다.
이후 필요에 따라 기능을 추가로 결합하세요. 각 기능은 상호 보완적입니다. Tool Search Tool은 적절한 도구를 찾아주고, Programmatic Tool Calling은 효율적인 실행을 보장하며, Tool Use Examples는 정확한 호출을 돕습니다.
도구 검색은 이름과 설명을 기반으로 매칭되므로, 명확하고 서술적인 도구 정의를 작성할수록 검색 정확도가 높아집니다.
// Good
{
"name": "search_customer_orders",
"description": "Search for customer orders by date range, status, or total amount. Returns order details including items, shipping, and payment info."
}
// Bad
{
"name": "query_db_orders",
"description": "Execute order query"
}시스템 프롬프트에 안내를 추가하여 Claude가 사용 가능한 도구를 인지하도록 설정하세요:
You have access to tools for Slack messaging, Google Drive file management,
Jira ticket tracking, and GitHub repository operations. Use the tool search
to find specific capabilities.가장 자주 사용하는 도구 3~5개는 항상 로드해 두고, 나머지는 필요할 때 검색하도록 구성하세요. 이렇게 하면 일상적인 작업에는 즉시 접근하면서도 나머지 도구는 온디맨드로 탐색하는 균형 잡힌 구조를 만들 수 있습니다.
Claude가 도구 출력을 파싱하는 코드를 직접 작성하므로, 반환 형식을 명확하게 문서화해야 합니다. 그래야 Claude가 정확한 파싱 로직을 작성할 수 있습니다:
{
"name": "get_orders",
"description": "Retrieve orders for a customer.
Returns:
List of order objects, each containing:
- id (str): Order identifier
- total (float): Order total in USD
- status (str): One of 'pending', 'shipped', 'delivered'
- items (list): Array of {sku, quantity, price}
- created_at (str): ISO 8601 timestamp"
}프로그래밍 방식의 오케스트레이션에 적합한 도구 유형은 다음과 같습니다:
행동 의도를 명확히 전달하는 예시를 작성하세요:
이 기능들은 현재 베타로 제공됩니다. 사용하려면 베타 헤더를 추가하고 필요한 도구를 포함하세요:
client.beta.messages.create(
betas=["advanced-tool-use-2025-11-20"],
model="claude-sonnet-4-5-20250929",
max_tokens=4096,
tools=[
{"type": "tool_search_tool_regex_20251119", "name": "tool_search_tool_regex"},
{"type": "code_execution_20250825", "name": "code_execution"},
# Your tools with defer_loading, allowed_callers, and input_examples
]
)자세한 API 문서와 SDK 예제는 아래를 참고하세요:
이 기능들은 도구 사용을 단순한 함수 호출에서 지능적인 오케스트레이션으로 끌어올립니다. 에이전트가 수십 개의 도구와 대규모 데이터셋에 걸친 복잡한 워크플로를 처리하게 되면, 동적 탐색, 효율적 실행, 안정적 호출이 핵심 기반이 됩니다.
여러분이 어떤 것을 만들어 낼지 기대됩니다.
Bin Wu가 작성했으며, Adam Jones, Artur Renault, Henry Tay, Jake Noble, Nathan McCandlish, Noah Picard, Sam Jiang, 그리고 Claude Developer Platform 팀이 함께 기여했습니다. 이 글은 Chris Gorgolewski, Daniel Jiang, Jeremy Fox, Mike Lambert의 기초 연구를 바탕으로 하고 있습니다. 또한 Joel Pobar의 LLMVM, Cloudflare의 Code Mode, Code Execution as MCP 등 AI 생태계 전반의 다양한 프로젝트에서 영감을 받았습니다. 아낌없는 도움을 준 Andy Schumeister, Hamish Kerr, Keir Bradwell, Matt Bleifer, Molly Vorwerck에게 감사드립니다.