Skip to content

feat(dsh): give the harness MCP tools over ACP session/new (#639) - #656

Open
umi-appcoder[bot] wants to merge 1 commit into
kc/639-dsh-token-coveragefrom
kc/639-dsh-mcp
Open

feat(dsh): give the harness MCP tools over ACP session/new (#639)#656
umi-appcoder[bot] wants to merge 1 commit into
kc/639-dsh-token-coveragefrom
kc/639-dsh-mcp

Conversation

@umi-appcoder

@umi-appcoder umi-appcoder Bot commented Sep 4, 2026

Copy link
Copy Markdown
Contributor

Eighth of #639. Stacked on #655. This is a bug fix, not a nicety.

The bug

HYPERVISOR_PREAMBLE tells every thread:

You have dashboard MCP tools to read live workspace state … and to act on it

A deepseek-harness thread had none. Every other assistant reaches those tools somehow — claude via --mcp-config, ante/opencode/codex via a config file boot-seeded for them. The harness has no config file we write; ACP takes the server list as a request field on session/new instead — which is exactly why it is absent from mcp_registry's file-based fan-out (documented there in #654).

The fix

The bridge grows --mcp, taking either the literal default or the same {"mcpServers": {…}} object every other MCP surface in this repo already speaks, and translating it into ACP's McpServer[] (env as name/value pairs).

  • Hypervisor adapter → passes _HYPERVISOR_MCP_CONFIG verbatim.
  • Builds and sub-agents → pass default, whose contents a test asserts are byte-identical to it. Mirrored constants with a lockstep test, the same discipline EFFORT_CAP uses, because the bridge must not import the session module.

Deliberately the curated dashboard + memory pair, not the full boot-seeded set. Under ACP that choice is sharper than elsewhere: session/new connects every declared server before publishing the agent and rolls the whole session back if any connection fails. A slow npx-launched server (playwright, sequential-thinking) would be a dead session, not a missing tool.

A defect the schema does not state, caught by driving the real harness

{"type": "error", "text": "session/new failed: Invalid params: mcpServers[0].command must be an absolute path"}

The harness requires an absolute command and rejects the entire session otherwise — while every config in this repo spells it python3, because the other harnesses resolve it on PATH. parse_mcp now resolves it via shutil.which, and drops only the offending server when it cannot: one missing tool beats a rejected session.

After the fix, the same real dsh --profile acp accepts the session and actually starts the server:

[acp-bridge] dsh: [mcp_dashboard] started (base=http://127.0.0.1:6080, readonly=False)
{"type": "session", "sessionId": "e125ed39-fe8d-43f2-a3c2-67aac0d213e9"}
{"type": "error", "text": "Internal error: turn failed: Authentication Fails, ..."}
[acp-bridge] dsh: [mcp_dashboard] exiting on EOF

It gets all the way to the model call and fails only for want of an API key — which is as far as this workspace can go.

Tests — 20

The mirrored-constant lockstep; the ACP translation including env pairs and a bare server map; absolute-path resolution and its fallback; that malformed input degrades to no servers and that one junk entry doesn't lose its neighbours; that non-stdio entries are skipped rather than guessed at; that the servers reach session/new and session/resume (a second turn would otherwise silently lose every tool); and that all three launch paths ask for them.

Full suite: 2529 vs 2509 on the parent branch — exactly the 20 added — with the same 8 pre-existing boards_* import errors that reproduce on unmodified main here.

Docs updated with the per-session MCP model and why the set is curated.

Refs #639

🤖 Generated with Claude Code

@umi-appcoder

umi-appcoder Bot commented Sep 4, 2026

Copy link
Copy Markdown
Contributor Author

Note on the empty checks list. CI is pull_request: branches: [main], so a PR targeting another branch gets no checks — that is the cost of stacking, not a signal about this branch. Checks fire once the parent merges and GitHub retargets this to main. Everything CI runs was run locally on this branch: make python-tests, helm lint, helm unittest.

Eighth slice of #639, and a bug fix rather than a nicety: the Hypervisor
preamble tells every thread "You have `dashboard` MCP tools to read live
workspace state", and a deepseek-harness thread had none. Every other
assistant reaches those tools somehow — claude via --mcp-config,
ante/opencode/codex via a config file boot-seeded for them. The harness
has no config file we write; ACP takes the server list as a request field
instead, which is exactly why it is absent from mcp_registry's file-based
fan-out (documented there in the previous PR).

The bridge grows `--mcp`, taking either the literal `default` or the same
{"mcpServers": {…}} object every other MCP surface in this repo already
speaks, and translating it into ACP's McpServer[] (env as name/value
pairs). The Hypervisor adapter passes _HYPERVISOR_MCP_CONFIG verbatim;
Builds and sub-agents pass `default`, whose contents a test asserts are
byte-identical to it — the same mirrored-constant discipline EFFORT_CAP
uses, because the bridge must not import the session module.

Deliberately the curated dashboard+memory pair, not the full boot-seeded
set. Under ACP that choice is sharper than elsewhere: session/new
CONNECTS every declared server before publishing the agent and rolls the
whole session back if any connection fails, so a slow npx-launched
server (playwright, sequential-thinking) is a dead session rather than a
missing tool.

Driving the real `dsh --profile acp` caught a defect the schema does not
state: the harness requires an ABSOLUTE command and rejects the entire
session otherwise —

  session/new failed: Invalid params: mcpServers[0].command must be an
  absolute path

— while every config in this repo spells it `python3`, because the other
harnesses resolve it on PATH. parse_mcp now resolves it, and drops only
the offending server when it cannot: one missing tool beats a rejected
session. After the fix the same real harness accepts the session and
actually starts the server ("[mcp_dashboard] started"), failing only at
the model call for want of an API key.

20 tests: the mirrored-constant lockstep; the ACP translation including
env pairs and a bare server map; absolute-path resolution and its
fallback; that malformed input degrades to no servers and one junk entry
doesn't lose its neighbours; that non-stdio entries are skipped rather
than guessed at; that the servers reach session/new AND session/resume
(a second turn would otherwise silently lose every tool); and that all
three launch paths ask for them.

Refs #639
@umi-appcoder
umi-appcoder Bot force-pushed the kc/639-dsh-token-coverage branch from 7117e9d to 5e2bf91 Compare September 4, 2026 18:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

0 participants