Commit graph

40 commits

Author SHA1 Message Date
Kashyap Murali
b37d9ed69d
Merge main into feat/tool-callbacks
Resolved conflicts by:
- Keeping both tool callback features and MCP server support
- Integrating sdk_mcp_servers parameter into Query initialization
- Combining imports and exports from both branches
2025-09-03 09:07:54 -07:00
Kashyap Murali
4f4fcfe3f3
feat: Full TypeScript SDK compatibility for tool permissions
- Align PermissionResult structure with TypeScript SDK
  - Add PermissionResultAllow with updatedInput and updatedPermissions
  - Add PermissionResultDeny with message and interrupt flag
  - Remove misleading ToolPermissionResponse alias (no actual backward compat)

- Add PermissionUpdate types matching TypeScript
  - PermissionUpdate with all update types
  - PermissionRuleValue for rule definitions
  - PermissionUpdateDestination and PermissionBehavior literals

- Update all tests and examples to use new types
- Update Query handler to properly handle Allow/Deny variants

Breaking change: Callbacks must now return PermissionResultAllow or
PermissionResultDeny instead of the old ToolPermissionResponse format.

This ensures full compatibility with TypeScript SDK's permission system.
2025-09-03 09:05:51 -07:00
kashyap murali
9ef57859af
feat: Add in-process SDK MCP server support (#142)
## Summary

Adds in-process SDK MCP server support to the Python SDK, building on
the control protocol from #139.

**Note: Targets `dickson/control` branch (PR #139), not `main`.**

## Key Changes

- Added `@tool` decorator and `create_sdk_mcp_server()` API for defining
in-process MCP servers
- SDK MCP servers run directly in the Python process (no subprocess
overhead)
- Moved SDK MCP handling from Transport to Query class for proper
architectural layering
- Added `McpSdkServerConfig` type and integrated with control protocol

## Example

```python
from claude_code_sdk import tool, create_sdk_mcp_server

@tool("greet", "Greet a user", {"name": str})
async def greet_user(args):
    return {"content": [{"type": "text", "text": f"Hello, {args['name']}!"}]}

server = create_sdk_mcp_server(name="my-tools", tools=[greet_user])

options = ClaudeCodeOptions(mcp_servers={"tools": server})
```

## Testing

- Added integration tests in `test_sdk_mcp_integration.py`
- Added example calculator server in `examples/mcp_calculator.py`

---------

Co-authored-by: Dickson Tsai <dickson@anthropic.com>
Co-authored-by: Ashwin Bhat <ashwin@anthropic.com>
Co-authored-by: Claude <noreply@anthropic.com>
2025-09-03 08:29:32 -07:00
Kashyap Murali
4f78e10691
refactor: Align with TypeScript SDK naming conventions
- Rename tool_permission_callback to can_use_tool in ClaudeCodeOptions
- Rename ToolPermissionCallback type to CanUseTool
- Update all references in examples and tests
- Remove dict backward compatibility (already done by user)

Addresses PR review comments for better SDK consistency
2025-09-02 19:40:57 -07:00
Kashyap Murali
9674b3bdfd
Address review comments
- Move ToolPermissionContext/Response imports to top of query.py (no circular dependency)
- Remove dict compatibility, require ToolPermissionResponse only
- Update type hints to enforce cleaner API
- Remove test for dict return compatibility
- Fix all whitespace and import issues
- Add newline at end of test file
2025-09-02 15:37:19 -07:00
Kashyap Murali
a774031c34
feat: Add tool permission and hook callbacks support
- Added ToolPermissionCallback type for controlling tool execution
- Added HookCallback type for intercepting tool events
- Added callbacks to ClaudeCodeOptions
- Thread callbacks through InternalClient and ClaudeSDKClient to Query
- Updated Query to handle tool permission requests with new types
- Support both ToolPermissionResponse and dict returns for compatibility
- Added example demonstrating permission control and input modification
- Added comprehensive tests for callbacks functionality
- Fixed NotRequired import for Python 3.11+ compatibility
2025-09-02 15:35:39 -07:00
Dickson Tsai
22fa9f473e
Implement control protocol support for Python SDK (#139)
Some checks failed
Lint / lint (push) Has been cancelled
Test / test (3.10) (push) Has been cancelled
Test / test (3.11) (push) Has been cancelled
Test / test (3.12) (push) Has been cancelled
Test / test (3.13) (push) Has been cancelled
## Summary

This PR implements control protocol support in the Python SDK, aligning
it with the TypeScript implementation pattern. The refactor introduces a
Query + Transport separation to enable bidirectional communication
between the SDK and CLI.

## Motivation

The previous Python SDK implementation used a high-level abstraction in
the Transport ABC (`send_request`/`receive_messages`) that couldn't
handle bidirectional communication. This prevented support for:
- Control messages from CLI to SDK that need responses
- Hooks implementation  
- Dynamic permission mode changes
- SDK MCP servers

## Changes

### Core Architecture Refactor

1. **New Query Class** (`src/claude_code_sdk/_internal/query.py`)
   - Manages control protocol on top of Transport
   - Handles control request/response routing
   - Manages initialization handshake with timeout
   - Supports hook callbacks and tool permission callbacks
   - Implements message streaming

2. **Refactored Transport ABC**
(`src/claude_code_sdk/_internal/transport/__init__.py`)
- Changed from high-level (`send_request`/`receive_messages`) to
low-level (`write`/`read_messages`) interface
   - Now handles raw I/O instead of protocol logic
   - Aligns with TypeScript ProcessTransport pattern

3. **Updated SubprocessCLITransport**
(`src/claude_code_sdk/_internal/transport/subprocess_cli.py`)
   - Simplified to focus on raw message streaming
   - Removed protocol logic (moved to Query)
   - Improved cleanup and error handling

4. **Enhanced ClaudeSDKClient** (`src/claude_code_sdk/client.py`)
   - Now uses Query for control protocol
   - Supports initialization messages
   - Better error handling for control protocol failures

### Control Protocol Features

- **Initialization handshake**: SDK sends initialize request, CLI
responds with supported commands
- **Control message types**: 
  - `initialize`: Establish bidirectional connection
  - `interrupt`: Cancel ongoing operations  
  - `set_permission_mode`: Change permission mode dynamically
- **Timeout handling**: 60-second timeout for initialization to handle
CLI versions without control support

### Examples

Updated `examples/streaming_mode.py` to demonstrate control protocol
initialization and error handling.

## Testing

- Tested with current CLI (no control protocol support yet) - gracefully
falls back
- Verified backward compatibility with existing `query()` function
- Tested initialization timeout handling
- Verified proper cleanup on errors

## Design Alignment

This implementation closely follows the TypeScript reference:
- `src/core/Query.ts` → `src/claude_code_sdk/_internal/query.py`
- `src/transport/ProcessTransport.ts` →
`src/claude_code_sdk/_internal/transport/subprocess_cli.py`
- `src/entrypoints/sdk.ts` → `src/claude_code_sdk/client.py`

## Next Steps

Once the CLI implements the control protocol handler, this will enable:
- Hooks support
- Dynamic permission mode changes
- SDK MCP servers
- Improved error recovery

🤖 Generated with [Claude Code](https://claude.ai/code)

---------

Co-authored-by: Ashwin Bhat <ashwin@anthropic.com>
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Kashyap Murali <kashyap@anthropic.com>
2025-09-01 23:04:22 -07:00
Suzanne Wang
f794e17e78
Add support for custom env vars (#131)
Some checks failed
Lint / lint (push) Has been cancelled
Test / test (3.10) (push) Has been cancelled
Test / test (3.11) (push) Has been cancelled
Test / test (3.12) (push) Has been cancelled
Test / test (3.13) (push) Has been cancelled
## Key changes
- Adds env field to `ClaudeCodeOptions`, allowing custom env vars to cli
- Updates tests and examples

## Motivation
Bringing Python SDK to feature parity with TS SDK, which supports custom
env vars

## Notes
- Environment variables are merged in order: system env → user env → SDK
required vars
- This implementation seems slightly more robust than the TypeScript
version, which can exclude OS envs vars if a user passes a minimal env
object
- Some linting changes seem to have been picked up
2025-08-25 14:02:03 -07:00
Michael Gendy
30df222bfc
Move thinking block parsing from user to assistant messages (#119)
Some checks failed
Lint / lint (push) Has been cancelled
Test / test (3.10) (push) Has been cancelled
Test / test (3.11) (push) Has been cancelled
Test / test (3.12) (push) Has been cancelled
Test / test (3.13) (push) Has been cancelled
Related to https://github.com/anthropics/claude-code-sdk-python/pull/28
cc @dicksontsai
2025-08-19 13:36:12 -07:00
Michael Gendy
b6a5b98604
Add support for plan permission mode (#116)
Some checks failed
Lint / lint (push) Has been cancelled
Test / test (3.10) (push) Has been cancelled
Test / test (3.11) (push) Has been cancelled
Test / test (3.12) (push) Has been cancelled
Test / test (3.13) (push) Has been cancelled
2025-08-07 13:06:05 -07:00
yokomotod
f1e6dda230
fix: add support for thinking content blocks (#28)
Some checks are pending
Lint / lint (push) Waiting to run
Test / test (3.10) (push) Waiting to run
Test / test (3.11) (push) Waiting to run
Test / test (3.12) (push) Waiting to run
Test / test (3.13) (push) Waiting to run
## Summary

Fixes an issue where `thinking` content blocks in Claude Code responses
were not being parsed, resulting in empty `AssistantMessage` content
arrays.

## Changes
- Added `ThinkingBlock` dataclass to handle thinking content with
`thinking` and `signature` fields
- Updated client parsing logic in `_internal/client.py` to recognize and
create `ThinkingBlock` instances
- Added comprehensive test coverage for thinking block functionality

## Before

```python
# Claude Code response with thinking block resulted in:
AssistantMessage(content=[])  # Empty content!
```

## After

```python
# Now correctly parses to:
AssistantMessage(content=[
    ThinkingBlock(thinking="...", signature="...")
])
```

Fixes #27

---------

Co-authored-by: Dickson Tsai <dickson@anthropic.com>
2025-08-06 19:00:02 -07:00
Sam Fu
f233df1852
teach ClaudeCodeOptions.mcp_servers to accept a filepath (#114)
Some checks are pending
Lint / lint (push) Waiting to run
Test / test (3.10) (push) Waiting to run
Test / test (3.11) (push) Waiting to run
Test / test (3.12) (push) Waiting to run
Test / test (3.13) (push) Waiting to run
`claude`'s `--mcp-config` takes either a JSON file or string, so support
this in `ClaudeCodeOptions`
```
  --mcp-config <file or string>    Load MCP servers from a JSON file or string
```

---------

Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com>
Co-authored-by: Sam Fu <shunfu@users.noreply.github.com>
2025-08-06 14:13:59 -07:00
Dickson Tsai
9fc830028e
Add extra_args field to ClaudeCodeOptions for forward compatibility (#111)
Some checks failed
Lint / lint (push) Has been cancelled
Test / test (3.10) (push) Has been cancelled
Test / test (3.11) (push) Has been cancelled
Test / test (3.12) (push) Has been cancelled
Test / test (3.13) (push) Has been cancelled
## Summary
- Adds `extra_args` field to `ClaudeCodeOptions` to support passing
arbitrary CLI flags
- Enables forward compatibility with future CLI flags without requiring
SDK updates
- Supports both valued flags (`--flag value`) and boolean flags
(`--flag`)

## Changes
- Add `extra_args: dict[str, str | None]` field to `ClaudeCodeOptions`
- Implement logic in `SubprocessCLITransport` to handle extra args:
- `None` values create boolean flags (e.g., `{"verbose": None}` →
`--verbose`)
- String values create flags with arguments (e.g., `{"output": "json"}`
→ `--output json`)
- Add comprehensive tests for the new functionality

## Test plan
- [x] Added unit tests for settings file path handling
- [x] Added unit tests for settings JSON object handling  
- [x] Added unit tests for extra_args with both valued and boolean flags
- [x] All tests pass (`python -m pytest tests/`)
- [x] Type checking passes (`python -m mypy src/`)

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-authored-by: Claude <noreply@anthropic.com>
2025-08-04 22:47:24 -07:00
Dickson Tsai
0cb5efa923
Support --add-dir flag (#104) 2025-07-31 20:52:30 -07:00
Dickson Tsai
fa3962de3f
Improve UserMessage types to include ToolResultBlock (#101)
Fixes https://github.com/anthropics/claude-code-sdk-python/issues/90
2025-07-31 07:51:39 -07:00
Dickson Tsai
b25cdf81c9
Add changelog and changelog format check (#77)
Some checks failed
Lint / lint (push) Has been cancelled
Test / test (3.10) (push) Has been cancelled
Test / test (3.11) (push) Has been cancelled
Test / test (3.12) (push) Has been cancelled
Test / test (3.13) (push) Has been cancelled
2025-07-21 10:53:01 -07:00
Dickson Tsai
5325dea9fd
Lint 2025-07-19 20:19:40 -07:00
Dickson Tsai
3e7da418ce
Fix json error handling 2025-07-19 20:16:45 -07:00
Dickson Tsai
b57e05afa5
Improve examples 2025-07-19 19:57:17 -07:00
Dickson Tsai
a813a4d665
Fix lint 2025-07-19 15:26:30 -07:00
Dickson Tsai
e65c2f417a
Fix test 2025-07-19 15:25:34 -07:00
Dickson Tsai
c95c077b9b
Ruff 2025-07-19 15:21:02 -07:00
Dickson Tsai
712948c2e7
Fix test 2025-07-19 15:20:02 -07:00
Dickson Tsai
eeb0be9955
Close stdin for query() 2025-07-19 15:01:43 -07:00
Dickson Tsai
489677d614
Add tests 2025-07-19 13:57:52 -07:00
Lina Tawfik
343ec4812c
Merge pull request #42 from RomainGehrig/fix/cwd-not-exists
Some checks failed
Test / test (3.13) (push) Has been cancelled
Lint / lint (push) Has been cancelled
Test / test (3.10) (push) Has been cancelled
Test / test (3.11) (push) Has been cancelled
Test / test (3.12) (push) Has been cancelled
Explicit error if the cwd does not exist
2025-07-05 14:29:03 -07:00
Lina Tawfik
67193350e2
Remove implementation-specific test
The test was checking source code for specific strings rather than
testing behavior. The existing subprocess tests already verify the
functionality works correctly.
2025-07-01 15:23:55 -07:00
Lina Tawfik
9ef9da9ab6
Fix linting issues 2025-07-01 15:12:26 -07:00
Romain Gehrig
d336a22ab4
Explicit error if the cwd does not exist
Previously was raised as a CLINotFoundError
2025-07-01 16:33:30 +02:00
Lina Tawfik
404c50bce0
Fix FastAPI SSE streaming compatibility (fixes #4)
Remove anyio.create_task_group() from receive_messages() to fix the
RuntimeError "Attempted to exit cancel scope in a different task than
it was entered in" when using the SDK with FastAPI's SSE streaming.

The issue occurred because FastAPI can move async generators between
different asyncio tasks during the streaming lifecycle, which conflicts
with anyio's cancel scope tracking.

Changes:
- Remove task group usage from receive_messages()
- Read stderr sequentially after stdout completes
- Add test to ensure no task groups are used
- Fix existing test expectation for buffer overflow

This is a minimal fix that maintains compatibility while solving the
core issue. The trade-off is that stderr is read after stdout instead
of concurrently, but this is unlikely to cause issues in practice.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-07-01 00:04:50 -07:00
Lina Tawfik
1791031d20
chore: Remove obvious comments from code
Removed redundant comments that simply restate what the code is doing.
Kept only comments that provide valuable context or explain complex behavior.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-06-30 22:27:48 -07:00
Lina Tawfik
977bf6438b
style: apply ruff formatting 2025-06-30 21:58:37 -07:00
Lina Tawfik
24295417ac
fix: handle JSON messages split across multiple stream reads
Adds buffering to accumulate incomplete JSON messages that are split
across multiple stream reads due to asyncio's 64KB default buffer limit.

- Implement 1MB buffer to accumulate partial JSON
- Clear buffer on successful parse or size limit exceeded
- Add comprehensive tests for split JSON scenarios

Fixes CLIJSONDecodeError when reading large minified JSON files.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-06-30 21:55:06 -07:00
Lina Tawfik
7610791803
Apply ruff formatting
- Fixed quotes to use double quotes consistently
- Adjusted line breaks per ruff formatter rules
- No functional changes

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-06-27 15:08:20 -07:00
Lina Tawfik
ecad93a0c0
Apply ruff linting fixes
- Fixed formatting in test_subprocess_buffering.py
- No functional changes, only code style improvements

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-06-27 15:06:28 -07:00
Lina Tawfik
3f25c3bb83
Fix test to use anyio.run() pattern and add edge case tests
- Convert async test to use anyio.run() pattern consistent with other tests
- Remove unused CLIJSONDecodeError import
- Add tests for JSON with embedded newlines
- Add tests for multiple newlines between objects
- All tests passing

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-06-27 15:06:28 -07:00
Bradley-Butcher
87e2699f6a
fix multi-line buffering issue
Signed-off-by: Bradley-Butcher <brad@phoebe.ai>
2025-06-19 11:39:44 +01:00
Lina Tawfik
b3448f4baa
Fix tests to match updated ResultMessage type
- Remove cost_usd field from test expectations
- Update to use total_cost_usd instead of total_cost
- All 30 tests now pass

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-06-17 16:51:28 -07:00
Lina Tawfik
63ef121e18
Fix code formatting for CI 2025-06-12 00:20:28 -07:00
Lina Tawfik
6ca3514261
Initial Python SDK import 2025-06-12 00:16:19 -07:00