You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
[FEATURE] Port bypass_multi_tools_limit for built-in search tools from adk-python聽#1598
Is your feature request related to a specific problem?
On the Java engine, a built-in search tool has to be the only tool on its agent when the agent runs through runAsync. On main (2d4a59e), nothing handles that case for you:
GoogleSearchTool next to a function tool, or on an agent with transfer targets (sub-agents, or a parent or peers, which add transfer_to_agent), goes into the same request as those function declarations. google/adk-python@f3250bd reports that Gemini rejects such a request with 400 INVALID_ARGUMENT: Tool use with function calling is unsupported. Documented exceptions include the Live API and, for generateContent, the Gemini 3 tool-combination preview (see Alternatives); ADK Java's runAsync path uses neither.
GoogleSearchAgentTool and VertexAiSearchAgentTool (added in feat: Add VertexAiSearchTool and AgentTools for search聽#692) wrap the search in a nested agent, but nothing creates them for you. They also return only the nested agent's text: AgentTool.runAsync turns the last event into {"result": text}, so the grounding metadata from the search (queries, sources, and for Google Search the searchEntryPoint with the Search Suggestions) never reaches the caller.
When the agent has more than one tool or any transfer target, LlmAgent.canonical_tools replaces a flagged GoogleSearchTool with GoogleSearchAgentTool, and a flagged VertexAiSearchTool with DiscoveryEngineSearchTool.
For a search tool without the flag on an agent with transfer targets, the agent-transfer processor raises a ValueError that names the flag when a sub-agent is a target, and otherwise leaves out transfer_to_agent (google/adk-python@f3250bd).
ADK Kotlin has the same flag (GoogleSearchTool(bypassMultiToolsLimit = true), with a builder for Java callers). When the agent's tools and toolsets add up to more than one (transfer targets are not counted), it replaces flagged tools with GoogleSearchAgentTool and VertexAiSearchAgentTool, both created with propagateGroundingMetadata = true.
Describe the Solution You'd Like
The same opt-in behavior on the Java engine, in three steps that can be reviewed separately:
Grounding propagation. An opt-in on AgentTool that carries the grounding metadata of the nested agent's last content event back to the caller, turned on in GoogleSearchAgentTool and VertexAiSearchAgentTool. adk-python stores it under temp:_adk_grounding_metadata and attaches it to the caller's next model response; ADK Kotlin writes it under the same key into the state delta of the caller's function response event.
Google Search. A bypassMultiToolsLimit option on GoogleSearchTool, off by default, with INSTANCE unchanged. When the agent has more than one tool or any transfer target, a flagged GoogleSearchTool is replaced with GoogleSearchAgentTool.create(model) in the tool list that requests are built from. On main, BaseLlmFlow.getRequestProcessorFromTools reads LlmAgent.toolsUnion() directly, so the replacement has to reach that path as well as LlmAgent.canonicalTools. The search agent's instruction would also change the way google/adk-python@56d3cae changed it, so that the model answers with its built-in grounding instead of emitting a client-side function call (google:search).
Vertex AI Search. The same option on VertexAiSearchTool.
Questions before I start:
Is the Java engine the right place for this, or is the ADK Kotlin engine through tokt the intended path for Java apps that need it? (See Alternatives for what that path covers today.)
For a search tool without the flag on an agent with transfer targets, do you want adk-python's behavior in Java (the ValueError, or leaving out transfer_to_agent), or should that stay out of scope for now?
For the option itself, a builder (GoogleSearchTool.builder().bypassMultiToolsLimit(true).build(), like the one ADK Kotlin exposes to Java) or a constructor argument?
Impact on your work
On the Java engine, agents that need Google Search or Vertex AI Search together with function tools or sub-agents have to be wired by hand today, and they lose the search sources on the way. For Google Search, that includes the searchEntryPoint that carries the Search Suggestions the Gemini API terms ask apps to show alongside grounded results. No hard timeline on my side.
Willingness to contribute
Yes. Once the questions above are settled, I would start with step 1. It also has to wait for #1563, since both change AgentTool.runAsync.
馃煛 Recommended Information
Describe Alternatives You've Considered
Wrapping by hand with GoogleSearchAgentTool.create(model) or AgentTool.create(searchAgent). Then no request mixes the built-in tool with function declarations, but the grounding metadata is dropped (see below), and users first have to find the wrapper. Built-in tools not available in sub agents (issue with GoogleSearchTool)聽#550 asked how to use Google Search in a multi-agent setup. The limitations page in the ADK docs shows AgentTool.create(...) for Java and tags the bypass_multi_tools_limit workaround as supported in Java, which it is not yet.
Running on the ADK Kotlin engine through tokt. Since 1.10.0 (on Maven Central from 1.10.1), the google-adk-tokt module lets a Java app run agents on the ADK Kotlin engine, where ADK Kotlin's GoogleSearchTool.builder().bypassMultiToolsLimit(true).build() is available. That means rebuilding the agent tree on the Kotlin engine (tokt adapts tools, toolsets, plugins, models and services, not whole Java agents). ADK Kotlin's replacement also does not count transfer targets, and its transfer processor adds transfer_to_agent without checking for built-in tools, so the sub-agent case from Built-in tools not available in sub agents (issue with GoogleSearchTool)聽#550 would still send both. This request is about the Java engine.
Gemini 3 tool combination. The Gemini Developer API (generateContent) accepts built-in tools together with function declarations for Gemini 3 models (released on 2026-03-18 and still marked Preview; it needs tool_config.include_server_side_tool_invocations, and the toolCall/toolResponse parts have to be sent back on every turn). That could replace the workaround later, but it is Preview and Gemini 3 only, and adk-python keeps the workaround: google/adk-python@deabb2a (2026-07-08) says "The built-in tools still cannot be combined with other tools, so the workaround stays", and google/adk-python@f3250bd and google/adk-python@56d3cae changed the workaround after that. Because the flag is opt-in, it would be easy to retire.
Proposed API / Implementation
LlmAgentroot =
LlmAgent.builder()
.name("root")
.model("gemini-flash-latest")
.tools(
// or a constructor argument; see the questions aboveGoogleSearchTool.builder().bypassMultiToolsLimit(true).build(),
FunctionTool.create(WeatherTools.class, "getWeather"))
.subAgents(bookingAgent)
.build();
// Requests from root declare google_search_agent (GoogleSearchAgentTool), getWeather and// transfer_to_agent, with no built-in googleSearch tool. After a search, the root agent's next// model response carries the search agent's grounding metadata.
Additional Context
What main (2d4a59e) does today, from a scratch JUnit test with TestLlm (no network):
Setup
Result
GoogleSearchTool.INSTANCE + one sub-agent
One request with the googleSearch tool and the transfer_to_agent declaration
GoogleSearchTool.INSTANCE + a function tool
Both in one request; nothing is replaced
Root agent with GoogleSearchAgentTool + a function tool; the search agent's model response has grounding metadata
The function response is {"result": <text>}, and none of the root agent's events has grounding metadata
The search agent run on its own
Its final event keeps the grounding metadata, so it is lost in AgentTool
The test covers only the sub-agent case. An agent whose only transfer targets are an LlmAgent parent or its peers gets transfer_to_agent from the same AgentTransfer.processRequest.
The grounding case from that test
GroundingMetadatagrounding =
GroundingMetadata.builder().webSearchQueries(ImmutableList.of("adk java")).build();
TestLlmsearchLlm =
createTestLlm(
LlmResponse.builder()
.content(Content.builder().role("model").parts(Part.fromText("ADK Java is a toolkit")).build())
.groundingMetadata(grounding)
.build());
TestLlmrootLlm =
createTestLlm(
createFunctionCallLlmResponse(
"call-1", "google_search_agent", ImmutableMap.of("request", "adk java")),
createTextLlmResponse("final answer"));
LlmAgentroot =
createTestAgentBuilder(rootLlm)
.name("root")
.tools(GoogleSearchAgentTool.create(searchLlm), newEchoTool())
.build();
// Run root through an InMemoryRunner with "what is adk java?", then:// - the function response is {"result": "ADK Java is a toolkit"}// - no event from the root agent has groundingMetadata()
Across ADK languages: adk-python and ADK Kotlin have the flag and the replacement; adk-js has the flag on VertexAiSearchTool only, to skip its Gemini 1.x check; adk-go has neither.
Hi @innoprej, Thank you for submitting this feature request. our team is currently reviewing your proposal and we will reach out if we need any further information. Thank you!
馃敶 Required Information
Is your feature request related to a specific problem?
On the Java engine, a built-in search tool has to be the only tool on its agent when the agent runs through
runAsync. Onmain(2d4a59e), nothing handles that case for you:GoogleSearchToolnext to a function tool, or on an agent with transfer targets (sub-agents, or a parent or peers, which addtransfer_to_agent), goes into the same request as those function declarations. google/adk-python@f3250bd reports that Gemini rejects such a request with400 INVALID_ARGUMENT: Tool use with function calling is unsupported. Documented exceptions include the Live API and, forgenerateContent, the Gemini 3 tool-combination preview (see Alternatives); ADK Java'srunAsyncpath uses neither.GoogleSearchAgentToolandVertexAiSearchAgentTool(added in feat: Add VertexAiSearchTool and AgentTools for search聽#692) wrap the search in a nested agent, but nothing creates them for you. They also return only the nested agent's text:AgentTool.runAsyncturns the last event into{"result": text}, so the grounding metadata from the search (queries, sources, and for Google Search thesearchEntryPointwith the Search Suggestions) never reaches the caller.adk-python handles both behind an opt-in flag:
GoogleSearchTool(bypass_multi_tools_limit=True)andVertexAiSearchTool(..., bypass_multi_tools_limit=True)(google/adk-python@9a6b850; off by default since google/adk-python@6da7274).LlmAgent.canonical_toolsreplaces a flaggedGoogleSearchToolwithGoogleSearchAgentTool, and a flaggedVertexAiSearchToolwithDiscoveryEngineSearchTool.GoogleSearchAgentToolhas carried the nested agent's grounding metadata to the caller's next model response since it was added (google/adk-python@d3148da); google/adk-python@d689a04 moved that intoAgentToolbehind an opt-inpropagate_grounding_metadata, whichGoogleSearchAgentToolsets toTrue. The Java port of the wrapper in feat: Add VertexAiSearchTool and AgentTools for search聽#692 does not carry it.ValueErrorthat names the flag when a sub-agent is a target, and otherwise leaves outtransfer_to_agent(google/adk-python@f3250bd).ADK Kotlin has the same flag (
GoogleSearchTool(bypassMultiToolsLimit = true), with a builder for Java callers). When the agent's tools and toolsets add up to more than one (transfer targets are not counted), it replaces flagged tools withGoogleSearchAgentToolandVertexAiSearchAgentTool, both created withpropagateGroundingMetadata = true.Describe the Solution You'd Like
The same opt-in behavior on the Java engine, in three steps that can be reviewed separately:
AgentToolthat carries the grounding metadata of the nested agent's last content event back to the caller, turned on inGoogleSearchAgentToolandVertexAiSearchAgentTool. adk-python stores it undertemp:_adk_grounding_metadataand attaches it to the caller's next model response; ADK Kotlin writes it under the same key into the state delta of the caller's function response event.bypassMultiToolsLimitoption onGoogleSearchTool, off by default, withINSTANCEunchanged. When the agent has more than one tool or any transfer target, a flaggedGoogleSearchToolis replaced withGoogleSearchAgentTool.create(model)in the tool list that requests are built from. Onmain,BaseLlmFlow.getRequestProcessorFromToolsreadsLlmAgent.toolsUnion()directly, so the replacement has to reach that path as well asLlmAgent.canonicalTools. The search agent's instruction would also change the way google/adk-python@56d3cae changed it, so that the model answers with its built-in grounding instead of emitting a client-side function call (google:search).VertexAiSearchTool.Questions before I start:
toktthe intended path for Java apps that need it? (See Alternatives for what that path covers today.)VertexAiSearchAgentTool(as in ADK Kotlin: no new dependency, and the search stays the model's built-in retrieval) or a port ofDiscoveryEngineSearchTool(as in adk-python)?bypass_multi_tools_limitonVertexAiSearchToolis a leaky abstraction: unexpected dependencies, silent shift from inbuilt RAG to tool calling, and prompt fragility聽adk-python#7100 lists problems with the second approach.ValueError, or leaving outtransfer_to_agent), or should that stay out of scope for now?GoogleSearchTool.builder().bypassMultiToolsLimit(true).build(), like the one ADK Kotlin exposes to Java) or a constructor argument?Impact on your work
On the Java engine, agents that need Google Search or Vertex AI Search together with function tools or sub-agents have to be wired by hand today, and they lose the search sources on the way. For Google Search, that includes the
searchEntryPointthat carries the Search Suggestions the Gemini API terms ask apps to show alongside grounded results. No hard timeline on my side.Willingness to contribute
Yes. Once the questions above are settled, I would start with step 1. It also has to wait for #1563, since both change
AgentTool.runAsync.馃煛 Recommended Information
Describe Alternatives You've Considered
GoogleSearchAgentTool.create(model)orAgentTool.create(searchAgent). Then no request mixes the built-in tool with function declarations, but the grounding metadata is dropped (see below), and users first have to find the wrapper. Built-in tools not available in sub agents (issue with GoogleSearchTool)聽#550 asked how to use Google Search in a multi-agent setup. The limitations page in the ADK docs showsAgentTool.create(...)for Java and tags thebypass_multi_tools_limitworkaround as supported in Java, which it is not yet.tokt. Since 1.10.0 (on Maven Central from 1.10.1), thegoogle-adk-toktmodule lets a Java app run agents on the ADK Kotlin engine, where ADK Kotlin'sGoogleSearchTool.builder().bypassMultiToolsLimit(true).build()is available. That means rebuilding the agent tree on the Kotlin engine (toktadapts tools, toolsets, plugins, models and services, not whole Java agents). ADK Kotlin's replacement also does not count transfer targets, and its transfer processor addstransfer_to_agentwithout checking for built-in tools, so the sub-agent case from Built-in tools not available in sub agents (issue with GoogleSearchTool)聽#550 would still send both. This request is about the Java engine.generateContent) accepts built-in tools together with function declarations for Gemini 3 models (released on 2026-03-18 and still marked Preview; it needstool_config.include_server_side_tool_invocations, and thetoolCall/toolResponseparts have to be sent back on every turn). That could replace the workaround later, but it is Preview and Gemini 3 only, and adk-python keeps the workaround: google/adk-python@deabb2a (2026-07-08) says "The built-in tools still cannot be combined with other tools, so the workaround stays", and google/adk-python@f3250bd and google/adk-python@56d3cae changed the workaround after that. Because the flag is opt-in, it would be easy to retire.Proposed API / Implementation
Additional Context
What
main(2d4a59e) does today, from a scratch JUnit test withTestLlm(no network):GoogleSearchTool.INSTANCE+ one sub-agentgoogleSearchtool and thetransfer_to_agentdeclarationGoogleSearchTool.INSTANCE+ a function toolGoogleSearchAgentTool+ a function tool; the search agent's model response has grounding metadata{"result": <text>}, and none of the root agent's events has grounding metadataAgentToolThe test covers only the sub-agent case. An agent whose only transfer targets are an
LlmAgentparent or its peers getstransfer_to_agentfrom the sameAgentTransfer.processRequest.The grounding case from that test
Across ADK languages: adk-python and ADK Kotlin have the flag and the replacement; adk-js has the flag on
VertexAiSearchToolonly, to skip its Gemini 1.x check; adk-go has neither.Related: #550, #692, #387.