Allow validated `McpToolset` args in local YAML agents served by `adk web`
- Dominant language
- Python
- Stars
- 21.5k
- Forks
- 4k
- Avg merge
- 1d 14h
- Merged PRs (30d)
- 37
Description
## 🔴 Required Information
### Is your feature request related to a specific problem?
Yes.
`adk web` enables a recursive denylist that rejects every YAML key named `args`.
This is an important defense-in-depth mitigation for CVE-2026-4810: a generic tool configuration can combine a Python code reference in `name` with attacker-controlled `args`, causing ADK to import and invoke arbitrary Python code (74f235b).
However, the key-level denylist also rejects built-in tools whose arguments are declarative configuration.
In particular, the existing `McpToolset` YAML sample cannot be loaded by `adk web`:
```yaml
name: retrospective_master
instruction: Use the MCP tools.
model: gemini-3.7-flash
tools:
- name: McpToolset
args:
streamable_http_connection_params:
url: https://hurikaeri-site.viva-tweet-x.workers.dev/mcp
```
The same agent works when constructed in Python or loaded by `adk run`.
Under `adk web`, the local YAML agent fails to load and is absent from the agent loader, so a subsequent `POST /run_sse` returns `404 Not Found`.
https://github.com/google/adk-python/blob/25e8ea6dea82713fdac9bc4117ec425386582b1f/src/google/adk/agents/config_agent_utils.py#L86
https://github.com/google/adk-python/blob/25e8ea6dea82713fdac9bc4117ec425386582b1f/src/google/adk/agents/config_agent_utils.py#L99-L104
### Describe the Solution You'd Like
Replace the all-or-nothing key check for locally loaded YAML with a positive allowlist for security-reviewed ADK built-ins.
For an allowlisted built-in such as the exact literal name `McpToolset`:
1. Do not resolve a module name supplied by YAML.
2. Validate `args` with a fixed ADK-owned Pydantic schema.
3. Reject values that cannot be represented by that schema, including config-supplied callable fields.
4. Construct the fixed ADK-owned tool class through its normal `from_config()` implementation.
5. Continue rejecting `args` for user-defined tools, factories, custom agents, callbacks, and every unregistered built-in.
6. Preserve the existing default rejection of config-supplied stdio MCP servers unless `ADK_ALLOW_CONFIG_STDIO_MCP_SERVERS=1` is explicitly set.
This proposal applies only to configurations loaded from the local agents directory.
Agent Builder uploads should continue rejecting every `args` key until ADK has a separate policy for config-supplied remote MCP URLs and SSRF.
### Impact on your work
This restores the ability to define an `McpToolset` in a local `root_agent.yaml` and use the agent from the ADK Web chat UI, without reopening the generic `name` plus `args` arbitrary-code-execution path.
It also makes the existing config-based MCP sample consistent with `adk web` when the documented stdio opt-in is enabled.
### Willingness to contribute
Yes.
---
## 🟡 Recommended Information
### Describe Alternatives You've Considered
1. Keep rejecting every `args` key.
This closes the reported RCE path but prevents declarative configuration for `McpToolset` and other reviewed built-ins.
2. Add more blocked Python modules.
Module denylists are useful defense in depth, but do not provide a safe path for restoring declarative built-in configuration.
Alias modules and future Python releases also require continuing denylist maintenance.
3. Allow `args` whenever `name` appears to reference `McpToolset`.
This is too broad if arbitrary qualified names or custom agent schemas can opt into the exception.
Matching must be limited to the typed `LlmAgent.tools` position and an exact, statically registered built-in name.
4. Permit the same exception in Agent Builder uploads.
This would also allow uploaded YAML to select remote MCP URLs.
That requires a separate SSRF policy, such as allowed origins or operator-controlled server references, and is intentionally excluded from the initial change.
5. Require Python-based agent definitions.
This is a valid workaround, but makes the checked-in YAML MCP sample
unusable with the ADK Web chat UI.
### Proposed API / Implementation
The user-facing YAML retains its existing shape:
```yaml
name: mcp_agent
model: gemini-3.7-flash
instruction: Use the MCP tools.
tools:
- name: McpToolset
args:
streamable_http_connection_params:
url: https://example.com/mcp
```
Conceptually, validation uses a static registry:
```python
SAFE_BUILTIN_TOOL_ARGS_VALIDATORS = {
"McpToolset": validate_mcp_toolset_args,
}
validator = SAFE_BUILTIN_TOOL_ARGS_VALIDATORS.get(tool.name)
if validator is not None:
validator(tool.args)
return construct_registered_builtin(tool.name, tool.args)
reject_args()
```
The registry key is matched literally and is never passed to `importlib.import_module()`.
For stdio configurations, the existing `ADK_ALLOW_CONFIG_STDIO_MCP_SERVERS=1` operator opt-in remains required.
The exception applies only when `args` appears on a registered tool under the `tools` field of a built-in `LlmAgent`.
A custom agent containing a lookalike `tools` field does not receive the exception.
### Acceptance Criteria
- A local `root_agent.yaml` containing valid registered `McpToolset.args` loads under `adk web`.
- A remote HTTP McpToolset can be constructed without importing a YAML-supplied module.
- A stdio McpToolset remains rejected by default.
- A stdio McpToolset loads only after the existing explicit operator opt-in.
- `args` on user-defined tools and factories remains rejected.
- A callable field such as `httpx_client_factory: os.system` is rejected.
- A custom agent containing a lookalike `tools` field cannot opt into the exception.
- Agent Builder uploads continue rejecting every `args` key.
- Existing module-reference and project-boundary checks continue to pass.
### Additional Context
Related security work:
- CVE-2026-4810 discussion: https://github.com/google/adk-python/discussions/5346
- Restrict Builder YAML code references: https://github.com/google/adk-python/issues/5292
- Restrict arbitrary module imports from YAML: https://github.com/google/adk-python/issues/5822
Contributor guide
Assessment
This issue has not been assessed yet.