Replace separate system_prompt and append_system_prompt fields with a
single system_prompt field that accepts:
- string: custom system prompt
- {"preset": "claude_code"}: use default Claude Code prompt
- {"preset": "claude_code", "append": "..."}: default prompt with
additions
- None/undefined: vanilla Claude with no system prompt
This matches the TypeScript SDK API design and provides more flexible
system prompt configuration.
🤖 Generated with [Claude Code](https://claude.ai/code)
---------
Co-authored-by: Claude <noreply@anthropic.com>
## 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>
## 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
`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>
## 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>