Description
When an agent has multiple toolsets that produce tools with the same name, the Anthropic API rejects the request with HTTP 400: "tools: Tool names must be unique.". There is no deduplication anywhere in the tool collection pipeline.
Issue #1969 fixed this for the specific case of multiple LSP toolsets (via the LSP multiplexer in teamloader.go), but the general problem remains for all other toolset combinations.
Observed in session: cca99566-4277-457a-92fe-cfd877825c0b
Error:
all models failed: error receiving from stream: HTTP 400: POST
"https://ai-backend-service-stage.docker.com/proxy/v1/messages?beta=true": 400
Bad Request {"type":"error","error":{"type":"invalid_request_error",
"message":"tools: Tool names must be unique."}}
Steps to Reproduce
Option 1 — Duplicate built-in toolsets:
agents:
root:
model: anthropic/claude-sonnet-4-0
instruction: You are a helpful assistant.
toolsets:
- type: filesystem
- type: filesystem
Both filesystem toolsets produce identical tool names (read_file, write_file, edit_file, directory_tree, etc.).
Option 2 — Two unnamed MCP servers exposing the same tool names:
agents:
root:
model: anthropic/claude-sonnet-4-0
instruction: You are a helpful assistant.
toolsets:
- type: mcp
command: npx
args: ["-y", "@modelcontextprotocol/server-filesystem", "/tmp"]
- type: mcp
command: npx
args: ["-y", "@modelcontextprotocol/server-filesystem", "/home"]
Both MCP servers expose identically-named tools and neither has a name prefix configured.
Option 3 — MCP server colliding with a built-in:
agents:
root:
model: anthropic/claude-sonnet-4-0
instruction: You are a helpful assistant.
toolsets:
- type: fetch
- type: mcp
command: npx
args: ["-y", "@modelcontextprotocol/server-fetch"]
The built-in fetch toolset and the MCP fetch server both produce a tool named fetch.
Root Cause
Agent.Tools() in pkg/agent/agent.go collects tools from all toolsets via simple append with no deduplication. No downstream code checks for uniqueness either:
Agent.Tools() (pkg/agent/agent.go) — appends from all toolsets, no dedup
getTools() (pkg/runtime/loop.go) — passes tools through as-is
convertTools() (pkg/model/provider/anthropic/client.go) — maps 1:1 to API params, no dedup
- Anthropic API rejects the request
Scenarios that produce duplicates
- Two MCP servers without a
name prefix (or with the same prefix) exposing same-named tools
- An MCP server returning a tool that collides with a built-in name
- Same toolset type configured twice in agent config
- Dynamic MCP
ToolListChanged notifications adding tools that collide with existing names
Suggested Fix
Add deduplication in Agent.Tools() — the single collection point where all tools converge before being sent to any provider. Keep the first occurrence of each tool name and log a warning for dropped duplicates.
Description
When an agent has multiple toolsets that produce tools with the same name, the Anthropic API rejects the request with HTTP 400:
"tools: Tool names must be unique.". There is no deduplication anywhere in the tool collection pipeline.Issue #1969 fixed this for the specific case of multiple LSP toolsets (via the LSP multiplexer in
teamloader.go), but the general problem remains for all other toolset combinations.Observed in session:
cca99566-4277-457a-92fe-cfd877825c0bError:
Steps to Reproduce
Option 1 — Duplicate built-in toolsets:
Both filesystem toolsets produce identical tool names (
read_file,write_file,edit_file,directory_tree, etc.).Option 2 — Two unnamed MCP servers exposing the same tool names:
Both MCP servers expose identically-named tools and neither has a
nameprefix configured.Option 3 — MCP server colliding with a built-in:
The built-in
fetchtoolset and the MCP fetch server both produce a tool namedfetch.Root Cause
Agent.Tools()inpkg/agent/agent.gocollects tools from all toolsets via simpleappendwith no deduplication. No downstream code checks for uniqueness either:Agent.Tools()(pkg/agent/agent.go) — appends from all toolsets, no dedupgetTools()(pkg/runtime/loop.go) — passes tools through as-isconvertTools()(pkg/model/provider/anthropic/client.go) — maps 1:1 to API params, no dedupScenarios that produce duplicates
nameprefix (or with the same prefix) exposing same-named toolsToolListChangednotifications adding tools that collide with existing namesSuggested Fix
Add deduplication in
Agent.Tools()— the single collection point where all tools converge before being sent to any provider. Keep the first occurrence of each tool name and log a warning for dropped duplicates.