aboutsummaryrefslogtreecommitdiffstats

multillm-claude

Claude Agent SDK provider for multillm.

This provider enables autonomous agents that can use tools and perform multi-step tasks.

Installation

pip install multillm-claude

Configuration

Choose one of the following methods (priority: direct config > env var > config file):

Option 1: Claude CLI OAuth (recommended)

claude login

Option 2: Config file

Create ~/.config/multillm/providers/claude.json:

{
  "api_key": "sk-ant-..."
}

Or for OAuth token:

{
  "oauth_token": "sk-ant-oat01-..."
}

Set permissions:

chmod 600 ~/.config/multillm/providers/claude.json

Option 3: Environment variables

# API Key
export ANTHROPIC_API_KEY=sk-ant-...

# Or OAuth Token
export CLAUDE_CODE_OAUTH_TOKEN=your-token

Option 4: Direct config

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

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:

# 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:

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

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:

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: <detailed error message>

Error Details:
  stderr: <actual error from subprocess>
  stdout: <subprocess output>
  exit_code: 1
======================================================================

Authentication Errors:

# Check authentication status
claude login --check

# Re-authenticate if needed
claude login

Permission Errors:

Always specify permission_mode when using tools:

result = await client.single(
    "claude/default",
    "List files",
    allowed_tools=["Bash"],
    permission_mode="acceptEdits"  # Required!
)

Full Debugging Guide

See DEBUGGING.md for comprehensive debugging information, including: - How to read error messages - Common issues and solutions - Debug mode usage - Testing error handling - Reporting bugs