CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-claude-code-sdk

Python SDK for Claude Code providing simple query functions and advanced bidirectional interactive conversations with custom tool support

Overview
Eval results
Files

configuration-options.mddocs/

Configuration and Options

The ClaudeCodeOptions dataclass provides comprehensive configuration for controlling Claude Code behavior including tool permissions, working directory, system prompts, MCP server configurations, and advanced features.

Capabilities

Main Configuration Class

Central configuration object for all Claude Code SDK options and settings.

@dataclass
class ClaudeCodeOptions:
    """Query options for Claude SDK."""

    allowed_tools: list[str] = field(default_factory=list)
    system_prompt: str | None = None
    append_system_prompt: str | None = None
    mcp_servers: dict[str, McpServerConfig] | str | Path = field(default_factory=dict)
    permission_mode: PermissionMode | None = None
    continue_conversation: bool = False
    resume: str | None = None
    max_turns: int | None = None
    disallowed_tools: list[str] = field(default_factory=list)
    model: str | None = None
    permission_prompt_tool_name: str | None = None
    cwd: str | Path | None = None
    settings: str | None = None
    add_dirs: list[str | Path] = field(default_factory=list)
    env: dict[str, str] = field(default_factory=dict)
    extra_args: dict[str, str | None] = field(default_factory=dict)
    debug_stderr: Any = sys.stderr
    can_use_tool: CanUseTool | None = None
    hooks: dict[HookEvent, list[HookMatcher]] | None = None
    user: str | None = None
    include_partial_messages: bool = False

Permission Modes

Control how tool execution permissions are handled.

PermissionMode = Literal["default", "acceptEdits", "plan", "bypassPermissions"]

Permission Modes:

  • "default": CLI prompts for dangerous tools (interactive)
  • "acceptEdits": Auto-accept file edits while prompting for other dangerous operations
  • "plan": Show execution plan without running tools
  • "bypassPermissions": Allow all tools without prompts (use with caution)

MCP Server Configuration Types

Support for multiple MCP server types including in-process SDK servers.

McpServerConfig = (
    McpStdioServerConfig | McpSSEServerConfig | McpHttpServerConfig | McpSdkServerConfig
)

class McpStdioServerConfig(TypedDict):
    """MCP stdio server configuration."""
    type: NotRequired[Literal["stdio"]]  # Optional for backwards compatibility
    command: str
    args: NotRequired[list[str]]
    env: NotRequired[dict[str, str]]

class McpSSEServerConfig(TypedDict):
    """MCP SSE server configuration."""
    type: Literal["sse"]
    url: str
    headers: NotRequired[dict[str, str]]

class McpHttpServerConfig(TypedDict):
    """MCP HTTP server configuration."""
    type: Literal["http"]
    url: str
    headers: NotRequired[dict[str, str]]

class McpSdkServerConfig(TypedDict):
    """SDK MCP server configuration."""
    type: Literal["sdk"]
    name: str
    instance: "McpServer"

Usage Examples

Basic Configuration

from claude_code_sdk import ClaudeCodeOptions, query

# Simple configuration
options = ClaudeCodeOptions(
    system_prompt="You are a helpful Python developer",
    allowed_tools=["Read", "Write", "Bash"],
    max_turns=5
)

async def main():
    async for message in query(
        prompt="Create a simple web server",
        options=options
    ):
        print(message)

Working Directory Configuration

from pathlib import Path
from claude_code_sdk import ClaudeCodeOptions

# Using string path
options = ClaudeCodeOptions(
    cwd="/home/user/my-project",
    allowed_tools=["Read", "Write", "Bash"]
)

# Using Path object
project_path = Path.home() / "projects" / "my-app"
options = ClaudeCodeOptions(
    cwd=project_path,
    allowed_tools=["Read", "Write", "Bash"]
)

Permission Configuration

from claude_code_sdk import ClaudeCodeOptions

# Auto-accept file edits but prompt for other dangerous operations
options = ClaudeCodeOptions(
    permission_mode="acceptEdits",
    allowed_tools=["Read", "Write", "Edit", "Bash"]
)

# Bypass all permissions (use with extreme caution)
options = ClaudeCodeOptions(
    permission_mode="bypassPermissions",
    allowed_tools=["Read", "Write", "Edit", "Bash", "WebSearch"]
)

# Plan mode - show what would be done without executing
options = ClaudeCodeOptions(
    permission_mode="plan",
    allowed_tools=["Read", "Write", "Edit", "Bash"]
)

MCP Server Configuration

from claude_code_sdk import ClaudeCodeOptions, create_sdk_mcp_server, tool

# SDK (in-process) server
@tool("calculate", "Perform calculation", {"expression": str})
async def calculate(args):
    result = eval(args["expression"])  # In production, use safe evaluation
    return {"content": [{"type": "text", "text": f"Result: {result}"}]}

sdk_server = create_sdk_mcp_server("calculator", tools=[calculate])

# External stdio server
stdio_server = {
    "type": "stdio",
    "command": "python",
    "args": ["-m", "external_server"],
    "env": {"DEBUG": "1"}
}

# HTTP server
http_server = {
    "type": "http",
    "url": "http://localhost:8080/mcp",
    "headers": {"Authorization": "Bearer token123"}
}

# SSE server
sse_server = {
    "type": "sse",
    "url": "http://localhost:8080/sse",
    "headers": {"X-API-Key": "key123"}
}

options = ClaudeCodeOptions(
    mcp_servers={
        "calculator": sdk_server,      # In-process server
        "external": stdio_server,      # External process
        "remote_http": http_server,    # HTTP server
        "remote_sse": sse_server       # SSE server
    },
    allowed_tools=[
        "mcp__calculator__calculate",
        "mcp__external__some_tool",
        "mcp__remote_http__api_call",
        "mcp__remote_sse__stream_data"
    ]
)

Advanced Configuration

from claude_code_sdk import ClaudeCodeOptions, HookMatcher

async def pre_tool_hook(input_data, tool_use_id, context):
    # Custom validation logic
    return {}

options = ClaudeCodeOptions(
    # Basic settings
    system_prompt="You are an expert developer",
    append_system_prompt="Always explain your reasoning",
    model="claude-3-5-sonnet-20241022",
    max_turns=10,
    user="developer_123",

    # Tool configuration
    allowed_tools=["Read", "Write", "Edit", "Bash", "WebSearch"],
    disallowed_tools=["dangerous_tool"],

    # Permission and security
    permission_mode="acceptEdits",

    # Working environment
    cwd="/workspace/project",
    add_dirs=["/workspace/shared", "/workspace/libs"],
    env={
        "PYTHON_PATH": "/workspace/python",
        "NODE_PATH": "/workspace/node_modules",
        "DEBUG": "1"
    },

    # Advanced features
    continue_conversation=True,
    resume="session_id_123",
    include_partial_messages=True,

    # Hooks
    hooks={
        "PreToolUse": [
            HookMatcher(matcher="Bash|Write|Edit", hooks=[pre_tool_hook])
        ]
    },

    # CLI integration
    settings="/path/to/claude/settings.json",
    extra_args={
        "--verbose": None,
        "--output-style": "detailed",
        "--custom-flag": "value"
    },
    debug_stderr=open("/tmp/claude_debug.log", "w")
)

Permission Callback Configuration

from claude_code_sdk import (
    ClaudeCodeOptions, PermissionResultAllow, PermissionResultDeny,
    ToolPermissionContext
)

async def custom_permission_checker(
    tool_name: str,
    tool_input: dict[str, Any],
    context: ToolPermissionContext
) -> PermissionResult:
    """Custom permission checker for tool usage."""

    # Allow read operations
    if tool_name in ["Read", "Glob", "Grep"]:
        return PermissionResultAllow()

    # Require approval for write operations
    if tool_name in ["Write", "Edit", "MultiEdit"]:
        # In a real application, you might show a UI prompt
        user_approval = await get_user_approval(f"Allow {tool_name} operation?")

        if user_approval:
            return PermissionResultAllow()
        else:
            return PermissionResultDeny(
                message=f"User denied {tool_name} operation",
                interrupt=False
            )

    # Deny dangerous operations
    if tool_name == "Bash" and any(cmd in str(tool_input) for cmd in ["rm -rf", "format"]):
        return PermissionResultDeny(
            message="Dangerous command blocked",
            interrupt=True
        )

    # Default allow
    return PermissionResultAllow()

# Note: can_use_tool requires streaming mode (AsyncIterable prompt)
options = ClaudeCodeOptions(
    can_use_tool=custom_permission_checker,
    allowed_tools=["Read", "Write", "Edit", "Bash"]
)

# This requires using ClaudeSDKClient, not the query() function
async def main():
    async with ClaudeSDKClient(options=options) as client:
        await client.query("Create a new Python file")

        async for msg in client.receive_response():
            print(msg)

Environment and Directory Configuration

import os
from pathlib import Path
from claude_code_sdk import ClaudeCodeOptions

# Set up project environment
project_root = Path("/workspace/my-project")
venv_path = project_root / "venv"

options = ClaudeCodeOptions(
    cwd=project_root,
    add_dirs=[
        project_root / "src",
        project_root / "tests",
        project_root / "docs"
    ],
    env={
        "VIRTUAL_ENV": str(venv_path),
        "PATH": f"{venv_path / 'bin'}:{os.environ['PATH']}",
        "PYTHONPATH": str(project_root / "src"),
        "PROJECT_ROOT": str(project_root),
        "ENVIRONMENT": "development"
    },
    allowed_tools=["Read", "Write", "Edit", "Bash", "WebSearch"]
)

Configuration Validation

Some configuration options have validation rules:

Permission System Validation

  • can_use_tool callback requires streaming mode (AsyncIterable prompt)
  • can_use_tool and permission_prompt_tool_name are mutually exclusive
  • When using can_use_tool, the SDK automatically sets permission_prompt_tool_name="stdio"

MCP Server Validation

  • Server names in mcp_servers must be unique
  • SDK MCP servers must have valid McpServer instances
  • External server configurations must have valid command/URL paths

Tool Configuration Validation

  • Tools in allowed_tools must exist or be provided by configured MCP servers
  • disallowed_tools takes precedence over allowed_tools
  • MCP tool names follow the pattern mcp__<server_name>__<tool_name>

Default Values

When ClaudeCodeOptions() is instantiated with no arguments:

ClaudeCodeOptions(
    allowed_tools=[],                    # No tools allowed by default
    system_prompt=None,                  # No custom system prompt
    permission_mode=None,                # Use Claude Code's default behavior
    cwd=None,                           # Use current working directory
    mcp_servers={},                     # No MCP servers
    max_turns=None,                     # No turn limit
    continue_conversation=False,         # Start fresh conversation
    include_partial_messages=False,     # No streaming message updates
    # ... other fields use their default values
)

Integration with SDK Functions

  • query(): Accepts options parameter with ClaudeCodeOptions instance
  • ClaudeSDKClient: Accepts options parameter in constructor
  • All configuration options work with both unidirectional and bidirectional communication patterns

For specific tool and permission configurations, see:

  • Custom Tools for MCP server setup
  • Hook System for hook configuration
  • Error Handling for debug and error handling options

Install with Tessl CLI

npx tessl i tessl/pypi-claude-code-sdk

docs

configuration-options.md

custom-tools.md

error-handling.md

hook-system.md

index.md

interactive-client.md

message-types.md

simple-queries.md

transport-system.md

tile.json