From 18e852a1313f922120a3bb55fc45e781c39778e9 Mon Sep 17 00:00:00 2001 From: Ashwin Bhat Date: Wed, 3 Dec 2025 17:47:34 -0800 Subject: [PATCH] feat: add tools option to ClaudeAgentOptions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add support for the `tools` option matching the TypeScript SDK, which controls the base set of available tools separately from allowed/disallowed tool filtering. Supports three modes: - Array of tool names: `["Read", "Edit", "Bash"]` - Empty array: `[]` (disables all built-in tools) - Preset object: `{"type": "preset", "preset": "claude_code"}` 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- .../_internal/transport/subprocess_cli.py | 12 +++++ src/claude_agent_sdk/types.py | 8 ++++ tests/test_transport.py | 46 +++++++++++++++++++ 3 files changed, 66 insertions(+) diff --git a/src/claude_agent_sdk/_internal/transport/subprocess_cli.py b/src/claude_agent_sdk/_internal/transport/subprocess_cli.py index 73c1b29..c6f8649 100644 --- a/src/claude_agent_sdk/_internal/transport/subprocess_cli.py +++ b/src/claude_agent_sdk/_internal/transport/subprocess_cli.py @@ -185,6 +185,18 @@ class SubprocessCLITransport(Transport): ["--append-system-prompt", self._options.system_prompt["append"]] ) + # Handle tools option (base set of tools) + if self._options.tools is not None: + tools = self._options.tools + if isinstance(tools, list): + if len(tools) == 0: + cmd.extend(["--tools", ""]) + else: + cmd.extend(["--tools", ",".join(tools)]) + else: + # Preset object - 'claude_code' preset maps to 'default' + cmd.extend(["--tools", "default"]) + if self._options.allowed_tools: cmd.extend(["--allowedTools", ",".join(self._options.allowed_tools)]) diff --git a/src/claude_agent_sdk/types.py b/src/claude_agent_sdk/types.py index f37fd3c..c8b0528 100644 --- a/src/claude_agent_sdk/types.py +++ b/src/claude_agent_sdk/types.py @@ -26,6 +26,13 @@ class SystemPromptPreset(TypedDict): append: NotRequired[str] +class ToolsPreset(TypedDict): + """Tools preset configuration.""" + + type: Literal["preset"] + preset: Literal["claude_code"] + + @dataclass class AgentDefinition: """Agent definition configuration.""" @@ -603,6 +610,7 @@ Message = UserMessage | AssistantMessage | SystemMessage | ResultMessage | Strea class ClaudeAgentOptions: """Query options for Claude SDK.""" + tools: list[str] | ToolsPreset | None = None allowed_tools: list[str] = field(default_factory=list) system_prompt: str | SystemPromptPreset | None = None mcp_servers: dict[str, McpServerConfig] | str | Path = field(default_factory=dict) diff --git a/tests/test_transport.py b/tests/test_transport.py index b834671..c634fc2 100644 --- a/tests/test_transport.py +++ b/tests/test_transport.py @@ -647,3 +647,49 @@ class TestSubprocessCLITransport: assert network["allowLocalBinding"] is True assert network["httpProxyPort"] == 8080 assert network["socksProxyPort"] == 8081 + + def test_build_command_with_tools_array(self): + """Test building CLI command with tools as array of tool names.""" + transport = SubprocessCLITransport( + prompt="test", + options=make_options(tools=["Read", "Edit", "Bash"]), + ) + + cmd = transport._build_command() + assert "--tools" in cmd + tools_idx = cmd.index("--tools") + assert cmd[tools_idx + 1] == "Read,Edit,Bash" + + def test_build_command_with_tools_empty_array(self): + """Test building CLI command with tools as empty array (disables all tools).""" + transport = SubprocessCLITransport( + prompt="test", + options=make_options(tools=[]), + ) + + cmd = transport._build_command() + assert "--tools" in cmd + tools_idx = cmd.index("--tools") + assert cmd[tools_idx + 1] == "" + + def test_build_command_with_tools_preset(self): + """Test building CLI command with tools preset.""" + transport = SubprocessCLITransport( + prompt="test", + options=make_options(tools={"type": "preset", "preset": "claude_code"}), + ) + + cmd = transport._build_command() + assert "--tools" in cmd + tools_idx = cmd.index("--tools") + assert cmd[tools_idx + 1] == "default" + + def test_build_command_without_tools(self): + """Test building CLI command without tools option (default None).""" + transport = SubprocessCLITransport( + prompt="test", + options=make_options(), + ) + + cmd = transport._build_command() + assert "--tools" not in cmd