Include the three bug fixes that were merged but not documented:
- #388: Faster CLI error propagation
- #385: Pydantic 2.12+ compatibility
- #391: Concurrent subagent write lock
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
## Summary
When the CLI exits with an error (e.g., invalid session ID passed to
`--resume`), signal all pending control requests immediately instead of
waiting for the 60-second timeout.
**The fix adds 4 lines** to the exception handler in `_read_messages`:
```python
# Signal all pending control requests so they fail fast instead of timing out
for request_id, event in list(self.pending_control_responses.items()):
if request_id not in self.pending_control_results:
self.pending_control_results[request_id] = e
event.set()
```
## Problem
When the CLI exits with an error, the SDK's message reader catches it
but doesn't notify pending control requests. This causes `initialize()`
to wait for the full 60-second timeout even though the error is known
within seconds.
Example scenario:
1. User passes invalid session ID via
`ClaudeAgentOptions(resume="invalid-id")`
2. CLI prints `No conversation found with session ID: xxx` and exits
with code 1
3. SDK message reader catches the error after ~3 seconds
4. But `initialize()` keeps waiting for 60 seconds before timing out
## Solution
The existing code at `_send_control_request` lines 361-362 already
handles exceptions in results:
```python
if isinstance(result, Exception):
raise result
```
The fix simply signals all pending control events when an error occurs,
allowing them to fail fast with the actual error instead of timing out.
## Test Plan
- [ ] Test with invalid session ID - should fail fast (~3s) instead of
timing out (60s)
- [ ] Test normal flow still works
- [ ] Test multiple pending requests all get signaled
Fixes#387
## Summary
- Add runtime placeholder for `McpServer` type to fix Pydantic 2.12+
compatibility
- `McpServer` was only imported under `TYPE_CHECKING`, causing
`PydanticUserError` at runtime
Fixes#384
Co-authored-by: lyrica <lyrica@example.com>
## TL;DR
Adds a write lock to `SubprocessCLITransport` to prevent concurrent
writes from parallel subagents.
---
## Overview
When multiple subagents run in parallel and invoke MCP tools, the CLI
sends concurrent `control_request` messages. Each handler tries to write
a response back to the subprocess stdin at the same time. Trio's
`TextSendStream` isn't thread-safe for concurrent access, so this causes
`BusyResourceError`.
This PR adds an `anyio.Lock` around all write operations (`write()`,
`end_input()`, and the stdin-closing part of `close()`). The lock
serializes concurrent writes so they happen one at a time. The `_ready`
flag is now set inside the lock during `close()` to prevent a TOCTOU
race where `write()` checks `_ready`, then `close()` sets it and closes
the stream before `write()` actually sends data.
---
## Call Flow
```mermaid
flowchart TD
A["write()<br/>subprocess_cli.py:505"] --> B["acquire _write_lock<br/>subprocess_cli.py:507"]
B --> C["check _ready & stream<br/>subprocess_cli.py:509"]
C --> D["_stdin_stream.send()<br/>subprocess_cli.py:523"]
E["close()<br/>subprocess_cli.py:458"] --> F["acquire _write_lock<br/>subprocess_cli.py:478"]
F --> G["set _ready = False<br/>subprocess_cli.py:479"]
G --> H["close _stdin_stream<br/>subprocess_cli.py:481"]
I["end_input()<br/>subprocess_cli.py:531"] --> J["acquire _write_lock<br/>subprocess_cli.py:533"]
J --> K["close _stdin_stream<br/>subprocess_cli.py:535"]
```
This PR updates the version to 0.1.12 after publishing to PyPI.
## Changes
- Updated version in `pyproject.toml` to 0.1.12
- Updated version in `src/claude_agent_sdk/_version.py` to 0.1.12
- Updated `CHANGELOG.md` with release notes
## Release Information
- Published to PyPI: https://pypi.org/project/claude-agent-sdk/0.1.12/
- Bundled CLI version: 2.0.58
- Install with: `pip install claude-agent-sdk==0.1.12`
🤖 Generated by GitHub Actions
---------
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com>
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Ashwin Bhat <ashwin@anthropic.com>
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>
Port the SdkBeta type and betas option from the TypeScript SDK to enable
SDK users to pass beta feature flags (e.g., 1M context window) to the
CLI.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: Claude <noreply@anthropic.com>
This PR updates the version to 0.1.11 after publishing to PyPI.
## Changes
- Updated version in `pyproject.toml` to 0.1.11
- Updated version in `src/claude_agent_sdk/_version.py` to 0.1.11
- Updated `CHANGELOG.md` with release notes
## Release Information
- Published to PyPI: https://pypi.org/project/claude-agent-sdk/0.1.11/
- Bundled CLI version: 2.0.57
- Install with: `pip install claude-agent-sdk==0.1.11`
🤖 Generated by GitHub Actions
---------
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com>
Currently wheels with packaged claude-code CLI are only published for
windows amd64, linux x86_64 and macos arm64. For version 0.1.10, we can
see the following download files are available on pypi contain the
following artifacts:
https://pypi.org/project/claude-agent-sdk/0.1.10/#files
- claude_agent_sdk-0.1.10.tar.gz
- claude_agent_sdk-0.1.10-py3-none-win_amd64.whl
- claude_agent_sdk-0.1.10-py3-none-manylinux_2_17_x86_64.whl
- claude_agent_sdk-0.1.10-py3-none-macosx_11_0_arm64.whl
The existing publishing code should support adding a new linux arm64
wheel builder, using the Github ARM runners:
https://github.blog/changelog/2025-08-07-arm64-hosted-runners-for-public-repositories-are-now-generally-available/
Unfortunately, there's no `ubuntu-latest-arm` label similar to the one
we use for the other builds, so I'm using the `ubuntu-24.04-arm` label.
Port SDK MCP fix from TypeScript to Python.
Now, when SDK MCP servers or hooks are present, stream_input() waits for
the first result message before closing stdin, allowing bidirectional
control protocol communication to complete.
Fixes repro in
https://github.com/anthropics/claude-agent-sdk-python/issues/266.
The `query()` design requires input streams to be held open by the user
for SDK MCP bidirectional communication to work. This has confused a lot
of folks, so we're moving towards a more explicit lifecycle design. In
the meantime, this is the way we've addressed it with V1 APIs in
https://github.com/anthropics/claude-agent-sdk-typescript.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: Claude <noreply@anthropic.com>
Update type: ignore comments to use `untyped-decorator` instead of
`misc` to match the actual mypy error codes reported in CI.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: Claude <noreply@anthropic.com>
Adds programmatic sandbox configuration to the Python SDK, matching the
TypeScript SDK's approach.
Changes:
- Add SandboxSettings, SandboxNetworkConfig, SandboxIgnoreViolations
types
- Add sandbox field to ClaudeAgentOptions
- Merge sandbox into --settings CLI flag in SubprocessCLITransport
- Export sandbox types from package __init__.py
- Add comprehensive tests for sandbox settings
**Important:** Filesystem and network restrictions are configured via
permission rules (Read/Edit/WebFetch), not via these sandbox settings.
The sandbox settings control sandbox behavior (enabled, auto-allow,
excluded commands, etc.).
Example usage:
```python
from claude_agent_sdk import query, SandboxSettings
result = query(
prompt='Build and test the project',
options=ClaudeAgentOptions(
sandbox={
'enabled': True,
'autoAllowBashIfSandboxed': True,
'excludedCommands': ['docker'],
'network': {
'allowLocalBinding': True,
'allowUnixSockets': ['/var/run/docker.sock']
}
}
)
)
```
Co-authored-by: Claude <noreply@anthropic.com>
This PR updates the version to 0.1.10 after publishing to PyPI.
## Changes
- Updated version in `pyproject.toml` to 0.1.10
- Updated version in `src/claude_agent_sdk/_version.py` to 0.1.10
- Updated `CHANGELOG.md` with release notes
## Release Information
- Published to PyPI: https://pypi.org/project/claude-agent-sdk/0.1.10/
- Bundled CLI version: 2.0.53
- Install with: `pip install claude-agent-sdk==0.1.10`
🤖 Generated by GitHub Actions
---------
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com>
Posts new issues to claude-agent-sdk-feedback. Can't use the default
github slack bot as it's too noisy (all comments and issue updates such
as comments, closing, etc...)
---------
Co-authored-by: Ashwin Bhat <ashwin@anthropic.com>
This PR updates the version to 0.1.9 after publishing to PyPI.
## Changes
- Updated version in `pyproject.toml` to 0.1.9
- Updated version in `src/claude_agent_sdk/_version.py` to 0.1.9
- Updated `CHANGELOG.md` with release notes
## Release Information
- Published to PyPI: https://pypi.org/project/claude-agent-sdk/0.1.9/
- Bundled CLI version: 2.0.49
- Install with: `pip install claude-agent-sdk==0.1.9`
🤖 Generated by GitHub Actions
---------
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com>
## Summary
- Adds optional `timeout` field to `HookMatcher` dataclass in `types.py`
that allows users to specify a custom timeout (in seconds) for hooks
- Propagates the timeout value through:
- `client.py` and `_internal/client.py`: `_convert_hooks_to_internal()`
method
- `_internal/query.py`: hook config sent to CLI
## Test plan
- [x] Verify hooks work without timeout specified (default behavior)
- [x] Verify custom timeout is passed to CLI when specified in
HookMatcher
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: Claude <noreply@anthropic.com>
Remove the claude_code_version workflow input and instead read the CLI
version directly from src/claude_agent_sdk/_cli_version.py. This allows
the version to be managed separately and updated by automation.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: Claude <noreply@anthropic.com>
This PR updates the version to 0.1.8 after publishing to PyPI.
## Changes
- Updated version in `pyproject.toml` to 0.1.8
- Updated version in `src/claude_agent_sdk/_version.py` to 0.1.8
- Updated bundled CLI version in `src/claude_agent_sdk/_cli_version.py`
to 2.0.45
- Updated `CHANGELOG.md` with release notes
## Release Information
- Published to PyPI: https://pypi.org/project/claude-agent-sdk/0.1.8/
- Bundled CLI version: 2.0.45
- Install with: `pip install claude-agent-sdk==0.1.8`
🤖 Generated by GitHub Actions
---------
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com>
Co-authored-by: Ashwin Bhat <ashwin@anthropic.com>
The build_wheel.py script uses `python -m wheel tags` to retag wheels
with platform-specific tags, but `wheel` wasn't explicitly installed.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: Claude <noreply@anthropic.com>
Wheels are already ZIP files - double compression via GitHub Actions
artifacts can cause "Mis-matched data size" errors on PyPI upload.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: Claude <noreply@anthropic.com>
The bash install script (install.sh) explicitly rejects Windows. Use the
PowerShell installer (install.ps1) instead when running on Windows,
matching the approach used in test.yml.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: Claude <noreply@anthropic.com>
Windows console encoding (cp1252) doesn't support Unicode emoji
characters, causing UnicodeEncodeError in CI. Replaced all emoji
characters with plain text equivalents.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: Claude <noreply@anthropic.com>
Bundle platform-specific Claude Code CLI binaries directly in the Python
package, eliminating the need for separate CLI installation.
## Changes
### Build System
- Created `scripts/download_cli.py` to fetch CLI during build
- Created `scripts/build_wheel.py` for building platform-specific wheels
- Created `scripts/update_cli_version.py` to track bundled CLI version
- Updated `pyproject.toml` to properly bundle CLI without duplicate file
warnings
- Made twine check non-blocking (License-File warnings are false
positives)
### Runtime
- Modified `subprocess_cli.py` to check for bundled CLI first
- Added `_cli_version.py` to track which CLI version is bundled
- SDK automatically uses bundled CLI, falling back to system
installation if not found
- Users can still override with `cli_path` option
### Release Workflow
- Updated GitHub workflow to build separate wheels per platform (macOS,
Linux, Windows)
- Workflow now accepts two inputs:
- `version`: Package version to publish (e.g., `0.1.5`)
- `claude_code_version`: CLI version to bundle (e.g., `2.0.0` or
`latest`)
- Workflow builds platform-specific wheels with bundled CLI
- Creates release PR that updates:
- `pyproject.toml` version
- `src/claude_agent_sdk/_version.py`
- `src/claude_agent_sdk/_cli_version.py` with bundled CLI version
- `CHANGELOG.md` with auto-generated release notes
### Documentation
- Updated README to reflect bundled CLI (removed Node.js requirement)
- Added release workflow documentation
- Added local wheel building instructions
## Benefits
- **Zero external dependencies**: No need for Node.js or npm
- **Easier installation**: Single `pip install` gets everything
- **Version control**: Track exactly which CLI version is bundled
- **Flexible releases**: Can release new package versions with updated
CLI without code changes
- **Better user experience**: Works out of the box with no setup
Platform-specific wheels are automatically selected by pip during
installation based on the user's OS and architecture.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: Claude <noreply@anthropic.com>
This PR updates the version to 0.1.7 after publishing to PyPI.
## Changes
- Updated version in `pyproject.toml`
- Updated version in `src/claude_agent_sdk/_version.py`
- Updated `CHANGELOG.md` with release notes
## Release Information
- Published to PyPI: https://pypi.org/project/claude-agent-sdk/0.1.7/
- Install with: `pip install claude-agent-sdk==0.1.7`
🤖 Generated by GitHub Actions
---------
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com>
Co-authored-by: inigo <bogini@users.noreply.github.com>
Add support for automatic model fallback when primary model is
overloaded. The Python SDK passes the fallback_model parameter to the
Claude CLI, which handles the validation and fallback logic.
Changes:
- Add fallback_model parameter to ClaudeAgentOptions
- Pass --fallback-model to CLI subprocess
- Add test for fallback model command building
The validation that fallback_model != model happens at the CLI layer,
keeping the SDK implementation simple and focused on parameter passing.
---------
Co-authored-by: Claude <noreply@anthropic.com>
Add ~/.claude/local/claude to the list of locations checked when finding
the Claude CLI binary.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: Claude <noreply@anthropic.com>
This PR updates the version to 0.1.6 after publishing to PyPI.
## Changes
- Updated version in `pyproject.toml`
- Updated version in `src/claude_agent_sdk/_version.py`
- Updated `CHANGELOG.md` with release notes
## Release Information
- Published to PyPI: https://pypi.org/project/claude-agent-sdk/0.1.6/
- Install with: `pip install claude-agent-sdk==0.1.6`
🤖 Generated by GitHub Actions
---------
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com>
Co-authored-by: Ashwin Bhat <ashwin@anthropic.com>
Reduce CI job count by only running examples on Python 3.13 instead of
all Python versions (3.10-3.13). This reduces the combinatorial
explosion while still ensuring examples work on the latest Python
version.
Co-authored-by: Claude <noreply@anthropic.com>
Add support for limiting API costs using the max_budget_usd option,
mirroring the TypeScript SDK functionality. When the budget is exceeded,
query execution stops and returns a result with subtype
'error_max_budget_usd'.
- Add max_budget_usd field to ClaudeAgentOptions
- Pass --max-budget-usd flag to Claude Code CLI
- Add test coverage for budget limit behavior
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: Claude <noreply@anthropic.com>
Add support for controlling the maximum number of tokens allocated to
extended thinking blocks via the max_thinking_tokens parameter.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: Claude <noreply@anthropic.com>