Skip to content

feat(tools): inject the tools MCP into the session CLI config at spawn (#39) - #45

Open
Reese-max wants to merge 2 commits into
openabdev:mainfrom
Reese-max:devin/issue-39
Open

Reese-max wants to merge 2 commits into
openabdev:mainfrom
Reese-max:devin/issue-39

Conversation

@Reese-max

Copy link
Copy Markdown

Refs #39

Summary

OPENAB_TOOLS_MCP_URL put the loopback endpoint in the child's environment,
but wiring it into the CLI was manual: mcp add per session plus a
hand-edited allowlist, and both hard-coded a URL whose key dies on the next
spawn. At spawn the runtime now writes the session-independent form for
the CLI variants it knows — kiro-cli first:

  • ~/.local/share/openab-pty/computer-mcp-bridge.js — a small stdio bridge
    embedded in the binary and rewritten each spawn; every stdin line is one
    JSON-RPC message POSTed to $OPENAB_TOOLS_MCP_URL (JSON and SSE replies,
    Mcp-Session-Id echoed, per-id JSON-RPC errors on HTTP failure).
  • ~/.kiro/settings/mcp.json gains exactly the computer server
    {command: <bundled bun / PATH bun / node>=18, args: [bridge], env: {OPENAB_TOOLS_MCP_URL: "${OPENAB_TOOLS_MCP_URL}"}} — every other
    server and setting is preserved, and the file never holds a URL or key, so
    the shared workspace config needs no refresh when the key rotates.
  • every ~/.kiro/agents/*.json gains @computer/* in allowedTools, plus
    @computer in a restrictive tools list and a computer entry in
    mcpServers for agents that opt out of the shared mcp.json. Agent files
    are merged, never created.
  • Writes are atomic (sibling tempfile + rename, write through resolvable
    symlinks, preserve existing file modes). Malformed, non-object, or
    foreign-shaped files are left byte-identical; dangling symlinks are left
    alone rather than replaced.
  • Unknown CLI variant → nothing is written and the §9.3 env-var manual path
    stands. tools_listen unset → injection never runs. A failed merge warns
    and never costs a spawn.

Detection: kiro-cli on the child's PATH or under the installer's
~/.local layout; JS runtime = bundled bun → ~/.local/share/kiro-cli/bun
→ PATH bun → PATH node (with a --version probe, node ≥ 18 required for
fetch).

Also includes a preparatory commit hoisting the crate into a repo-root
Cargo workspace (Cargo.toml/Cargo.lock at root), so cargo test/fmt/clippy --workspace work from a checkout root as well as from
runtime/; Dockerfiles, CI cache, and dependabot updated to match.

Test plan

  • cargo fmt --all -- --check
  • cargo test --workspace (175 unit + 13 config tests incl. injection
    merge/preservation, unknown-variant, tools-off, node<18, dangling
    symlink, mode-preservation, and restart-rotation byte-identical)
  • cargo clippy --workspace --all-targets -- -D warnings
  • bridge e2e (ignored): real node process + real HTTP stub — JSON-RPC
    round-trip, SSE multi-line normalization, notification silence
  • session tests: fresh tools spawn injects zero-step config; restart
    rotates the env key while the file stays byte-identical; unwritable
    workspace still spawns

Docs: runtime/CLIENT-CONTRACT.md §9.3 rewritten for the auto-injection +
manual fallback; docs/k8s-howto.md §6 notes the zero-step CLI side.

Generated with Devin

devin-ai-integration Bot and others added 2 commits September 30, 2026 09:13
The crate used to be its own workspace rooted at runtime/, so cargo only
worked from inside that directory: `cargo test --workspace` at the repo
root had no manifest to find. Moving the workspace manifest (and the lock)
to the root makes the whole-repo commands behave the way every other Rust
repo's do, while `cd runtime && cargo …` keeps working through the member.

Dockerfile and Dockerfile.nightly pick up the new manifest/lock layout;
dependabot and the CI cache now look at the root workspace.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
openabdev#39)

OPENAB_TOOLS_MCP_URL put the loopback endpoint in the child's environment,
but wiring it into the CLI was manual: `mcp add` per session plus a
hand-edited allowlist, and both hard-coded a URL whose key dies on the
next spawn. At spawn the runtime now writes the session-independent form
for the CLI variants it knows — kiro-cli first:

- ~/.kiro/settings/mcp.json gains a `computer` server that runs a small
  stdio bridge on the installer's bundled bun (or node). The bridge reads
  the URL from *its* environment — `${OPENAB_TOOLS_MCP_URL}` in `env`, the
  one place kiro expands variables — so the shared workspace file serves
  every session and never holds a key. A hard-coded per-session URL was
  the live failure in the field report: three sessions, one computer.
- every ~/.kiro/agents/*.json gains `@computer/*` in allowedTools — the
  sandbox-served set under the platform-neutral alias — plus `@computer`
  in a restrictive `tools` list, and an agent that opted out of mcp.json
  gets its own copy of the server entry. Agent files are merged in place,
  never created.
- nothing is written for a CLI the runtime does not know, when the tools
  plane is off, or when no JS runtime exists; the §9.3 env-var manual path
  stands in all three, and a failed merge never costs a spawn.

Refs openabdev#39

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
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.

1 participant