Skip to content

feat(desktop): host oab-mcp core as a bundled MCP sidecar (macOS-only) - #9

Merged
brettchien merged 8 commits into
mainfrom
feat/desktop-mcp-sidecar
Aug 10, 2026
Merged

feat(desktop): host oab-mcp core as a bundled MCP sidecar (macOS-only)#9
brettchien merged 8 commits into
mainfrom
feat/desktop-mcp-sidecar

Conversation

@brettchien

Copy link
Copy Markdown
Contributor

What

Make the MCP server start with the desktop app instead of running separately, and route the desktop's own reads through it — killing the two-divergent-paths problem where agents used oab-mcp but the desktop shortcut-linked studio-cp in-process.

  • Desktop spawns oab-mcp as a Tauri sidecar (externalBin) at launch, does the MCP initialize handshake, and forwards tool calls.
  • deploy_list bridge = deploy_list (services) + deploy_get per service (per-instance 6-state) over MCP. Console shape unchangedTauriSource/MockSource stay interchangeable.
  • src-tauri drops studio-cp / aws-config — single core path; creds resolve inside the sidecar.

Scope (per Brett)

macOS only for now (arm64, unsigned). Windows / universal / signing / auto-update deferred.

Files

  • src-tauri/src/mcp.rs — minimal MCP stdio client (spawn, id-muxed JSON-RPC, handshake, call_tool)
  • src-tauri/src/lib.rs — shell plugin + spawn/manage McpClient, bridge over MCP
  • src-tauri/tauri.conf.jsonexternalBin: binaries/oab-mcp
  • src-tauri/capabilities/default.json — shell sidecar permission
  • .github/workflows/desktop.yml — build oab-mcp for the target triple → bundle macOS
  • docs/adr/desktop-core-sidecar.md — decision record (amends ADR-3 slice-2)

Verification / caveats

Authored on a Linux box with no Tauri toolchain — CI's macOS runner is the compiler here. Draft until bundle-macos is green. Two spots I couldn't validate locally and may need a CI-driven tweak:

  1. Tauri v2 shell sidecar permission identifiers in capabilities/default.json (shell:allow-spawn scope shape).
  2. Whether tauri-build validates externalBin presence at cargo build time (compile-linux builds+places the linux sidecar to be safe).

MCP handshake assumes protocol 2024-11-05 + notifications/initialized; rmcp server should accept.

🤖 Generated with Claude Code

Orca and others added 6 commits August 9, 2026 23:51
The desktop now reaches the control plane over the MCP contract instead of an
in-process studio-cp link: it spawns `oab-mcp` as a Tauri sidecar at launch,
runs the initialize handshake, and forwards `deploy_list`+`deploy_get` as
`tools/call`. Console view-model shape is unchanged.

- src-tauri/src/mcp.rs: minimal MCP stdio client (spawn, id-muxed JSON-RPC,
  handshake, call_tool)
- src-tauri/src/lib.rs: register shell plugin, spawn+manage McpClient, bridge
  deploy_list over MCP; drop studio-cp / aws-config deps
- tauri.conf.json externalBin + capabilities sidecar permission
- desktop.yml: build oab-mcp for the target triple and bundle macOS (arm64,
  unsigned); Windows/universal deferred
- docs/adr/desktop-core-sidecar.md: decision record (amends ADR-3 slice-2)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
E0599 in compile-linux — the manage() method needs the Manager trait in scope.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Show core lifecycle + failures on launch, so it's obvious when the app starts
and whether the sidecar/handshake went wrong.

- src-tauri: forward core lifecycle (spawning → handshake → ready), the
  sidecar's stderr, exit, and bridge errors to a frontend `app-log` event
- console: log.ts pane (streams app-log; browser build notes "no core"),
  shown above the roster; deduped roster errors also logged

Frontend verified locally: tsc --noEmit clean, vitest 8/8, vite build ok.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Display 2 shows every MCP message to/from the sidecar (→ request/notification,
← response), so the raw app↔core interaction is visible and can be captured for
debugging. Display 1 stays lifecycle/errors.

- src-tauri/mcp.rs: emit every outgoing/incoming JSON-RPC message as `mcp-io`
  (dir out|in); Activity stays on `app-log`
- console: log.ts createPane() + bindBackend() routing app-log/mcp-io into two
  panes; MCP pane added above the roster

Frontend verified locally: tsc clean, vitest 8/8, vite build ok.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Replace the stacked panes with a two-tab switcher; roster stays below. An
unread dot flags the hidden tab when it gets new lines, so an error landing on
the non-visible tab isn't missed.

Frontend-only; verified locally: tsc clean, vitest 8/8, vite build ok.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Three bugs the first live run exposed:

1. Lifecycle logs were lost — the core was spawned in setup(), emitting
   "spawning/handshake/ready" before the webview subscribed. Now the frontend
   subscribes first, then invokes a `start_core` command; spawn happens with a
   listener attached.
2. Tool errors were unreadable — a failed tool call returns isError:true with
   the message as text; the client tried to JSON-decode it ("expected ident").
   Now isError is surfaced verbatim; decode errors include the raw text.
3. Frontend showed "undefined" — Tauri rejections are strings, not Errors.
   Stringify with a helper.

Frontend verified locally: tsc clean, vitest 8/8, vite build ok.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
brettchien pushed a commit that referenced this pull request Aug 10, 2026
- 'Realizes' ADR-3's deferred streaming, not 'Supersedes' (ADR-3 not yet accepted)
- subscriber semantics: read current state then resources/updated deltas
  (matches MCP; no auto-snapshot on subscribe)
- reference the sidecar reader as PR #9 rather than a not-yet-on-main file link

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Orca and others added 2 commits August 10, 2026 12:35
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Static app `version` (0.1.0) can't tell builds apart, so stamp each build with
version + short git sha + build time (injected by vite at build; falls back to
GITHUB_SHA / "unknown"). Shown under "OAB Studio" in the topbar (hover → build
time) and logged to the Activity pane on launch.

- vite.config.ts: define __APP_VERSION__/__BUILD_SHA__/__BUILD_TIME__
- adds @types/node (config now uses node builtins)

Verified locally: tsc clean, vitest 8/8, vite build stamps "v0.1.0 · <sha>".

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@brettchien
brettchien marked this pull request as ready for review August 10, 2026 23:40
@brettchien
brettchien merged commit a88f6d0 into main Aug 10, 2026
3 checks passed
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