zed-industries/zed · error

Anthropic does not support custom tools

Error message

Anthropic does not support custom tools

What it means

Thrown by Zed's Anthropic provider while converting a LanguageModelRequest into a Messages API call. The adapter only maps LanguageModelRequestToolInput::Function tools (name, description, JSON Schema); the Custom variant used for MCP-style client-executed tools has no Anthropic equivalent, so the request fails during tool conversion before any network call is made.

Source

Thrown at crates/anthropic/src/completion.rs:334

        Some(StringOrContents::String(system_message))
    };

    let mut tools: Vec<Tool> = request
        .tools
        .into_iter()
        .map(|tool| match tool.input {
            LanguageModelRequestToolInput::Function {
                input_schema,
                use_input_streaming,
            } => Ok(Tool {
                name: tool.name,
                description: tool.description,
                input_schema,
                eager_input_streaming: use_input_streaming,
                cache_control: None,
            }),
            LanguageModelRequestToolInput::Custom { .. } => {
                Err(anyhow::anyhow!("Anthropic does not support custom tools"))
            }
        })
        .collect::<Result<_>>()?;
    if let Some(cache_control) = long_lived_cache
        && let Some(last_tool) = tools.last_mut()
    {
        last_tool.cache_control = Some(cache_control);
    }

    let thinking = if request.thinking_allowed {
        match mode {
            AnthropicModelMode::Thinking { budget_tokens } => {
                Some(Thinking::Enabled { budget_tokens })
            }
            AnthropicModelMode::AdaptiveThinking => Some(Thinking::Adaptive {
                display: Some(AdaptiveThinkingDisplay::Summarized),
            }),
            AnthropicModelMode::Default => None,

View on GitHub (pinned to bc538def45)

Solutions

  1. Route the request to a provider that supports custom tools (e.g. an OpenAI-compatible or Google model) instead of Anthropic.
  2. Convert the custom tool into a function tool: give it a JSON Schema input_schema, let the model call it by name, and execute it client-side on receipt.
  3. Upgrade Zed - later adapters may translate custom tools into Anthropic function-calling form instead of rejecting them.
  4. Filter custom tools before dispatch and surface a targeted message so the user knows which tool is unsupported.

Example fix

// before
request.tools.push(LanguageModelRequestTool {
    name: "read_file".into(),
    description: None,
    input: LanguageModelRequestToolInput::Custom { /* ... */ }, // rejected by Anthropic
});

// after: express it as a function tool the model calls by name
request.tools.push(LanguageModelRequestTool {
    name: "read_file".into(),
    description: Some("Read a file from disk".into()),
    input: LanguageModelRequestToolInput::Function {
        input_schema: json_schema!({
            "type": "object",
            "properties": { "path": { "type": "string" } },
            "required": ["path"]
        }),
        use_input_streaming: false,
    },
});
Defensive patterns

Strategy: validation

Validate before calling

let uses_custom_tools = request
    .tools
    .iter()
    .any(|tool| matches!(tool.input, LanguageModelRequestToolInput::Custom { .. }));
if uses_custom_tools && provider_id.is_anthropic() {
    return Err(anyhow::anyhow!(
        "the Anthropic provider does not support custom tools; convert them to function tools or pick another provider"
    ));
}

Type guard

fn has_only_function_tools(request: &LanguageModelRequest) -> bool {
    request
        .tools
        .iter()
        .all(|tool| matches!(tool.input, LanguageModelRequestToolInput::Function { .. }))
}

Try / catch

match provider.complete(request).await {
    Err(ref e) if e.to_string().contains("does not support custom tools") => {
        // degrade gracefully: strip custom tools and retry, or surface a targeted message
    }
    other => other,
}

Prevention

When it happens

Trigger: Sending a LanguageModelRequest through the Anthropic provider where request.tools contains a tool whose input is LanguageModelRequestToolInput::Custom; the collect::<Result<_>>() over the tool-mapping closure returns this error immediately.

Common situations: An assistant profile or MCP integration registers custom tools and the active model is a Claude model; switching a workspace from an OpenAI-compatible provider (which accepts custom tools) to Anthropic; a newer client emitting Custom tool input against this adapter.

Related errors


AI-assisted analysis of zed-industries/zed@bc538def45 (2026-08-16). Data as JSON: /api/errors/69f75f3698262b28. Report an issue: GitHub.