opencode-cluade-auth
v1.3.9
Published
Claude Code CLI-backed Anthropic auth plugin for OpenCode.
Maintainers
Readme
opencode-cluade-auth
Claude Code CLI-backed Anthropic auth plugin for OpenCode.
This package registers an anthropic auth hook and translates Anthropic-style
/v1/messages requests into local claude CLI executions instead of reusing
Claude OAuth or API-key transport directly.
Quick Start
Restricted internal production scope
This package is supported only for restricted internal production use within OpenCode. It is not a general-purpose Anthropic adapter and is not supported for third-party consumption. Keep deployments bounded to the documented OpenCode CLI bridge path.
For normal usage, the main thing you do is add the published package to your OpenCode config. You do not need a global npm install first.
Recommended setup
Add the published package directly in your OpenCode config:
{
"$schema": "https://opencode.ai/config.json",
"plugin": ["opencode-cluade-auth@latest"],
"provider": {
"anthropic": {
"name": "Anthropic",
"npm": "@ai-sdk/anthropic"
}
}
}For most users, this is enough.
If you want deterministic installs, pin a specific version instead of latest:
{
"plugin": ["[email protected]"]
}Restart OpenCode after updating the config, then send a simple request through the Anthropic provider path.
Prerequisites
This plugin depends on the local Claude Code CLI. Before debugging OpenCode behavior, make sure the local CLI itself works on the same machine:
claude auth status
claude -p "Reply with exactly OK" --output-format stream-json --dangerously-skip-permissions --tools "" --no-session-persistence --verboseExpected result:
claude auth statusreports"loggedIn": true- the
claude -p ...command returns a normalsystem/initevent followed by anassistant/resultsuccess event
If the CLI itself fails here, fix the local Claude CLI setup first. This adapter only proxies requests to the local claude binary and does not replace its login, MCP, or runtime behavior.
Optional installation paths
Manual npm install
Use this only if your environment specifically requires a manually installed package artifact, or if you want to inspect the installed files directly while debugging.
npm install opencode-cluade-authThis is usually not the first step for normal OpenCode usage, and it does not need to be a global install.
Install from a local build (development)
For local development or testing unpublished changes:
npm install
npm run buildThen reference the built entry file directly from OpenCode:
{
"plugin": ["/absolute/path/to/opencode-cluade-auth/dist/opencode-cluade-auth.js"]
}Runtime note
The OpenCode config directory is not always the same place as the resolved plugin installation path.
- Your config file may contain only
"opencode-cluade-auth@latest" - OpenCode may resolve and execute the package from a separate cache, store, or npm-managed location
- If you are debugging runtime behavior, verify the actual installed package artifact, not just the config file
Verify OpenCode is using the plugin
After updating the config, restart OpenCode and issue a simple request through the Anthropic provider path.
Useful checks:
- Confirm the config references either
opencode-cluade-auth@<version>or the direct built file path. - For normal usage, start by checking the config entry before looking for a manually installed copy.
- If requests fail, test the local
claudeCLI separately first. - If behavior differs between repo source and runtime, inspect the actual installed artifact that OpenCode loaded.
- Only look for a manual npm-installed copy if you intentionally chose the optional manual install path.
Published package and release flow
This package is now published to npm as:
opencode-cluade-authRecommended usage forms:
"plugin": ["opencode-cluade-auth@latest"]"plugin": ["opencode-cluade-auth@<exact-version>"]- optional manual install:
npm install opencode-cluade-auth
Release automation summary:
- GitHub Actions workflow:
.github/workflows/release-opencode-cluade-auth.yml - release trigger: pushes to
mainor manualworkflow_dispatch - release strategy:
semantic-release - release tags remain in the
opencode-cluade-auth-v<version>format - GitHub Releases and release notes are generated automatically from release commits
- npm publishing runs automatically from the release workflow using
NPM_TOKEN_OPENCODE_CLAUDE_AUTH
Commit message guidance for automatic releases
Automatic releases depend on conventional commit semantics on main:
feat:-> minor releasefix:/perf:-> patch releaseBREAKING CHANGE:or!-> major releasedocs:,test:,chore:, andci:do not publish a release by themselves
If you want a code change to publish a new npm version automatically, make sure the merge commit on main uses the appropriate release-driving commit type.
What It Does
- seeds a local
anthropicauth entry for OpenCode - proxies Anthropic-style
/v1/messagesrequests to the localclaudeCLI - converts Claude CLI output back into Anthropic-style JSON or live SSE, with bounded fallback buffering only where required
- supports normal text replies and tool-calling-compatible response envelopes
Requirements
- Claude Code CLI installed and logged in on the same machine
claude --helpmust expose the safe flags used by the adapter:-por--print--output-format
--dangerously-skip-permissions--tools--no-session-persistence
Runtime Behavior
- the main user prompt is sent over
stdin - large system prompts are written to a temporary file and passed via
--system-prompt-file - the adapter runs Claude in non-interactive print mode with conservative flags
- multi-turn Anthropic
messages[].contentarrays are serialized into a deterministic plain-text transcript with explicit turn markers and block markers for supported request-side text,tool_use,tool_result, image, and document blocks - the plugin returns structured fetch errors when the local CLI is missing or exits unsuccessfully
Operations
Supported CLI flags
| Flag | Used when | Notes |
|----------|----------|---------|
| -p / --print | Always | Keeps Claude Code CLI in the bounded non-interactive print path used by this adapter. |
| --output-format | Always | Selects the CLI output mode required for Anthropic-shaped JSON or SSE adaptation. |
| --dangerously-skip-permissions | Always | Runs the bridge in non-interactive bypass mode so headless tool and stream flows do not stall on permission prompts. |
| --tools | Tool requests only | Enables the filtered Claude-safe tool list for bounded tool-use flows. |
| --no-session-persistence | Always | Avoids widening behavior through local session persistence side effects. |
| --system-prompt-file | Large / file-backed system prompts only | Used only when the system prompt must be handed off through a temporary file. |
| --effort | Request includes effort or metadata.effort | Forwarded only for the explicitly supported effort controls. |
The adapter does not widen support by routing Anthropic-facing controls through undocumented or broader CLI flag combinations.
HTTP failure status mapping
| HTTP status | Anthropic error type | Trigger |
|----------|----------|---------|
| 400 | invalid_request_error | Malformed JSON, missing required request fields, unsupported request controls, unsupported metadata keys, unsupported request-side block types, or unsupported request-side block richness. |
| 401 | authentication_error | Claude Code CLI authentication failures, including expired or missing local OAuth state. |
| 403 | permission_error | Permission failures returned through the local Claude CLI path. |
| 404 | not_found_error | Not-found surfaces returned by the upstream/provider-facing CLI path. |
| 413 | request_too_large | Oversized request surfaces rejected by the provider-facing path. |
| 429 | rate_limit_error | Rate-limit surfaces returned by the provider-facing path. |
| 502 | api_error | Other CLI process failures and invalid provider tool payloads that do not map to a more specific Anthropic error family. |
| 503 | api_error | Missing Claude CLI binary or equivalent local infrastructure availability failures. |
| 504 | timeout_error | Local Claude CLI timeout failures. |
| 529 | overloaded_error | Overloaded upstream/provider-facing surfaces. |
Logging and temp-file handling
- treat debug dumps, stderr captures, tool payloads, and auth-status hints as sensitive operational data
- do not enable shared or long-retention logging sinks unless they are approved for prompt content, tool arguments, and provider output
- temporary system-prompt files and extracted image directories are created per request and cleaned up after buffered completion, stream end, cancellation, and handled failure paths
- if the host process is terminated before cleanup runs, operators should remove stale
opencode-claude-system-*or image extraction temp directories during incident cleanup
OAuth-only compatibility matrix
This adapter is an OAuth-only Claude Code CLI bridge. It does not provide direct Anthropic auth ownership, native Anthropic transport, or broad SDK drop-in equivalence. Every compatibility claim falls into exactly one of these buckets:
- Exact — matches the OAuth-only CLI contract directly
- Synthetic — preserves meaningful Anthropic-facing behavior through transcript/envelope adaptation, but not native transport parity
- Explicit Reject/Non-goal — intentionally fails closed because the CLI-backed path cannot honor the surface truthfully
| Surface | Classification | Contract |
|----------|----------------|----------|
| Auth ownership | Exact | Auth is owned by the local Claude Code CLI OAuth session. |
| Direct Anthropic auth | Explicit Reject/Non-goal | No direct Anthropic API-key or native auth mode is provided. |
| Request controls (max_tokens, stop_sequences, sampling controls, context_management) | Explicit Reject/Non-goal | Unsupported official controls fail with 400 invalid_request_error instead of being approximated. |
| Beta headers/toggles (anthropic-beta, betas) | Explicit Reject/Non-goal | Beta surfaces remain explicit policy rejects under the CLI path. |
| Supported request-side structured blocks | Synthetic | Supported text, tool_use, tool_result, image, and document inputs are transcriptized into deterministic prompt text with bounded differences. |
| Unsupported request-side block richness/types | Explicit Reject/Non-goal | Native-only richness such as citations, thinking blocks, cache semantics, and other unsupported fields/types fail closed before execution. |
| Tool semantics | Synthetic | Tool behavior is reconstructed through validated JSON/SSE envelopes where structured CLI/provider signals exist; this is not native Anthropic tool transport. |
| Streaming granularity | Synthetic | Streams are surfaced as live Anthropic-style SSE where Claude emits usable stream events, with bounded fallback buffering only for edge cases. |
| Model reporting fidelity | Synthetic | Provider/CLI canonical model identity is preserved when exposed, otherwise the adapter falls back to the request-facing alias. |
| SDK drop-in compatibility claims | Explicit Reject/Non-goal | The adapter exposes a bounded Anthropic-compatible surface, not full native SDK feature parity. |
Request-side content block support
The adapter separates request-side input handling from output-side provider block preservation.
Supported request-side transcriptized inputs
These request-side block shapes are accepted because the adapter can still preserve meaningful operator-visible semantics when converting them into the Claude CLI prompt channel:
| Block type | Supported shape | Bounded difference |
|----------|----------|---------|
| text | type, text | Serialized into transcript text/block markers only. |
| tool_use | type, id, name, input | Preserved as explicit transcript block markers, not native Anthropic transport. |
| tool_result | type, tool_use_id, content | Preserved as explicit transcript block markers plus transcriptized nested content. |
| image | type, source, optional media_type | Reduced to an image/media-type label in the transcript; binary image semantics are not preserved natively. |
| document | type, source, optional title | Reduced to document labels and inline text when available; native document/citation semantics are not preserved natively. |
Rejected request-side inputs
The adapter now fails closed with 400 invalid_request_error before execution when request-side content includes:
- unsupported input block types (for example
thinking) - materially unsupported fields on otherwise transcriptizable blocks (for example
citations,cache_control, or other extra block-level fields the CLI prompt transport cannot preserve truthfully)
Output-side provider block preservation
This rejection policy applies only to request-side inputs. On the output-side, opaque provider-native block types emitted by Claude (for example server_tool_use) are still preserved in adapted responses where existing support already exists.
Request Control Support
The adapter only claims support for request controls it can pass through to the local Claude Code CLI truthfully.
Controls that the CLI cannot honor are rejected with 400 invalid_request_error on purpose, not silently ignored.
These rejects are policy-constrained non-goals of the OAuth-only CLI adapter, not accidental omissions.
Supported request fields
| Field | Status | Notes |
|----------|----------|---------|
| model | Supported | Normalized to Claude CLI model aliases when needed. |
| messages | Supported | Converted into the CLI prompt payload. |
| system | Supported | Passed through for normal requests. In relaxed OpenCode-compatible tool mode, upstream orchestrator-style system prompts are replaced with a short compatibility note before tool/JSON wrapping. |
| tools | Supported | Filtered to Claude-safe tool definitions before execution. |
| stream | Supported | Prefers live Anthropic-style SSE for both text and tool flows, with bounded fallback buffering when structured streaming is unavailable. |
| effort | Supported | Forwarded to the CLI as --effort. |
| metadata.effort | Supported | Accepted as the metadata-only request control override for --effort. |
Rejected request fields
| Field | Status | Why it fails |
|----------|----------|---------|
| max_tokens | Rejected | Claude Code CLI does not expose a truthful equivalent control. |
| stop_sequences | Rejected | Claude Code CLI cannot honor Anthropic stop sequence semantics. |
| temperature | Rejected | Claude Code CLI does not expose temperature control. |
| top_p | Rejected | Claude Code CLI does not expose top-p control. |
| context_management | Rejected | Claude Code CLI does not expose Anthropic context management controls. |
| unsupported metadata keys | Rejected | Only metadata.effort is supported. All other metadata keys fail explicitly. |
| betas | Rejected | Claude Code CLI cannot truthfully honor Anthropic beta request toggles. |
| anthropic-beta | Rejected | Claude Code CLI cannot truthfully honor Anthropic beta headers. |
Additional permanent explicit rejects:
- final assistant prefill combined with tool mode outside relaxed OpenCode-compatible requests
- unsupported request-side native-only block richness such as citations, thinking blocks, and other non-transcriptizable fields/types
Compatibility-mode system prompt handling
When OpenCode compatibility mode is triggered for tool/structured-output requests, this adapter does not forward the upstream orchestrator system prompt verbatim into the inner Claude tool wrapper.
Instead, it replaces the upstream system section with a short compatibility note and treats the conversation transcript plus the validated tool/JSON constraints as the source of truth for the turn.
This is intentional. Real OpenCode orchestration prompts can be very large and semantically heavy, and forwarding them unchanged inside the adapter's tool-disabled JSON wrapper was verified to cause Claude-side request rejection. Neutralizing that upstream system content in compatibility mode preserves the tool contract while avoiding the provider-side failure.
Configuration
Optional binary overrides:
export OPENCODE_CLAUDE_AUTH_BIN="/path/to/claude"
# optional behavior overrides
export OPENCODE_CLAUDE_AUTH_TOOL_REMAP='[{"preferred":"aegis_read","suppressed":["read"]}]'
export OPENCODE_CLAUDE_AUTH_TIMEOUT_MS="300000"
export OPENCODE_CLAUDE_AUTH_MAX_OUTPUT_CHARS="200000"Programmatic usage:
import ClaudeCodeCLIAuthPlugin from "opencode-cluade-auth";
const plugin = await ClaudeCodeCLIAuthPlugin();Verification
These checks are used during development before publish:
npm run build
bun run typecheck
bun test
bun run smoke -- "Reply with exactly SMOKE_OK"
bun run smoke -- --stream "Reply with exactly SMOKE_OK"
npm pack --dry-run
npm publish --dry-runFor internal smoke-repeat verification, rerun the smoke commands until the bounded CLI bridge path stays stable across repeated executions.
GitHub Actions Publish
This repo includes a package-specific publish workflow at
.github/workflows/publish-opencode-cluade-auth.yml.
- trigger manually with
workflow_dispatch, or push a tag matchingopencode-cluade-auth-v* - the workflow hard-checks that
package.jsonis publishingopencode-cluade-auth - npm auth is read only from the GitHub secret
NPM_TOKEN_OPENCODE_CLAUDE_AUTH
That secret is intended to be repo-scoped so it does not overlap with other
packages or repos such as oh-my-aegis.
Limitations
- this package is an OAuth-only Claude Code CLI adapter, not a direct Anthropic auth or native transport implementation
- this package is not for general-purpose Anthropic adapter usage or third-party consumption outside OpenCode internal production paths
- streaming responses prefer live Anthropic-style SSE for both text and tool flows, with bounded fallback buffering retained for edge cases
- Anthropic request-side content blocks are not forwarded to Claude Code CLI as native structured blocks; supported inputs are reduced to a deterministic plain-text transcript for the CLI prompt channel
- in relaxed OpenCode-compatible tool mode, upstream orchestrator system prompts are intentionally omitted/replaced before the inner Claude compat wrapper is built
- materially unsupported request-side block types or block-level fields now fail closed instead of degrading to opaque transcript placeholders
- image and document inputs preserve transcript labels and inline text where available, but not binary payload semantics, citations, cache semantics, or other native Anthropic block behaviors
- output-side opaque provider block preservation remains bounded to the response adaptation paths that already pass those blocks through
- the package depends on the local
claudeCLI login state - this package does not bundle the Claude CLI itself
License
MIT
Development
npm run buildbun run typecheckbun testbun run smoke -- "Reply with exactly SMOKE_OK"bun run smoke -- --stream "Reply with exactly SMOKE_OK"
