Tool Search & Call Shim
Relevant source files
The following files were used as context for generating this wiki page:
The Tool Search & Call Shim is a specialized translation layer within shunt that mediates between Claude Code's tool-discovery mechanisms and various upstream provider protocols. Its primary purpose is to handle the ToolSearch tool, which Claude Code uses to dynamically discover and load secondary tools (skills) during a conversation.
This system supports two primary modes of operation: a Native protocol for providers that support native tool-searching (such as the Responses API) and a Text-based Shim (progressive-reveal shim) for providers that require manual tool definition injection via text descriptions.
Sources: src/model/responses_request.rs:16-36, ARCHITECTURE.md:43-52
Architectural Overview
The Tool Search & Call Shim resides within the model layer of the application, specifically integrated into the request translation pipeline. It intercepts Anthropic-formatted messages and identifies blocks related to tool discovery.
Request-Scoped Context
At the start of every request translation, a ToolSearchContext is initialized. This context performs a non-allocating pre-scan of the incoming request to identify:
- Defined Tools: Mapping tool names to their descriptions and input schemas.
- Deferred Tools: Identifying tools marked with
defer_loading: true. - Search History: Tracking existing
tool_useIDs that originated from theToolSearchtool to ensure their results are correctly translated.
Sources: src/model/responses_request.rs:25-36, src/model/responses_request.rs:39-95
Workflow Diagram
The following diagram illustrates how the gateway resolves a tool search call during request translation:
The diagram shows the decision logic for choosing between native tool-search protocols and the shim implementation.
Sources: src/model/responses_request.rs:105-130, src/model/responses_request.rs:205-220
Core Components
ToolSearchContext Struct
The ToolSearchContext is the central data structure for managing tool discovery state.
| Field | Type | Description |
|---|---|---|
native | bool | Whether to use native #82 protocol or #43 text shim. |
schema_map | HashMap<&str, (&str, &Value)> | Store of tool names linked to descriptions and schemas. |
deferred_names | HashSet<&str> | Set of tools that should not be visible until loaded. |
loaded_tools | HashSet<&str> | Tools already activated via a tool_reference. |
search_call_ids | HashSet<&str> | IDs of active ToolSearch calls in message history. |
Sources: src/model/responses_request.rs:25-36
The Progressive Reveal Shim
When a provider does not support native tool searching, shunt employs a "progressive-reveal" shim. This mechanism:
- Withholds Deferred Tools: Tools marked as deferred are stripped from the initial
toolsarray provided to the LLM. - Reveals via Tool Results: When a
ToolSearchresult contains atool_reference, the shim injects the full tool description and parameter schema into the conversation history as text. - Enables Selection: Once revealed, the tool is added to the
loaded_toolsset, allowing it to be included in subsequent turns'toolsarrays.
Sources: src/model/responses_request.rs:327-330, src/model/responses_request.rs:408-422
Native Tool Search Integration
For compatible backends (e.g., specific ChatGPT/Codex flavors), the shim translates ToolSearch directly into native protocol items.
Translation Mapping
The system maps Anthropic blocks to Responses native items as follows:
| Anthropic Block | Responses Item | Description |
|---|---|---|
tool_use (Name: ToolSearch) | tool_search_call | Native client-side search execution. |
tool_result (linked to Search) | tool_search_output | Carries full loadable function specs. |
tool_reference | Loadable Function Spec | Injected into the tools array of a tool_search_output. |
Sources: src/model/responses_request.rs:246-270, src/model/responses_request.rs:373-380
Tool Loading Sequence
This sequence shows the interaction for loading a new tool into the provider's context using the native protocol.
Sources: src/model/responses_request.rs:246-270, src/model/responses_request.rs:39-95
Specialized Tool Handling
Web Search Tool (web_search_20250305)
Claude Code includes a hosted web search tool. Unlike standard tools that the client executes, this tool is intended to be executed upstream.
- Translation: It is mapped to the Responses
web_searchtype. - Filtering: It includes logic to handle
allowed_domainsandblocked_domainswithin afiltersobject. - Flavor Constraints: On certain flavors like
xAI, this tool is dropped because the developer API does not yet support hosted web access.
Sources: src/model/responses_request.rs:445-475, src/model/responses_request.rs:488-500
Schema Normalization
Before any tool (standard or discovered) is sent upstream, its JSON schema is normalized to ensure compatibility with OpenAI-style backends. This includes:
- Ensuring
type: "object"is present. - Providing default
propertiesandadditionalProperties: true. - Stripping invalid
requiredarrays that are not of the array type.
Sources: src/model/responses_request.rs:515-530
Implementation Details
// From src/model/responses_request.rs:16
pub(crate) const TOOL_SEARCH_NAME: &str = "ToolSearch";
// From src/model/responses_request.rs:252
fn tool_search_output_item(call_id: &str, block: &Value, context: &ToolSearchContext) -> Value {
let tools = block
.get("content")
.and_then(Value::as_array)
.map(|blocks| {
// Keep the first reference to each tool
let mut seen = std::collections::HashSet::new();
blocks
.iter()
.filter(|inner| inner.get("type").and_then(Value::as_str) == Some("tool_reference"))
.filter_map(|inner| inner.get("tool_name").and_then(Value::as_str))
.filter(|name| seen.insert(*name))
.filter_map(|name| loadable_tool_spec(name, context))
.collect::<Vec<_>>()
})
.unwrap_or_default();
// ... returns tool_search_output object
}
Sources: src/model/responses_request.rs:16, src/model/responses_request.rs:252-270
The Tool Search & Call Shim ensures that Claude Code's sophisticated dynamic tool discovery remains functional even when using LLM providers that do not natively implement the same tool-reveal lifecycle. By maintaining a request-scoped state of discovered tools and translating between protocol-specific discovery items, shunt provides a seamless bridge for agentic workflows.