Files
VolcanicAgenticCore/docs/claude-code-provider.md
2026-09-25 14:42:12 +00:00

5.0 KiB

Claude Code provider

Volcanic.AgenticCore.Extensions.ClaudeCode lets the core use a Claude subscription through the locally installed claude CLI (Claude Code) instead of an Anthropic API key. It is a C# port of chem/pi-claude-code-provider; read that project's DESIGN.md for the full reasoning behind each decision.

Requirements

  • claude (Claude Code) 2.1.x on PATH (or VOLCANIC_CLAUDE_CODE_PATH), logged in with a claude.ai subscription (claude auth status must report loggedIn: true, authMethod: claude.ai, apiProvider: firstParty).
  • The .NET runtime (dotnet) on PATH or Environment.ProcessPath: claude spawns the bridge as dotnet Volcanic.AgenticCore.Extensions.ClaudeCode.Bridge.dll.
  • None of ANTHROPIC_API_KEY, ANTHROPIC_AUTH_TOKEN, ANTHROPIC_BASE_URL, CLAUDE_CODE_OAUTH_TOKEN, CLAUDE_CODE_USE_BEDROCK, CLAUDE_CODE_USE_VERTEX in the host environment: the extension refuses to run with them, because it is designed for subscription auth only.

Register it and pick a model:

o.AddExtension<ClaudeCodeProviderExtension>();
o.DefaultModel = "claude-code/sonnet";      // or opus, haiku, fable, or the pinned claude-opus-5-5

Run /claude-code-doctor in a session to check the installation, auth, required CLI flags and the bridge handshake. It costs no quota.

How it works

Every model call spawns a fresh claude -p process. Nothing is resumed on Claude's side: the whole conversation is replayed each time.

  1. The active tools are written to a JSON catalogue; each tool becomes an MCP tool mcp__volcanic__<alias>.
  2. claude is started with --tools "" (all built-in tools off), --permission-mode dontAsk, --strict-mcp-config --mcp-config <bridge>, --input-format stream-json --output-format stream-json --include-partial-messages --verbose, --system-prompt-file <tmp>, --model <id>, --effort <level>, plus settings that disable hooks, auto-memory, plugins, slash commands and the token reminder (which would defeat prompt caching). Only an allow-list of environment variables is passed through, and compaction inside Claude is disabled: the core owns compaction.
  3. The bridge (a tiny console app) is the MCP server. It answers initialize and tools/list with the catalogue and touches a "ready" marker file. It never executes anything: tools/call returns an error and touches a "violation" marker that fails the request.
  4. The transcript is sent as one stream-json user message: a header record, then one JSON record per message (user, assistant with text/thinking/toolCall, toolResult). @ characters are escaped because Claude expands @path references. A cache breakpoint is placed on the last record so Claude's prompt cache keeps hitting as the conversation grows. Images are written to a per-session content-addressed store and referenced as @"path" attachments.
  5. Claude's stream-json output is mapped to the core's streaming events. When the model asks for a tool (message_delta.stop_reason == "tool_use"), Claude would deny it itself (dontAsk mode), so the provider terminates the process immediately and publishes Done(ToolUse). The core executes the tool and the next call replays the transcript including the result.
  6. The system/init record is validated strictly: the exposed tools, permissionMode, empty slash commands, apiKeySource == "none" (proof of subscription auth) and a connected volcanic MCP server. Anything else fails closed.

Limitations (inherited from the design)

  • Full-history replay costs more context than the Messages API and cannot replay thinking signatures.
  • Claude adds its own identity line and an environment block to every request; a Claude Code upgrade invalidates the prompt cache.
  • Claude still runs git ls-files / rg --files in the working directory at startup (slow in huge repos) and reads .claude/settings*.json there.
  • Mid-stream API recovery (api_retry, message id changes) fails the request as stream_interrupted; the core's retry policy retries it.
  • Cost is always reported as 0. Cancelling may still consume quota.
  • Windows: process-tree termination is best effort.

Configuration

Setting Where Default
CLI path VOLCANIC_CLAUDE_CODE_PATH or ClaudeCodeProviderOptions.ClaudePath claude on PATH
Idle timeout VOLCANIC_CLAUDE_CODE_IDLE_TIMEOUT_MS 5 min
Total timeout VOLCANIC_CLAUDE_CODE_TOTAL_TIMEOUT_MS 30 min
Thinking session thinking level → --effort (low…max), not sent for haiku model default

Errors

Provider failures surface as assistant error messages with a code prefix, for example [executable_missing] ..., [auth] ..., [mcp_startup] ..., [protocol_init] .... Two are special: stream_interrupted messages contain stream ended before message_stop (retried automatically), and context overflow messages start with context_length_exceeded: (the core compacts and retries once).