mirror of
https://github.com/anthropics/claude-code-sdk-python.git
synced 2025-12-23 09:19:52 +00:00
## Summary This PR adds support for custom tool callbacks and comprehensive e2e testing for MCP calculator functionality. ## Key Features Added - **Custom tool permission callbacks** - Allow dynamic tool permission control via `can_use_tool` callback - **E2E test suite** - Real Claude API tests validating MCP tool execution end-to-end - **Fixed MCP calculator example** - Now properly uses `allowed_tools` for permission management ## Changes ### Custom Callbacks - Added `ToolPermissionContext` and `PermissionResult` types for tool permission handling - Implemented `can_use_tool` callback support in SDK client - Added comprehensive tests in `tests/test_tool_callbacks.py` ### E2E Testing Infrastructure - Created `e2e-tests/` directory with pytest-based test suite - `test_mcp_calculator.py` - Tests all calculator operations with real API calls - `conftest.py` - Pytest config with mandatory API key validation - GitHub Actions workflow for automated e2e testing on main branch - Comprehensive documentation in `e2e-tests/README.md` ### Bug Fixes - Fixed MCP calculator example to use `allowed_tools` instead of incorrect `permission_mode` - Resolved tool permission issues preventing MCP tools from executing ## Testing E2E tests require `ANTHROPIC_API_KEY` environment variable and will fail without it. Run locally: ```bash export ANTHROPIC_API_KEY=your-key python -m pytest e2e-tests/ -v -m e2e ``` Run unit tests including callback tests: ```bash python -m pytest tests/test_tool_callbacks.py -v ``` 🤖 Generated with [Claude Code](https://claude.ai/code) --------- Co-authored-by: Claude <noreply@anthropic.com> Co-authored-by: Kashyap Murali <kashyap@anthropic.com>
43 lines
No EOL
1.4 KiB
Python
43 lines
No EOL
1.4 KiB
Python
"""End-to-end tests for tool permission callbacks with real Claude API calls."""
|
|
|
|
import pytest
|
|
|
|
from claude_code_sdk import (
|
|
ClaudeCodeOptions,
|
|
ClaudeSDKClient,
|
|
PermissionResultAllow,
|
|
PermissionResultDeny,
|
|
ToolPermissionContext,
|
|
)
|
|
|
|
|
|
@pytest.mark.e2e
|
|
@pytest.mark.asyncio
|
|
async def test_permission_callback_gets_called():
|
|
"""Test that can_use_tool callback gets invoked."""
|
|
callback_invocations = []
|
|
|
|
async def permission_callback(
|
|
tool_name: str,
|
|
input_data: dict,
|
|
context: ToolPermissionContext
|
|
) -> PermissionResultAllow | PermissionResultDeny:
|
|
"""Track callback invocation."""
|
|
print(f"Permission callback called for: {tool_name}, input: {input_data}")
|
|
callback_invocations.append(tool_name)
|
|
return PermissionResultAllow()
|
|
|
|
options = ClaudeCodeOptions(
|
|
can_use_tool=permission_callback,
|
|
)
|
|
|
|
async with ClaudeSDKClient(options=options) as client:
|
|
await client.query("Write 'hello world' to /tmp/test.txt")
|
|
|
|
async for message in client.receive_response():
|
|
print(f"Got message: {message}")
|
|
pass # Just consume messages
|
|
|
|
print(f'Callback invocations: {callback_invocations}')
|
|
# Verify callback was invoked
|
|
assert "Write" in callback_invocations, f"can_use_tool callback should have been invoked for Write tool, got: {callback_invocations}" |