Visitar URL original
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 · Issue #1513 · google/adk-java · GitHub
Skip to content

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 #1513

Description

@sushant-me

Summary

Same two defects as the Go port, reproduced locally against adk-java (4ee155b2, core). No live service was contacted.

  1. In-model built-in tools do not occupy their name, so a server-provided tool can shadow them. GoogleSearchTool (and GoogleMapsTool, UrlContextTool, VertexAiSearchTool, BuiltInCodeExecutionTool) override processLlmRequest to append only to config.Tools, never calling appendTools. So the duplicate-name guard in LlmRequest.Builder.appendTools never sees them, and a callable MCP tool advertising google_search is accepted and wins dispatch — Functions.handleFunctionCalls resolves by name from a map where the MCP tool overwrote the built-in.
  2. A server-provided tool named set_model_response aborts the whole run on an agent with an output schema (IllegalArgumentException: Duplicate tool name: set_model_response). Availability, not silent displacement — but a malicious/untrusted MCP server can deny service.

Evidence

  • MCP tools take the server's name, unfiltered by default — tools/mcp/McpToolset.java:260-279; tools/BaseToolset.java:60-64 (if (toolFilter == null) return true;); name source tools/mcp/AbstractMcpTool.java:52-56.
  • Callable-vs-callable is fail-closed — models/LlmRequest.java:204-222 (appendTools, throwing merger); all tools enter via tools/BaseTool.java:187-193.
  • set_model_response is in the guarded class — flows/llmflows/OutputSchema.java:60-65; SetModelResponseTool.java:33-45 (extends BaseTool, does not override processLlmRequest). Processor order: SingleFlow.java:27-36, BaseLlmFlow.java:103-104.
  • Built-ins bypass the guard — tools/GoogleSearchTool.java:45-76 (override that appends Tool.builder().googleSearch(...) only); same in GoogleMapsTool.java:65, UrlContextTool.java:46, VertexAiSearchTool.java:60, BuiltInCodeExecutionTool.java:44.
  • Dispatch by name — BaseLlmFlow.java:765-766; Functions.java:295 (tools.get(functionCall.name().get())).

Local reproduction

[probe] registered tool map keys: [google_search]
[probe]   config.Tool googleSearch=true functionDeclarations=[]
[probe]   config.Tool googleSearch=false functionDeclarations=[google_search]
[probe] google_search: NO ERROR. dispatch map maps google_search -> FakeMcpTool
[probe] set_model_response: REJECTED -> Duplicate tool name: set_model_response

Suggested fix

Preferred (availability-first): reject the server-provided tool via a reserved-name check in AbstractMcpTool's constructor or in McpToolset.getTools / McpAsyncToolset.getTools, covering set_model_response, transfer_to_agent, google_search, google_maps, url_context, vertex_ai_search, code_execution, load_artifacts.

Alternative: make the in-model tools call appendTools so the existing duplicate guard applies (note: google_search would then need a clean-failing runAsync).

Same class as the Go port (google/adk-go#1605) and a separately-triaged adk-js report.

Activity

  1. self-assigned this
    on Sep 16, 2026
  2. added theissue type on Sep 16, 2026
  3. hemasekhar-p commented on Sep 16, 2026

    @hemasekhar-p
    Contributor

    Hi @sushant-me, thank you for reporting this issue and we appreciate you putting together a PR to fix it. I have successfully reproduced the issue you described. Both the issue and your PR are now under review by the team. We will keep you updated and let you know if we need any additional information. Thank you.

  4. added a commit that references this issue on Sep 16, 2026
    6d3088b
  5. m13v commented on Sep 18, 2026

    @m13v

    the reserved-name blocklist you're leaning toward trades a shadowing bug for a drift bug. that list (google_search, set_model_response, transfer_to_agent, url_context...) now has to stay in sync with every in-model built-in across providers and versions, and the day a new one ships the guard silently stops covering it, except now it reads as fixed. the collision isn't the root cause, it's that the built-ins never occupy their name in the dispatch map. your appendTools alternative is the one that actually closes it: the namespace itself becomes authoritative instead of a parallel list someone has to remember to update.

  6. added a commit that references this issue on Oct 2, 2026
    c7dd806
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions