No description
Find a file
Dickson Tsai 27b49bcdca
Fix subprocess deadlock with MCP servers by redirecting stderr to temp file
The SDK was experiencing deadlocks when MCP servers produced verbose stderr
output. The issue occurred because:
1. The SDK read stdout and stderr sequentially (stdout first, then stderr)
2. When stderr buffer filled up (64KB on Linux, 16KB on macOS), the subprocess
   would block trying to write more to stderr
3. Since the subprocess was blocked, it couldn't write to stdout
4. The parent process was waiting for stdout, creating a deadlock

This fix redirects stderr to a temporary file instead of a pipe, which:
- Eliminates the pipe buffer limitation (files have no such restriction)
- Prevents any possibility of deadlock
- Still captures stderr for error reporting (keeping last 100 lines)
- Works consistently across all async backends (asyncio, trio, etc.)

The temp file is automatically cleaned up when the subprocess ends, and we
use a circular buffer (deque) to keep only the last 100 lines in memory for
error reporting purposes.

Fixes deadlock issue reported in Slack where SDK would hang indefinitely
when receiving messages from MCP servers.

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-07-31 11:01:38 -07:00
.github/workflows Fix "Publish to PyPI" workflow: Add commit signing and improve diff (#82) 2025-07-21 09:53:24 -07:00
examples Improve UserMessage types to include ToolResultBlock (#101) 2025-07-31 07:51:39 -07:00
scripts Fix "Publish to PyPI" workflow: Add commit signing and improve diff (#82) 2025-07-21 09:53:24 -07:00
src/claude_code_sdk Fix subprocess deadlock with MCP servers by redirecting stderr to temp file 2025-07-31 11:01:38 -07:00
tests Improve UserMessage types to include ToolResultBlock (#101) 2025-07-31 07:51:39 -07:00
.gitignore Initial Python SDK import 2025-06-12 00:16:19 -07:00
CHANGELOG.md Add settings option to ClaudeCodeOptions (#100) 2025-07-30 23:59:21 -07:00
CLAUDE.md Fix lint and test 2025-07-19 20:04:58 -07:00
LICENSE Initial Python SDK import 2025-06-12 00:16:19 -07:00
pyproject.toml chore: bump version to 0.0.17 (#88) 2025-07-23 22:52:20 -07:00
README.md fix: update the tool list in README.md 2025-06-27 06:30:54 +10:00

Claude Code SDK for Python

Python SDK for Claude Code. See the Claude Code SDK documentation for more information.

Installation

pip install claude-code-sdk

Prerequisites:

  • Python 3.10+
  • Node.js
  • Claude Code: npm install -g @anthropic-ai/claude-code

Quick Start

import anyio
from claude_code_sdk import query

async def main():
    async for message in query(prompt="What is 2 + 2?"):
        print(message)

anyio.run(main)

Usage

Basic Query

from claude_code_sdk import query, ClaudeCodeOptions, AssistantMessage, TextBlock

# Simple query
async for message in query(prompt="Hello Claude"):
    if isinstance(message, AssistantMessage):
        for block in message.content:
            if isinstance(block, TextBlock):
                print(block.text)

# With options
options = ClaudeCodeOptions(
    system_prompt="You are a helpful assistant",
    max_turns=1
)

async for message in query(prompt="Tell me a joke", options=options):
    print(message)

Using Tools

options = ClaudeCodeOptions(
    allowed_tools=["Read", "Write", "Bash"],
    permission_mode='acceptEdits'  # auto-accept file edits
)

async for message in query(
    prompt="Create a hello.py file", 
    options=options
):
    # Process tool use and results
    pass

Working Directory

from pathlib import Path

options = ClaudeCodeOptions(
    cwd="/path/to/project"  # or Path("/path/to/project")
)

API Reference

query(prompt, options=None)

Main async function for querying Claude.

Parameters:

  • prompt (str): The prompt to send to Claude
  • options (ClaudeCodeOptions): Optional configuration

Returns: AsyncIterator[Message] - Stream of response messages

Types

See src/claude_code_sdk/types.py for complete type definitions:

  • ClaudeCodeOptions - Configuration options
  • AssistantMessage, UserMessage, SystemMessage, ResultMessage - Message types
  • TextBlock, ToolUseBlock, ToolResultBlock - Content blocks

Error Handling

from claude_code_sdk import (
    ClaudeSDKError,      # Base error
    CLINotFoundError,    # Claude Code not installed
    CLIConnectionError,  # Connection issues
    ProcessError,        # Process failed
    CLIJSONDecodeError,  # JSON parsing issues
)

try:
    async for message in query(prompt="Hello"):
        pass
except CLINotFoundError:
    print("Please install Claude Code")
except ProcessError as e:
    print(f"Process failed with exit code: {e.exit_code}")
except CLIJSONDecodeError as e:
    print(f"Failed to parse response: {e}")

See src/claude_code_sdk/_errors.py for all error types.

Available Tools

See the Claude Code documentation for a complete list of available tools.

Examples

See examples/quick_start.py for a complete working example.

License

MIT