Ouroboros and DeepSeek Harness
(dsh) connect in two independent directions. Pick the one that matches where
you want to sit:
| You want to… | Direction | Set up |
|---|---|---|
| Work in a dsh chat and call Ouroboros from there | dsh → Ouroboros | Install the plugin |
| Work in Ouroboros and have DeepSeek's models answer the interview/Seed/QA calls | Ouroboros → dsh | Configure the dsh LLM backend |
They are unrelated at runtime. Installing the plugin does not change which LLM
Ouroboros uses, and selecting the dsh backend does not install anything into
your dsh profile.
One command, and every Ouroboros MCP tool appears in your dsh profile:
dsh plugin --profile <your-profile> add "github:Q00/ouroboros#main&path:integrations/dsh-plugin"--profile is required by dsh plugin; it names the profile to install into.
Boot as usual afterwards (dsh --profile <your-profile>), and
dsh --profile <your-profile> --dump-config will show a # == dsh-ouroboros
layer.
Then just type what you want in the chat:
ooo interview I want a CLI that keeps my dotfiles in sync
ooo auto add retry-with-backoff to the fetch layer
The model finds the matching mcp__ouroboros__* tool on its own — each of the
36 tools carries its own description, so no extra prompting is needed.
uvonPATH. That's the only prerequisite: the plugin spawnsuvx --from 'ouroboros-ai[mcp]' ouroboros mcp serve, which fetches Ouroboros into an isolated environment on first launch.- An executable agent runtime (
claude-cli,codex,opencode, …) if you wantooo autoto run its execution step. Either runouroboros setuponce or exportOUROBOROS_AGENT_RUNTIME. An MCP subprocess cannot host the in-processclaudeSDK runtime, so this has to be an executable one.
dsh scrubs every credential-shaped variable — anything matching
/KEY|PASSWORD|SECRET|TOKEN/i — out of a child process by design, so that
harness credentials never leak into a spawned program. A plugin's explicit
env layer merges after that scrub, which is the only way a credential
reaches ouroboros mcp serve.
The bundle therefore forwards a short, deliberate allowlist rather than everything:
ANTHROPIC_API_KEY— Ouroboros' default LLM backendDEEPSEEK_API_KEY— thedshbackend loopback below
To forward one more (OPENAI_API_KEY, OPENROUTER_API_KEY, …), override the
mcp-ouroboros row in your own profile's cordis.patch.yml with that extra
name in env. A later patch layer replaces a row's entire config rather
than deep-merging it, so copy the bundle's config block and add your line to
it. Everything non-credential-shaped — PATH, HOME, the OUROBOROS_*
selectors — passes through untouched and needs no entry at all.
Startup failures are non-fatal (failOnStartupError: false): a machine without
uv still boots dsh with every other plugin working, just without the
Ouroboros tools. Recovery is not automatic in general — whether mcp-client
retries depends on your dsh build, and where a reconnect loop exists it gives
up after a bounded number of attempts. After fixing the cause, reload the
plugin or restart dsh.
Full bundle reference: integrations/dsh-plugin/README.md.
dsh is an LLM-only backend: it answers interview, Seed authoring, and QA
calls. It is not valid for orchestrator.runtime_backend, because dsh's ACP
surface is deliberately text-only and fresh-session — right for completions,
wrong for a tool-using execution runtime.
Selecting it is not a one-variable switch. Ouroboros spawns its own
dsh-acp-demo child process, and that child fails closed with
invalid_config unless you give it a composition to load.
Build DeepSeek Harness from source (pnpm install && pnpm run build, Node.js
= 22) and make its
dsh-acp-demobin reachable — onPATH, or named byOUROBOROS_DSH_CLI_PATH/orchestrator.dsh_cli_path. Installing the published@deepseek-ai/dsh-acp-demopackage still fails on a peer-dependency conflict inside its owndsh-tool-bashchain, so a source build is the working path today.
export OUROBOROS_DSH_CONFIG_PATH=/absolute/path/to/cordis.ymlTwo rules the client enforces or inherits:
- Absolute only. A relative path is rejected on purpose — it would resolve against the untrusted project cwd, and the composition selects which plugins the Node process loads, which is code execution.
- It must live where dsh's
node_modules(or workspace) is reachable. Plugin package names inside a composition resolve relative to the composition file's own directory.
Both OUROBOROS_DSH_CLI_PATH and OUROBOROS_DSH_CONFIG_PATH are on the
project .env denylist for the same reason: a checked-out repository must not
be able to redirect which executable or plugin tree Ouroboros loads. Set them
in your shell or in ~/.ouroboros/config.yaml.
Usually DEEPSEEK_API_KEY. The spawned child inherits your environment, minus
the Ouroboros selector variables (so a nested ooo inside dsh is not confused
about which backend it is).
ouroboros mcp serve --runtime claude-cli --llm-backend dsh
# or
export OUROBOROS_LLM_BACKEND=dsh--runtime is not optional here. dsh answers only the LLM-only calls, so the
execution runtime is still a separate choice — and an MCP 2 server rejects the
SDK-backed claude / claude-sdk default, so name an executable one
(claude-cli, codex, opencode, …).
Or persist it in ~/.ouroboros/config.yaml:
llm:
backend: dsh
orchestrator:
dsh_cli_path: /absolute/path/to/dsh-acp-demo
dsh_config_path: /absolute/path/to/cordis.ymldeepseek_harness is accepted as an alias for dsh only where backend names
are resolved at runtime — OUROBOROS_LLM_BACKEND=deepseek_harness and
programmatic create_llm_adapter(backend="deepseek_harness"). The typed
surfaces reject it: --llm-backend deepseek_harness fails argument validation,
and llm.backend: deepseek_harness fails config validation. Write dsh in the
CLI and in config.yaml.
The ACP wire carries no model parameter — the Cordis composition owns the
provider and model. Ouroboros reports the honest dsh-composition sentinel
instead of inventing a model name, and reports zero token usage because the
protocol does not return counts. To change models, edit the composition.
They compose: install the plugin so a dsh chat can drive Ouroboros, and set
OUROBOROS_LLM_BACKEND=dsh so the interview questions that come back are
written by DeepSeek's own models. That loopback is exactly why the bundle
forwards DEEPSEEK_API_KEY explicitly — without it the tools list fine and the
first call fails.
Note that the loopback spawns a second dsh-acp-demo process; it does not
reuse the dsh you are chatting in, so steps 1–3 above still apply.