Skip to content

Commit 2edcd29

Browse files
committed
docs(run-from-source): document pnpm dev watch flow
Restructure to cover both flows: pnpm dev for live HMR + Rust auto-recompile, and build + swap into /Applications for an installed app. Add Keychain prompt explainer and shared-data table. Signed-off-by: crimsonsunset <jsangio1@gmail.com>
1 parent da91fd9 commit 2edcd29

1 file changed

Lines changed: 100 additions & 24 deletions

File tree

docs/run-from-source-macos.md

Lines changed: 100 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,19 @@
1-
# Build from Source and Replace the Installed App (macOS)
1+
# Run McpMux from Source (macOS)
22

3-
Replace the release McpMux in `/Applications` with a locally built copy from this repo. Useful when running a fork, a feature branch, or patches that haven't shipped yet.
3+
Two flows for working against this repo, picked by what you're doing:
4+
5+
| Flow | Use when | Speed | Cursor / Claude / VS Code see it? |
6+
| ---- | -------- | ----- | --------------------------------- |
7+
| **Dev watch mode** (`pnpm dev`) | Iterating on UI or Rust — you want HMR for React and auto-recompile for Tauri commands | Vite HMR is instant; Rust changes ~5–15s incremental | Yes — same `localhost:45818` endpoint while `pnpm dev` is running |
8+
| **Build + swap** (replace `/Applications/McpMux.app`) | You want a real installed app on this branch — autostart, system tray, runs without a terminal, survives reboot | Full build ~5–10 min, incremental ~1–3 min | Yes — and stays running after you close your editor |
9+
10+
Quick rule of thumb: **`pnpm dev` while you're coding, swap when you're done** so other AI clients keep working when Cursor isn't open.
411

512
---
613

7-
## What survives a swap
14+
## What survives between flows
815

9-
Replacing the `.app` bundle does **not** touch your data. McpMux stores everything outside the app:
16+
Both dev mode and a swapped `.app` use the same `com.mcpmux.desktop` bundle identifier, so they share data:
1017

1118
| Data | Location |
1219
| ---- | -------- |
@@ -16,9 +23,9 @@ Replacing the `.app` bundle does **not** touch your data. McpMux stores everythi
1623
| Encryption master key | macOS Keychain (`com.mcpmux.desktop` service) |
1724
| OAuth tokens / credentials | Encrypted in SQLite + Keychain |
1825

19-
The app identifier (`com.mcpmux.desktop`) is unchanged between release and source builds, so the new binary reads the same data directory and keychain entries.
26+
The new binary reads the same data dir and keychain entries as the release. Spaces, server installs, and access keys persist across `pnpm dev``/Applications` swaps.
2027

21-
**What you might need to redo:** OAuth re-auth in Cursor/Claude Desktop if DCR or token validation changed on your branch. Your McpMux config, spaces, and server installs stay put.
28+
**What you might need to redo:** OAuth re-auth in Cursor/Claude Desktop if DCR or token validation changed on your branch.
2229

2330
---
2431

@@ -39,19 +46,84 @@ pnpm install
3946

4047
---
4148

42-
## Option A — Full build (recommended)
49+
## Flow 1 — Dev watch mode (`pnpm dev`)
50+
51+
Live-reload while you code. Best for tight iteration on UI or Rust.
52+
53+
### What it does
54+
55+
| Layer | Behavior |
56+
| ----- | -------- |
57+
| React / Tailwind / TS | Vite dev server on `localhost:1420` with **HMR** — change a `.tsx`/`.css`, see it instantly without losing app state |
58+
| Rust (Tauri commands, gateway, storage) | Recompiles + relaunches the Tauri window on any `.rs` save under `src-tauri/` or `crates/` |
59+
| Bundle ID | Same `com.mcpmux.desktop` — reads your real DB and Keychain entries |
60+
61+
### Run it
62+
63+
```bash
64+
# Quit the installed app first so the dev gateway can bind 127.0.0.1:45818
65+
osascript -e 'tell application "McpMux" to quit' 2>/dev/null; sleep 2
66+
67+
pnpm dev
68+
```
69+
70+
A Tauri window opens. Edit `.tsx` files for instant HMR; edit Rust and the window will relaunch on its own after recompile.
71+
72+
### Frontend-only iteration
73+
74+
If you're only changing UI and want the fastest possible loop:
75+
76+
```bash
77+
pnpm dev:web
78+
```
79+
80+
This runs Vite alone in a browser tab — no Rust, no Tauri shell. Tauri `invoke()` calls won't work (no backend), but for pure layout/styling work it's the quickest path.
81+
82+
### Gotchas
83+
84+
| Symptom | Why | Fix |
85+
| ------- | --- | --- |
86+
| Keychain prompts on first launch of the dev binary | Different signer than `/Applications/McpMux.app` | Click **Always Allow** once — sticks for that built artifact. See `Keychain prompts` below for detail |
87+
| `Address already in use: 45818` | Installed `.app` still running | `osascript -e 'tell application "McpMux" to quit'` then retry |
88+
| Cursor's MCP server "disconnected" mid-session | You stopped `pnpm dev` | Cursor reconnects when the gateway is back on `localhost:45818` (either flow) |
89+
| Rust recompile feels slow | Big edits in `mcpmux-gateway` / `mcpmux-storage` | Expected — keep edits scoped or use `pnpm dev:web` for UI |
90+
| `pnpm dev` keeps crashing with "Master key not found" | DB/keychain mismatch from manual deletion | Don't manually delete keychain entries — see `Keychain prompts` below |
91+
92+
### Keychain prompts
93+
94+
McpMux reads two secrets from Keychain on startup:
95+
96+
1. **Master encryption key** — every app launch (decrypts SQLite credentials)
97+
2. **JWT signing secret** — first time you start the gateway in a session
98+
99+
macOS scopes Keychain access to the **specific signed binary**, not just the bundle ID. So:
100+
101+
- First launch of a `pnpm dev` build → 1–2 prompts
102+
- First launch after a fresh `pnpm build` swap → 1–2 prompts
103+
- Subsequent launches of the **same** built binary → silent if you clicked **Always Allow**
104+
- Alternating between `pnpm dev` and `/Applications/McpMux.app` → may re-prompt because each is a different signer
105+
106+
This is expected. Click **Always Allow** the first time you see each prompt for a new build.
107+
108+
---
109+
110+
## Flow 2 — Build and swap into `/Applications`
111+
112+
Use when you want the source build to behave like an installed app: launch from Spotlight/Dock, autostart, run in the background without a dev terminal, survive reboots.
113+
114+
### Option A — Full build (recommended)
43115

44116
Rebuilds the React frontend and produces a fresh `.app` bundle. Use this when frontend or Tauri config changed, or when you want a clean bundle.
45117

46-
### 1. Quit the running app
118+
#### 1. Quit the running app
47119

48120
```bash
49121
osascript -e 'tell application "McpMux" to quit' 2>/dev/null || true
50122
# Give it a moment to release the gateway port
51123
sleep 2
52124
```
53125

54-
### 2. Build
126+
#### 2. Build
55127

56128
```bash
57129
cd /path/to/mcp-mux
@@ -67,7 +139,9 @@ target/release/bundle/macos/McpMux.app
67139
target/release/bundle/dmg/McpMux_*.dmg # optional installer artifact
68140
```
69141

70-
### 3. Backup and swap
142+
> **Note:** the build may exit non-zero at the very end with `TAURI_SIGNING_PRIVATE_KEY` missing. That only blocks the auto-update artifact; the `.app` and `.dmg` are still produced and usable.
143+
144+
#### 3. Backup and swap
71145

72146
```bash
73147
# Backup current install (skip if you already have a recent .bak)
@@ -80,7 +154,7 @@ sudo cp -R target/release/bundle/macos/McpMux.app /Applications/
80154
sudo chown -R "$(whoami):admin" /Applications/McpMux.app
81155
```
82156

83-
### 4. Re-sign (required after manual swap)
157+
#### 4. Re-sign (required after manual swap)
84158

85159
macOS Gatekeeper rejects a bundle whose binary was replaced without re-signing:
86160

@@ -89,17 +163,15 @@ xattr -dr com.apple.quarantine /Applications/McpMux.app 2>/dev/null || true
89163
codesign --force --deep --sign - /Applications/McpMux.app
90164
```
91165

92-
### 5. Launch
166+
#### 5. Launch
93167

94168
```bash
95169
open /Applications/McpMux.app
96170
```
97171

98-
Verify: spaces, installed servers, and gateway on `localhost:45818` should look exactly as before.
99-
100-
---
172+
Verify: spaces, installed servers, and gateway on `localhost:45818` should look exactly as before. First launch will trigger 1–2 Keychain prompts because the new ad-hoc signature is a different signer than the previous build — click **Always Allow** once and you're set until the next swap.
101173

102-
## Option B — Binary-only swap (fast path)
174+
### Option B — Binary-only swap (fast path)
103175

104176
When you changed **Rust only** (no frontend, no `tauri.conf.json` changes). Skips the Vite build and DMG step.
105177

@@ -124,7 +196,9 @@ Keeps the existing bundle shell (icons, Info.plist, embedded frontend from last
124196

125197
---
126198

127-
## Rollback
199+
## Rollback (Flow 2 only)
200+
201+
If a swapped build is broken, restore the previous `/Applications/McpMux.app`. Dev-mode (`pnpm dev`) doesn't need a rollback — just stop the dev process.
128202

129203
### Full build rollback
130204

@@ -149,13 +223,15 @@ open /Applications/McpMux.app
149223

150224
## Troubleshooting
151225

152-
| Symptom | Fix |
153-
| ------- | --- |
154-
| "App is damaged" / won't open | Re-run `codesign --force --deep --sign - /Applications/McpMux.app` |
155-
| Gateway port already in use | Old process still running — `pkill -f mcpmux` then relaunch |
156-
| Cursor OAuth fails after swap | Re-trigger MCP OAuth in Cursor (DCR redirect URI validation may have changed) |
157-
| Empty app / missing UI | You used binary-only swap but frontend changed — run Option A (full build) |
158-
| Permission denied on `/Applications` | Use `sudo` for mv/cp/chown, or install to `~/Applications/` and skip sudo |
226+
| Symptom | Applies to | Fix |
227+
| ------- | ---------- | --- |
228+
| "App is damaged" / won't open | Flow 2 | Re-run `codesign --force --deep --sign - /Applications/McpMux.app` |
229+
| Gateway port already in use | Both | Old process still running — `pkill -f mcpmux` then relaunch |
230+
| Cursor OAuth fails after swap | Both | Re-trigger MCP OAuth in Cursor (DCR redirect URI validation may have changed) |
231+
| Empty app / missing UI | Flow 2 (Option B) | You used binary-only swap but frontend changed — run Option A (full build) |
232+
| Permission denied on `/Applications` | Flow 2 | Use `sudo` for mv/cp/chown, or install to `~/Applications/` and skip sudo |
233+
| Keychain prompts on every launch of the same binary | Both | Click **Always Allow** (not just **Allow**); check Keychain Access for duplicate `master-encryption-key` / `jwt-signing-secret` entries from old signers |
234+
| `pnpm dev` won't start — `EADDRINUSE 45818` | Flow 1 | The installed `.app` is still running — quit it before `pnpm dev` |
159235

160236
---
161237

0 commit comments

Comments
 (0)