Install configured MCP servers as additive harness tools
Problem
NativeSettings loads selected Claude/Codex MCP declarations, but callers still assemble transports, discovery, filtering and registration themselves. Installed tools also need stable identities across restart and independent execution bounds.
Design
McpConfig::install consumes configuration and an owned ToolRegistry, concurrently initializes stdio/HTTP sessions, discovers typed catalogs under finite limits, filters tools and adds registrations. Connections remain owned by installed tools. Required failures reject startup; optional failures become diagnostics in configuration order. The result supplies exact grants for host attenuation. Existing tool payloads and hook names remain intact; collisions fail.
Revisions derive from the selected schemas, namespace, connection binding and tool policy. Stdio resolves its executable once to the absolute launcher path selected by PATH/cwd and spawns that path. Tool request/response bytes have a separate finite bound from discovery bytes; both configured transport scenarios exercise a 1 KiB catalog with tool exchanges above 2 KiB.
Registration uses the existing native worker pool and the original startup deadline. Cancellation checks run before/after schema compilation and before catalog publication. On timeout, cancellation is published and the worker is awaited, releasing its connection before returning. Dropping the installation also publishes cancellation. Catalog publication remains atomic.
Decisions
- Connection identity includes explicit configured environment, resolved absolute executable, argv, working directory and dialect. Inherited environment is deliberately excluded: unrelated ambient changes must preserve resume. Native configuration is always approved; there is no approval gate to bypass.
- Resolve PATH once during installation, including an explicit configured PATH override. Spawn that same absolute launcher path and bind the same string. Preserve symlinks and path components: canonicalization changes virtualenv/argv[0] behavior.
- Cancellation is cooperative at schema boundaries. Timeout cleanup may await an in-flight schema compilation; it never abandons a worker or connection. Registration no longer creates Tokio blocking tasks that runtime shutdown must join.
- Keep the original positive discovery limits and separate finite tool I/O limits; no defaults were raised. Never retry uncertain calls.
- Concurrent startup merges deterministically. Credential values remain in native connection state; diagnostics do not disclose them.
- Reuse the trusted settings loader and existing native transports. LocalHarness remains the composition root; its wiring follows when #495 lands.
Deletions
Removed random installation UUIDs, catalog-derived execution limits, the JSON conversion/cloning discovery path and abandoned Tokio registration jobs. Registry assembly moves entries instead of cloning caller payloads. No additional settings loader, task store, journal or relay protocol.
Full PR: +2919/-33, net +2886 lines. The launcher fix is +74/-15, net +59 lines. This review update: +1290/-99, net +1191; 663 added lines are the standalone fuzz lockfile. Verification code exercises production cancellation, registration, codec and path-resolution functions.
The symlink regression executes a venv-style launcher and checks its reported invocation path. Absolute configuration, relative configuration and absolute/relative PATH entries select the same launcher and identity; invoking its target directly has a different binding. The shrinking property now uses symlinks on Unix, and the resolver fuzz target checks that absolute launcher spellings survive resolution.
Tests run
Signed head: 63bfe8d6f712cd049b54173b425aa33886fdccdb. All builds/tests ran through Q:/lanes/bs.ps1 with its incremental defaults and warm target, using one native feature set.
- cargo test --no-run -p acyclic-harness --lib --features hooks,mcp-stdio,mcp-http: passed (default features included), 27.37 s compilation.
- Built native binary directly, MCP filter: 52/52 passed, 0.43 s wall. Includes stable restart revision/approval lookup, independent execution bounds, ambient-environment identity, changed resolved executable, and eight timed-out registrations asserting zero active jobs and released connection ownership before return.
- Loom: exhaustively explores the real cancellation guard/worker path and real codec bound handoff with two threads. Checks cancellation visibility, worker/connection retirement, successful delivery and consistent codec limits. No preemption/permutation truncation; no new mutexes or lock order.
- Shrinking proptests: unrelated ambient environment preserves revisions; changing the resolved executable changes them; absolute launcher resolution is idempotent and preserves symlinks; arbitrary JSON round-trips across generated chunks and limit transitions; rejected encoding preserves its existing buffer; cancelled registration never publishes a partial catalog.
- cargo +nightly fuzz build --dev --fuzz-dir rust/crates/harness/fuzz: both targets originally built with ASan in 12.13 s; the changed resolver target rebuilt in 0.75 s at this head. Targets include the production codec and executable resolver directly, with pinned rmcp/which versions and checked-in seed corpora.
- Built fuzz binaries directly, 512 runs each: unchanged mcp_stdio_codec previously passed in 0.56 s; updated mcp_executable passed at this head in 0.24 s. Each run bounded to 4096-byte inputs and 3 seconds.
- Previous unchanged native-settings suite: 13/13, 0.01 s. No sleeps or polling in new tests.
- rustfmt --check and git diff --check passed. Clean worktree; signed commit verified.
Build logs:
- /home/var/log/buildbox/2026-10-15T215913433Z-2701308.log
- /home/var/log/buildbox/2026-10-15T220627844Z-2733647.log
Latest native/resolver-fuzz gate: /home/var/log/buildbox/2026-10-15T221837328Z-2837847.log (rc=0).