mirror of
https://github.com/anthropics/claude-code-sdk-python.git
synced 2025-12-23 09:19:52 +00:00
feat: add tools option to ClaudeAgentOptions (#389)
Some checks are pending
Lint / lint (push) Waiting to run
Test / test (macos-latest, 3.10) (push) Waiting to run
Test / test (macos-latest, 3.11) (push) Waiting to run
Test / test (macos-latest, 3.13) (push) Waiting to run
Test / test (ubuntu-latest, 3.10) (push) Waiting to run
Test / test (macos-latest, 3.12) (push) Waiting to run
Test / test (ubuntu-latest, 3.11) (push) Waiting to run
Test / test (ubuntu-latest, 3.12) (push) Waiting to run
Test / test (ubuntu-latest, 3.13) (push) Waiting to run
Test / test (windows-latest, 3.10) (push) Waiting to run
Test / test (windows-latest, 3.11) (push) Waiting to run
Test / test (windows-latest, 3.12) (push) Waiting to run
Test / test (windows-latest, 3.13) (push) Waiting to run
Test / test-e2e (macos-latest, 3.10) (push) Blocked by required conditions
Test / test-e2e (macos-latest, 3.11) (push) Blocked by required conditions
Test / test-e2e (ubuntu-latest, 3.10) (push) Blocked by required conditions
Test / test-e2e (ubuntu-latest, 3.12) (push) Blocked by required conditions
Test / test-e2e (ubuntu-latest, 3.13) (push) Blocked by required conditions
Test / test-e2e (windows-latest, 3.10) (push) Blocked by required conditions
Test / test-e2e (windows-latest, 3.11) (push) Blocked by required conditions
Test / test-e2e (macos-latest, 3.12) (push) Blocked by required conditions
Test / test-e2e (macos-latest, 3.13) (push) Blocked by required conditions
Test / test-e2e (ubuntu-latest, 3.11) (push) Blocked by required conditions
Test / test-e2e (windows-latest, 3.13) (push) Blocked by required conditions
Test / test-examples (3.13) (push) Blocked by required conditions
Test / test-e2e (windows-latest, 3.12) (push) Blocked by required conditions
Some checks are pending
Lint / lint (push) Waiting to run
Test / test (macos-latest, 3.10) (push) Waiting to run
Test / test (macos-latest, 3.11) (push) Waiting to run
Test / test (macos-latest, 3.13) (push) Waiting to run
Test / test (ubuntu-latest, 3.10) (push) Waiting to run
Test / test (macos-latest, 3.12) (push) Waiting to run
Test / test (ubuntu-latest, 3.11) (push) Waiting to run
Test / test (ubuntu-latest, 3.12) (push) Waiting to run
Test / test (ubuntu-latest, 3.13) (push) Waiting to run
Test / test (windows-latest, 3.10) (push) Waiting to run
Test / test (windows-latest, 3.11) (push) Waiting to run
Test / test (windows-latest, 3.12) (push) Waiting to run
Test / test (windows-latest, 3.13) (push) Waiting to run
Test / test-e2e (macos-latest, 3.10) (push) Blocked by required conditions
Test / test-e2e (macos-latest, 3.11) (push) Blocked by required conditions
Test / test-e2e (ubuntu-latest, 3.10) (push) Blocked by required conditions
Test / test-e2e (ubuntu-latest, 3.12) (push) Blocked by required conditions
Test / test-e2e (ubuntu-latest, 3.13) (push) Blocked by required conditions
Test / test-e2e (windows-latest, 3.10) (push) Blocked by required conditions
Test / test-e2e (windows-latest, 3.11) (push) Blocked by required conditions
Test / test-e2e (macos-latest, 3.12) (push) Blocked by required conditions
Test / test-e2e (macos-latest, 3.13) (push) Blocked by required conditions
Test / test-e2e (ubuntu-latest, 3.11) (push) Blocked by required conditions
Test / test-e2e (windows-latest, 3.13) (push) Blocked by required conditions
Test / test-examples (3.13) (push) Blocked by required conditions
Test / test-e2e (windows-latest, 3.12) (push) Blocked by required conditions
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 <noreply@anthropic.com>
This commit is contained in:
parent
4e56cb12a9
commit
ea0ef25e71
4 changed files with 177 additions and 0 deletions
111
examples/tools_option.py
Normal file
111
examples/tools_option.py
Normal file
|
|
@ -0,0 +1,111 @@
|
|||
#!/usr/bin/env python3
|
||||
"""Example demonstrating the tools option and verifying tools in system message."""
|
||||
|
||||
import anyio
|
||||
|
||||
from claude_agent_sdk import (
|
||||
AssistantMessage,
|
||||
ClaudeAgentOptions,
|
||||
ResultMessage,
|
||||
SystemMessage,
|
||||
TextBlock,
|
||||
query,
|
||||
)
|
||||
|
||||
|
||||
async def tools_array_example():
|
||||
"""Example with tools as array of specific tool names."""
|
||||
print("=== Tools Array Example ===")
|
||||
print("Setting tools=['Read', 'Glob', 'Grep']")
|
||||
print()
|
||||
|
||||
options = ClaudeAgentOptions(
|
||||
tools=["Read", "Glob", "Grep"],
|
||||
max_turns=1,
|
||||
)
|
||||
|
||||
async for message in query(
|
||||
prompt="What tools do you have available? Just list them briefly.",
|
||||
options=options,
|
||||
):
|
||||
if isinstance(message, SystemMessage) and message.subtype == "init":
|
||||
tools = message.data.get("tools", [])
|
||||
print(f"Tools from system message: {tools}")
|
||||
print()
|
||||
elif isinstance(message, AssistantMessage):
|
||||
for block in message.content:
|
||||
if isinstance(block, TextBlock):
|
||||
print(f"Claude: {block.text}")
|
||||
elif isinstance(message, ResultMessage):
|
||||
if message.total_cost_usd:
|
||||
print(f"\nCost: ${message.total_cost_usd:.4f}")
|
||||
print()
|
||||
|
||||
|
||||
async def tools_empty_array_example():
|
||||
"""Example with tools as empty array (disables all built-in tools)."""
|
||||
print("=== Tools Empty Array Example ===")
|
||||
print("Setting tools=[] (disables all built-in tools)")
|
||||
print()
|
||||
|
||||
options = ClaudeAgentOptions(
|
||||
tools=[],
|
||||
max_turns=1,
|
||||
)
|
||||
|
||||
async for message in query(
|
||||
prompt="What tools do you have available? Just list them briefly.",
|
||||
options=options,
|
||||
):
|
||||
if isinstance(message, SystemMessage) and message.subtype == "init":
|
||||
tools = message.data.get("tools", [])
|
||||
print(f"Tools from system message: {tools}")
|
||||
print()
|
||||
elif isinstance(message, AssistantMessage):
|
||||
for block in message.content:
|
||||
if isinstance(block, TextBlock):
|
||||
print(f"Claude: {block.text}")
|
||||
elif isinstance(message, ResultMessage):
|
||||
if message.total_cost_usd:
|
||||
print(f"\nCost: ${message.total_cost_usd:.4f}")
|
||||
print()
|
||||
|
||||
|
||||
async def tools_preset_example():
|
||||
"""Example with tools preset (all default Claude Code tools)."""
|
||||
print("=== Tools Preset Example ===")
|
||||
print("Setting tools={'type': 'preset', 'preset': 'claude_code'}")
|
||||
print()
|
||||
|
||||
options = ClaudeAgentOptions(
|
||||
tools={"type": "preset", "preset": "claude_code"},
|
||||
max_turns=1,
|
||||
)
|
||||
|
||||
async for message in query(
|
||||
prompt="What tools do you have available? Just list them briefly.",
|
||||
options=options,
|
||||
):
|
||||
if isinstance(message, SystemMessage) and message.subtype == "init":
|
||||
tools = message.data.get("tools", [])
|
||||
print(f"Tools from system message ({len(tools)} tools): {tools[:5]}...")
|
||||
print()
|
||||
elif isinstance(message, AssistantMessage):
|
||||
for block in message.content:
|
||||
if isinstance(block, TextBlock):
|
||||
print(f"Claude: {block.text}")
|
||||
elif isinstance(message, ResultMessage):
|
||||
if message.total_cost_usd:
|
||||
print(f"\nCost: ${message.total_cost_usd:.4f}")
|
||||
print()
|
||||
|
||||
|
||||
async def main():
|
||||
"""Run all examples."""
|
||||
await tools_array_example()
|
||||
await tools_empty_array_example()
|
||||
await tools_preset_example()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
anyio.run(main)
|
||||
|
|
@ -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)])
|
||||
|
||||
|
|
|
|||
|
|
@ -29,6 +29,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."""
|
||||
|
|
@ -606,6 +613,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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue