66 lines
2.9 KiB
Markdown
66 lines
2.9 KiB
Markdown
|
|
# mcpctl × dsh (DeepSeek harness)
|
|||
|
|
|
|||
|
|
## What `mcpctl config dsh --project X` wires up
|
|||
|
|
|
|||
|
|
| Piece | Where | Purpose |
|
|||
|
|
|-------|-------|---------|
|
|||
|
|
| MCP entry | `~/.dsh/profiles/<profile>/cordis.patch.yml` | one `@deepseek-ai/dsh-mcp-client` mount named `mcpctl`, running the stdio bridge `mcpctl mcp -p X` |
|
|||
|
|
| Project marker | `./.mcpctl-project` | what `skills sync` reads to know the scope (`--skip-marker` opts out) |
|
|||
|
|
| Skills | `~/.dsh/skills/` | the project's `SKILL.md` bundles, in the user root `dsh-skill-filesystem` scans and watches |
|
|||
|
|
|
|||
|
|
`--profile` picks the dsh profile (default `web`); `-o` points at a patch file
|
|||
|
|
directly.
|
|||
|
|
|
|||
|
|
## Why the stdio bridge, not the HTTP gateway
|
|||
|
|
|
|||
|
|
dsh's MCP client speaks both stdio and Streamable HTTP, but its config is ONE
|
|||
|
|
user-owned YAML patch file — `cordis.patch.yml` — which is 0644 and commonly
|
|||
|
|
kept in a dotfiles repo. The HTTP mount would need the project bearer token in
|
|||
|
|
a header there; the stdio bridge (`mcpctl mcp -p X`) authenticates from
|
|||
|
|
`~/.mcpctl/credentials` instead, exactly like the Claude Code integration, so
|
|||
|
|
nothing secret ever lands in the patch file. The trade-off is the same as
|
|||
|
|
Claude's: switching projects means re-running `config dsh` and restarting dsh,
|
|||
|
|
where opencode/prime-agent re-point their HTTP mounts live.
|
|||
|
|
|
|||
|
|
## One entry, merged in place
|
|||
|
|
|
|||
|
|
The patch file is dsh's own: bundles compose it, the user hand-edits it, and
|
|||
|
|
dsh itself appends UI state to it at runtime. So `config dsh` parses it with a
|
|||
|
|
comment-preserving YAML editor and touches ONLY its own entry:
|
|||
|
|
|
|||
|
|
- Ours is the entry with id `mcp-mcpctl` — or, adopted, any `dsh-mcp-client`
|
|||
|
|
entry whose `serverName` is `mcpctl` (that mount name is this CLI's; dsh
|
|||
|
|
requires unique serverNames, so a hand-configured second gateway with its own
|
|||
|
|
name never matches).
|
|||
|
|
- A project switch rewrites the bridge args of the existing entry in place and
|
|||
|
|
keeps any keys the user added to it (an `env:` block, say).
|
|||
|
|
- Everything else — other entries, comments, formatting — survives
|
|||
|
|
byte-for-byte. A corrupt or non-array file aborts the run instead of being
|
|||
|
|
overwritten.
|
|||
|
|
- Tools surface to the model as `mcp__mcpctl__<tool>`, the same namespace
|
|||
|
|
Claude Code sees.
|
|||
|
|
|
|||
|
|
## Skills
|
|||
|
|
|
|||
|
|
`~/.dsh/skills` is the "user" root of dsh's filesystem skill provider: a skill
|
|||
|
|
is a `<name>/SKILL.md` bundle with `name` + `description` frontmatter — the
|
|||
|
|
same format every other target uses, so the shared sync writes it unchanged.
|
|||
|
|
dsh watches the root, so synced skills appear without a restart. The tree has
|
|||
|
|
the same shared-tree semantics as prime-agent/pi/opencode: per-project
|
|||
|
|
ownership tracking, untracked hand-authored skills are preserved, state in
|
|||
|
|
`~/.mcpctl/skills-state-dsh.json`.
|
|||
|
|
|
|||
|
|
Re-sync later with:
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
mcpctl skills sync --agent dsh --project X
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## Skipping steps
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
mcpctl config dsh --project X --skip-skills # don't sync skills
|
|||
|
|
mcpctl config dsh --project X --skip-marker # don't touch .mcpctl-project here
|
|||
|
|
mcpctl config dsh --project X --dry-run # print what would change
|
|||
|
|
```
|