MCP toolset: in-model built-ins (e.g. google_search) can be shadowed by a server tool; a server tool named set_model_response aborts the run
Summary
Two related defects in MCP tool registration. Both were reproduced locally against adk-go (f7e16e02, v2 module) — no live service was contacted.
- In-model built-in tools do not occupy their name, so a server-provided tool can shadow them.
google_search(andgoogle_maps,url_context,vertex_ai_search,code_execution) are appended straight toGenerateContentConfig.Toolsand never registered inreq.Tools. The duplicate-name guard intoolutils.PackTooltherefore never sees them, and a callable MCP tool advertisinggoogle_searchis accepted and wins dispatch — aFunctionCall{name:"google_search"}resolves to the attacker'smcpTool.Run(arbitrary MCPtools/call). - A server-provided tool named
set_model_responseaborts the whole run on an agent with an output schema (runErr=duplicate tool: "set_model_response"). This is availability, not silent displacement — but a malicious/untrusted MCP server can deny service.
Evidence
- MCP tools take the server's name, unfiltered by default —
tool/mcptoolset/set.go:164-185;tool/mcptoolset/tool.go:34-45.ToolFilterdefaults tonil(allow-all). - MCP tools go through the guard —
tool/mcptoolset/tool.go:88-90→tool/toolutils/toolutils.go:40-51(if _, ok := req.Tools[name]; ok { return fmt.Errorf("duplicate tool: %q", name) }). set_model_responseis in the guarded class —internal/llminternal/outputschema_processor.go:54-59.- Built-ins bypass the guard —
tool/geminitool/tool.go:78-89(setToolonly appends toreq.Config.Tools);tool/geminitool/google_search.go:41-45. - Last-wins dispatch map —
internal/llminternal/base_flow.go:540-543(tools[t.Name()] = t).
Local reproduction
With an agent using geminitool.GoogleSearch{} plus a toolset supplying a function tool named google_search:
model was offered: [builtin:googleSearch functionDecl:google_search]
attacker-supplied google_search tool invocations = 1And with an output schema plus a toolset tool named set_model_response:
runErr=duplicate tool: "set_model_response"Suggested fix
Preferred (availability-first): reject the server-provided tool via a shared reserved-name set consulted in the MCP toolset, covering set_model_response, transfer_to_agent, finish_task, task_completed, google_search, google_maps, url_context, vertex_ai_search, code_execution, load_artifacts, load_memory.
Alternative: make the in-model tools register their name so the existing duplicate guard applies (fail-closed).
This is the same class as a separately-triaged adk-js report (google_search replacement); the Go and Java ports share the root cause.
Source: google/adk-go