Back to home@shaneconner

dsh-claude-bridge

Use a Claude Pro or Max subscription as a DeepSeek Harness model provider, via the Claude Code CLI.

Stars
0
Language
TypeScript
Created
Aug 30, 2026
Updated
Aug 30, 2026
GitHub repo

Introduction

dsh-claude-bridge

Use a Claude Pro or Max subscription as a DeepSeek Harness model provider, by driving the Claude Code CLI through the Agent SDK.

Install

dsh plugin --profile web add dsh-claude-bridge

Restart dsh. It registers one provider route, claude-bridge, and does not disable anything: it sits beside whatever else serves models and is chosen per agent by provider id.

You need to be signed in to Claude Code already. This bridge does not authenticate: it runs a CLI that reads the credential claude login wrote, at ~/.claude/.credentials.json (or $CLAUDE_CONFIG_DIR/.credentials.json). Sign in with claude in a terminal, then /login there.

The CLI it runs is the one bundled with the Agent SDK, not whatever claude is on your PATH; PATH is not consulted. Only the credential file is shared. Set pathToClaudeCodeExecutable to run a different binary.

Settings are not read off disk. The child loads no user, project or local settings.json, so your own hooks, plugins and skills stay out of it, and a stray env block cannot move the route off your plan.

Why not just sign in to Anthropic

The harness can already do that, and it works. dsh-provider-login reaches the OAuth flow labelled "Anthropic (Claude Pro/Max)" that the harness registers.

The difference is billing, not capability. Per Pi's documentation of the same underlying library, third-party harness usage through that route draws from extra usage and is billed per token, not against Claude plan limits. Driving the CLI through the Agent SDK is the mechanism that lands usage on the plan instead.

That ground is not perfectly settled: Anthropic announced and then unannounced a change to how Agent SDK usage is billed. Know that before you depend on it.

This approach is not original. It is the same one pi-claude-bridge takes for the Pi agent, by Eli Dickinson, which is where the design here comes from.

What works, and what does not

Works. Streaming text and reasoning; tool calls, including multi-round conversations; token usage with cache reads and writes; finish reasons; model listing and resolution; reasoning effort; and the one-million-token window as an opt-in.

Tools are the interesting part, because the two sides disagree about who blocks. The harness treats a tool round as two calls: stream() ends with a tool-calls finish, the harness runs the tools, and it calls stream() again with the results appended. The CLI treats the same round as one call that never ended: it invokes a tool and its query sits blocked inside that handler.

So the query is not closed when the first stream() returns. It is parked, with the handler still awaiting, and resumed when the results arrive. The harness's tools reach the CLI over an in-process MCP server, with their JSON Schemas served verbatim rather than round-tripped through Zod, which loses everything below the top level. Calls are paired to results by the tool-use id the CLI stamps on each request, because call order is not promised to match the order the blocks were emitted.

A result can also arrive before the CLI has got round to asking for it, so delivery and waiting are decoupled: whichever comes second finds the other. Assuming an order there deadlocks the pair.

What a parked round is waiting for is the set of calls the adapter reported to the harness, not the set the child has dispatched. Those differ on every parallel round, because the child dispatches its calls one at a time while the harness answers all of them in one message. Gating on the dispatched set drops the answers to the rest, and nothing carries them again.

A named limitation on history. The CLI owns its own session history, and the faithful way to carry a conversation into it is to write its session file and resume. This build states the turns inline in the prompt instead. The model sees the whole conversation, but the CLI's prefix cache sees a new one each turn: correct, and expensive, in that order. Within a single tool round the query stays open, so that round does reuse the cache.

Cancelling. The harness cancels by abandoning the stream, so that is where the child is ended: the abort reaches it directly rather than only through a control request it has to be well enough to answer, and an interrupt that is not acknowledged within five seconds stops being waited on. Otherwise a cancelled turn keeps running to completion, on your plan.

Concurrency. Sessions are held per harness session id, but a call that offers no tools never takes one: with no MCP server it cannot park, so it runs detached and cannot evict a round parked under the same id. The shipped session-title provider issues exactly that call, concurrently, under the live turn's own id.

Configuration

- id: claude-bridge
  name: 'dsh-claude-bridge'
  config:
    # Ask for the 1M window. Off by default: outside a Max plan it draws extra
    # usage, which is the exact thing this bridge exists to avoid.
    longContext: false
    # Run a different claude binary. PATH is not consulted; left unset, the SDK
    # runs the one bundled in its own dependency.
    pathToClaudeCodeExecutable: /home/you/.local/bin/claude
    # Working directory for the CLI child. Defaults to the harness process's.
    cwd: /home/you/project

Some decisions are taken for you and are not options. The CLI child runs with ENABLE_CLAUDEAI_MCP_SERVERS=0 and DISABLE_AUTO_COMPACT=1, its preset is told to exclude CLAUDE.md and .claude/rules, and it loads no settings files at all. Each hands a decision back to the harness that the CLI would otherwise make for itself: the harness owns tool discovery, compaction, and context files.

The variables that would move the child onto a different account, ANTHROPIC_API_KEY, ANTHROPIC_AUTH_TOKEN, ANTHROPIC_BASE_URL, CLAUDE_CODE_USE_BEDROCK and CLAUDE_CODE_USE_VERTEX, are removed from its environment rather than forwarded. The harness materializes <cwd>/.env and $DSH_HOME/.env into its own environment before any plugin loads, so one of these left in a .env for another tool would otherwise reach the child and bill per token.

Development

npm run check   # typecheck
npm test        # the gate suite
npm run build   # emit lib/

The suite covers the orderings the two sides can arrive in, which is where every real defect here has been. The adapter tests replace the CLI but not its tool calls: a genuine MCP client drives the server the adapter builds, through the same handler and the same tool-use id pairing the CLI uses.

License

MIT