Why Native Tool Use?
| Aspect | XML in Prompt | Native API |
|---|---|---|
| Accuracy | Depends on the model parsing XML | Structured by the API, unambiguous |
| Tokens | XML consumes context window tokens | Separate fields, more efficient |
| Cache | No optimization | cache_control:ephemeral on Anthropic |
| Validation | Manual | Automatic by the provider API |
| Compatibility | Any provider | OpenAI, Anthropic, ZAI, MiniMax, Moonshot, OpenRouter (others continue with XML) |
Architecture
ToolAwareClient Interface
TheToolAwareClient interface extends the base LLMClient with tool support:
Automatic Detection
Detection is done via type assertion, with no configuration required:Providers that do not implement
ToolAwareClient continue to work normally via SendPrompt.Provider Tool Use Support
Not all providers implement native tool use. Features like/coder mode and multi-agent orchestration work best with providers that support SendPromptWithTools:
| Provider | Native Tool Use | Streaming | Impact |
|---|---|---|---|
| OpenAI | Yes | Yes | Full /coder and multi-agent support |
| Anthropic (Claude) | Yes | Yes (OAuth) | Full /coder and multi-agent support |
| Google (Gemini) | No | No | Falls back to text-based tool parsing — /coder works via XML extraction |
| ZAI (Zhipu AI) | Yes | Yes | Full /coder and multi-agent support |
| MiniMax | Yes | Yes | Full /coder and multi-agent support. Disabled when MINIMAX_API_COMPAT=anthropic (falls back to XML). |
| OpenRouter | Yes | Yes | Full /coder and multi-agent support. Uses OpenAI-compatible function calling. Tool definitions can also be injected via OPENROUTER_TOOLS. |
| xAI (Grok) | No | No | Falls back to text-based tool parsing |
| GitHub Copilot | No | No | Falls back to text-based tool parsing |
| GitHub Models | No | No | Falls back to text-based tool parsing (OpenAI-compatible API) |
| Ollama | No | No | Falls back to text-based tool parsing |
| StackSpot | No | No | Falls back to text-based tool parsing |
Data Types
ToolDefinition
ToolDefinition
Defines a tool available to the model:
ToolCall and ToolResult
ToolCall and ToolResult
Represent a tool call by the model and its result:The
IsError field (aligned with Anthropic’s Messages API) is emitted natively as is_error: true in Claude’s tool_result block. For OpenAI-compatible providers (OpenAI, Moonshot, MiniMax, ZAI, OpenRouter), the models.Message also carries ErrorCode (ENOENT, Timeout, ExitCode:N, InvalidArgs, …) and the adapter prepends content with [ERROR:<code>] — the model gets the signal even without a native error field.LLMResponse
LLMResponse
Unified response that can contain text and/or tool calls:
Provider Implementations
- OpenAI
- Anthropic (Claude)
Uses the
tools field in the Chat Completions API:- Sends tools as a
toolsarray withtool_choice: "auto" - Processes
tool_callsinchoices[0].message toolmessages in the history link results to thetool_call_id
Tool result with is_error / ErrorCode (provider-agnostic)
Tool results carry two orthogonal signals that travel to the model:IsError bool— true when the tool executed but reported a business-level failure (non-zero exit, HTTP 4xx, file not found, invalid args schema). False = success.ErrorCode string— locale-independent classification:ENOENT,EACCES,EISDIR,EEXIST,Timeout,Canceled,ExitCode:N,NetworkError,DNSError,InvalidArgs, etc. Empty whenIsError=false.
| Provider family | How it’s emitted |
|---|---|
| Anthropic (claudeai, Bedrock Claude, StackSpot Claude) | {"type":"tool_result","is_error":true,"content":"[ERROR:ENOENT] <body>"} |
| OpenAI Chat Completions (openai, moonshot, minimax, zai, openrouter, xai, copilot) | {"role":"tool","tool_call_id":...,"content":"[ERROR:ENOENT] <body>"} |
| Others (no native support) | content with [ERROR] header when applicable |
[ERROR:<code>] to decide retry/recovery without parsing English — InvalidArgs means “fix the schema”, Timeout means “try again”, ENOENT means “wrong file”.
ContentBlock with Cache Control
For Anthropic, the system prompt is split into blocks with cache control:Fallback Integration
The fallback chain (llm/fallback) supports SendPromptWithTools automatically. Providers without native tool use support are skipped in the tool call chain but remain available for plain text requests.