Skip to main content
ChatCLI supports tool calls via native API for OpenAI, Anthropic, ZAI (Zhipu AI), MiniMax, Moonshot (Kimi), and OpenRouter, replacing the XML-embedded-in-prompt approach with structured API calls. This improves accuracy, reduces token consumption, and enables cache optimizations.

Why Native Tool Use?

AspectXML in PromptNative API
AccuracyDepends on the model parsing XMLStructured by the API, unambiguous
TokensXML consumes context window tokensSeparate fields, more efficient
CacheNo optimizationcache_control:ephemeral on Anthropic
ValidationManualAutomatic by the provider API
CompatibilityAny providerOpenAI, Anthropic, ZAI, MiniMax, Moonshot, OpenRouter (others continue with XML)

Architecture

ToolAwareClient Interface

The ToolAwareClient interface extends the base LLMClient with tool support:
type ToolAwareClient interface {
    LLMClient
    SendPromptWithTools(ctx context.Context, prompt string, history []models.Message,
        tools []models.ToolDefinition, maxTokens int) (*models.LLMResponse, error)
    SupportsNativeTools() bool
}

Automatic Detection

Detection is done via type assertion, with no configuration required:
if client.IsToolAware(c) {
    tac, _ := client.AsToolAware(c)
    resp, err := tac.SendPromptWithTools(ctx, prompt, history, tools, maxTokens)
    // resp.ToolCalls contains the structured calls
}
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:
ProviderNative Tool UseStreamingImpact
OpenAIYesYesFull /coder and multi-agent support
Anthropic (Claude)YesYes (OAuth)Full /coder and multi-agent support
Google (Gemini)NoNoFalls back to text-based tool parsing — /coder works via XML extraction
ZAI (Zhipu AI)YesYesFull /coder and multi-agent support
MiniMaxYesYesFull /coder and multi-agent support. Disabled when MINIMAX_API_COMPAT=anthropic (falls back to XML).
OpenRouterYesYesFull /coder and multi-agent support. Uses OpenAI-compatible function calling. Tool definitions can also be injected via OPENROUTER_TOOLS.
xAI (Grok)NoNoFalls back to text-based tool parsing
GitHub CopilotNoNoFalls back to text-based tool parsing
GitHub ModelsNoNoFalls back to text-based tool parsing (OpenAI-compatible API)
OllamaNoNoFalls back to text-based tool parsing
StackSpotNoNoFalls back to text-based tool parsing
When using providers without native tool use, the agent and coder modes still function but rely on XML-based tool call parsing from the LLM’s text output. This is less reliable than native tool calling and may occasionally require format correction retries (up to 3 attempts). For production /coder workflows, OpenAI, Claude, ZAI, MiniMax, or OpenRouter are recommended.

Data Types

Defines a tool available to the model:
type ToolDefinition struct {
    Type     string          `json:"type"`     // "function"
    Function ToolFunctionDef `json:"function"`
}

type ToolFunctionDef struct {
    Name        string                 `json:"name"`
    Description string                 `json:"description"`
    Parameters  map[string]interface{} `json:"parameters"` // JSON Schema
}
Represent a tool call by the model and its result:
type ToolCall struct {
    ID        string                 `json:"id"`
    Type      string                 `json:"type"`       // "function"
    Name      string                 `json:"name"`
    Arguments map[string]interface{} `json:"arguments"`
}

type ToolResult struct {
    ToolCallID string `json:"tool_call_id"`
    Content    string `json:"content"`
    IsError    bool   `json:"is_error,omitempty"`
}
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.
Unified response that can contain text and/or tool calls:
type LLMResponse struct {
    Content    string     `json:"content"`
    ToolCalls  []ToolCall `json:"tool_calls,omitempty"`
    Usage      *UsageInfo `json:"usage,omitempty"`
    StopReason string     `json:"stop_reason,omitempty"`
}

Provider Implementations

Uses the tools field in the Chat Completions API:
  • Sends tools as a tools array with tool_choice: "auto"
  • Processes tool_calls in choices[0].message
  • tool messages in the history link results to the tool_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 when IsError=false.
Provider familyHow 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
The model pattern-matches on [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:
type ContentBlock struct {
    Type         string        `json:"type"`
    Text         string        `json:"text"`
    CacheControl *CacheControl `json:"cache_control,omitempty"`
}

type CacheControl struct {
    Type string `json:"type"` // "ephemeral"
}
The cache_control:ephemeral tells Anthropic that the system prompt block can be cached between requests, significantly reducing latency and cost in long conversations.

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.