Hi! I’ve been building ClaudeCode, an SDK that lets you embed Claude as an agent in your Elixir apps - not just a chatbot, but an AI that can take actions.
The Problem
Most AI integrations are text-in/text-out. Building anything useful means parsing responses, manually wiring tool calls, handling conversation state, and mocking everything by hand for tests.
The Solution: Hermes + ClaudeCode
ClaudeCode wraps the claude cli, giving Claude full agentic capabilities. Combined with hermes_mcp v0.14.1 — Documentation , Claude can call your Elixir functions directly:
# Define a tool
defmodule MyApp.Tools.RefundOrder do
use Hermes.Server.Component, type: :tool
schema do
field :order_id, :string, required: true
end
def execute(%{order_id: id}, frame) do
{:ok, refund} = MyApp.Orders.refund(id)
{:reply, Response.json(Response.tool(), refund), frame}
end
end
# Create an MCP server
defmodule MyApp.SupportTools do
use Hermes.Server, name: "myapp", version: "1.0.0", capabilities: [:tools]
component MyApp.Tools.RefundOrder
component MyApp.Tools.OrderLookup
end
# Start a session
{:ok, session} = ClaudeCode.start_link(mcp_servers: %{"myapp" => MyApp.SupportTools})
# Ask it to do something
session
|> ClaudeCode.stream("Customer jane@example.com wants a refund for her last order")
|> ClaudeCode.Stream.tap(&IO.inspect/1)
|> ClaudeCode.Stream.final_text()
Claude will call OrderLookup, find Jane’s order, call RefundOrder, and explain what happened. No decision trees. No hardcoded workflows.
Testing Without API Calls
test "support agent processes refunds" do
ClaudeCode.Test.stub(ClaudeCode, fn _query, _opts ->
[
ClaudeCode.Test.tool_use("OrderLookup", %{email: "jane@example.com"}),
ClaudeCode.Test.tool_result(%{id: "ORD-123", total: 99.99}]),
ClaudeCode.Test.text("Processing refund for order ORD-123..."),
ClaudeCode.Test.tool_use("RefundOrder", %{order_id: "ORD-123", reason: "request"}),
ClaudeCode.Test.tool_result(%{status: "processed"}),
ClaudeCode.Test.text("Done! Refunded $99.99.")
]
end)
{:ok, session} = ClaudeCode.start_link()
result = session |> ClaudeCode.stream("Refund Jane") |> ClaudeCode.Stream.final_text()
assert result =~ "Refunded $99.99"
end
Millisecond tests. No API calls. Full async: true support.
Also Included
- OTP Supervision - Sessions restart on failure
- Native Streaming - Elixir Streams, LiveView-ready
- Multi-turn Memory - Context retained across queries
- Concurrent Agents - Run multiple sessions with the actor model
Links
- Hex: claude_code | Hex
- Docs: ClaudeCode v0.13.2 — Documentation
- GitHub:
Early days - I’d love to hear what agents you’d build, or what’s missing that would make this useful for your projects.






















