# multillm-claude Claude Agent SDK provider for [multillm](../multillm). This provider enables autonomous agents that can use tools and perform multi-step tasks. ## Installation ```bash pip install multillm-claude ``` ## Configuration Choose one of the following methods (priority: direct config > env var > config file): **Option 1: Claude CLI OAuth (recommended)** ```bash claude login ``` **Option 2: Config file** Create `~/.config/multillm/providers/claude.json`: ```json { "api_key": "sk-ant-..." } ``` Or for OAuth token: ```json { "oauth_token": "sk-ant-oat01-..." } ``` Set permissions: ```bash chmod 600 ~/.config/multillm/providers/claude.json ``` **Option 3: Environment variables** ```bash # API Key export ANTHROPIC_API_KEY=sk-ant-... # Or OAuth Token export CLAUDE_CODE_OAUTH_TOKEN=your-token ``` **Option 4: Direct config** ```python client = multillm.Client({ "claude": {"api_key": "sk-ant-..."} }) ``` ### Config File Fields | Field | Required | Description | |-------|----------|-------------| | `api_key` | No* | Your Anthropic API key | | `oauth_token` | No* | Your Claude OAuth token | *One of `api_key` or `oauth_token` must be provided, unless using `claude login`. ## Usage ```python import asyncio import multillm async def main(): client = multillm.Client() # Simple query with tools answer = await client.single( "claude/default", "What Python version is installed?", allowed_tools=["Bash"], permission_mode="acceptEdits", ) print(answer) # Streaming agent events options = multillm.AgentOptions( system_prompt="You are a helpful assistant.", max_turns=10, allowed_tools=["Read", "Write", "Bash"], permission_mode="acceptEdits", ) async for msg in client.run("claude/default", "Create hello.py", options): if msg.type == "text": print(msg.content, end="") elif msg.type == "tool_use": print(f"\n[{msg.tool_name}]", end="") asyncio.run(main()) ``` ## Model Selection Specify the model after the provider prefix: - `claude/default` - Uses the default model - `claude/claude-sonnet-4-20250514` - Claude Sonnet - `claude/claude-opus-4-20250514` - Claude Opus ## Tool Support ### Supported Tools These tools work correctly with the Claude Agent SDK provider: | Tool | Description | Permission Required | |------|-------------|---------------------| | `Bash` | Execute bash commands | Yes | | `Read` | Read files from filesystem | No | | `Write` | Create or overwrite files | Yes | | `Edit` | Edit existing files | Yes | | `Glob` | Find files by pattern | No | | `Grep` | Search file contents | No | | `Task` | Launch sub-agents | Varies | | `WebFetch` | Fetch web content | No | | `WebSearch` | Search the web | No | | `NotebookEdit` | Edit Jupyter notebooks | Yes | | `KillShell` | Kill background shells | Yes | | `EnterPlanMode` | Enter planning mode | No | | `ExitPlanMode` | Exit planning mode | No | ### Interactive Tools | Tool | CLI Support | Notes | |------|-------------|-------| | `AskUserQuestion` | ✅ **Auto-converts** | CLI automatically uses custom `ask_user` tool | | Custom tools | ✅ Full support | Provide Tool objects with interactive handlers | **About Interactive Tools with Claude:** The Claude Agent SDK's built-in `AskUserQuestion` runs in a subprocess and can't access our terminal's stdin/stdout for interactive prompting. To solve this, **multillm-cli automatically provides a custom interactive tool** when you request `AskUserQuestion`. **What happens:** When you use `--allowed-tools AskUserQuestion`, the CLI: 1. Removes the built-in AskUserQuestion (which doesn't work interactively) 2. Adds a custom `ask_user` tool with an interactive handler 3. The agent uses this tool instead, with full interactive support **Usage:** ```bash # Request AskUserQuestion - CLI auto-provides working alternative multillm -m claude/default \ -p "Ask me about my preferences and create a summary" \ --allowed-tools AskUserQuestion \ --permission-mode acceptEdits ``` **What you'll see:** ``` ℹ️ Using custom 'ask_user' tool instead of AskUserQuestion for interactive prompting ====================================================================== ❓ QUESTION FROM ASSISTANT ====================================================================== What is your favorite programming language? Suggested options: 1. Python 2. JavaScript 3. Rust Your answer: 1 ====================================================================== ``` **Programmatic usage:** For programmatic use, provide your own `ask_user` tool with an interactive handler: ```python import multillm # Define interactive ask_user tool ask_user_tool = multillm.Tool( name="ask_user", description="Ask the user a question", parameters={ "type": "object", "properties": { "question": {"type": "string"}, "options": {"type": "array", "items": {"type": "string"}} }, "required": ["question"] }, handler=lambda args: { "answer": input(f"\n{args['question']}\nYour answer: ") } ) # Use with Claude async for msg in client.run( "claude/default", "Ask me questions", options=multillm.AgentOptions(max_turns=10), tools=[ask_user_tool] # Provide custom tool ): if msg.type == "text": print(msg.content) ``` **Why not use the built-in AskUserQuestion?** The SDK's built-in `AskUserQuestion` is designed for Claude Code CLI's interactive mode where the subprocess has special stdin/stdout handling. In multillm, this doesn't work because: - The SDK subprocess can't access our terminal - We can't intercept and respond to built-in tool calls - The tool returns an error instead of prompting **Solution:** Use custom tools (which the CLI provides automatically!) **Comparison:** | Approach | Works? | How | |----------|--------|-----| | `--allowed-tools AskUserQuestion` | ✅ Yes | CLI auto-converts to custom tool | | Custom `ask_user` Tool | ✅ Yes | Provide Tool object with handler | | SDK built-in (direct) | ❌ No | Subprocess can't access stdin/stdout | | Chat provider `ask_user` | ✅ Yes | Via agentwrap | ## Agent Options ```python options = multillm.AgentOptions( system_prompt="...", # System prompt max_turns=10, # Maximum agent turns allowed_tools=["Read", "Bash"], # Tools the agent can use permission_mode="acceptEdits", # Permission handling working_directory="/path", # Working directory ) ``` ## Message Types When streaming with `client.run()`: | Type | Description | |------|-------------| | `text` | Text content from the agent | | `tool_use` | Agent is using a tool | | `tool_result` | Result from a tool | | `result` | Final result | | `system` | System messages | ## Debugging ### Enable Debug Mode Set the `MULTILLM_DEBUG` environment variable to see detailed error information and SDK options: ```bash export MULTILLM_DEBUG=1 python your_script.py ``` This will show: - Detailed error messages with full stderr/stdout - SDK configuration options - Full Python tracebacks ### Common Issues **Error: "Command failed with exit code 1"** The provider now captures and displays all available error information to stderr. Look for: ``` ====================================================================== CLAUDE AGENT SDK ERROR ====================================================================== Error: Error Details: stderr: stdout: exit_code: 1 ====================================================================== ``` **Authentication Errors:** ```bash # Check authentication status claude login --check # Re-authenticate if needed claude login ``` **Permission Errors:** Always specify `permission_mode` when using tools: ```python result = await client.single( "claude/default", "List files", allowed_tools=["Bash"], permission_mode="acceptEdits" # Required! ) ``` ### Full Debugging Guide See [DEBUGGING.md](./DEBUGGING.md) for comprehensive debugging information, including: - How to read error messages - Common issues and solutions - Debug mode usage - Testing error handling - Reporting bugs