diff options
| author | t <t@tjp.lol> | 2026-06-03 09:11:36 -0600 |
|---|---|---|
| committer | t <t@tjp.lol> | 2026-06-03 12:33:44 -0600 |
| commit | 2416a308c0e467f8dbdbe09942aa903bca751c0e (patch) | |
| tree | c6f5f724ec83b1cf045c478d0a668d69a6a9d6e3 /docs | |
| parent | 442888c6c306cfff0708c3d4bbbeda685cec2e75 (diff) | |
plan doc: claude subscription provider
Diffstat (limited to 'docs')
| -rw-r--r-- | docs/anthropic-subscription-provider.md | 180 |
1 files changed, 180 insertions, 0 deletions
diff --git a/docs/anthropic-subscription-provider.md b/docs/anthropic-subscription-provider.md new file mode 100644 index 0000000..c6d44eb --- /dev/null +++ b/docs/anthropic-subscription-provider.md @@ -0,0 +1,180 @@ +# Anthropic subscription provider sketch + +Goal: expose a pantograph provider backed by an interactive Claude Code subscription session, without using Anthropic API keys, `claude -p`, or Agent SDK metering. + +## Core idea + +Run `claude` as an interactive TUI process under a PTY, but make it behave like a minimal local model provider: + +- pantograph owns the outer UX and provider API. +- Claude Code provides subscription-backed model turns. +- Claude built-in tools are disabled. +- pantograph tools are exposed to Claude Code through MCP. +- Claude Code hooks emit structured lifecycle/tool/session events to pantograph over a private FIFO or Unix socket. + +This avoids parsing only terminal paint where possible. The PTY remains necessary for interactive subscription behavior, but structured telemetry comes from hooks. + +## Why not `--bare` + +`claude --bare` is not suitable for this provider. Local `claude --help` says it: + +- skips hooks, +- skips keychain reads, +- sets `CLAUDE_CODE_SIMPLE=1`, +- and uses only `ANTHROPIC_API_KEY` or `apiKeyHelper` auth. + +That defeats both requirements: subscription auth and hook instrumentation. + +## Intended invocation + +Approximate command shape: + +```sh +env -u ANTHROPIC_API_KEY \ + PANTO_HOOK_EVENTS="$PANTO_HOOK_EVENTS" \ + claude \ + --system-prompt "$PANTO_SYSTEM_PROMPT" \ + --tools "" \ + --disable-slash-commands \ + --no-chrome \ + --strict-mcp-config \ + --mcp-config "$PANTO_MCP_CONFIG" \ + --settings "$PANTO_CLAUDE_SETTINGS" \ + --setting-sources local \ + --dangerously-skip-permissions \ + --model sonnet +``` + +`PANTO_HOOK_EVENTS` should point at a private FIFO or Unix socket created by pantograph before launching Claude Code. + +Notes: + +- `--system-prompt` replaces Claude Code's default system prompt. Do not use `--append-system-prompt` for provider mode. +- `--tools ""` disables Claude Code's built-in tool set. +- A manual test confirmed MCP tools still work with `--tools ""`. +- `--strict-mcp-config` prevents ambient MCP servers from user/project config leaking into the session. +- `--dangerously-skip-permissions` bypasses both tool permission prompts and the workspace trust prompt. +- Because dangerous-skip may not mark the workspace trusted, do not rely on project `.claude/settings.json`; pass harness settings explicitly via `--settings`. +- Always clear `ANTHROPIC_API_KEY` so Claude Code cannot silently choose API billing. + +## MCP tool bridge + +pantograph should expose its active tool registry as an MCP server, probably named `pantograph`. + +Claude sees tools such as: + +```text +mcp__pantograph__read +mcp__pantograph__edit +mcp__pantograph__bash +mcp__pantograph__write +``` + +This gives pantograph control over: + +- permission policy, +- extension-provided tools, +- tool result formatting, +- audit logging, +- sandboxing, +- and future compatibility with non-Claude providers. + +Claude Code should not execute meaningful work through built-in tools in this mode. + +## Hook harness + +Claude Code hooks should be generated into `$PANTO_CLAUDE_SETTINGS`. Hooks write newline-delimited JSON events to a private FIFO or Unix socket. + +Events to capture: + +- `SessionStart` +- `SessionEnd` +- `UserPromptSubmit` +- `PreToolUse` +- `PostToolUse` +- `PostToolUseFailure` +- `PostToolBatch` +- `Stop` +- `StopFailure` +- `Notification` +- `PermissionRequest` if it still appears unexpectedly +- `PermissionDenied` if auto policy blocks anything + +Example event envelope: + +```json +{ + "type": "claude_hook", + "event": "PreToolUse", + "session_id": "...", + "tool_name": "mcp__pantograph__bash", + "tool_input": {}, + "timestamp_ms": 0 +} +``` + +The hook program should be tiny and deterministic: + +1. read hook JSON from stdin, +2. wrap it in a panto event envelope, +3. write one NDJSON line to the hook event endpoint, +4. return the desired Claude Code hook decision. + +For normal MCP tool use, hooks should observe, not deny. + +## Built-in tool policy + +Preferred path: `--tools ""` removes all built-in tools, leaving only MCP tools. + +Fallback if a future Claude Code release changes behavior: + +- allow the minimum required built-ins, +- install `PreToolUse` hooks matching `Bash|Read|Write|Edit|Glob|Grep|Task|WebFetch|WebSearch|Notebook.*`, +- deny them with a reason telling Claude to use the corresponding `mcp__pantograph__...` tool. + +Hooks can block built-ins, but they cannot transparently replace a built-in tool call with a successful custom result. Replacement should happen by steering the model toward MCP tools, not by hijacking built-ins. + +## PTY responsibilities + +The PTY layer still matters for: + +- satisfying Claude Code's interactive mode requirements, +- sending user prompts, +- receiving assistant-visible text, +- detecting gross process state, +- handling login/session expiry screens, +- and recovering from unexpected prompts. + +Do not depend on terminal scraping for tool execution state if hooks can provide it. Terminal scraping should be best-effort display mirroring. + +## Provider behavior + +The first implementation should be deliberately narrow: + +1. one long-lived Claude Code process per panto provider session, +2. no `claude -p`, +3. no session resume until the basic flow is stable, +4. no built-in Claude tools, +5. panto tools only through MCP, +6. hook NDJSON as the structured event stream, +7. PTY text as the assistant display stream. + +Start with a single-turn text response, then add MCP tools, then add session lifecycle handling. + +## Known risks + +- Anthropic may change Claude Code's CLI flags, hook schemas, or trust behavior. +- Heavy automation of the interactive subscription product may be treated differently in the future. +- `--dangerously-skip-permissions` is acceptable only if pantograph enforces its own policy at the MCP layer. +- Hook delivery is only as reliable as Claude Code's hook system and the FIFO/socket transport. +- Terminal UI changes can still break prompt submission or assistant text extraction. +- OAuth/keychain subscription auth must remain active; API-key auth must be prevented. + +## Open questions + +- Can assistant message streaming be captured from a hook or debug channel, or only from the PTY? +- Does `--setting-sources local` load `--settings` exactly as needed across Claude Code versions? +- Which Claude Code lifecycle event best marks an assistant turn as complete? +- How should pantograph handle Claude Code compacting or context management? +- Can a generated settings file fully avoid user/project Claude configuration? +- What is the safest hook event transport: FIFO or Unix socket? |
