dsh integration: config dsh + skills sync --agent dsh #141

Merged
michal merged 1 commits from feat/dsh-integration into main 2026-10-13 22:16:06 +00:00
Owner

dsh (the DeepSeek harness) joins claude, pi, prime-agent and opencode as a first-class host, with the same shape of integration each of them got.

What mcpctl config dsh --project X does

  1. Mounts the stdio bridge — merges ONE @deepseek-ai/dsh-mcp-client entry (id mcp-mcpctl, mount name mcpctl, bridge mcpctl mcp -p X) into the profile's user patch layer (~/.dsh/profiles/<profile>/cordis.patch.yml, --profile defaults to web). The stdio bridge keeps auth in ~/.mcpctl/credentials, exactly like the Claude integration — the patch file is 0644 and commonly committed to dotfiles, so the HTTP mount's bearer header is not an option there. Tools surface as mcp__mcpctl__<tool>.
  2. Touches only its own entry — the file is edited with the comment-preserving yaml Document API (new dep on the cli package; already used by mcplocal). dsh appends runtime UI state to the same file and bundles/users own the rest, so every other entry, comment and blank line survives byte-for-byte; a corrupt or non-array file aborts instead of being overwritten. Re-runs re-point the bridge args in place and keep user-added keys (env: etc); a same-mount entry under another id is adopted; a hand-configured server with its own serverName is never touched.
  3. Marker + skills — writes .mcpctl-project with the prime-agent semantics (explicit -p is authoritative, never from $HOME, --skip-marker opts out) and syncs skills into ~/.dsh/skills — the user root dsh-skill-filesystem scans AND watches, so synced skills appear without a restart. dsh becomes a skills sync --agent target with the shared-tree semantics, state in ~/.mcpctl/skills-state-dsh.json.

Verification

  • 12 new tests in tests/config/dsh-patch.test.ts (create/merge/re-point/adopt/refuse/preserve-comments); cli suite green; completions regenerated (their freshness test covers the new subcommand).
  • Live against dsh 0.2.0-rc.2: the web profile boots, spawns mcpctl mcp -p sre as its child within a second, and serves with the mount active; the patch file's comments and dsh's own appended entries survived repeated re-runs. Note the first MCP connect can delay dsh's startup banner noticeably (dsh-mcp-client's documented behaviour).
  • Docs: docs/dsh-integration.md + a README "Connect dsh" section beside the other hosts.

Pre-existing failure, not touched

tests/commands/prime-agent.test.ts > does not write a .mcpctl-project marker when run from $HOME fails on main too on this machine and leaks a real ~/.mcpctl-project (proj-x) — a test-isolation issue worth its own fix.

🤖 Generated with Claude Code

https://claude.ai/code/session_01V6HNx6cyr4J4CiMGCy1wkF

dsh (the DeepSeek harness) joins claude, pi, prime-agent and opencode as a first-class host, with the same shape of integration each of them got. ## What `mcpctl config dsh --project X` does 1. **Mounts the stdio bridge** — merges ONE `@deepseek-ai/dsh-mcp-client` entry (id `mcp-mcpctl`, mount name `mcpctl`, bridge `mcpctl mcp -p X`) into the profile's user patch layer (`~/.dsh/profiles/<profile>/cordis.patch.yml`, `--profile` defaults to `web`). The stdio bridge keeps auth in `~/.mcpctl/credentials`, exactly like the Claude integration — the patch file is 0644 and commonly committed to dotfiles, so the HTTP mount's bearer header is not an option there. Tools surface as `mcp__mcpctl__<tool>`. 2. **Touches only its own entry** — the file is edited with the comment-preserving `yaml` Document API (new dep on the cli package; already used by mcplocal). dsh appends runtime UI state to the same file and bundles/users own the rest, so every other entry, comment and blank line survives byte-for-byte; a corrupt or non-array file aborts instead of being overwritten. Re-runs re-point the bridge args in place and keep user-added keys (`env:` etc); a same-mount entry under another id is adopted; a hand-configured server with its own `serverName` is never touched. 3. **Marker + skills** — writes `.mcpctl-project` with the prime-agent semantics (explicit `-p` is authoritative, never from `$HOME`, `--skip-marker` opts out) and syncs skills into `~/.dsh/skills` — the user root `dsh-skill-filesystem` scans AND watches, so synced skills appear without a restart. `dsh` becomes a `skills sync --agent` target with the shared-tree semantics, state in `~/.mcpctl/skills-state-dsh.json`. ## Verification - 12 new tests in `tests/config/dsh-patch.test.ts` (create/merge/re-point/adopt/refuse/preserve-comments); cli suite green; completions regenerated (their freshness test covers the new subcommand). - Live against dsh 0.2.0-rc.2: the `web` profile boots, spawns `mcpctl mcp -p sre` as its child within a second, and serves with the mount active; the patch file's comments and dsh's own appended entries survived repeated re-runs. Note the first MCP connect can delay dsh's startup banner noticeably (dsh-mcp-client's documented behaviour). - Docs: `docs/dsh-integration.md` + a README "Connect dsh" section beside the other hosts. ## Pre-existing failure, not touched `tests/commands/prime-agent.test.ts > does not write a .mcpctl-project marker when run from $HOME` fails on main too on this machine and leaks a real `~/.mcpctl-project` (`proj-x`) — a test-isolation issue worth its own fix. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01V6HNx6cyr4J4CiMGCy1wkF
michal added 1 commit 2026-10-13 21:59:01 +00:00
feat(cli): dsh integration — config dsh + skills sync --agent dsh
Some checks failed
CI/CD / typecheck (pull_request) Successful in 1m20s
CI/CD / test (pull_request) Failing after 28s
CI/CD / lint (pull_request) Successful in 2m51s
CI/CD / smoke (pull_request) Has been skipped
CI/CD / build (pull_request) Has been skipped
CI/CD / publish (pull_request) Has been skipped
5f6d054ffa
dsh (the DeepSeek harness) joins claude, pi, prime-agent and opencode as
a first-class host. `mcpctl config dsh --project X`:

- merges ONE dsh-mcp-client entry (id mcp-mcpctl, mount name mcpctl,
  stdio bridge `mcpctl mcp -p X`) into the profile's user patch layer
  (~/.dsh/profiles/<profile>/cordis.patch.yml). The stdio bridge keeps
  auth in ~/.mcpctl/credentials — the patch file is 0644 and commonly
  committed to dotfiles, so the HTTP mount's bearer header is not an
  option there.
- edits with the comment-preserving `yaml` Document API and touches only
  its own entry: dsh appends runtime UI state to the same file, bundles
  and users own the rest, and a corrupt or non-array file aborts instead
  of being overwritten. Re-runs re-point the bridge args in place and
  keep user-added keys; a same-mount entry under another id is adopted,
  a hand-configured server with its own serverName is never touched.
- writes the .mcpctl-project marker with the prime-agent semantics and
  syncs skills into ~/.dsh/skills — the user root dsh-skill-filesystem
  scans AND watches, so synced skills appear without a restart. `dsh`
  becomes a skills sync target with the shared-tree semantics, state in
  ~/.mcpctl/skills-state-dsh.json.

Verified against dsh 0.2.0-rc.2: the web profile boots, spawns the
bridge as its child within a second, and serves with the mount active;
patch-file comments and dsh's own appended entries survive re-runs.
Completions regenerated for the new subcommand.

Note: tests/commands/prime-agent.test.ts ("does not write a marker when
run from $HOME") fails on main too on this machine and leaks a real
~/.mcpctl-project — pre-existing test-isolation issue, untouched here.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V6HNx6cyr4J4CiMGCy1wkF
michal force-pushed feat/dsh-integration from 5f6d054ffa to 2ed6d38784 2026-10-13 22:08:42 +00:00 Compare
michal merged commit c07431c32e into main 2026-10-13 22:16:06 +00:00
Sign in to join this conversation.
No Reviewers
No Label
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: michal/mcpctl#141