도구 검색 도구

도구 검색 도구 (Tool search tool)

도구 검색 도구는 Claude가 도구들을 발견하고 필요할 때 로드해서 수백·수천 개의 도구로 작업할 수 있게 해 줘요. 모든 도구 정의를 컨텍스트 창에 미리 로드하는 대신, Claude가 도구 카탈로그(도구 이름, 설명, 인자 이름, 인자 설명 포함)를 검색해 필요한 도구만 로드해요. 도구 라이브러리가 커질 때 컨텍스트 부풀림과 도구 선택 정확도 문제를 해결해 주는 기능이에요.

출처: 문서

본문

도구 검색 도구는 Claude가 수백·수천 개의 도구로 작업할 수 있게 해 줘요. 발견하고 필요한 도구만 로드하는 방식으로요. 모든 도구 정의를 컨텍스트 창에 미리 로드하는 대신, Claude가 도구 카탈로그(도구 이름, 설명, 인자 이름, 인자 설명 포함)를 검색해 필요한 도구만 로드해요.

모든 도구 정의를 미리 로드하면 도구 라이브러리가 커질수록 두 가지 문제가 생겨요:

  • 컨텍스트 부풀림: 전형적인 멀티서버 설정(GitHub, Slack, Sentry, Grafana, Splunk)은 Claude가 아무 일도 하기 전에 정의에 약 55k 토큰을 쓸 수 있어요. 도구 검색은 보통 이를 85% 이상 줄여, 주어진 요청에 Claude가 필요한 3~5개 도구만 로드해요.
  • 도구 선택 정확도: 사용 가능한 도구가 30~50개를 넘으면 Claude가 올바른 도구를 고르는 능력이 떨어져요. 도구 검색은 관련 도구의 초점 맞춘 집합만 필요할 때 로드하므로, 수천 개의 도구에서도 선택 정확도가 높게 유지돼요.

도구 검색을 지원하는 모델은 모델 호환성을 참고하세요.

도구 검색이 해결하는 확장 문제의 배경은 [고급 도구 사용](https://www.anthropic.com/engineering/advanced-tool-use)을 참고하세요. 도구 검색의 주문형 로딩은 [효과적인 컨텍스트 엔지니어링](https://www.anthropic.com/engineering/effective-context-engineering-for-ai-agents)에서 설명하는 더 넓은 적시 검색 원칙의 한 예이기도 해요.

도구 검색은 서버 쪽 도구로 실행되지만, 우리만의 클라이언트 쪽 도구 검색도 구현할 수 있어요. 자세한 내용은 커스텀 도구 검색 구현을 참고하세요.

이 기능에 대한 의견은 [피드백 양식](https://forms.gle/MhcGFFwLxuwnWTkYA)을 통해 보내 주세요. 이 기능에 Zero Data Retention(ZDR)이 어떻게 적용되는지 알아보려면 [API와 데이터 보존](https://platform.claude.com/docs/en/manage-claude/api-and-data-retention) 문서를 참고하세요. Amazon Bedrock에서는 서버 쪽 도구 검색이 Converse API가 아니라 [InvokeModel API](https://docs.aws.amazon.com/bedrock/latest/userguide/bedrock-runtime_example_bedrock-runtime_InvokeModel_AnthropicClaude_section.html)를 통해서만 사용할 수 있어요. [AWS의 Claude Platform](https://platform.claude.com/docs/en/build-with-claude/claude-platform-on-aws)에서는 서버 쪽 도구 검색이 Claude API와 똑같이 동작해요. AWS의 Claude Platform은 Anthropic Messages API를 직접 사용하므로 InvokeModel이나 Converse 구분이 없어요.

모델 호환성 (Model compatibility)

두 도구 검색 변형 모두 다음 모델에서 사용할 수 있어요:

Model Tool versions
Claude Fable 5.1 (claude-fable-5-1) tool_search_tool_regex_20251119, tool_search_tool_bm25_20251119
Claude Mythos 5.1 (claude-mythos-5-1) tool_search_tool_regex_20251119, tool_search_tool_bm25_20251119
Claude Fable 5 (claude-fable-5) tool_search_tool_regex_20251119, tool_search_tool_bm25_20251119
Claude Mythos 5 (claude-mythos-5) tool_search_tool_regex_20251119, tool_search_tool_bm25_20251119
Claude Opus 5.5 (claude-opus-5-5) tool_search_tool_regex_20251119, tool_search_tool_bm25_20251119
Claude Opus 5 (claude-opus-5) tool_search_tool_regex_20251119, tool_search_tool_bm25_20251119
Claude Opus 4.8 (claude-opus-4-8) tool_search_tool_regex_20251119, tool_search_tool_bm25_20251119
Claude Opus 4.7 (claude-opus-4-7) tool_search_tool_regex_20251119, tool_search_tool_bm25_20251119
Claude Opus 4.6 (claude-opus-4-6) tool_search_tool_regex_20251119, tool_search_tool_bm25_20251119
Claude Sonnet 4.6 (claude-sonnet-4-6) tool_search_tool_regex_20251119, tool_search_tool_bm25_20251119
Claude Opus 4.5 (claude-opus-4-5-20251101) tool_search_tool_regex_20251119, tool_search_tool_bm25_20251119
Claude Sonnet 4.5 (claude-sonnet-4-5-20250929) tool_search_tool_regex_20251119, tool_search_tool_bm25_20251119
Claude Haiku 4.5 (claude-haiku-4-5-20251001) tool_search_tool_regex_20251119, tool_search_tool_bm25_20251119

Claude Opus 4.1 이하 모델은 도구 검색 도구를 지원하지 않아요.

도구 검색의 동작 방식 (How tool search works)

두 가지 도구 검색 변형이 있어요:

  • Regex (tool_search_tool_regex_20251119): Claude가 도구를 검색하기 위해 정규식 패턴을 구성해요.
  • BM25 (tool_search_tool_bm25_20251119): Claude가 도구를 검색하기 위해 자연어 쿼리를 사용해요.

도구 검색 도구를 활성화하면:

  1. tools 목록에 도구 검색 도구(예: tool_search_tool_regex_20251119 또는 tool_search_tool_bm25_20251119)를 포함해요.
  2. tools 배열에 모든 도구 정의를 제공하고, 미리 로드하지 말아야 할 도구에는 defer_loading: true를 설정해요. 보통 도구 검색 도구 자신인 최소 하나의 도구는 지연되지 않아야 해요.
  3. 처음에는 Claude의 컨텍스트에 도구 검색 도구와 지연되지 않은 도구만 들어 있어요.
  4. Claude가 추가 도구가 필요하면 도구 검색 도구로 검색해요.
  5. API가 검색을 실행하고 일치하는 도구를 tool_reference 블록으로 반환해요(기본 최대 5개, Claude는 검색 입력에 limit를 설정할 수 있어요).
  6. API가 이 참조를 전체 도구 정의로 자동 확장해요.
  7. Claude가 발견된 도구 중에서 골라 호출해요.

빠른 시작 (Quick start)

다음 예시는 도구 검색 도구와 두 개의 지연 도구를 포함해요:

```bash cURL curl https://api.anthropic.com/v1/messages \ -H "x-api-key: $ANTHR...KEY" \ -H "anthropic-version: 2023-06-01" \ -H "content-type: application/json" \ -d '{ "model": "claude-opus-5-5", "max_tokens": 2048, "messages": [ { "role": "user", "content": "What is the weather in San Francisco?" } ], "tools": [ { "type": "tool_search_tool_regex_20251119", "name": "tool_search_tool_regex" }, { "name": "get_weather", "description": "Get the weather at a specific location", "input_schema": { "type": "object", "properties": { "location": {"type": "string"}, "unit": { "type": "string", "enum": ["celsius", "fahrenheit"] } }, "required": ["location"] }, "defer_loading": true }, { "name": "search_files", "description": "Search through files in the workspace", "input_schema": { "type": "object", "properties": { "query": {"type": "string"}, "file_types": { "type": "array", "items": {"type": "string"} } }, "required": ["query"] }, "defer_loading": true } ] }' ```
ant messages create <<'YAML'
model: claude-opus-5-5
max_tokens: 2048
messages:
  - role: user
    content: What is the weather in San Francisco?
tools:
  - type: tool_search_tool_regex_20251119
    name: tool_search_tool_regex
  - name: get_weather
    description: Get the weather at a specific location
    input_schema:
      type: object
      properties:
        location:
          type: string
        unit:
          type: string
          enum: [celsius, fahrenheit]
      required: [location]
    defer_loading: true
  - name: search_files
    description: Search through files in the workspace
    input_schema:
      type: object
      properties:
        query:
          type: string
        file_types:
          type: array
          items:
            type: string
      required: [query]
    defer_loading: true
YAML
client = anthropic.Anthropic()

response = client.messages.create(
    model="claude-opus-5-5",
    max_tokens=2048,
    messages=[{"role": "user", "content": "What is the weather in San Francisco?"}],
    tools=[
        {"type": "tool_search_tool_regex_20251119", "name": "tool_search_tool_regex"},
        {
            "name": "get_weather",
            "description": "Get the weather at a specific location",
            "input_schema": {
                "type": "object",
                "properties": {
                    "location": {"type": "string"},
                    "unit": {"type": "string", "enum": ["celsius", "fahrenheit"]},
                },
                "required": ["location"],
            },
            "defer_loading": True,
        },
        {
            "name": "search_files",
            "description": "Search through files in the workspace",
            "input_schema": {
                "type": "object",
                "properties": {
                    "query": {"type": "string"},
                    "file_types": {"type": "array", "items": {"type": "string"}},
                },
                "required": ["query"],
            },
            "defer_loading": True,
        },
    ],
)

print(response)
const client = new Anthropic();

const response = await client.messages.create({
  model: "claude-opus-5-5",
  max_tokens: 2048,
  messages: [
    {
      role: "user",
      content: "What is the weather in San Francisco?"
    }
  ],
  tools: [
    {
      type: "tool_search_tool_regex_20251119",
      name: "tool_search_tool_regex"
    },
    {
      name: "get_weather",
      description: "Get the weather at a specific location",
      input_schema: {
        type: "object" as const,
        properties: {
          location: { type: "string" },
          unit: {
            type: "string",
            enum: ["celsius", "fahrenheit"]
          }
        },
        required: ["location"]
      },
      defer_loading: true
    },
    {
      name: "search_files",
      description: "Search through files in the workspace",
      input_schema: {
        type: "object" as const,
        properties: {
          query: { type: "string" },
          file_types: {
            type: "array",
            items: { type: "string" }
          }
        },
        required: ["query"]
      },
      defer_loading: true
    }
  ]
});

console.log(response);
AnthropicClient client = new();

var parameters = new MessageCreateParams
{
    Model = Model.ClaudeOpus5_5,
    MaxTokens = 2048,
    Messages = [
        new() {
            Role = Role.User,
            Content = "What is the weather in San Francisco?"
        }
    ],
    Tools = [
        new ToolUnion(new ToolSearchToolRegex20251119
        {
            Type = ToolSearchToolRegex20251119Type.ToolSearchToolRegex20251119
        }),
        new ToolUnion(new Tool()
        {
            Name = "get_weather",
            Description = "Get the weather at a specific location",
            InputSchema = new InputSchema()
            {
                Properties = new Dictionary<string, JsonElement>
                {
                    ["location"] = JsonSerializer.SerializeToElement(new { type = "string" }),
                    ["unit"] = JsonSerializer.SerializeToElement(new { type = "string", @enum = new[] { "celsius", "fahrenheit" } }),
                },
                Required = ["location"],
            },
            DeferLoading = true,
        }),
        new ToolUnion(new Tool()
        {
            Name = "search_files",
            Description = "Search through files in the workspace",
            InputSchema = new InputSchema()
            {
                Properties = new Dictionary<string, JsonElement>
                {
                    ["query"] = JsonSerializer.SerializeToElement(new { type = "string" }),
                    ["file_types"] = JsonSerializer.SerializeToElement(new { type = "array", items = new { type = "string" } }),
                },
                Required = ["query"],
            },
            DeferLoading = true,
        }),
    ]
};

var message = await client.Messages.Create(parameters);
Console.WriteLine(message);
client := anthropic.NewClient()

response, err := client.Messages.New(context.TODO(), anthropic.MessageNewParams{
	Model:     anthropic.ModelClaudeOpus5_5,
	MaxTokens: 2048,
	Messages: []anthropic.MessageParam{
		anthropic.NewUserMessage(anthropic.NewTextBlock("What is the weather in San Francisco?")),
	},
	Tools: []anthropic.ToolUnionParam{
		{OfToolSearchToolRegex20251119: &anthropic.ToolSearchToolRegex20251119Param{
			Type: anthropic.ToolSearchToolRegex20251119TypeToolSearchToolRegex20251119,
		}},
		{OfTool: &anthropic.ToolParam{
			Name:        "get_weather",
			Description: anthropic.String("Get the weather at a specific location"),
			InputSchema: anthropic.ToolInputSchemaParam{
				Properties: map[string]any{
					"location": map[string]any{"type": "string"},
					"unit": map[string]any{
						"type": "string",
						"enum": []string{"celsius", "fahrenheit"},
					},
				},
				Required: []string{"location"},
			},
			DeferLoading: anthropic.Bool(true),
		}},
		{OfTool: &anthropic.ToolParam{
			Name:        "search_files",
			Description: anthropic.String("Search through files in the workspace"),
			InputSchema: anthropic.ToolInputSchemaParam{
				Properties: map[string]any{
					"query":      map[string]any{"type": "string"},
					"file_types": map[string]any{"type": "array", "items": map[string]any{"type": "string"}},
				},
				Required: []string{"query"},
			},
			DeferLoading: anthropic.Bool(true),
		}},
	},
})
if err != nil {
	log.Fatal(err)
}
fmt.Println(response.RawJSON())
import com.anthropic.models.messages.ToolSearchToolRegex20251119;

void main() {
    AnthropicClient client = AnthropicOkHttpClient.fromEnv();

    InputSchema weatherSchema = InputSchema.builder()
        .properties(JsonValue.from(Map.of(
            "location", Map.of("type", "string"),
            "unit", Map.of(
                "type", "string",
                "enum", List.of("celsius", "fahrenheit")
            )
        )))
        .putAdditionalProperty("required", JsonValue.from(List.of("location")))
        .build();

    InputSchema searchSchema = InputSchema.builder()
        .properties(JsonValue.from(Map.of(
            "query", Map.of("type", "string"),
            "file_types", Map.of(
                "type", "array",
                "items", Map.of("type", "string")
            )
        )))
        .putAdditionalProperty("required", JsonValue.from(List.of("query")))
        .build();

    MessageCreateParams params = MessageCreateParams.builder()
        .model(Model.CLAUDE_OPUS_5_5)
        .maxTokens(2048L)
        .addUserMessage("What is the weather in San Francisco?")
        .addTool(ToolSearchToolRegex20251119.builder()
            .type(ToolSearchToolRegex20251119.Type.TOOL_SEARCH_TOOL_REGEX_20251119)
            .build())
        .addTool(Tool.builder()
            .name("get_weather")
            .description("Get the weather at a specific location")
            .inputSchema(weatherSchema)
            .deferLoading(true)
            .build())
        .addTool(Tool.builder()
            .name("search_files")
            .description("Search through files in the workspace")
            .inputSchema(searchSchema)
            .deferLoading(true)
            .build())
        .build();

    Message response = client.messages().create(params);
    IO.println(response);
}
$client = new Client();

$message = $client->messages->create(
    maxTokens: 2048,
    messages: [
        ['role' => 'user', 'content' => 'What is the weather in San Francisco?'],
    ],
    model: 'claude-opus-5-5',
    tools: [
        [
            'type' => 'tool_search_tool_regex_20251119',
            'name' => 'tool_search_tool_regex',
        ],
        [
            'name' => 'get_weather',
            'description' => 'Get the weather at a specific location',
            'input_schema' => [
                'type' => 'object',
                'properties' => [
                    'location' => ['type' => 'string'],
                    'unit' => [
                        'type' => 'string',
                        'enum' => ['celsius', 'fahrenheit'],
                    ],
                ],
                'required' => ['location'],
            ],
            'defer_loading' => true,
        ],
        [
            'name' => 'search_files',
            'description' => 'Search through files in the workspace',
            'input_schema' => [
                'type' => 'object',
                'properties' => [
                    'query' => ['type' => 'string'],
                    'file_types' => [
                        'type' => 'array',
                        'items' => ['type' => 'string'],
                    ],
                ],
                'required' => ['query'],
            ],
            'defer_loading' => true,
        ],
    ],
);

echo $message;
client = Anthropic::Client.new

message = client.messages.create(
  model: "claude-opus-5-5",
  max_tokens: 2048,
  messages: [
    { role: "user", content: "What is the weather in San Francisco?" }
  ],
  tools: [
    {
      type: "tool_search_tool_regex_20251119",
      name: "tool_search_tool_regex"
    },
    {
      name: "get_weather",
      description: "Get the weather at a specific location",
      input_schema: {
        type: "object",
        properties: {
          location: { type: "string" },
          unit: {
            type: "string",
            enum: ["celsius", "fahrenheit"]
          }
        },
        required: ["location"]
      },
      defer_loading: true
    },
    {
      name: "search_files",
      description: "Search through files in the workspace",
      input_schema: {
        type: "object",
        properties: {
          query: { type: "string" },
          file_types: {
            type: "array",
            items: { type: "string" }
          }
        },
        required: ["query"]
      },
      defer_loading: true
    }
  ]
)

puts message

Claude가 카탈로그를 검색해 get_weather를 발견하고 호출해요. 응답은 stop_reason: "tool_use"로 끝나요. 발견된 도구를 실행하고 도구 호출 처리하기처럼 tool_result를 반환하세요. 응답 형식이 반환되는 블록과 다음에 보낼 것을 보여줘요.

도구 정의 (Tool definition)

도구 검색 도구에는 두 변형이 있어요:

{
  "type": "tool_search_tool_regex_20251119",
  "name": "tool_search_tool_regex"
}
{
  "type": "tool_search_tool_bm25_20251119",
  "name": "tool_search_tool_bm25"
}
**Regex 변형 쿼리 형식: 자연어가 아닌 Python regex**

tool_search_tool_regex_20251119를 사용하면 Claude는 자연어 쿼리가 아니라 Python re.search() 패턴을 써요. 매칭은 대소문자를 구분하지 않아요. 흔한 패턴은 다음과 같아요:

  • "weather": "weather"가 포함된 도구 이름과 설명에 매칭
  • "get_.*_data": get_user_data, get_weather_data 같은 도구에 매칭
  • "database.*query|query.*database": 두 단어 순서 모두에 매칭

최대 패턴 길이: 200자

**BM25 변형 쿼리 형식: 자연어**

tool_search_tool_bm25_20251119를 사용하면 Claude는 자연어 쿼리로 검색해요. 최대 쿼리 길이: 500자.

지연 도구 로딩 (Deferred tool loading)

defer_loading: true를 추가해 주문형 로딩용 도구로 표시하세요:

{
  "name": "get_weather",
  "description": "Get current weather for a location",
  "input_schema": {
    "type": "object",
    "properties": {
      "location": { "type": "string" },
      "unit": { "type": "string", "enum": ["celsius", "fahrenheit"] }
    },
    "required": ["location"]
  },
  "defer_loading": true
}

defer_loading은 요청에 무엇을 보내는지가 아니라, 무엇이 컨텍스트 창에 들어오는지를 제어해요:

  • 매 요청마다 tools 배열에 지연된 도구를 포함한 모든 도구의 전체 정의를 여전히 보내요. API가 서버 쪽에서 검색을 실행하고 tool_reference 블록을 확장하려면 그것들이 필요해요.
  • defer_loading이 없는 도구는 즉시 컨텍스트로 로드돼요.
  • defer_loading: true가 있는 도구는 Claude가 검색으로 발견할 때만 로드돼요.
  • 도구 검색 도구 자체에는 절대 defer_loading: true를 설정하지 마세요.
  • 가장 자주 쓰는 3~5개 도구는 지연 상태로 두지 않아서, Claude가 먼저 검색하지 않고 호출할 수 있게 하세요.

컴퓨터 사용·브라우저 사용 툴셋(computer_toolset_20260801browser_toolset_20260801)은 항목 자체가 아니라 항목의 configs 객체 안에서 멤버 도구마다 defer_loading을 받아요. 항목 수준에서 설정하는 요청은 거부돼요. 툴셋은 하나의 단위로 지연되고 확장되므로 defer_loading은 모든 활성 멤버에서 같은 값으로 결정되어야 하고, Claude가 검색으로 툴셋을 발견하면 모든 활성 멤버가 한꺼번에 로드돼요. configs 형식은 클라이언트 툴셋을 참고하세요.

두 도구 검색 변형(regexbm25) 모두 도구 이름, 설명, 인자 이름, 인자 설명을 검색해요.

내부적으로 API는 지연된 도구를 시스템 프롬프트 접두사에서 제외해요. Claude가 도구 검색으로 지연된 도구를 발견하면 API가 tool_reference 블록을 대화에 인라인으로 추가한 뒤, Claude에게 전달하기 전에 전체 도구 정의로 확장해요. 접두사는 건드리지 않으므로 프롬프트 캐싱이 보존돼요. 엄격 모드의 문법(도구 호출 출력을 우리 스키마에 맞게 제약하는 규칙)은 전체 툴셋에서 구축되므로, defer_loading과 엄격 모드는 문법 재컴파일 없이 함께 동작해요.

응답 형식 (Response format)

Claude가 도구 검색 도구를 사용하면 응답에 다음 블록 타입들이 포함돼요:

{
  "role": "assistant",
  "content": [
    {
      "type": "text",
      "text": "I'll search for tools to help with the weather information."
    },
    {
      "type": "server_tool_use",
      "id": "srvtoolu_01ABC123",
      "name": "tool_search_tool_regex",
      "input": {
        "pattern": "weather",
        "limit": 10
      }
    },
    {
      "type": "tool_search_tool_result",
      "tool_use_id": "srvtoolu_01ABC123",
      "content": {
        "type": "tool_search_tool_search_result",
        "tool_references": [{ "type": "tool_reference", "tool_name": "get_weather" }]
      }
    },
    {
      "type": "text",
      "text": "I found a weather tool. Let me get the weather for San Francisco."
    },
    {
      "type": "tool_use",
      "id": "toolu_01XYZ789",
      "name": "get_weather",
      "input": { "location": "San Francisco", "unit": "fahrenheit" }
    }
  ],
  "stop_reason": "tool_use"
}

응답 이해하기 (Understanding the response)

  • server_tool_use: 도구 검색 도구에 대한 Claude의 호출이에요. 검색은 Anthropic 서버에서 실행돼요. 그 srvtoolu_... ID에 대해 tool_result를 절대 반환하지 마세요. input이 검색(regex 변형은 pattern, BM25는 query)을 담고, 검색이 반환하는 일치 도구 수를 제한하는 정수 1~10,000인 선택적 limit(기본 5)를 담을 수 있어요.
  • tool_search_tool_result: 중첩된 tool_search_tool_search_result 객체 안의 검색 결과예요. 메시지 기록에 그대로 두세요.
  • tool_references: 발견된 도구를 가리키는 tool_reference 객체 배열이에요. API가 Claude를 위해 이들을 확장해요. 직접 확장하지 마세요.
  • tool_use: 발견된 도구에 대한 Claude의 호출이에요. 표준 도구 사용과 똑같이 실행하고 tool_result를 반환하세요.

API는 tool_reference 블록을 Claude에게 보여주기 전에 자동으로 전체 도구 정의로 확장해요. tools 매개변수에 모든 일치 도구 정의를 제공하는 한, 이 확장을 직접 처리할 필요가 없어요.

대화 이어가기 (Continuing the conversation)

다음 요청에서는 server_tool_usetool_search_tool_result 블록을 포함해 어시스턴트 콘텐츠를 그대로 전달하세요. 발견된 도구에 대한 tool_result를 사용자 메시지에 추가하고, 같은 tools 배열(검색 도구와 모든 지연 정의)을 보내세요. srvtoolu_... ID에 대한 tool_result는 반환하지 마세요. API가 요청을 거부해요. API는 대화 기록 전체에서 tool_reference 블록을 확장하므로, Claude는 재검색 없이 이후 턴에서 발견된 도구를 재사용할 수 있어요. 아무것도 일치하지 않는 검색은 오류가 아니라 빈 tool_references 배열이 담긴 tool_search_tool_search_result를 반환해요.

MCP 통합 (MCP integration)

도구가 MCP 커넥터를 통해 MCP 서버에서 온다면 개별 도구 정의에 defer_loading을 설정하지 마세요. 대신 mcp_toolset 항목의 default_config에 서버 전체에 한 번, 또는 configs에서 도구마다 설정하세요. MCP 툴셋 구성을 참고하세요.

커스텀 도구 검색 구현 (Custom tool search implementation)

커스텀 도구에서 tool_reference 블록을 반환해서 우리만의 도구 검색 로직(예: 임베딩이나 의미 검색)을 구현할 수 있어요. Claude가 커스텀 검색 도구를 호출하면, content 배열에 tool_reference 블록이 담긴 표준 tool_result를 반환하세요:

{
  "type": "tool_result",
  "tool_use_id": "toolu_your_tool_id",
  "content": [{ "type": "tool_reference", "tool_name": "discovered_tool_name" }]
}

참조된 모든 도구는 최상위 tools 매개변수에 해당 도구 정의가 있어야 하고, 보통 defer_loading: true와 함께 있어요. 이렇게 하면 내장 변형이 제공하지 않는 방법(임베딩 기반 검색 같은)을 쓸 수 있고, API가 반환된 tool_reference 블록을 같은 방식으로 확장해요.

[응답 형식](#response-format) 섹션에 나온 `tool_search_tool_result` 형식은 Anthropic의 내장 도구 검색이 내부적으로 쓰는 서버 쪽 형식이에요. 커스텀 클라이언트 쪽 구현에는 항상 앞선 예시처럼 `tool_reference` 콘텐츠 블록이 있는 표준 `tool_result` 형식을 쓰세요.

임베딩을 사용하는 완전한 예시는 도구 검색 with embeddings 레시피를 참고하세요.

오류 처리 (Error handling)

[도구 사용 예시](https://platform.claude.com/docs/en/agents-and-tools/tool-use/define-tools#providing-tool-use-examples)는 도구 검색과 함께 동작해요. Claude가 지연된 도구를 발견하면 API가 그 `input_examples`도 정의와 함께 확장해요.

HTTP 오류 (400 상태) (HTTP errors)

이 오류들은 API가 요청을 처리하지 못하게 해요:

모든 도구가 지연됨:

{
  "type": "error",
  "error": {
    "type": "invalid_request_error",
    "message": "At least one tool must have defer_loading=false. All tools cannot be deferred."
  }
}

도구 정의 누락:

{
  "type": "error",
  "error": {
    "type": "invalid_request_error",
    "message": "Tool reference 'unknown_tool' not found in available tools"
  }
}

도구 결과 오류 (200 상태) (Tool result errors)

도구 검색 작업이 실행 중에 실패하면 API가 본문에 오류가 담긴 200 응답을 반환해요:

{
  "type": "tool_search_tool_result",
  "tool_use_id": "srvtoolu_01ABC123",
  "content": {
    "type": "tool_search_tool_result_error",
    "error_code": "invalid_tool_input",
    "error_message": "Invalid regular expression pattern: missing ) at position 1"
  }
}

error_code 필드는 네 가지 값을 가질 수 있어요:

  • invalid_tool_input: 잘못된 정규식 패턴이나 200자 한도가 넘는 패턴처럼 검색 입력이 무효함
  • unavailable: 검색이 타임아웃되거나 서비스가 이용 불가한 것처럼 실행할 수 없음
  • too_many_requests: 도구 검색 작업의 속도 제한 초과
  • execution_time_exceeded: 검색이 실행 시간 한도를 초과

흔한 실수 (Common mistakes)

**원인:** 도구 검색 도구를 포함한 모든 도구에 `defer_loading: true`를 설정함.

해결: 도구 검색 도구에서 defer_loading을 제거하세요:

{
  "type": "tool_search_tool_regex_20251119",
  "name": "tool_search_tool_regex"
}
**원인:** `tool_reference`가 `tools` 배열에 없는 도구를 가리킴.

해결: 발견될 수 있는 모든 도구에 완전한 정의가 있는지 확인하세요:

{
  "name": "my_tool",
  "description": "Full description here",
  "input_schema": {
    "type": "object"
  },
  "defer_loading": true
}
**원인:** 정규식 패턴이 도구의 이름, 설명, 인자 이름, 인자 설명과 일치하지 않음.

디버깅 단계:

  1. 도구 이름, 설명, 인자 이름, 인자 설명을 확인하세요. Claude는 이 모든 필드를 검색해요.
  2. 패턴을 테스트하세요: import re; re.search(r"your_pattern", "tool_name", re.IGNORECASE).
  3. 매칭은 대소문자를 구분하지 않으므로 대소문자 차이는 문제가 아니에요.
  4. Claude는 정확한 일치가 아니라 ".*weather.*" 같은 넓은 패턴을 사용해요.

팁: 발견 가능성을 높이려면 도구 설명에 흔한 키워드를 추가하세요.

프롬프트 캐싱 (Prompt caching)

defer_loading이 프롬프트 캐싱을 보존하는 방법은 프롬프트 캐싱과 함께하는 도구 사용을 참고하세요.

defer_loading: true가 있는 도구는 cache_control도 가질 수 없어요. API가 400을 반환해요. 캐시 중단점은 지연되지 않은 도구에 두세요.

스트리밍 (Streaming)

스트리밍을 켜면 도구 검색 이벤트를 스트림의 일부로 받아요:

event: content_block_start
data: {"type": "content_block_start", "index": 1, "content_block": {"type": "server_tool_use", "id": "srvtoolu_xyz789", "name": "tool_search_tool_regex"}}

// Search pattern streamed
event: content_block_delta
data: {"type": "content_block_delta", "index": 1, "delta": {"type": "input_json_delta", "partial_json": "{\"pattern\":\"weather\"}"}}

// Pause while search executes

// Search results streamed
event: content_block_start
data: {"type": "content_block_start", "index": 2, "content_block": {"type": "tool_search_tool_result", "tool_use_id": "srvtoolu_xyz789", "content": {"type": "tool_search_tool_search_result", "tool_references": [{"type": "tool_reference", "tool_name": "get_weather"}]}}}

// Claude continues with discovered tools

배치 요청 (Batch requests)

Messages Batches API에 도구 검색 도구를 포함할 수 있어요.

한계와 모범 사례 (Limits and best practices)

한계 (Limits)

  • 최대 지연 도구: 요청당 defer_loading: true인 도구 10,000개
  • 검색 결과: 각 검색은 기본적으로 최대 5개 일치 도구를 반환하고, Claude는 검색 입력에 limit를 1~10,000 사이 정수로 설정할 수 있어요.
  • 패턴·쿼리 길이: 정규식 패턴은 최대 200자, BM25 쿼리는 500자
  • 모델 지원: 모델 호환성 참고

도구 검색을 쓸 때 (When to use tool search)

다음 중 하나라도 해당하면 도구 검색을 쓰세요:

  • 사용 가능한 도구가 10개 이상일 때
  • 도구 정의가 1만 개 이상의 토큰을 소비할 때
  • 툴셋이 커질수록 도구 선택 정확도가 떨어질 때
  • 여러 MCP 서버를 집계할 때(200+개 도구)
  • 도구 라이브러리가 시간이 지나며 커질 때

도구가 10개 미만이거나, 모든 도구를 매 요청에 쓰거나, 도구 정의가 작을 때(총 100 토큰 미만)는 도구 검색 없이 표준 도구 호출이 더 잘 맞아요.

최적화 팁 (Optimization tips)

  • 가장 자주 쓰는 3~5개 도구를 지연 상태로 두지 마세요.
  • 명확하고 서술적인 도구 이름과 설명을 쓰세요.
  • 도구 이름에 일관된 네임스페이스를 쓰세요. 서비스나 리소스로 접두사를 붙이면(github_, slack_) 한 검색이 그룹 전체에 매칭돼요.
  • 설명에 사용자가 작업을 설명하는 방식과 일치하는 키워드를 쓰세요.
  • 사용 가능한 도구 범주를 설명하는 시스템 프롬프트 섹션을 추가하세요: "Slack, GitHub, Jira와 상호작용할 도구를 검색할 수 있어요."
  • Claude가 발견하는 도구를 모니터링해 설명을 다듬으세요.

사용량 (Usage)

도구 검색은 별도의 서버 도구로 계량되지 않아요. 응답의 usage.server_tool_use 객체에 도구 검색 필드가 없고, 검색이 컨텍스트로 로드하는 도구 정의는 다른 도구 정의처럼 입력 토큰으로 계산돼요.

더 알아보기 (Learn more)