Claude Code SDK for Elixir
View SourceAn Elixir SDK for programmatically interacting with Claude Code. This library provides a simple interface to query Claude and handle responses using the familiar Elixir streaming patterns.
Architecture
graph TB
subgraph "Your Elixir Application"
A[ClaudeCodeSDK] --> B[Process Manager]
B --> C[Message Parser]
B --> D[Auth Checker]
end
subgraph "Claude Code CLI"
E[claude-code executable]
E --> F[API Communication]
end
subgraph "Claude API"
G[Claude Service]
end
A -->|spawn & control| E
E -->|HTTPS| G
G -->|Responses| E
E -->|JSON stream| B
C -->|Parsed Messages| A
style A fill:#4a9eff,stroke:#2d7dd2,stroke-width:2px,color:#000
style G fill:#ff6b6b,stroke:#ff4757,stroke-width:2px,color:#000
Prerequisites
This SDK requires the Claude Code CLI to be installed:
npm install -g @anthropic-ai/claude-code
Installation
Add claude_code_sdk
to your list of dependencies in mix.exs
:
def deps do
[
{:claude_code_sdk, "~> 0.0.1"}
]
end
Then run:
mix deps.get
Quick Start
Authenticate the CLI (do this once):
claude login
Install dependencies:
mix deps.get
Run the showcase:
# Safe demo with mocks (no API costs) mix showcase # Live demo with real API calls (requires authentication) mix showcase --live
Try the live script runner:
# Run example scripts with live API calls mix run.live examples/basic_example.exs mix run.live examples/simple_analyzer.exs lib/claude_code_sdk.ex
Implementation Status
โ Currently Implemented
- Core SDK Functions:
query/2
,continue/2
,resume/3
with stdin support - Live Script Runner:
mix run.live
for executing scripts with real API calls - Message Processing: Structured message types with proper parsing
- Options Configuration: Full CLI argument mapping with smart presets and correct CLI formats
- Subprocess Management: Robust erlexec integration with stdin support
- JSON Parsing: Custom parser without external dependencies
- Authentication: CLI delegation with status checking and diagnostics
- Error Handling: Improved error detection and timeout handling
- Stream Processing: Lazy evaluation with Elixir Streams
- Mocking System: Comprehensive testing without API calls (supports stdin workflows)
- Code Quality: Full dialyzer and credo compliance with refactored complex functions
- Developer Tools: ContentExtractor, AuthChecker, OptionBuilder, DebugMode
- Smart Configuration: Environment-aware defaults and preset configurations
๐ฎ Planned Features
- Advanced Error Handling: Retry logic, timeout handling, comprehensive error recovery
- Performance Optimization: Caching, parallel processing, memory optimization
- Integration Patterns: Phoenix LiveView, OTP applications, worker pools
- Security Features: Input validation, permission management, sandboxing
- Developer Tools: Debug mode, troubleshooting helpers, session management
- Advanced Examples: Code analysis pipelines, test generators, refactoring tools
- MCP Support: Model Context Protocol integration and tool management
Basic Usage
# Simple query with smart content extraction
alias ClaudeCodeSDK.{ContentExtractor, OptionBuilder}
# Use preset development options
options = OptionBuilder.build_development_options()
ClaudeCodeSDK.query("Say exactly: Hello from Elixir!", options)
|> Enum.each(fn msg ->
case msg.type do
:assistant ->
content = ContentExtractor.extract_text(msg)
IO.puts("๐ค Claude: #{content}")
:result ->
if msg.subtype == :success do
IO.puts("โ
Success! Cost: $#{msg.data.total_cost_usd}")
end
end
end)
Testing with Mocks
The SDK includes a comprehensive mocking system for testing without making actual API calls.
Running Tests
# Run tests with mocks (default)
mix test
# Run tests with live API calls
MIX_ENV=test mix test.live
# Run specific test with live API
MIX_ENV=test mix test.live test/specific_test.exs
Using Mocks in Your Code
# Enable mocking
Application.put_env(:claude_code_sdk, :use_mock, true)
# Start the mock server
{:ok, _} = ClaudeCodeSDK.Mock.start_link()
# Set a mock response
ClaudeCodeSDK.Mock.set_response("hello", [
%{
"type" => "assistant",
"message" => %{"content" => "Hello from mock!"}
}
])
# Query will return mock response
ClaudeCodeSDK.query("say hello") |> Enum.to_list()
Mock Demo
Run the included demo to see mocking in action:
mix run demo_mock.exs
For detailed documentation about the mocking system, see MOCKING.md.
Available Files to Run
๐ฏ Showcase (Recommended Starting Point)
# Safe demo with mocks (no API costs)
mix showcase
# Live demo with real API calls (requires authentication)
mix showcase --live
Additional Examples & Tests
mix run final_test.exs
- Complete test showing message parsing and interactionmix run example.exs
- Basic usage examplemix run demo_mock.exs
- Mock system demonstrationmix run test_full.exs
- Alternative test formatmix run test_mix.exs
- Basic erlexec functionality test
๐ Start with mix showcase
for a complete overview of all features!
Live Script Runner
The SDK includes a powerful mix run.live
task for executing Elixir scripts with live Claude API calls:
Usage
# Run any .exs script with live API
mix run.live script.exs [args...]
# Examples
mix run.live examples/basic_example.exs
mix run.live examples/simple_analyzer.exs lib/claude_code_sdk.ex
mix run.live examples/file_reviewer.exs path/to/your/file.txt
Features
- ๐ด Live API Integration: Makes real Claude API calls with proper stdin handling
- โ ๏ธ Cost Warnings: Clear warnings about API usage and costs
- ๐ Argument Passing: Supports passing arguments to scripts
- ๐ก๏ธ Safe by Default: Requires explicit live mode activation
- ๐ญ Mock Fallback: Scripts can still run in mock mode during development
Difference from Regular mix run
Command | API Calls | Costs | Authentication Required |
---|---|---|---|
mix run script.exs | None (mock mode) | $0.00 | No |
mix run.live script.exs | Real API calls | Real costs | Yes (claude login ) |
Example Scripts
The SDK includes several example scripts you can run immediately:
# Basic factorial function generation
mix run.live examples/basic_example.exs
# Code analysis with file input
mix run.live examples/simple_analyzer.exs lib/claude_code_sdk.ex
# Simple batch processing
mix run.live examples/simple_batch.exs
# File review and analysis
mix run.live examples/file_reviewer.exs README.md
Creating Your Own Live Scripts
Create scripts that automatically work in both mock and live modes:
#!/usr/bin/env elixir
# Check if we're in live mode
if Application.get_env(:claude_code_sdk, :use_mock, false) do
{:ok, _} = ClaudeCodeSDK.Mock.start_link()
IO.puts("๐ญ Mock mode enabled")
else
IO.puts("๐ด Live mode enabled")
end
# Your script logic here...
response = ClaudeCodeSDK.query("Your prompt here")
|> extract_response()
IO.puts("Response: #{response}")
๐ญ Mock vs Live Mode
All examples and tests can run in two modes:
Mode | Command Format | API Calls | Costs | Authentication Required |
---|---|---|---|---|
Mock | mix showcase | None (mocked) | $0.00 | No |
Live | mix showcase --live | Real API calls | Real costs | Yes (claude login ) |
๐ฏ Showcase Features
The showcase demonstrates all SDK functionality:
Feature Demonstrated | What It Shows |
---|---|
OptionBuilder | Smart configuration presets for development, production, chat, analysis |
AuthChecker | Environment validation and authentication diagnostics |
Basic SDK Usage | Core query functionality with mocked/real responses |
ContentExtractor | Easy text extraction from complex message formats |
DebugMode | Message analysis, benchmarking, troubleshooting tools |
Mock System | Complete testing infrastructure without API costs |
Advanced Configurations | Real-world scenarios for different use cases |
Performance Features | Benchmarking and timing analysis |
๐ Running Examples
โ ๏ธ Live mode will make real API calls and incur costs. Always test with mock mode first!
Command | Status | Notes |
---|---|---|
mix showcase | โ Working | Mock mode, fast, no costs |
mix showcase --live | โ Working | Live mode, real API calls, no hanging |
mix test | โ Working | Mock mode, 75 tests, 17 skipped |
mix test.live | โ Working | Live mode, properly warns about costs |
mix run example.exs | โ Working | Uses mock mode by default, auto-starts Mock |
mix run examples/simple_analyzer.exs | โ Working | Uses mock mode by default |
mix run.live examples/basic_example.exs | โ Working | Live mode, real API calls, stdin support |
mix run.live examples/simple_analyzer.exs | โ Working | Live mode, file analysis with arguments |
API Reference
Main Functions
ClaudeCodeSDK.query(prompt, options \\ nil)
Runs a query against Claude Code and returns a stream of messages.
# Simple query
ClaudeCodeSDK.query("Write a hello world function")
|> Enum.to_list()
# With options
options = %ClaudeCodeSDK.Options{max_turns: 5, verbose: true}
ClaudeCodeSDK.query("Complex task", options)
|> Enum.to_list()
ClaudeCodeSDK.continue(prompt \\ nil, options \\ nil)
Continues the most recent conversation.
ClaudeCodeSDK.continue("Now add error handling")
|> Enum.to_list()
ClaudeCodeSDK.resume(session_id, prompt \\ nil, options \\ nil)
Resumes a specific conversation by session ID.
ClaudeCodeSDK.resume("session-id-here", "Add tests")
|> Enum.to_list()
Options & Smart Presets
Configure requests with ClaudeCodeSDK.Options
or use smart presets:
# Manual configuration
%ClaudeCodeSDK.Options{
max_turns: 10, # Maximum conversation turns
system_prompt: "Custom...", # Override system prompt
output_format: :stream_json,# Output format
verbose: true, # Enable verbose logging
cwd: "/path/to/project" # Working directory
}
# Smart presets with OptionBuilder
alias ClaudeCodeSDK.OptionBuilder
# Development: permissive settings, verbose logging
options = OptionBuilder.build_development_options()
# Production: restricted settings, minimal tools
options = OptionBuilder.build_production_options()
# Analysis: read-only tools for code analysis
options = OptionBuilder.build_analysis_options()
# Chat: simple conversations
options = OptionBuilder.build_chat_options()
# Auto-detect based on Mix.env()
options = OptionBuilder.for_environment()
# Custom combinations
options = OptionBuilder.merge(:development, %{max_turns: 5})
Message Types
The SDK returns a stream of ClaudeCodeSDK.Message
structs with these types:
:system
- Session initialization (session_id, model, tools):user
- User messages:assistant
- Claude's responses:result
- Final result with cost/duration stats
Message Processing
Use the built-in ContentExtractor
for easy message processing:
alias ClaudeCodeSDK.ContentExtractor
# Extract all assistant responses
content = ClaudeCodeSDK.query("Your prompt")
|> Stream.filter(fn msg -> msg.type == :assistant end)
|> Stream.map(&ContentExtractor.extract_text/1)
|> Enum.join("\n")
# Check if message has text content
if ContentExtractor.has_text?(message) do
text = ContentExtractor.extract_text(message)
IO.puts("Response: #{text}")
end
Authentication
This SDK uses your already-authenticated Claude CLI instance. No API keys needed - just run claude login
once and the SDK uses the stored session.
Authentication Checking
Use AuthChecker
to verify your setup before making queries:
alias ClaudeCodeSDK.AuthChecker
# Quick boolean check
if AuthChecker.authenticated?() do
# Proceed with queries
ClaudeCodeSDK.query("Hello!")
else
IO.puts("Please run: claude login")
end
# Full diagnostic check
diagnosis = AuthChecker.diagnose()
# Returns: %{
# cli_installed: true,
# authenticated: true,
# status: :ready,
# recommendations: []
# }
# Ensure ready or raise error
AuthChecker.ensure_ready!()
Error Handling
ClaudeCodeSDK.query("prompt")
|> Enum.each(fn msg ->
case msg do
%{type: :result, subtype: :success} ->
IO.puts("โ
Success!")
%{type: :result, subtype: error_type} when error_type in [:error_max_turns, :error_during_execution] ->
IO.puts("โ Error: #{error_type}")
_ ->
# Process other message types
end
end)
Architecture
The SDK works by:
- Spawning the Claude CLI as a subprocess using
erlexec
- Communicating via JSON messages over stdout/stderr
- Parsing responses into Elixir structs
- Returning lazy Streams for efficient processing
Key benefits:
- โ Uses existing CLI authentication
- โ Efficient streaming processing
- โ No external JSON dependencies
- โ Robust subprocess management with erlexec
Troubleshooting
Module not available error: Run with mix run
instead of plain elixir
:
# โ Won't work
elixir final_test.exs
# โ
Works
mix run final_test.exs
Authentication errors: Make sure Claude CLI is authenticated:
claude login
Process errors: Ensure Claude CLI is installed:
npm install -g @anthropic-ai/claude-code
CLI argument format errors: Recent improvements have fixed common CLI format issues:
- Output format: Now correctly uses
stream-json
instead ofstream_json
- Permission modes: Now correctly uses
acceptEdits
instead ofaccept_edits
- These fixes ensure compatibility with the latest Claude CLI versions
Live mode not working: Make sure you're using mix run.live
for live API calls:
# โ Won't make live API calls
mix run examples/basic_example.exs
# โ
Makes live API calls
mix run.live examples/basic_example.exs
Debug Mode
Use DebugMode
for detailed troubleshooting:
alias ClaudeCodeSDK.DebugMode
# Run full diagnostics
DebugMode.run_diagnostics()
# Debug a specific query with timing
messages = DebugMode.debug_query("Hello")
# Benchmark performance
results = DebugMode.benchmark("Test query", nil, 3)
# Returns timing and cost statistics
# Analyze message statistics
stats = DebugMode.analyze_messages(messages)
Developer Tools
The SDK includes four powerful modules to enhance your development experience:
๐ง OptionBuilder - Smart Configuration
Pre-configured option sets for common use cases:
build_development_options()
- Permissive settings for dev workbuild_production_options()
- Secure settings for productionbuild_analysis_options()
- Read-only tools for code analysisbuild_chat_options()
- Simple conversation settingsfor_environment()
- Auto-detects based on Mix.env()merge/2
- Combine presets with custom options
๐ AuthChecker - Environment Validation
Prevents authentication errors with proactive checking:
authenticated?/0
- Quick boolean checkdiagnose/0
- Full diagnostic with recommendationsensure_ready!/0
- Raises if not ready for queries- Helpful error messages and setup instructions
๐ ContentExtractor - Message Processing
Simplifies extracting text from complex message formats:
extract_text/1
- Get text from any message typehas_text?/1
- Check if message contains text content- Handles strings, arrays, tool responses gracefully
- No more manual message parsing
๐ DebugMode - Troubleshooting Tools
Comprehensive debugging and performance analysis:
debug_query/2
- Execute queries with detailed loggingrun_diagnostics/0
- Full environment health checkbenchmark/3
- Performance testing with statisticsanalyze_messages/1
- Extract insights from message streams
Main Use Cases
๐ Code Analysis & Review
# Analyze code quality and security with smart configuration
alias ClaudeCodeSDK.{OptionBuilder, ContentExtractor}
# Use analysis-specific options (read-only tools)
options = OptionBuilder.build_analysis_options()
analysis_result = ClaudeCodeSDK.query("""
Review this code for security vulnerabilities and performance issues:
#{File.read!("lib/user_auth.ex")}
""", options)
|> Stream.filter(&(&1.type == :assistant))
|> Stream.map(&ContentExtractor.extract_text/1)
|> Enum.join("\n")
IO.puts("๐ Analysis Result:\n#{analysis_result}")
๐ Documentation Generation (FUTURE/PLANNED)
# Generate API documentation - FUTURE/PLANNED
ClaudeCodeSDK.query("Generate comprehensive docs for this module: #{file_content}")
|> Enum.filter(&(&1.type == :assistant))
|> Enum.map(&extract_content/1) # extract_content helper not yet implemented
๐งช Test Generation (FUTURE/PLANNED)
# Create test suites automatically - FUTURE/PLANNED
options = %ClaudeCodeSDK.Options{max_turns: 5}
ClaudeCodeSDK.query("Generate ExUnit tests for this module", options)
๐ Code Refactoring (FUTURE/PLANNED)
# Multi-step refactoring with session management - FUTURE/PLANNED
session_id = start_refactoring_session("lib/legacy_code.ex") # Not yet implemented
ClaudeCodeSDK.resume(session_id, "Now optimize for performance")
ClaudeCodeSDK.resume(session_id, "Add proper error handling")
๐ค Interactive Development Assistant (FUTURE/PLANNED)
# Pair programming sessions - FUTURE/PLANNED
ClaudeCodeSDK.query("I'm working on a GenServer. Help me implement proper state management")
|> Stream.each(&IO.puts(extract_content(&1))) # extract_content helper not yet implemented
|> Stream.run()
๐๏ธ Project Scaffolding (FUTURE/PLANNED)
# Generate boilerplate code - FUTURE/PLANNED
ClaudeCodeSDK.query("""
Create a Phoenix LiveView component for user authentication with:
- Login/logout functionality
- Session management
- Form validation
""")
Testing and Development
Environment Configuration
The SDK supports different configurations for different environments:
- Test Environment: Mocks enabled by default (
config/test.exs
) - Development Environment: Real API calls (
config/dev.exs
) - Production Environment: Real API calls (
config/prod.exs
)
Writing Tests with Mocks
defmodule MyAppTest do
use ExUnit.Case
alias ClaudeCodeSDK.Mock
setup do
# Clear any existing mock responses
Mock.clear_responses()
:ok
end
test "my feature works correctly" do
# Set up mock response
Mock.set_response("analyze", [
%{
"type" => "assistant",
"message" => %{"content" => "Analysis complete: No issues found."}
}
])
# Your code that uses ClaudeCodeSDK
result = MyApp.analyze_code("def hello, do: :world")
# Assertions
assert result == "Analysis complete: No issues found."
end
end
๐ Comprehensive Documentation
For detailed documentation covering all features, advanced patterns, and integration examples, see:
The comprehensive manual includes:
- ๐๏ธ Architecture Deep Dive - Internal workings and design patterns โ IMPLEMENTED
- โ๏ธ Advanced Configuration - MCP support, security, performance tuning (FUTURE/PLANNED)
- ๐ง Integration Patterns - Phoenix LiveView, OTP applications, task pipelines (FUTURE/PLANNED)
- ๐ก๏ธ Security & Best Practices - Input validation, permission management (FUTURE/PLANNED)
- ๐ Troubleshooting Guide - Common issues and debugging techniques (FUTURE/PLANNED)
- ๐ก Real-World Examples - Code analysis, test generation, refactoring tools (FUTURE/PLANNED)
License
MIT License