Skip to content

guess/claude_code

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

πŸ€– Claude Code SDK for Elixir

The Elixir SDK for building AI agents with Claude Code.

  • πŸ”„ Native Streaming: Built on Elixir Streams for real-time responses
  • πŸ’¬ Conversation Continuity: Automatic context retention across queries
  • 🏭 Production-Ready Supervision: Fault-tolerant GenServers with automatic restarts
  • ⚑ High-Performance Concurrency: Multiple concurrent sessions with Elixir's actor model
  • πŸ”§ Zero-Config Phoenix: Drop-in support for LiveView and Phoenix apps
  • πŸ§ͺ Built-in Test Stubs: Mock Claude responses for fast, deterministic tests without API calls
  • πŸ”Œ MCP Tool Integration: Expose Elixir functions to Claude via Hermes

Hex.pm Documentation License Elixir

ClaudeCode

🎯 30-Second Demo

# Simple one-off query (uses ANTHROPIC_API_KEY from env)
{:ok, result} = ClaudeCode.query("Explain Elixir GenServers in one sentence")
IO.puts(result.result)

# For multi-turn conversations, use sessions with streaming
{:ok, session} = ClaudeCode.start_link()

session
|> ClaudeCode.stream("My favorite language is Elixir")
|> Stream.run()

# Claude remembers context across queries
session
|> ClaudeCode.stream("What's my favorite language?")
|> ClaudeCode.Stream.text_content()
|> Enum.each(&IO.write/1)
# => "Your favorite language is Elixir!"

ClaudeCode.stop(session)

πŸ“¦ Installation

Step 1: Add to your mix.exs

def deps do
  [{:claude_code, "~> 0.13"}]
end

Step 2: Install dependencies

mix deps.get

Step 3: Get the Claude CLI

# Install the Claude Code CLI: https://docs.anthropic.com/en/docs/claude-code
claude --version  # Verify installation

Step 4: Authenticate (choose one)

# Option A: Use your Claude subscription (no API key needed)
claude  # Then type /login to authenticate

# Option B: Use an API key
export ANTHROPIC_API_KEY="sk-ant-your-api-key-here"

πŸŽ‰ Ready to go! Try the quick demo above.

⚑ Quick Examples

# Basic usage (uses ANTHROPIC_API_KEY from environment)
{:ok, session} = ClaudeCode.start_link()

# Stream response messages
session
|> ClaudeCode.stream("Hello, Claude!")
|> Enum.each(&IO.inspect/1)

# File operations
session
|> ClaudeCode.stream("Review my mix.exs file", allowed_tools: ["View", "Edit"])
|> ClaudeCode.Stream.final_text()

# Custom agents
agents = %{
  "code-reviewer" => %{
    "description" => "Expert code reviewer. Use proactively after code changes.",
    "prompt" => "You are a senior code reviewer. Focus on quality and best practices.",
    "tools" => ["Read", "Grep", "Glob"],
    "model" => "sonnet"
  }
}
{:ok, session} = ClaudeCode.start_link(agents: agents)

# Production with supervision
{:ok, _} = ClaudeCode.Supervisor.start_link(name: :assistant)
:assistant
|> ClaudeCode.stream("Help with this task")
|> ClaudeCode.Stream.final_text()

πŸ“– Complete Getting Started Guide β†’

🏭 Production Usage

Supervised Sessions

# In your application.ex
def start(_type, _args) do
  children = [
    MyAppWeb.Endpoint,
    {ClaudeCode.Supervisor, [
      [name: :code_reviewer, system_prompt: "You review code"],
      [name: :general_assistant]
    ]}
  ]
  Supervisor.start_link(children, strategy: :one_for_one)
end

# Use from anywhere in your app
review =
  :code_reviewer
  |> ClaudeCode.stream("Review this code")
  |> ClaudeCode.Stream.final_text()

Benefits:

  • βœ… Fault tolerance - Sessions restart automatically on crashes
  • βœ… Zero downtime - Hot code reloading preserves session state
  • βœ… Global access - Named sessions work from anywhere in your app
  • βœ… Distributed support - Sessions work across Elixir clusters

Phoenix Integration

# Simple controller usage
def ask(conn, %{"prompt" => prompt}) do
  response =
    :assistant
    |> ClaudeCode.stream(prompt)
    |> ClaudeCode.Stream.final_text()

  json(conn, %{response: response})
end

For LiveView with real-time streaming, use stream/3 with a Task:

# LiveView with streaming responses
def handle_event("send", %{"message" => msg}, socket) do
  parent = self()
  Task.start(fn ->
    :assistant
    |> ClaudeCode.stream(msg, include_partial_messages: true)
    |> ClaudeCode.Stream.text_deltas()
    |> Enum.each(&send(parent, {:chunk, &1}))
    send(parent, :complete)
  end)
  {:noreply, assign(socket, streaming: true)}
end

def handle_info({:chunk, chunk}, socket) do
  {:noreply, assign(socket, response: socket.assigns.response <> chunk)}
end

def handle_info(:complete, socket) do
  {:noreply, assign(socket, streaming: false)}
end

πŸ“– Full Phoenix Integration Guide β†’

Error Handling

# Stream errors are thrown and can be caught
try do
  session
  |> ClaudeCode.stream("Hello")
  |> Enum.to_list()
catch
  {:stream_timeout, _ref} -> IO.puts("Request timed out")
  {:stream_init_error, {:cli_not_found, msg}} -> IO.puts("CLI error: #{msg}")
  {:stream_error, reason} -> IO.puts("Stream error: #{inspect(reason)}")
end

# Or use one-off query with result pattern matching
case ClaudeCode.query("Hello") do
  {:ok, result} -> IO.puts(result.result)
  {:error, %{is_error: true, result: msg}} -> IO.puts("Error: #{msg}")
  {:error, reason} -> IO.puts("Error: #{inspect(reason)}")
end

Multi-turn Conversations

Sessions automatically maintain context across queries:

{:ok, session} = ClaudeCode.start_link()

# First turn
session
|> ClaudeCode.stream("What's the capital of France?")
|> ClaudeCode.Stream.text_content()
|> Enum.each(&IO.write/1)  # Paris

# Second turn - context is preserved automatically
session
|> ClaudeCode.stream("What about Germany?")
|> ClaudeCode.Stream.text_content()
|> Enum.each(&IO.write/1)  # Berlin

# Get session ID for later resume
session_id = ClaudeCode.get_session_id(session)

ClaudeCode.stop(session)

# Resume later with the same context
{:ok, new_session} = ClaudeCode.start_link(resume: session_id)

new_session
|> ClaudeCode.stream("What was the first capital I asked about?")
|> ClaudeCode.Stream.text_content()
|> Enum.each(&IO.write/1)  # Paris

Benefits:

  • βœ… Persistent connection - Single CLI process handles all queries
  • βœ… Auto-connect/disconnect - No manual lifecycle management
  • βœ… Resume support - Continue previous conversations with resume: session_id
  • βœ… Lower latency - No startup overhead between turns

Tool Callbacks

Monitor tool executions for logging, auditing, or analytics:

callback = fn event ->
  Logger.info("Tool #{event.name} executed",
    input: event.input,
    result: event.result,
    is_error: event.is_error
  )
end

{:ok, session} = ClaudeCode.start_link(tool_callback: callback)

MCP Integration (Optional)

Expose Elixir tools to Claude using Hermes MCP:

# Add {:hermes_mcp, "~> 0.14"} to your deps

# Start MCP server with your tools
{:ok, config_path} = ClaudeCode.MCP.Server.start_link(
  server: MyApp.MCPServer,
  port: 9001
)

# Connect ClaudeCode to MCP server
{:ok, session} = ClaudeCode.start_link(mcp_config: config_path)

# Claude can now use your custom tools!

πŸ§ͺ Testing

ClaudeCode includes a test adapter for fast, deterministic tests without API calls:

test "handles greeting" do
  ClaudeCode.Test.stub(ClaudeCode, fn _query, _opts ->
    [ClaudeCode.Test.text("Hello! How can I help?")]
  end)

  {:ok, session} = ClaudeCode.start_link()
  result = session |> ClaudeCode.stream("Hi") |> ClaudeCode.Stream.final_text()
  assert result == "Hello! How can I help?"
end

Includes message helpers (text, tool_use, tool_result, thinking), dynamic stubs, and concurrent test support.

πŸ“– Full Testing Guide β†’

πŸ“š Documentation

🀝 Contributing

We ❀️ contributions! Whether it's:

  • πŸ› Bug reports - Found an issue? Let us know!
  • πŸ’‘ Feature requests - Have an idea? We'd love to hear it!
  • πŸ“ Documentation - Help make our docs even better
  • πŸ”§ Code contributions - PRs welcome!

See our Contributing Guide to get started.

πŸ› οΈ Development

# Clone and setup
git clone https://github.com/guess/claude_code.git
cd claude_code
mix deps.get

# Run tests and quality checks
mix test
mix quality  # format, credo, dialyzer

πŸ“œ License

MIT License - see LICENSE for details.


Built for Elixir developers on top of the Claude Code CLI.

Releases

No releases published

Packages

No packages published

Contributors 3

  •  
  •  
  •  

Languages