{"id":29220106,"url":"https://github.com/guess/claude_code","last_synced_at":"2026-04-02T13:57:07.584Z","repository":{"id":298913603,"uuid":"1001514627","full_name":"guess/claude_code","owner":"guess","description":"Claude Agent SDK for Elixir – Build AI agents with Claude Code","archived":false,"fork":false,"pushed_at":"2026-03-26T19:07:38.000Z","size":7807,"stargazers_count":79,"open_issues_count":0,"forks_count":14,"subscribers_count":1,"default_branch":"main","last_synced_at":"2026-03-27T01:11:12.152Z","etag":null,"topics":["agents","ai","anthropic","beam","claude","elixir","genserver","liveview","otp","phoenix","sdk","streaming"],"latest_commit_sha":null,"homepage":"https://code.claude.com","language":"Elixir","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/guess.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-06-13T14:08:32.000Z","updated_at":"2026-03-26T19:07:41.000Z","dependencies_parsed_at":"2025-06-13T15:47:00.489Z","dependency_job_id":"62e50e1e-fd0d-4f0a-be0b-8d8fd311a7cc","html_url":"https://github.com/guess/claude_code","commit_stats":null,"previous_names":["guess/claude_code"],"tags_count":46,"template":false,"template_full_name":null,"purl":"pkg:github/guess/claude_code","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/guess%2Fclaude_code","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/guess%2Fclaude_code/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/guess%2Fclaude_code/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/guess%2Fclaude_code/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/guess","download_url":"https://codeload.github.com/guess/claude_code/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/guess%2Fclaude_code/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31307399,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-02T12:59:32.332Z","status":"ssl_error","status_checked_at":"2026-04-02T12:54:48.875Z","response_time":89,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["agents","ai","anthropic","beam","claude","elixir","genserver","liveview","otp","phoenix","sdk","streaming"],"created_at":"2025-07-03T02:37:58.870Z","updated_at":"2026-04-02T13:57:07.572Z","avatar_url":"https://github.com/guess.png","language":"Elixir","funding_links":[],"categories":["Agent Frameworks","Generative AI"],"sub_categories":["LLM Tools"],"readme":"# Claude Agent SDK for Elixir\n\nThe idiomatic Elixir SDK for building AI agents with Claude. Native streams, in-process tools, OTP lifecycle management.\n\n- **✅ Full Feature Parity** – 100% parity with the official Python and TypeScript SDKs\n- **📦 Zero Setup** – Bundled CLI binary, auto-installed on first use. Just add the dep.\n- **🏭 OTP Native** – Sessions are GenServers with standard OTP lifecycle management\n- **🔄 Elixir Streams** – Native streaming with backpressure and composable pipelines\n- **🔌 In-Process Tools \u0026 Hooks** – BEAM-native tools and lifecycle hooks with full access to application state\n- **🌐 Distributed Sessions** – Offload CLI processes to remote sandboxes, scale app and CLI independently\n- **⚡ Phoenix LiveView** – Stream tokens directly into LiveView and PubSub\n\n[![Hex.pm](https://img.shields.io/hexpm/v/claude_code.svg)](https://hex.pm/packages/claude_code)\n[![Documentation](https://img.shields.io/badge/docs-hexdocs-blue.svg)](https://hexdocs.pm/claude_code)\n[![License](https://img.shields.io/badge/license-MIT-green.svg)](https://github.com/guess/claude_code/blob/main/LICENSE)\n[![Elixir](https://img.shields.io/badge/elixir-%3E%3D1.18-purple.svg)](https://elixir-lang.org)\n\n\u003cdiv align=\"center\"\u003e\n    \u003cimg src=\"https://github.com/guess/claude_code/raw/main/docs/claudecode.png\" alt=\"ClaudeCode\" width=\"200\"\u003e\n\u003c/div\u003e\n\n```elixir\n{:ok, session} = ClaudeCode.start_link()\n\nsession\n|\u003e ClaudeCode.stream(\"Refactor the auth module and add tests\")\n|\u003e ClaudeCode.Stream.text_content()\n|\u003e Enum.each(\u0026IO.write/1)\n```\n\n## Why Elixir?\n\nAI agents are long-lived processes that execute tools, maintain state, and stream responses. That's just OTP:\n\n- **Sessions are GenServers** – link to a LiveView, spawn per-request, or supervise as a service\n- **Elixir Streams** – backpressure, composability, and direct piping into LiveView\n- **In-process tools** – direct access to Ecto repos, GenServers, and caches from inside the BEAM\n- **Distributed by default** – offload heavy CLI processes to dedicated sandbox servers via Erlang distribution. Your app stays lightweight; CLI resources scale independently.\n\n## Install\n\nAdd to `mix.exs`:\n\n```elixir\ndef deps do\n  [{:claude_code, \"~\u003e 0.36\"}]\nend\n```\n\n```bash\nmix deps.get\nmix claude_code.install  # optional – downloads on first use if skipped\n\n# Authenticate (pick one)\nexport ANTHROPIC_API_KEY=\"sk-...\"       # Option A: API key\nmix claude_code.setup_token             # Option B: Claude subscription (opens browser)\nexport CLAUDE_CODE_OAUTH_TOKEN=\"...\"    # Then set the token it prints\n```\n\n## Quick Start\n\n```elixir\n# One-off query (ResultMessage implements String.Chars)\n{:ok, result} = ClaudeCode.query(\"Explain GenServers in one sentence\")\nIO.puts(result)\n\n# Multi-turn session with streaming\n{:ok, session} = ClaudeCode.start_link()\n\nsession |\u003e ClaudeCode.stream(\"My favorite language is Elixir\") |\u003e Stream.run()\n\nsession\n|\u003e ClaudeCode.stream(\"What's my favorite language?\")\n|\u003e ClaudeCode.Stream.text_content()\n|\u003e Enum.each(\u0026IO.write/1)\n# =\u003e \"Your favorite language is Elixir!\"\n```\n\n## Example Scripts\n\nSee [`examples/README.md`](examples/README.md) for runnable examples including streaming and character-level streaming with `include_partial_messages: true`.\n\n## Features\n\n### In-Process Custom Tools\n\nDefine tools that run inside your BEAM VM. They have direct access to your Ecto repos, GenServers, caches – anything in your application.\n\n```elixir\ndefmodule MyApp.Tools do\n  use ClaudeCode.MCP.Server, name: \"app-tools\"\n\n  tool :query_user do\n    description \"Look up a user by email\"\n\n    field :email, :string, required: true\n\n    def execute(%{email: email}) do\n      case MyApp.Repo.get_by(MyApp.User, email: email) do\n        nil -\u003e {:error, \"User not found\"}\n        user -\u003e {:ok, \"#{user.name} (#{user.email})\"}\n      end\n    end\n  end\nend\n\n{:ok, result} = ClaudeCode.query(\"Find alice@example.com\",\n  mcp_servers: %{\"app-tools\" =\u003e MyApp.Tools},\n  allowed_tools: [\"mcp__app-tools__*\"]\n)\n```\n\nPass per-session context via assigns for scoped tools in LiveView:\n\n```elixir\n{:ok, session} = ClaudeCode.start_link(\n  mcp_servers: %{\n    \"app-tools\" =\u003e %{module: MyApp.Tools, assigns: %{scope: current_scope}}\n  }\n)\n```\n\n[Custom tools guide →](docs/guides/custom-tools.md) | [MCP guide →](docs/guides/mcp.md)\n\n### Real-Time Streaming\n\nNative Elixir Streams with character-level deltas, composable pipelines, and direct LiveView integration:\n\n```elixir\n{:ok, session} = ClaudeCode.start_link(include_partial_messages: true)\n\n# Character-level streaming\nsession\n|\u003e ClaudeCode.stream(\"Explain recursion\")\n|\u003e ClaudeCode.Stream.text_deltas()\n|\u003e Enum.each(\u0026IO.write/1)\n\n# Phoenix LiveView\npid = self()\nTask.start(fn -\u003e\n  session\n  |\u003e ClaudeCode.stream(message)\n  |\u003e ClaudeCode.Stream.text_deltas()\n  |\u003e Enum.each(\u0026send(pid, {:chunk, \u00261}))\nend)\n\n# PubSub broadcasting\nsession\n|\u003e ClaudeCode.stream(\"Generate report\")\n|\u003e ClaudeCode.Stream.text_deltas()\n|\u003e Enum.each(\u0026Phoenix.PubSub.broadcast(MyApp.PubSub, \"chat:#{id}\", {:chunk, \u00261}))\n```\n\nStream helpers: `text_deltas/1`, `thinking_deltas/1`, `content_deltas/1`, `text_content/1`, `tool_uses/1`, `final_text/1`, `final_result/1`, `collect/1`, and more.\n\n[Streaming guide →](docs/guides/streaming-output.md)\n\n### Subagents\n\nDefine specialized agents with isolated contexts, restricted tools, and independent model selection. Claude automatically delegates tasks based on each agent's description.\n\n```elixir\nalias ClaudeCode.Agent\n\n{:ok, session} = ClaudeCode.start_link(\n  agents: [\n    Agent.new(\n      name: \"code-reviewer\",\n      description: \"Expert code reviewer. Use for quality and security reviews.\",\n      prompt: \"You are a code review specialist. Focus on security and best practices.\",\n      tools: [\"Read\", \"Grep\", \"Glob\"],\n      model: \"sonnet\"\n    ),\n    Agent.new(\n      name: \"test-runner\",\n      description: \"Runs and analyzes test suites.\",\n      prompt: \"Run tests and provide clear analysis of results.\",\n      tools: [\"Bash\", \"Read\", \"Grep\"]\n    )\n  ],\n  allowed_tools: [\"Read\", \"Grep\", \"Glob\", \"Task\"]\n)\n```\n\n[Subagents guide →](docs/guides/subagents.md)\n\n### Hooks and Permissions\n\nIntercept every tool execution with `can_use_tool` for programmatic approval, or use lifecycle hooks for auditing, budget guards, and more:\n\n```elixir\n{:ok, session} = ClaudeCode.start_link(\n  # Programmatic tool approval\n  can_use_tool: fn %{tool_name: name}, _id -\u003e\n    if name in [\"Read\", \"Glob\", \"Grep\"], do: :allow, else: {:deny, message: \"Read-only mode\"}\n  end,\n  # Lifecycle hooks\n  hooks: %{\n    PostToolUse: [MyApp.AuditLogger],\n    Stop: [MyApp.BudgetGuard]\n  }\n)\n```\n\nSix [permission modes](docs/guides/permissions.md) plus fine-grained tool allow/deny lists with glob patterns:\n\n```elixir\n{:ok, session} = ClaudeCode.start_link(\n  permission_mode: :accept_edits,\n  allowed_tools: [\"Read\", \"Edit\", \"Bash(git:*)\"]\n)\n```\n\n[Hooks guide →](docs/guides/hooks.md) | [Permissions guide →](docs/guides/permissions.md)\n\n### Structured Outputs\n\nGet typed JSON data from agent workflows using JSON Schema. The agent uses tools autonomously, then returns structured results:\n\n```elixir\nschema = %{\n  \"type\" =\u003e \"object\",\n  \"properties\" =\u003e %{\n    \"todos\" =\u003e %{\n      \"type\" =\u003e \"array\",\n      \"items\" =\u003e %{\n        \"type\" =\u003e \"object\",\n        \"properties\" =\u003e %{\n          \"text\" =\u003e %{\"type\" =\u003e \"string\"},\n          \"file\" =\u003e %{\"type\" =\u003e \"string\"},\n          \"line\" =\u003e %{\"type\" =\u003e \"number\"}\n        },\n        \"required\" =\u003e [\"text\", \"file\", \"line\"]\n      }\n    },\n    \"total_count\" =\u003e %{\"type\" =\u003e \"number\"}\n  },\n  \"required\" =\u003e [\"todos\", \"total_count\"]\n}\n\n{:ok, result} = ClaudeCode.query(\n  \"Find all TODO comments in this codebase\",\n  output_format: %{type: :json_schema, schema: schema}\n)\n\nresult.structured_output\n# %{\"todos\" =\u003e [...], \"total_count\" =\u003e 12}\n```\n\n[Structured outputs guide →](docs/guides/structured-outputs.md)\n\n### Session Management\n\nResume conversations, fork sessions, and read history:\n\n```elixir\n{:ok, session} = ClaudeCode.start_link()\nsession |\u003e ClaudeCode.stream(\"Remember: the code is 12345\") |\u003e Stream.run()\n\n# Save session ID, stop, resume later\nsession_id = ClaudeCode.Session.session_id(session)\nClaudeCode.stop(session)\n\n{:ok, resumed} = ClaudeCode.start_link(resume: session_id)\n\n# Fork a conversation into a new branch\n{:ok, forked} = ClaudeCode.start_link(resume: session_id, fork_session: true)\n\n# Runtime controls without restarting\nClaudeCode.Session.set_model(session, \"claude-sonnet-4-5-20250929\")\nClaudeCode.Session.set_permission_mode(session, :accept_edits)\n```\n\n[Sessions guide →](docs/guides/sessions.md)\n\n### Cost Controls\n\nTrack per-model usage, set budget limits, and cap turn counts:\n\n```elixir\n{:ok, session} = ClaudeCode.start_link(\n  max_turns: 10,\n  max_budget_usd: 1.00\n)\n\nresult = session\n|\u003e ClaudeCode.stream(\"Analyze this codebase\")\n|\u003e ClaudeCode.Stream.final_result()\n\nIO.puts(\"Total cost: $#{result.total_cost_usd}\")\n\nEnum.each(result.model_usage, fn {model, usage} -\u003e\n  IO.puts(\"#{model}: $#{usage.cost_usd} (#{usage.output_tokens} output tokens)\")\nend)\n```\n\n[Cost tracking guide →](docs/guides/cost-tracking.md)\n\n### File Checkpointing\n\nTrack file changes during agent sessions and rewind to any previous state:\n\n```elixir\n{:ok, session} = ClaudeCode.start_link(\n  enable_file_checkpointing: true,\n  permission_mode: :accept_edits\n)\n\n# Stream emits a UserMessage with a uuid before each tool execution.\n# Capture it to use as a checkpoint for rewinding.\nmessages = session\n|\u003e ClaudeCode.stream(\"Refactor the authentication module\")\n|\u003e Enum.to_list()\n\ncheckpoint_id =\n  Enum.find_value(messages, fn\n    %ClaudeCode.Message.UserMessage{uuid: uuid} when is_binary(uuid) -\u003e uuid\n    _ -\u003e nil\n  end)\n\n# Undo all file changes back to that checkpoint\nClaudeCode.Session.rewind_files(session, checkpoint_id)\n```\n\n[File checkpointing guide →](docs/guides/file-checkpointing.md)\n\n### MCP Integration\n\nConnect to any MCP server – stdio, HTTP, SSE, or in-process. Mix all transport types in a single session:\n\n```elixir\n{:ok, session} = ClaudeCode.start_link(\n  mcp_servers: %{\n    \"app-tools\" =\u003e MyApp.Tools,                                           # In-process\n    \"github\" =\u003e %{command: \"npx\", args: [\"-y\", \"@modelcontextprotocol/server-github\"],\n                  env: %{\"GITHUB_TOKEN\" =\u003e System.get_env(\"GITHUB_TOKEN\")}},  # stdio\n    \"docs\" =\u003e %{type: \"http\", url: \"https://code.claude.com/docs/mcp\"}   # HTTP\n  },\n  allowed_tools: [\"mcp__app-tools__*\", \"mcp__github__*\", \"mcp__docs__*\"]\n)\n```\n\n[MCP guide →](docs/guides/mcp.md)\n\n### Distributed Sessions\n\nEvery session spawns a Claude CLI subprocess — a Node.js process that consumes real CPU and memory. On a single machine, your concurrency is capped by CLI overhead, not the BEAM. Distributed sessions fix this:\n\n```mermaid\ngraph LR\n  subgraph \"Your App Server (lightweight)\"\n    S1[Session]\n    S2[Session]\n    S3[Session]\n  end\n  subgraph \"Sandbox Server (beefy)\"\n    C1[\"CLI process\"]\n    C2[\"CLI process\"]\n    C3[\"CLI process\"]\n  end\n  S1 -- \"Erlang distribution\" --\u003e C1\n  S2 -- \"Erlang distribution\" --\u003e C2\n  S3 -- \"Erlang distribution\" --\u003e C3\n```\n\nOne line changes in your code — everything else stays the same:\n\n```elixir\n{:ok, session} = ClaudeCode.start_link(\n  cwd: \"/workspaces/#{tenant_id}\",\n  adapter: {ClaudeCode.Adapter.Node, [node: :\"claude@sandbox-server\"]}\n)\n\n# Same API — streaming, tools, hooks, resumption all work unchanged\nsession\n|\u003e ClaudeCode.stream(\"Analyze the codebase\")\n|\u003e ClaudeCode.Stream.text_content()\n|\u003e Enum.each(\u0026IO.write/1)\n```\n\nYour app server runs only GenServers. All CLI resource consumption lives on dedicated hardware you size and scale independently. `GenServer.call/2` and `send/2` work transparently across BEAM nodes — no custom protocol, no WebSocket, no sidecar.\n\n[Distributed sessions guide →](docs/guides/distributed-sessions.md) | [Hosting guide →](docs/guides/hosting.md)\n\n### And More\n\n- **[Slash commands](docs/guides/slash-commands.md)** – Custom `/commands` with arguments, file references, and bash execution\n- **[Skills](docs/guides/skills.md)** – Filesystem-based capabilities Claude invokes autonomously\n- **[Plugins](docs/guides/plugins.md)** – Package commands, agents, skills, hooks, and MCP servers for sharing\n- **[System prompts](docs/guides/modifying-system-prompts.md)** – Override, append, or use CLAUDE.md for project-level instructions\n- **[Secure deployment](docs/guides/secure-deployment.md)** – Sandboxing, least-privilege tools, audit trails, and ephemeral sessions\n\n## Testing\n\nBuilt-in test adapter for fast, deterministic tests without API calls:\n\n```elixir\ntest \"handles greeting\" do\n  ClaudeCode.Test.stub(ClaudeCode, fn _query, _opts -\u003e\n    [ClaudeCode.Test.text(\"Hello! How can I help?\")]\n  end)\n\n  {:ok, session} = ClaudeCode.start_link()\n  result = session |\u003e ClaudeCode.stream(\"Hi\") |\u003e ClaudeCode.Stream.final_text()\n  assert result == \"Hello! How can I help?\"\nend\n```\n\nIncludes message helpers (`text`, `tool_use`, `tool_result`, `thinking`), dynamic stubs, and concurrent test support.\n\n[Testing guide →](docs/reference/testing.md)\n\n## Documentation\n\n- **[Documentation Hub](docs/README.md)** – All guides and references\n- **[API Reference](https://hexdocs.pm/claude_code)** – Complete API docs on HexDocs\n- **[Examples](docs/reference/examples.md)** – Real-world usage patterns\n- **[Troubleshooting](docs/reference/troubleshooting.md)** – Common issues and solutions\n\n## Contributing\n\nWe welcome contributions! Bug reports, feature requests, documentation improvements, and code contributions are all appreciated.\n\nSee our [Contributing Guide](https://github.com/guess/claude_code/blob/main/CONTRIBUTING.md) to get started.\n\n## Development\n\n```bash\ngit clone https://github.com/guess/claude_code.git\ncd claude_code\nmix deps.get\nmix test\nmix quality  # format, credo, dialyzer\n```\n\n## License\n\nMIT License – see [LICENSE](https://github.com/guess/claude_code/blob/main/LICENSE) for details.\n\n---\n\nBuilt for Elixir developers on top of the [Claude Code CLI](https://github.com/anthropics/claude-code).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fguess%2Fclaude_code","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fguess%2Fclaude_code","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fguess%2Fclaude_code/lists"}