feat(desktop): host oab-mcp core as a bundled MCP sidecar (macOS-only) - #9
Merged
Conversation
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>
This was referenced Aug 10, 2026
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>
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>
This was referenced Aug 10, 2026
brettchien
marked this pull request as ready for review
August 10, 2026 23:40
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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-mcpbut the desktop shortcut-linkedstudio-cpin-process.oab-mcpas a Tauri sidecar (externalBin) at launch, does the MCPinitializehandshake, and forwards tool calls.deploy_listbridge =deploy_list(services) +deploy_getper service (per-instance 6-state) over MCP. Console shape unchanged —TauriSource/MockSourcestay interchangeable.src-tauridropsstudio-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/manageMcpClient, bridge over MCPsrc-tauri/tauri.conf.json—externalBin: binaries/oab-mcpsrc-tauri/capabilities/default.json— shell sidecar permission.github/workflows/desktop.yml— buildoab-mcpfor the target triple → bundle macOSdocs/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-macosis green. Two spots I couldn't validate locally and may need a CI-driven tweak:capabilities/default.json(shell:allow-spawnscope shape).tauri-buildvalidatesexternalBinpresence atcargo buildtime (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