Changelog

View Source

All notable changes to this project will be documented in this file.

The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.

[0.36.3] - 2026-03-30 | CC 2.1.76

Fixed

  • ClaudeCode.History ~/.claude path evaluated at runtime — The default ~/.claude directory was a module attribute computed at compile time, which could resolve to the wrong home directory in release builds or containerized environments. Now evaluated at runtime via a private function. ([90eb073])

[0.36.2] - 2026-03-30 | CC 2.1.76

Fixed

  • --control-timeout no longer sent to CLI — The :control_timeout option, which is internal to the Elixir SDK, was incorrectly passed to the CLI as a flag. ([5ac7a0a])
  • Silenced stray Plug messages in CallbackProxy — When MCP tools execute in-process API calls via Dispatch, Plug sends messages to the CallbackProxy process, which OTP logged as errors. These harmless messages are now silently discarded. ([27087c4])

Changed

  • :extra_args changed from list to map — The :extra_args option now accepts a map of %{flag => value} (or %{flag => true} for boolean flags) instead of a list, aligning with the Python SDK convention. ([5ac7a0a])

[0.36.1] - 2026-03-29 | CC 2.1.76

Removed

  • Query-level option overrides for stream/3 — Removed the ability to pass options to stream/3 at call time. Since adopting the control protocol, the CLI subprocess ignores per-query option changes. All configuration should be set at session start via start_link/1. ([addd8b4])

[0.36.0] - 2026-03-29 | CC 2.1.76

Added

  • :inherit_env option — Controls which system environment variables are inherited by the CLI subprocess. Defaults to :all (inherit everything except CLAUDECODE, matching Python SDK behavior). Set to a list of exact strings or {:prefix, "..."} tuples for selective inheritance, or [] to inherit nothing. See Secure Deployment.

  • :env now accepts false values — Setting a key to false in the :env option unsets that variable in the CLI subprocess, leveraging Erlang Port's native env unsetting. Useful for removing sensitive inherited vars: env: %{"RELEASE_COOKIE" => false}.

Fixed

  • Hardened timing-sensitive tests — Replaced Process.sleep calls across ClaudeCode.SessionTest, ClaudeCodeTest, and ClaudeCode.SupervisorTest with deterministic synchronization (MockCLI.poll_until/2, Process.monitor + assert_receive, synchronous :sys.get_state calls). Corrected two supervisor tests that assumed empty config would crash the child — api_key is now optional (defaults to ANTHROPIC_API_KEY env var), so the child starts successfully; tests now assert count == 1 to reflect current behavior.

Changed

  • MCP tool DSL — Tool descriptions moved from a positional argument into the block. This is a breaking change. ([44573a7])

    # Before
    tool :add, "Add two numbers" do
      ...
    end
    
    # After
    tool :add do
      description "Add two numbers"
      ...
    end
  • Port spawning refactored to direct spawn_executableClaudeCode.Adapter.Port now spawns the CLI binary directly via Erlang's native :spawn_executable with :args, :env, and :cd port options, replacing the previous /bin/sh -c approach that required hand-rolled shell escaping. This eliminates shell_escape/1, build_shell_command/4, and the @shell_safe_pattern module attribute entirely. Environment variables, arguments, and paths with special characters (e.g. !, #, <, >, [, ]) are now handled natively by the Erlang runtime without shell interpretation.

Fixed

  • CLI arg ordering for :plugins, :add_dir, and :file — Flag/value pairs were reversed (e.g., /path --plugin-dir instead of --plugin-dir /path), causing the CLI to misinterpret arguments.

  • Flaky health/1 provisioning testClaudeCode.Adapter.PortIntegrationTest now accepts both {:unhealthy, :provisioning} and {:unhealthy, :not_connected} during startup, fixing a race condition where fast CI runners could resolve the CLI before the assertion.

[0.35.0] - 2026-03-26 | CC 2.1.76

Added

Fixed

  • Shell escape safetyshell_escape now quotes all non-safe characters instead of allowlisting known dangerous ones, preventing shell interpretation of !, #, <, >, ?, [, ], {, }, *, ~, tab, etc. ([0882139])

Changed

  • MCP backend migrated from hermes to anubis — Internal MCP backend replaced hermes_mcp with anubis_mcp. No breaking changes to the public API. ([967216d])

[0.34.0] - 2026-03-26 | CC 2.1.76

Changed

  • anubis_mcp upgraded to 1.0 and made a required dependencyanubis_mcp has been bumped from ~> 0.17 (optional) to ~> 1.0 (required). Users who depend on MCP functionality no longer need to explicitly add anubis_mcp to their deps. ([47a9afd])

[0.33.1] - 2026-03-20 | CC 2.1.76

Changed

  • Enhanced can_use_tool callback context — The :can_use_tool hook callback now receives additional context fields: cwd, session_id, and permission_suggestions, enabling more informed permission decisions. ([59ea547])

[0.33.0] - 2026-03-15 | CC 2.1.76

Added

  • MCP backend abstraction — New ClaudeCode.MCP.Backend behaviour allows pluggable MCP library backends. Ships with Backend.Anubis (new default) and Backend.Hermes (legacy). Both backends are optional — only compile when their respective library is loaded. Centralized backend detection via ClaudeCode.MCP.backend_for/1. ([4c69cbb])
  • Enhanced session history (Python SDK parity) — New ClaudeCode.History.list_sessions/1 returns rich ClaudeCode.History.SessionInfo metadata (summary, custom title, first prompt, git branch, cwd) using fast head/tail reads without full JSONL parsing. Supports worktree-aware scanning, deduplication, and :limit. ([bf93d7c])
  • Chain-built message retrieval — New ClaudeCode.History.get_messages/2 and ClaudeCode.Session.get_messages/2 reconstruct conversations via parentUuid chain walking, correctly handling branched and compacted conversations. Returns ClaudeCode.History.SessionMessage structs with parsed content blocks (TextBlock, ToolUseBlock, etc.). Supports :limit and :offset pagination. ([bf93d7c])
  • ClaudeCode.History.SessionMessage struct — Typed struct for history messages with :user/:assistant atom types, chain metadata (uuid, session_id), and parsed message content. ([bf93d7c])
  • ClaudeCode.History.SessionInfo struct — Rich session metadata including session_id, summary, last_modified, file_size, custom_title, first_prompt, git_branch, and cwd. ([bf93d7c])
  • ClaudeCode.History.sanitize_path/1 — Python SDK-compatible path sanitization (replaces all non-alphanumeric chars with hyphens, handles long paths with hash suffix). ([bf93d7c])
  • Configurable control_timeout session option — Max time in ms to wait for CLI control responses (e.g. initialize handshake, MCP server startup). Defaults to 60,000ms to match the Python SDK. Useful when MCP servers are slow to start. ([fb28a46], [6758caa])

Changed

  • MCP tool macro rewrite — The tool/3 macro now generates standalone modules without Hermes dependency. Tools use execute/2 with (params, assigns) instead of Hermes frames. ([4c69cbb])
  • MCP Router decoupled from Hermes — Router delegates all tool operations to the configured backend instead of importing Hermes modules directly. ([4c69cbb])
  • Breaking: Removed ClaudeCode.History.conversation/2, ClaudeCode.History.conversation_from_file/1, and ClaudeCode.Session.conversation/2 — replaced by get_messages/2 which properly handles branched/compacted conversations via parentUuid chain building. ([bf93d7c])
  • Breaking: Removed :callback_timeout from ClaudeCode.Adapter.Node — proxy delegation for hooks and MCP now uses the unified :control_timeout option instead. If you were passing :callback_timeout in adapter config, change it to :control_timeout as a session option. ([6758caa])

[0.32.2] - 2026-03-14 | CC 2.1.76

Fixed

  • mix claude_code.install version checkClaudeCode.Adapter.Port.Installer.version_of/1 now retries once after 500ms on exit code 137 (SIGKILL). On macOS, Gatekeeper can kill a freshly-copied binary during initial code signature verification, causing claude --version to fail with empty output. This led to "Could not determine installed version" on every install and unnecessary reinstalls.

[0.32.1] - 2026-03-14 | CC 2.1.76

Fixed

  • Documentation fixes: corrected stop reason docs, permissions and user inputs docs, and typespec for :allow/:deny in hook responses.

[0.32.0] - 2026-03-14 | CC 2.1.76

Added

  • ClaudeCode.Hook.DebugLogger module — A diagnostic hook that logs every invocation with event name, tool name, and available input keys. Register it for any hook event to observe what the CLI sends. Includes ClaudeCode.Hook.DebugLogger.Permissive variant for :can_use_tool that returns :allow. ([9d75ed8])

Changed

  • Breaking: hermes_mcp is now a required dependency (was optional). It was already required at compile time for ClaudeCode.MCP.Server. If your project does not use MCP features, you will now pull in hermes_mcp as a transitive dependency — no code changes are needed.
  • Breaking: Removed ClaudeCode.MCP.available?/0 and ClaudeCode.MCP.require_hermes!/0 — no longer needed with hermes_mcp required.
  • Bumped bundled CLI version to 2.1.76. ([15fdff2])

Fixed

  • MCP parameter validationClaudeCode.MCP.Router now validates tool parameters against their schema using Hermes/Peri before execution, returning JSONRPC -32602 errors for invalid input. Previously, invalid parameters were passed directly to execute/2.
  • MCP PreToolUse hooksPreToolUse hooks now apply to in-process MCP tool calls, matching the mcp__<server>__<tool> naming convention. Previously, MCP tools bypassed the hook system entirely.
  • ClaudeCode.Adapter.Port buffer overflow false positive — The buffer overflow check now runs after extracting complete lines, not before. Previously, a burst of many small complete JSON messages arriving in a single chunk could trigger a false overflow even though only the remaining incomplete buffer should count against the limit.
  • ClaudeCode.MCP.Router generic notification handling — Handle all JSONRPC 2.0 notification types (notifications/*) instead of only notifications/initialized. Previously, other notification types like notifications/cancelled would crash with a FunctionClauseError because jsonrpc_error/3 requires an "id" field that notifications don't have.
  • ClaudeCode.Test.stub/2 shared ownershipstub/2 now works correctly when the session name has a shared owner, updating the stub instead of raising. ([95524e0])

Added

  • Restored :can_use_tool option — Single permission callback invoked before every tool execution. Simpler alternative to PreToolUse hooks when you don't need matchers. Accepts a module implementing ClaudeCode.Hook or a 2-arity function. Mutually exclusive with :permission_prompt_tool. See the Hooks guide.
  • ClaudeCode.Hook.Output module family — Structured hook output types replacing the flat ClaudeCode.Hook.Response module. Each hook event has a dedicated output struct (ClaudeCode.Hook.Output.PreToolUse, ClaudeCode.Hook.Output.PostToolUse, etc.) with to_wire/1 for CLI serialization. Includes coerce/2 for mapping shorthand returns (:ok, {:allow, opts}, {:deny, opts}, etc.) to the correct wire format.
  • ClaudeCode.Hook.PermissionDecision.Allow / ClaudeCode.Hook.PermissionDecision.Deny — Permission decision structs shared by :can_use_tool and PermissionRequest hooks.
  • mix claude_code.setup_token task — New mix task that runs claude setup-token to configure an OAuth token via an interactive browser flow. Allocates a PTY to support the CLI's terminal UI on both macOS and Linux. ([aff71ca])
  • ClaudeCode.Plugin module — Plugin management functions wrapping claude plugin CLI commands: list/1, install/2, uninstall/2, enable/2, disable/2, disable_all/1, update/2.
  • ClaudeCode.Plugin.Marketplace module — Marketplace management functions wrapping claude plugin marketplace CLI commands: list/1, add/2, remove/1, update/1.
  • ClaudeCode.Plugin.CLI module — Shared CLI execution helper for plugin and marketplace commands.

[0.31.0] - 2026-03-11 | CC 2.1.72

Added

  • Shorthand syntax for :hooks option — Accept bare modules and 2-arity functions directly in hook lists (e.g., hooks: %{PreToolUse: [MyApp.Guard]}), removing the need for map wrappers in the common case. The full map form remains available for matchers, timeouts, and :where. See ClaudeCode.Options and the Hooks guide.

Removed

  • Breaking: Removed the :can_use_tool option. Use PreToolUse hooks instead, which provide the same permission decision capability through the standard hooks API. The CLI no longer sends can_use_tool control requests — all permission decisions are routed through hook callbacks. Migrate by moving your callback into hooks: %{PreToolUse: [your_callback]}.
  • Removed handle_can_use_tool/2 from the adapter control handler
  • Removed ClaudeCode.Hook.Response.to_can_use_tool_wire/1
  • Removed can_use_tool field from the hook registry struct
  • Changed hook registry constructor from arity-2 to arity-1 (no longer accepts a can_use_tool parameter)

[0.30.0] - 2026-03-11 | CC 2.1.72

Breaking release — This release reorganizes the public API and module structure for long-term clarity. See the Changed section for a migration summary.

Added

New features

New struct fields (CLI 2.1.72 sync)

Fixed

  • Race condition in ClaudeCode.Session queued request error handling — When the CLI adapter failed before the stream caller registered as a subscriber, the error was silently lost and the stream would halt normally instead of raising. ([d52f77d])
  • Atom-safe map key conversion from CLI JSON payloads — Replaced unbounded String.to_atom with safe_atomize_keys/1 (uses binary_to_existing_atom) for map keys parsed from external CLI input. Unknown keys stay as strings instead of creating new atoms, preventing atom table exhaustion. ([3c9dc90])
  • ClaudeCode.Message.AssistantMessage now parses :refusal stop reason — Aligns with ClaudeCode.Message.ResultMessage and the stop_reason typespec. ([28c15a3])
  • Streaming docs/examples corrected — Documentation and examples now consistently show partial streaming configured at session start and streamed with ClaudeCode.stream/2 or ClaudeCode.stream/3.

Changed

API restructure and module reorganization

The top-level ClaudeCode module is now slimmed to 4 core functions: start_link/1, stream/3, query/2, stop/1. All session management, runtime configuration, MCP management, and introspection functions moved to ClaudeCode.Session. The GenServer implementation moved to Session.Server (internal module). ([b5a37b1])

Module moves (old → new):

Old locationNew location
ClaudeCode.get_session_id/1ClaudeCode.Session.session_id/1
ClaudeCode.get_mcp_status/1ClaudeCode.Session.mcp_status/1
ClaudeCode.get_server_info/1ClaudeCode.Session.server_info/1
ClaudeCode.TypesExtracted to ClaudeCode.Session.PermissionMode, ClaudeCode.Session.PermissionDenial, ClaudeCode.Model.Usage, ClaudeCode.Usage
ClaudeCode.StopReasonInlined into ClaudeCode.Message as stop_reason() type and parse_stop_reason/1
ClaudeCode.PermissionModeClaudeCode.Session.PermissionMode
ClaudeCode.PermissionDenialClaudeCode.Session.PermissionDenial
ClaudeCode.AccountInfoClaudeCode.Session.AccountInfo
ClaudeCode.AgentInfoClaudeCode.Session.AgentInfo
ClaudeCode.SlashCommandClaudeCode.Session.SlashCommand
ClaudeCode.McpServerStatusClaudeCode.MCP.Status
ClaudeCode.McpSetServersResultReplaced with typed map
ClaudeCode.RewindFilesResultReplaced with typed map
System message subtypes (e.g., CompactBoundaryMessage)ClaudeCode.Message.SystemMessage.* namespace (e.g., ClaudeCode.Message.SystemMessage.CompactBoundary)

New system message subtype: ClaudeCode.Message.SystemMessage.Init extracted from SystemMessage. ([80e2d0c])

Other changes

  • ClaudeCode.Session.server_info/1 returns atom-keyed map — Uses atom keys (%{commands: [...], models: [...], agents: [...], account: %AccountInfo{}, ...}) instead of string keys. ([8bc23e3])
  • ClaudeCode.Session.mcp_status/1 returns [ClaudeCode.MCP.Status.t()] — Returns the server list directly instead of %{"servers" => [...]}. ([ffa1b81])
  • ClaudeCode.Session.rewind_files/2 returns a typed map — Returns a typed map instead of a raw map. ([7acdfd5], [baaf9a7])
  • ClaudeCode.Model.Info boolean fields default to false — Fields like supports_thinking, supports_computer_use, etc. now default to false instead of nil. ([f6b38e5])
  • Upgraded bundled CLI to 2.1.72 ([89126bb])

Removed

  • ClaudeCode.Types module — See module moves table above. ([80e2d0c])
  • ClaudeCode.McpSetServersResult and ClaudeCode.RewindFilesResult structs — Replaced with typed maps returned directly from the control protocol. ([baaf9a7])
  • set_max_thinking_tokens control-plane function — Removed the deprecated mid-session set_max_thinking_tokens control command (matches TS SDK deprecation). Use the :thinking session option instead. ([b5a37b1])

[0.29.0] - 2026-03-02 | CC 2.1.62

Added

[0.28.0] - 2026-03-02 | CC 2.1.62

Added

  • Distributed callback proxy for MCP tools and hooks — When using ClaudeCode.Adapter.Node, in-process MCP tools and hooks now work across nodes. MCP tool requests route from the sandbox node back to the app node transparently. Hooks support a :where option on matcher configs (:local to run on the app node, :remote to run on the sandbox node — defaults to :local). The :can_use_tool permission callback always executes locally. Configurable :callback_timeout (default: 30s) on the adapter, with graceful degradation if the proxy process dies. ([f0a51de])

[0.27.0] - 2026-02-28 | CC 2.1.62

Added

Changed

  • ClaudeCode.Adapter.Local renamed to ClaudeCode.Adapter.Port — The local CLI adapter and its nested modules (Installer, Resolver) now live under ClaudeCode.Adapter.Port. Update any direct references: ClaudeCode.Adapter.LocalClaudeCode.Adapter.Port. ([9accfd2])
  • Session merges top-level opts into adapter config — Session-level options (:cwd, :model, :system_prompt, etc.) are now automatically merged into the adapter config. The adapter tuple only needs adapter-specific options (e.g., :node, :cookie). ([9accfd2])

[0.26.0] - 2026-02-27 | CC 2.1.62

Added

  • All subagent frontmatter fields on ClaudeCode.Agent — Added disallowed_tools, permission_mode, max_turns, skills, mcp_servers, hooks, memory, background, and isolation fields. Uses existing ClaudeCode.Types.permission_mode() atoms and new atom types for memory (:user | :project | :local) and isolation (:worktree). Also adds agent/1 test factory to ClaudeCode.Test.Factory. ([1e2fb95])

  • Result metadata in ClaudeCode.Stream.collect/1 — The summary map now includes session_id, usage, total_cost_usd, stop_reason, and num_turns from the result message, so callers can track costs, resume sessions, and understand why a conversation ended. ([7444177])
  • replay_user_messages option — Support the --replay-user-messages CLI flag which re-emits user messages from stdin back on stdout, useful for synchronization in bidirectional streaming conversations. ([caf6c1d])
  • is_replay and is_synthetic fields on ClaudeCode.Message.UserMessage — Track whether a user message is a replay or synthetic message from the CLI. Both fields default to nil.

Changed

  • Bundled CLI version bumped to 2.1.62 — Updated from 2.1.59 to 2.1.62.
  • Removed :request_timeout, renamed :stream_timeout back to :timeout — Since :request_timeout has been removed (use Task.yield for wall-clock caps externally), there's only one timeout again, so the simpler :timeout name is restored. Default remains :infinity. The :stream_timeout and :request_timeout options are no longer accepted.
  • :max_output_tokens in ClaudeCode.Message.AssistantMessage error type — Added explicit typespec entry and match clause for the max_output_tokens error, previously handled by a catch-all.

Fixed

  • Interrupt sent before closing port — On terminate, the adapter now sends an interrupt control request to the CLI before closing the port, preventing continued API token consumption. ([3a93f43])
  • User environment variables take precedence over SDK defaults — Fixed Map.merge order so user-provided environment variables override SDK-injected ones instead of the reverse. ([7385c03])

[0.25.0] - 2026-02-26 | CC 2.1.59

Added

  • 5 new CLI message types — Parse rate_limit_event, tool_progress, tool_use_summary, auth_status, and prompt_suggestion messages instead of silently dropping them. ([0724eef])
  • ClaudeCode.Stream.filter_type/2 support for new types — Filter streams by :rate_limit_event, :tool_progress, :tool_use_summary, :auth_status, and :prompt_suggestion. ([75e5a1b])
  • Factory functions for new message typesrate_limit_event/1, tool_progress_message/1, tool_use_summary_message/1, auth_status_message/1, prompt_suggestion_message/1 available in ClaudeCode.Test.Factory. ([75e5a1b])

Changed

  • RateLimitEvent.status is now an atom — The status field in rate_limit_info is parsed to :allowed, :allowed_warning, or :rejected instead of raw strings. ([75e5a1b])

Fixed

  • Queries rejected immediately when adapter has failed — Previously, if the adapter process crashed, queries would hang until timeout. Now they return {:error, {:adapter_not_running, reason}} immediately. ([17a6b89])

[0.24.0] - 2026-02-26 | CC 2.1.59

Added

  • :stream_timeout option — New name for the per-message stream timeout (max wait for the next message). Replaces :timeout for clarity alongside :request_timeout.
  • :dangerously_skip_permissions option — Directly bypass all permission checks. Unlike :allow_dangerously_skip_permissions which only enables bypassing as an option, this flag activates it immediately. Recommended only for sandboxed environments with no internet access.

Changed

  • Bundled CLI version bumped to 2.1.59 — Updated from 2.1.49 to 2.1.59.
  • --setting-sources always sent — The SDK now always sends --setting-sources "" when no setting sources are configured, matching the Python SDK behavior. This prevents unintended default setting source loading.

[0.23.0] - 2026-02-22 | CC 2.1.49

Added

  • :request_timeout option — Configurable wall-clock timeout for entire requests from start to finish. Previously hardcoded at 300 seconds, making long-running agentic tasks (MCP tool calls, large file generation) impossible beyond 5 minutes. Available at session, query, and app-config levels. ([d1b6f02])

[0.22.0] - 2026-02-20 | CC 2.1.49

Added

  • :max effort level — The :effort option now accepts :max in addition to :low, :medium, and :high, aligning with the Python SDK.

Changed

  • Bundled CLI version bumped to 2.1.49 — Updated from 2.1.42 to 2.1.49.

Fixed

  • Doc warnings for hidden Hook.Response module — Fixed references in the hooks guide that generated documentation warnings. ([6402c7a])
  • Documentation guides synced with official Agent SDK docs — Updated 16 guides to match the latest official documentation, including restructured file-checkpointing, new hook fields, fixed broken plugin links, and updated terminology. ([67f7d52])

[0.21.0] - 2026-02-14 | CC 2.1.42

Added

  • :thinking option — Idiomatic Elixir API for extended thinking configuration. Supports :adaptive, :disabled, and {:enabled, budget_tokens: N}. Takes precedence over the now-deprecated :max_thinking_tokens. ([fa6b39d])
  • :effort option — Control effort level per session or query with :low, :medium, or :high. ([fa6b39d])
  • caller field on ToolUseBlock — Parses the optional caller metadata from tool use content blocks. ([93de892])
  • speed field on ResultMessage usage — Captures the speed field from CLI usage data when present. ([93de892])
  • context_management on stream eventsPartialAssistantMessage now parses context_management data from stream events. ([93de892])
  • :refusal stop reasonResultMessage now parses the "refusal" stop reason from the CLI. ([a7a7cfc])

Changed

  • Bundled CLI version bumped to 2.1.42 ([789a813])
  • :max_thinking_tokens deprecated — Still works, but emits a Logger.warning directing users to the new :thinking option. ([fa6b39d])
  • Hook callback input keys atomized — Hook callbacks now receive atom-keyed maps (e.g., %{tool_name: "Bash"}) instead of string-keyed maps. ([0fcc415])

Fixed

  • :setting_sources documentation — Corrected example to use strings (["user", "project", "local"]) instead of atoms. ([bc2ba7a])

[0.20.0] - 2026-02-10 | CC 2.1.38

Added

  • Assigns for in-process MCP tools — Pass per-session context (e.g., current_scope from LiveView) to tools via :assigns in the server config. Tools using execute/2 can read frame.assigns. ([a156aae])
    • Usage: mcp_servers: %{"tools" => %{module: MyTools, assigns: %{scope: scope}}}
    • Tools using execute/1 are unaffected; mix both forms freely in the same server module

Changed

  • ClaudeCode.Tool.Server renamed to ClaudeCode.MCP.Server — Unified MCP namespace so all MCP-related modules live under ClaudeCode.MCP.* ([bda2260])
    • Update use ClaudeCode.Tool.Serveruse ClaudeCode.MCP.Server in your tool definitions
    • The DSL (tool, field, execute) is unchanged
  • Custom tools guide rewritten — Aligned with official SDK docs structure; in-process tool examples now use the simpler execute/1 form ([5db0e0a])

Removed

  • ClaudeCode.ToolCallback — Removed the :tool_callback option and ClaudeCode.ToolCallback module. Use :hooks with PostToolUse events instead. See the Hooks guide for migration examples.
  • ClaudeCode.MCP.Config — Legacy module for generating temporary MCP config files. The adapter now builds --mcp-config JSON inline. ([bda2260])
  • Old ClaudeCode.MCP.Server (HTTP GenServer) — Legacy HTTP-based MCP server wrapper, replaced by the in-process control protocol. ([bda2260])

Fixed

  • SDK MCP server initialization failures — Added missing version to MCP serverInfo response and fixed crash on JSONRPC notifications (no id field). In-process tool servers now connect successfully. ([2c746a1])
  • Hermes MCP tool examples in docs — Corrected to use the actual schema + execute/2 API and component registration instead of the non-functional definition/0 + call/1 pattern ([dcf2f41])

[0.19.0] - 2026-02-10 | CC 2.1.38

Added

BEAM-native extensibility

Hooks, permissions, and MCP tools that run inside your application process — no external subprocesses required.

  • In-process hooks and permission control ([22da55c], [f36b270], [a6034d0], [5faf1f2], [e3bdbc9], [df300a9])
    • ClaudeCode.Hook behaviour - Define hook modules implementing call/2 for lifecycle events (PreToolUse, PostToolUse, Stop, UserPromptSubmit, PreCompact, Notification, etc.)
    • :can_use_tool option - Permission callback (module or 2-arity function) invoked before tool execution; returns :allow, {:deny, reason}, or {:allow, updated_input}
    • :hooks option - Lifecycle hook configurations as a map of event names to matcher/callback pairs
  • In-process MCP tool servers ([5c049d4], [2b156c4], [f1a4420])
    • ClaudeCode.Tool.Server macro - Concise DSL for declaring tools with typed schemas and execute callbacks, generating Hermes Server.Component modules
    • ClaudeCode.MCP.Router - JSONRPC dispatcher that routes initialize, tools/list, and tools/call requests to in-process tool modules
    • Auto-detects Tool.Server modules in :mcp_servers and emits type: "sdk" config, routing through the control protocol instead of spawning a subprocess

Subagents

  • ClaudeCode.Agent struct - Idiomatic builder for subagent configurations ([1d0188b])
    • ClaudeCode.Agent.new/1 accepts keyword options: :name, :description, :prompt, :model, :tools
    • Pass a list of Agent structs to the :agents option instead of raw maps
    • Implements Jason.Encoder and JSON.Encoder protocols; raw map format still supported

Session control and new options

  • ClaudeCode.interrupt/1 - Fire-and-forget signal to cancel a running generation mid-stream ([5c04495])
  • :extra_args - Pass-through arbitrary CLI flags not covered by named options ([5c04495])
  • :max_buffer_size - Protection against unbounded buffer growth from large JSON responses. Default: 1MB ([5c04495])

[0.18.0] - 2026-02-10 | CC 2.1.37

Breaking

  • SDK bundles its own CLI binary by default - The SDK now downloads and manages its own Claude CLI in priv/bin/, auto-installing on first use. To use a globally installed CLI instead, set cli_path: :global or pass an explicit path like cli_path: "/usr/local/bin/claude". The bundled version defaults to the latest CLI version tested with the SDK, configurable via cli_version. See ClaudeCode.Options for details.
# config.exs
config :claude_code, cli_path: :global

Added

Control protocol

Runtime control of sessions without restarting. See Sessions — Runtime Control.

  • ClaudeCode.set_model/2 - Change the model mid-conversation ([7ba2007])
  • ClaudeCode.set_permission_mode/2 - Change the permission mode mid-conversation ([7ba2007])
  • ClaudeCode.get_mcp_status/1 - Query MCP server connection status ([7ba2007])
  • ClaudeCode.get_server_info/1 - Get server info cached from handshake ([228c57f])
  • ClaudeCode.rewind_files/2 - Rewind files to a checkpoint. See File Checkpointing. ([7ba2007])
  • Returns {:error, :not_supported} for adapters without control protocol support
  • Initialize handshake - Adapter sends initialize request on startup, transitions through :initializing:ready. Agents are now delivered through the handshake (matching the Python SDK) instead of as a CLI flag. See Subagents. ([228c57f], [2a4473b])

New options

  • :sandbox - Sandbox config for bash isolation (map merged into --settings). See Secure Deployment. ([5f48858])
  • :enable_file_checkpointing - Track file changes for rewinding. See File Checkpointing. ([5f48858])
  • :allow_dangerously_skip_permissions - Required guard for permission_mode: :bypass_permissions. See Permissions. ([c9dc6fa])
  • :file - File resources (repeatable, format: file_id:path) ([d6c1869])
  • :from_pr - Resume session linked to a PR ([d6c1869])
  • :debug / :debug_file - Debug mode with optional filter and log file ([d6c1869])

Adapter system

Swappable backends for different execution environments.

  • ClaudeCode.Adapter behaviour - 4 callbacks: start_link/2, send_query/4, health/1, stop/1 ([1582644])
  • Adapter notification helpers - notify_message/2, notify_done/2, notify_error/2, notify_status/2 ([1704326])
  • ClaudeCode.health/1 - Check adapter health (:healthy | :degraded | {:unhealthy, reason}). See Hosting. ([383dda6])

CLI management

Changed

  • :cli_path resolution modes - :bundled (default), :global, or explicit path string. See ClaudeCode.Options. ([94b5143])
  • Async adapter provisioning - start_link/1 returns immediately; CLI setup runs in the background. Queries queue until ready. ([f1a0875], [91ee60d], [6a60eb4])
  • Schema alignment with CLI v2.1.37 - New fields across message types ([482c603], [42b6c27])
    • AssistantMessage.error (:authentication_failed, :billing_error, :rate_limit, :invalid_request, :server_error, :unknown)
    • UserMessage.tool_use_result, ResultMessage.stop_reason, AssistantMessage usage inference_geo
    • SystemMessage handles all subtypes (init, hook_started, hook_response); plugins supports object format

[0.17.0] 2026-02-01 | CC 2.1.29

Added

  • :max_thinking_tokens option - Maximum tokens for thinking blocks (integer)
    • Available for both session and query options
    • Maps to --max-thinking-tokens CLI flag
  • :continue option - Continue the most recent conversation in the current directory (boolean)
    • Maps to --continue CLI flag
    • Aligns with Python/TypeScript SDK continue option
  • :plugins option - Load custom plugins from local paths (list of paths or maps)
    • Accepts ["./my-plugin"] or [%{type: :local, path: "./my-plugin"}]
    • Plugin type uses atom :local (only supported type currently)
    • Maps to multiple --plugin-dir CLI flags
    • Aligns with Python/TypeScript SDK plugins option
  • :output_format option - Structured output format configuration (replaces :json_schema)
    • Format: %{type: :json_schema, schema: %{...}}
    • Currently only :json_schema type is supported
    • Maps to --json-schema CLI flag
    • Aligns with Python/TypeScript SDK outputFormat option
  • context_management field in AssistantMessage - Support for context window management metadata in assistant messages ([f4ea348])
  • CLI installer - Automatic CLI binary management following phoenixframework/esbuild patterns
    • mix claude_code.install - Mix task to install CLI with --version, --if-missing, --force flags
    • ClaudeCode.Installer module for programmatic CLI management
    • Uses official Anthropic install scripts (https://claude.ai/install.sh)
    • Binary resolution checks: explicit path → bundled → PATH → common locations
  • :cli_path option - Specify a custom path to the Claude CLI binary
  • Configuration options for CLI management:
    • cli_version - Version to install (default: SDK's tested version)
    • cli_path - Explicit path to CLI binary (highest priority)
    • cli_dir - Directory for downloaded binary (default: priv/bin/)

[0.16.0] - 2026-01-27

Added

  • :env option - Pass custom environment variables to the CLI subprocess ([aa2d3eb])
    • Merge precedence: system env → user :env → SDK vars → :api_key
    • Useful for MCP tools that need specific env vars or custom PATH configurations
    • Aligns with Python SDK's environment handling

[0.15.0] - 2026-01-26

Added

  • Session history reading - Read and parse conversation history from session files ([ad737ea])
  • JSON encoding for all structs - Implement Jason.Encoder and JSON.Encoder protocols ([a511d5c])
    • All message types: SystemMessage, AssistantMessage, UserMessage, ResultMessage, PartialAssistantMessage, CompactBoundaryMessage
    • All content blocks: TextBlock, ThinkingBlock, ToolUseBlock, ToolResultBlock
    • Nil values are automatically excluded from encoded output
  • String.Chars for messages and content blocks - Use to_string/1 or string interpolation ([b3a9571])
    • TextBlock - returns the text content
    • ThinkingBlock - returns the thinking content
    • AssistantMessage - concatenates all text blocks from the message
    • PartialAssistantMessage - returns delta text (empty string for non-text deltas)

Changed

  • Conversation message parsing refactored - Extracted to dedicated module with improved error logging ([22c381c])

[0.14.0] - 2026-01-15

Added

  • :session_id option - Specify a custom UUID as the session ID for conversations ([2f2c919])
  • :disable_slash_commands option - Disable all skills/slash commands ([16f96b4])
  • :no_session_persistence option - Disable session persistence so sessions are not saved to disk ([16f96b4])
  • New permission modes - :delegate, :dont_ask, and :plan added to :permission_mode option ([16f96b4])
  • New usage tracking fields - cache_creation, service_tier, web_fetch_requests, cost_usd, context_window, max_output_tokens in result and assistant message usage ([bed060b])
  • New system message fields - claude_code_version, agents, skills, plugins for enhanced session metadata ([bed060b])

Fixed

  • SystemMessage slash_commands and output_style parsing - Fields were always empty/default ([bed060b])
  • ResultMessage model_usage parsing - Per-model token counts and costs were always 0/nil ([bed060b])

[0.13.3] - 2026-01-14

Changed

  • ResultMessage optional fields use sensible defaults - model_usage defaults to %{} and permission_denials defaults to [] instead of nil ([cda582b])

Fixed

  • ResultMessage.result is now optional - Error messages from the CLI may contain an errors array instead of a result field. The field no longer crashes when nil and displays errors appropriately ([c06e825])

[0.13.2] - 2026-01-08

Fixed

  • ToolResultBlock content parsing - When CLI returns content as a list of text blocks, they are now parsed into TextBlock structs instead of raw maps ([5361e2d])

[0.13.1] - 2026-01-07

Changed

  • Simplified test stub naming - Default stub name changed from ClaudeCode.Session to ClaudeCode ([2fd244f])
    • Config: adapter: {ClaudeCode.Test, ClaudeCode} instead of {ClaudeCode.Test, ClaudeCode.Session}
    • Stubs: ClaudeCode.Test.stub(ClaudeCode, fn ...) instead of stub(ClaudeCode.Session, fn ...)
    • Custom names still supported for multiple stub behaviors in same test

Added

  • tool_result/2 accepts maps - Maps are automatically JSON-encoded ([6d9fca6])
    • Example: ClaudeCode.Test.tool_result(%{status: "success", data: [1, 2, 3]})

Fixed

  • tool_result content format - Content is now [TextBlock.t()] instead of plain string ([dfba539])
    • Matches MCP CallToolResult format where content is an array of content blocks
    • Fixes compatibility with code expecting content: [%{"type" => "text", "text" => ...}]

[0.13.0] - 2026-01-07

Added

  • ClaudeCode.Test module - Req.Test-style test helpers for mocking Claude responses ([9f78103])
    • stub/2 - Register function or static message stubs for test isolation
    • allow/3 - Share stubs with spawned processes for async tests
    • set_mode_to_shared/0 - Enable shared mode for integration tests
    • Message helpers: text/2, tool_use/3, tool_result/2, thinking/2, result/2, system/1
    • Auto-generates system/result messages, links tool IDs, unifies session IDs
    • Uses NimbleOwnership for process-based isolation with async: true support
  • ClaudeCode.Test.Factory module - Test data generation for all message and content types ([54dcfd7])
    • Struct factories: assistant_message/1, user_message/1, result_message/1, system_message/1
    • Content block factories: text_block/1, tool_use_block/1, tool_result_block/1, thinking_block/1
    • Stream event factories for partial message testing
    • Convenience functions with positional arguments for common cases
  • Testing guide - Comprehensive documentation for testing ClaudeCode integrations ([7dfe509])

[0.12.0] - 2026-01-07

Added

  • New stream helpers for common use cases ([0775bd4])
    • final_text/1 - Returns only the final result text, simplest way to get Claude's answer
    • collect/1 - Returns structured summary with text, thinking, tool_calls, and result
    • tap/2 - Side-effect function for logging/monitoring without filtering the stream
    • on_tool_use/2 - Callback invoked for each tool use, useful for progress indicators

Changed

  • collect/1 returns tool_calls instead of tool_uses ([7eebfeb])
    • Now returns {tool_use, tool_result} tuples pairing each tool invocation with its result
    • If a tool use has no matching result, the result will be nil
    • Migration: Change summary.tool_uses to summary.tool_calls and update iteration to handle tuples

Removed

  • buffered_text/1 stream helper - Use final_text/1 or collect/1 instead ([4a1ee97])

[0.11.0] - 2026-01-07

Changed

  • Renamed StreamEventMessage to PartialAssistantMessage - Aligns with TypeScript SDK naming (SDKPartialAssistantMessage)
    • ClaudeCode.Message.StreamEventMessageClaudeCode.Message.PartialAssistantMessage
    • The struct still uses type: :stream_event to match the wire format
    • Helper function renamed: stream_event?/1partial_assistant_message?/1

Added

  • :fork_session option - Create a new session ID when resuming a conversation
    • Use with :resume to branch a conversation: start_link(resume: session_id, fork_session: true)
    • Original session continues unchanged, fork gets its own session ID after first query

[0.9.0] - 2026-01-06

Changed

  • BREAKING: Simplified public API - Renamed and reorganized query functions ([e7ca31a])
    • query_stream/3stream/3 - Primary API for session-based streaming queries
    • query/3 (session-based sync) → Removed - Use stream/3 instead
    • query/2 (new) - One-off convenience function with auto session management
    • Migration: Replace ClaudeCode.query(session, prompt) with ClaudeCode.stream(session, prompt) |> Enum.to_list()
    • Migration: Replace ClaudeCode.query_stream(session, prompt) with ClaudeCode.stream(session, prompt)

Added

  • Concurrent request queuing - Multiple concurrent streams on same session are now properly queued and executed sequentially ([e7ca31a])

Fixed

  • Named process handling - Stream cleanup now properly handles named processes (atoms, :via, :global tuples) ([e7ca31a])

[0.8.1] - 2026-01-06

Fixed

  • Process cleanup on stop - Claude subprocess now properly terminates when calling ClaudeCode.stop/1 ([a560ff1])

[0.8.0] - 2026-01-06

Changed

Added

  • New system message fields - Support for additional Claude Code features
    • :output_style - Claude's configured output style
    • :slash_commands - Available slash commands
    • :uuid - Session UUID
  • Extended message type fields - Better access to API response metadata
    • AssistantMessage: :priority, :sequence_id, :finalize_stack
    • ResultMessage: :session_id, :duration_ms, :usage, :parent_message_id, :sequence_id
    • UserMessage: :priority, :sequence_id, :finalize_stack

Fixed

  • :mcp_servers option validation - Fixed handling of MCP server configurations ([0c7e849])

[0.7.0] - 2026-01-02

Added

  • :strict_mcp_config option - Control MCP server loading behavior ([a095516])
    • When true, ignores global MCP server configurations
    • Useful for disabling all MCP tools: tools: [], strict_mcp_config: true
    • Or using only built-in tools: tools: :default, strict_mcp_config: true

Changed

  • BREAKING: ClaudeCode.query now returns full %Result{} struct instead of just text
    • Before: {:ok, "response text"} or {:error, {:claude_error, "message"}}
    • After: {:ok, %ClaudeCode.Message.Result{result: "response text", ...}} or {:error, %ClaudeCode.Message.Result{is_error: true, ...}}
    • Provides access to metadata: session_id, is_error, subtype, duration_ms, usage, etc.
    • Migration: Change {:ok, text} to {:ok, result} and use result.result to access the response text
    • Result implements String.Chars, so IO.puts(result) prints just the text

Removed

  • :input_format option - No longer exposed in public API ([c7ebab2])
  • :output_format option - No longer exposed in public API ([c7ebab2])

[0.6.0] - 2025-12-31

Added

  • :mcp_servers module map format - Pass Hermes modules with custom environment variables ([63d4b72])
    • Simple form: %{"tools" => MyApp.MCPServer}
    • Extended form with env: %{"tools" => %{module: MyApp.MCPServer, env: %{"DEBUG" => "1"}}}
    • Custom env is merged with defaults (MIX_ENV: "prod"), can override MIX_ENV
    • Updated MCP docs to recommend mcp_servers as the primary configuration method
  • :json_schema option - JSON Schema for structured output validation ([485513f])
    • Accepts a map (auto-encoded to JSON) or pre-encoded JSON string
    • Maps to --json-schema CLI flag
  • :max_budget_usd option - Maximum dollar amount to spend on API calls ([5bf996a])
    • Accepts float or integer values
    • Maps to --max-budget-usd CLI flag
  • :tools option - Specify available tools from the built-in set ([5bf996a])
    • Use :default for all tools, [] to disable all, or a list of tool names
    • Maps to --tools CLI flag
  • :agent option - Agent name for the session ([5bf996a])
    • Different from :agents which defines custom agent configurations
    • Maps to --agent CLI flag
  • :betas option - Beta headers to include in API requests ([5bf996a])
    • Accepts a list of beta feature names
    • Maps to --betas CLI flag

Removed

  • query_async/3 - Removed push-based async API in favor of query_stream/3
    • query_stream/3 provides a more idiomatic Elixir Stream-based API
    • For push-based messaging (LiveView, GenServers), wrap query_stream/3 in a Task
    • See Phoenix integration guide for migration examples
  • Advanced Streaming API - Removed low-level streaming functions
    • receive_messages/2 - Use query_stream/3 instead
    • receive_response/2 - Use query_stream/3 |> ClaudeCode.Stream.until_result() instead
    • interrupt/2 - To cancel, use Task.shutdown/2 on the consuming task

Changed

  • ClaudeCode.Stream - Now uses pull-based messaging internally instead of process mailbox

[0.5.0] - 2025-12-30

Removed

  • :permission_handler option - Removed unimplemented option from session schema

Added

  • Persistent streaming mode - Sessions use bidirectional stdin/stdout communication
    • Auto-connect on first query, auto-disconnect on session stop
    • Multi-turn conversations without subprocess restarts
    • New :resume option in start_link/1 for resuming sessions
    • New ClaudeCode.get_session_id/1 and ClaudeCode.Input module
  • Extended thinking support - ClaudeCode.Content.Thinking for reasoning blocks
  • MCP servers map option - :mcp_servers accepts inline server configurations
    • Supports stdio, sse, and http transport types
  • Character-level streaming - include_partial_messages: true option
  • Tool callback - :tool_callback option for logging/auditing tool usage
    • ClaudeCode.ToolCallback module for correlating tool use and results
  • Hermes MCP integration - Expose Elixir tools to Claude via MCP
    • Optional dependency: {:hermes_mcp, "~> 0.14", optional: true}
    • ClaudeCode.MCP.Config for generating MCP configuration
    • ClaudeCode.MCP.Server for starting Hermes MCP servers

Changed

[0.4.0] - 2025-10-02

Added

  • Custom agents support - :agents option for defining agent configurations
  • Settings options - :settings and :setting_sources for team settings

Changed

  • :api_key now optional - CLI handles ANTHROPIC_API_KEY fallback

Fixed

  • CLI streaming with explicit output-format support

[0.3.0] - 2025-06-16

Added

  • ClaudeCode.Supervisor - Production supervision for multiple Claude sessions
    • Static named sessions and dynamic session management
    • Global, local, and registry-based naming
    • OTP supervision with automatic restarts

[0.2.0] - 2025-06-16

Added

  • ANTHROPIC_API_KEY environment variable fallback

Changed

  • BREAKING: Renamed API functions:
    • query_sync/3query/3
    • query/3query_stream/3
  • start_link/1 options now optional (defaults to [])

[0.1.0] - 2025-06-16

Added

  • Complete SDK Implementation (Phases 1-4):

    • Session management with GenServer-based architecture
    • Synchronous queries with query_sync/3 (renamed to query/3 in later version)
    • Streaming queries with native Elixir streams via query/3 (renamed to query_stream/3 in later version)
    • Async queries with query_async/3 for manual message handling
    • Complete message type parsing (system, assistant, user, result)
    • Content block handling (text, tool use, tool result) with proper struct types
    • Flattened options API with NimbleOptions validation
    • Option precedence system: query > session > app config > defaults
    • Application configuration support via config :claude_code
    • Comprehensive CLI flag mapping for all Claude Code options
  • Core Modules:

  • Message Type Support:

    • System messages with session initialization
    • Assistant messages with nested content structure
    • User messages with proper content blocks
    • Result messages with error subtypes
    • Tool use and tool result content blocks
  • Streaming Features:

    • Native Elixir Stream integration with backpressure handling
    • Stream utilities: text_content/1, tool_uses/1, filter_type/2
    • Buffered text streaming with buffered_text/1
    • Concurrent streaming request support
    • Proper stream cleanup and error handling
  • Configuration System:

    • 15+ configuration options with full validation
    • Support for API key, model, system prompt, allowed tools
    • Permission mode options: :default, :accept_edits, :bypass_permissions
    • Timeout, max turns, working directory configuration
    • Custom permission handler support
    • Query-level option overrides

Implementation Details

  • Flattened options API for intuitive configuration
  • Updated CLI flag mappings to match latest Claude Code CLI
  • Enhanced error handling with proper message subtypes
  • Shell wrapper implementation to prevent CLI hanging
  • Proper JSON parsing for all message types
  • Concurrent query isolation with dedicated ports
  • Memory management for long-running sessions
  • Session continuity across multiple queries

Security

  • API keys passed via environment variables only
  • Shell command injection prevention with proper escaping
  • Subprocess isolation with dedicated ports per query
  • No sensitive data in command arguments or logs

Documentation

  • Complete module documentation with doctests
  • Comprehensive README with installation and usage examples
  • Streamlined roadmap focusing on current status and future enhancements

Testing

  • 146+ comprehensive tests covering all functionality
  • Unit tests for all modules with mock CLI support
  • Integration tests with real CLI when available
  • Property-based testing for message parsing
  • Stream testing with concurrent scenarios
  • Coverage reporting with ExCoveralls