From 1489baadf2a175ca5b56b44ce6ae23f22162b29d Mon Sep 17 00:00:00 2001 From: driller Date: Wed, 28 Jan 2026 10:32:48 +0900 Subject: [PATCH] fix: distinguish allowed_tools=[] from unset so empty list disables all tools `allowed_tools=[]` was treated as falsy and ignored, causing all tools to remain available instead of none. Change the default from `[]` to `None` and use `is not None` check to correctly pass `--allowedTools ""` to the CLI when an empty list is explicitly provided. Fixes #523 Co-Authored-By: Claude Opus 4.5 --- .../_internal/transport/subprocess_cli.py | 2 +- src/claude_agent_sdk/types.py | 2 +- tests/test_transport.py | 20 +++++++++++++++++++ tests/test_types.py | 2 +- 4 files changed, 23 insertions(+), 3 deletions(-) diff --git a/src/claude_agent_sdk/_internal/transport/subprocess_cli.py b/src/claude_agent_sdk/_internal/transport/subprocess_cli.py index a4882db1..305e2222 100644 --- a/src/claude_agent_sdk/_internal/transport/subprocess_cli.py +++ b/src/claude_agent_sdk/_internal/transport/subprocess_cli.py @@ -198,7 +198,7 @@ def _build_command(self) -> list[str]: # Preset object - 'claude_code' preset maps to 'default' cmd.extend(["--tools", "default"]) - if self._options.allowed_tools: + if self._options.allowed_tools is not None: cmd.extend(["--allowedTools", ",".join(self._options.allowed_tools)]) if self._options.max_turns: diff --git a/src/claude_agent_sdk/types.py b/src/claude_agent_sdk/types.py index 9c09345f..d80c24d3 100644 --- a/src/claude_agent_sdk/types.py +++ b/src/claude_agent_sdk/types.py @@ -618,7 +618,7 @@ class ClaudeAgentOptions: """Query options for Claude SDK.""" tools: list[str] | ToolsPreset | None = None - allowed_tools: list[str] = field(default_factory=list) + allowed_tools: list[str] | None = None system_prompt: str | SystemPromptPreset | None = None mcp_servers: dict[str, McpServerConfig] | str | Path = field(default_factory=dict) permission_mode: PermissionMode | None = None diff --git a/tests/test_transport.py b/tests/test_transport.py index fe9b6b22..5903ede3 100644 --- a/tests/test_transport.py +++ b/tests/test_transport.py @@ -684,6 +684,26 @@ def test_build_command_with_tools_preset(self): tools_idx = cmd.index("--tools") assert cmd[tools_idx + 1] == "default" + def test_build_command_with_allowed_tools_empty_array(self): + """Test that allowed_tools=[] passes --allowedTools with empty value.""" + transport = SubprocessCLITransport( + prompt="test", + options=make_options(allowed_tools=[]), + ) + cmd = transport._build_command() + assert "--allowedTools" in cmd + allowed_idx = cmd.index("--allowedTools") + assert cmd[allowed_idx + 1] == "" + + def test_build_command_without_allowed_tools(self): + """Test that allowed_tools=None (default) omits --allowedTools.""" + transport = SubprocessCLITransport( + prompt="test", + options=make_options(), + ) + cmd = transport._build_command() + assert "--allowedTools" not in cmd + def test_build_command_without_tools(self): """Test building CLI command without tools option (default None).""" transport = SubprocessCLITransport( diff --git a/tests/test_types.py b/tests/test_types.py index 21a84da1..7b17df6d 100644 --- a/tests/test_types.py +++ b/tests/test_types.py @@ -79,7 +79,7 @@ class TestOptions: def test_default_options(self): """Test Options with default values.""" options = ClaudeAgentOptions() - assert options.allowed_tools == [] + assert options.allowed_tools is None assert options.system_prompt is None assert options.permission_mode is None assert options.continue_conversation is False