@muhaven/mcp 0.1.3 → 0.1.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -7,6 +7,183 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.1.5] — 2026-05-17
11
+
12
+ Adds the `muhaven-broker stop` subcommand so operators can cleanly tear
13
+ down a detached daemon spawned by `muhaven-broker setup` without
14
+ hunting for PIDs in `ps` output or hand-rolling `taskkill` recipes.
15
+ Surfaced after `muhaven-broker setup` lands in 0.1.4 — operators
16
+ naturally asked "how do I stop this?" and the answer (`muhaven-broker
17
+ logout` + manual `kill`) was non-obvious.
18
+
19
+ ### Added
20
+
21
+ - **`muhaven-broker stop` subcommand** — clean shutdown:
22
+ - Probes the broker via `hello()`. Unreachable → "not running,
23
+ nothing to stop." exit 0.
24
+ - Best-effort `clearJwt()` so the OS keychain doesn't keep a stale
25
+ JWT after shutdown. Warning + continue on failure (don't abort
26
+ the kill).
27
+ - Reads `hello.pid` (new optional field — see below). On pre-0.1.5
28
+ daemons that omit the field, prints a manual-kill hint with
29
+ cross-platform commands and exits 1.
30
+ - `process.kill(pid, 'SIGTERM')` → polls `hello()` until it fails
31
+ (clean exit) or 5s elapses, then `process.kill(pid, 'SIGKILL')`.
32
+ - Pure orchestrator (`runStop` in `src/broker/stop.ts`) with
33
+ injectable IO so every branch is unit-testable without spawning
34
+ real processes.
35
+
36
+ ### Changed
37
+
38
+ - **`hello` response gains optional `pid?: number`** field (broker
39
+ protocol stays at 0.3.0 — additive optional field, back-compat with
40
+ pre-0.1.5 daemons). Populated from `process.pid` at request-handle
41
+ time; consumers MUST handle `undefined` for older daemons.
42
+
43
+ ### Tests
44
+
45
+ - 206 vitest pass (up from 197 in 0.1.4). Net +9 cases in new
46
+ `__tests__/stop.test.ts`:
47
+ - `runStop` not-running short-circuit
48
+ - happy path (hello → clearJwt → SIGTERM → exit-detected → 0)
49
+ - pre-0.1.5 daemon (no `pid` in hello) returns 1 with manual hint
50
+ - SIGKILL fallback after gracefulShutdownMs timeout
51
+ - SIGTERM permission error returns 1
52
+ - SIGKILL permission error returns 1 with "may be orphaned" hint
53
+ - `clearJwt` failure does NOT abort the kill (warning + continue)
54
+ - `defaultKillProcess` returns false on ESRCH (process gone)
55
+ - `defaultKillProcess` rethrows non-ESRCH errors (POSIX-only test)
56
+
57
+ ## [0.1.4] — 2026-05-17
58
+
59
+ Adds the one-shot `muhaven-broker setup` subcommand so a fresh install
60
+ goes from `npm install -g @muhaven/mcp` straight to a working MCP host
61
+ in two commands. Surfaced during the Wave 4 demo-recording prep — the
62
+ prior five-line manual ritual (env exports + session-key mint +
63
+ background daemon + login) was the longest opaque block in the demo
64
+ script. Also adds `--version` / `--help` to both `muhaven-broker` and
65
+ `muhaven-mcp` bins.
66
+
67
+ ### Added
68
+
69
+ - **`muhaven-broker setup` subcommand** — orchestrates env defaulting +
70
+ session-key minting + detached daemon spawn + login in a single
71
+ invocation. Flags:
72
+ - `--foreground` / `-f`: keep the daemon attached to the current
73
+ shell (useful when systemd/launchd will supervise instead of the
74
+ backgrounded child).
75
+ - `--skip-login`: spawn the daemon but defer the device-code flow.
76
+ - `--no-launch-browser`: pass-through to the embedded `login` step.
77
+ - `--broker-endpoint`, `--backend-base-url`, `--dashboard-base-url`:
78
+ same overrides as `login`.
79
+
80
+ Env defaults applied (only when the var is unset):
81
+ - `MUHAVEN_BACKEND_URL=https://api.muhaven.app`
82
+ - `MUHAVEN_DASHBOARD_URL=https://muhaven.app`
83
+ - `MUHAVEN_KEYRING=file` (auto-applied on Windows / WSL2 /
84
+ devcontainer / GitHub Codespace / SSH — same heuristic as
85
+ `muhaven-broker doctor`'s environment detector). Native macOS +
86
+ Linux desktop leave the value unset so the OS keychain remains
87
+ the default.
88
+
89
+ Idempotent: re-running `setup` against an already-up daemon detects
90
+ the existing JWT and short-circuits to `Login: skipped — JWT already
91
+ in keystore.`. Against a daemon that's up but unauthenticated, it
92
+ skips the spawn and only runs the login step.
93
+
94
+ Closing summary always surfaces the broker endpoint and a
95
+ platform-specific "Stop daemon" command (`kill <pid>` on POSIX,
96
+ `Stop-Process -Id <pid>` on Windows). Sign-out is explicitly
97
+ documented as separate from daemon shutdown — `muhaven-broker logout`
98
+ clears the JWT but leaves the daemon running.
99
+
100
+ - **`muhaven-broker --version` / `-v`** — prints `muhaven-broker
101
+ @muhaven/mcp@<version>` and exits 0. Wired into the dispatcher
102
+ alongside the existing `--help` / `-h`. Reads the package version
103
+ from the tsup-injected `__SERVER_VERSION__` constant.
104
+
105
+ - **`muhaven-mcp --version` / `-v` and `--help` / `-h`** — bin shim
106
+ short-circuits before requiring `dist/index.cjs`, so the flags exit
107
+ cleanly without spinning up the broker IPC + tool registry. Reads
108
+ the version from the sibling `package.json` directly.
109
+
110
+ ### Security
111
+
112
+ - **Session key never lands in `process.env`** — the orchestrator
113
+ builds a local `effectiveEnv` snapshot and passes the minted
114
+ session key only to the spawned daemon's env. Prior version
115
+ mutated `process.env.MUHAVEN_BROKER_SESSION_KEY` so any subsequent
116
+ child of the operator's shell would inherit the key. Foreground
117
+ mode brackets its required `process.env` mutation in a try/finally
118
+ that restores the original values on exit.
119
+
120
+ - **Spawned daemon strips `NODE_OPTIONS` / `NODE_TLS_REJECT_UNAUTHORIZED`
121
+ / `NODE_EXTRA_CA_CERTS` / `NODE_PATH`** from inherited env so a
122
+ same-user attacker who set those in the operator's shell can't
123
+ hijack the daemon's execution to exfiltrate the session key.
124
+
125
+ - **URL flag validation** — `--backend-base-url` / `--dashboard-base-url`
126
+ must be `https://` (with `http://localhost` / `127.0.0.1` /
127
+ `[::1]` dev carve-out). Rejects `javascript:`, `file:`, `data:`,
128
+ and plain `http:` to non-loopback BEFORE the spawn — defense
129
+ against the OAuth-device-flow phishing vector where a malicious
130
+ `--backend-base-url` would ship the JWT to an attacker host.
131
+
132
+ - **`--broker-endpoint` path validation** — must be a `\\.\pipe\…`
133
+ path on Windows or an absolute path on POSIX. Rejects relative
134
+ paths + flag-injection (e.g. `--broker-endpoint --from-daemon` is
135
+ parsed but rejected at validation, preventing the spawned daemon
136
+ from being bound to an attacker-controlled location).
137
+
138
+ - **Preserved env values not echoed** — `Env preserved: NAME (set in
139
+ your shell)` only — values stay opaque. Prior version printed
140
+ `Env preserved: NAME=value` which would leak operator-supplied
141
+ values to shell history / CI logs.
142
+
143
+ - **Session key minted via viem's `generatePrivateKey`** — guarantees
144
+ the result is in the valid secp256k1 scalar range. Prior version
145
+ used raw `crypto.randomBytes(32)`, which had a (negligible but
146
+ nonzero) probability of returning an out-of-range value that the
147
+ signer would reject as invalid much later in the flow.
148
+
149
+ - **Bin path resolved via `__dirname`** — `resolveBrokerBinPath` walks
150
+ from the bundled `dist/broker.cjs` to the sibling
151
+ `bin/muhaven-broker.cjs` deterministically, so Windows global-npm
152
+ shim wrappers (`.cmd` / `.ps1` in `process.argv[1]`) don't end up
153
+ as the spawn target.
154
+
155
+ - **`detectMcpHost` no longer falls through to `npm_lifecycle_event`**
156
+ — that var is the npm script name, not an MCP-host identity. The
157
+ device-flow `/link` page's "requesting client" panel would have
158
+ displayed "setup" for operators running via `npm run setup`,
159
+ misleading the passkey ceremony.
160
+
161
+ ### Tests
162
+
163
+ - 197 vitest pass (up from 134 in 0.1.3). Net +58 cases in
164
+ `__tests__/setup.test.ts` (+22 over the initial +36 after the
165
+ parallel agent security review) + 5 in `__tests__/cli-version-flag.test.ts`:
166
+ - **+10** `applyEnvDefaults` — defaults applied on empty env;
167
+ backend/dashboard preserved when set; KEYRING auto-applied on
168
+ win32/WSL2/SSH/devcontainer/Codespaces; left unset on native
169
+ macOS/Linux desktop; explicit `MUHAVEN_KEYRING=os` preserved on
170
+ Windows; empty-string vars treated as unset.
171
+ - **+2** `mintSessionKey` — 0x-prefixed 32-byte hex shape;
172
+ non-deterministic across calls.
173
+ - **+3** `decideSetupAction` — spawn-and-login / login-only /
174
+ already-ready decision tree.
175
+ - **+6** `parseSetupFlags` — defaults; `--foreground` and `-f`
176
+ aliases; `--skip-login`; `--no-launch-browser` pass-through; value
177
+ flag parsing; unknown-flag rejection.
178
+ - **+3** `waitForBroker` — first-call success; retry-until-success
179
+ with virtual clock; timeout throws with last error in message.
180
+ - **+12** `runSetup` orchestrator — flag-error path returns 2;
181
+ foreground mode short-circuits; spawn_and_login happy path;
182
+ login_only path; already_ready path; `--skip-login`; login-failure
183
+ bubbles exit code + leaves daemon running; wait timeout returns 1;
184
+ `--no-launch-browser` pass-through; value-flag pass-through;
185
+ session key minted vs preserved.
186
+
10
187
  ## [0.1.3] — 2026-05-16
11
188
 
12
189
  Q2 fix bundle from the post-§4 queue closing four findings from §3e⁶
@@ -1,5 +1,50 @@
1
1
  #!/usr/bin/env node
2
2
  /* eslint-disable */
3
+ //
4
+ // `muhaven-mcp` bin entrypoint.
5
+ //
6
+ // Production: MCPB hosts (Claude Desktop / Cursor / Claude Code) spawn this
7
+ // binary over STDIO and immediately start a JSON-RPC handshake. The host
8
+ // never passes argv flags — but operators occasionally run `muhaven-mcp
9
+ // --version` / `--help` from the shell to sanity-check the install. Those
10
+ // flags short-circuit BEFORE we wire up the STDIO transport so they exit
11
+ // cleanly without spinning up the broker IPC + tool registry.
12
+ //
13
+ // Keep this shim tiny — the production path is `runMcpStdioCli()` from the
14
+ // bundled dist. Anything richer goes in src/ where it's testable.
15
+ //
16
+
17
+ const args = process.argv.slice(2);
18
+
19
+ if (args.includes('--version') || args.includes('-v')) {
20
+ const pkg = require('../package.json');
21
+ process.stdout.write(`muhaven-mcp ${pkg.version}\n`);
22
+ process.exit(0);
23
+ }
24
+
25
+ if (args.includes('--help') || args.includes('-h')) {
26
+ const pkg = require('../package.json');
27
+ process.stdout.write(
28
+ [
29
+ `muhaven-mcp ${pkg.version} — MuHaven MCP STDIO server`,
30
+ ``,
31
+ `Usage:`,
32
+ ` muhaven-mcp Run the MCP server over STDIO`,
33
+ ` (called by Claude Desktop / Cursor /`,
34
+ ` Claude Code — not directly by humans)`,
35
+ ` muhaven-mcp --version | -v Print the @muhaven/mcp package version`,
36
+ ` muhaven-mcp --help | -h Show this help`,
37
+ ``,
38
+ `For first-time setup, run: muhaven-broker setup`,
39
+ `For troubleshooting, run: muhaven-broker doctor`,
40
+ ``,
41
+ `Docs: https://github.com/hasToDev/muhaven/blob/master/packages/mcp/README.md`,
42
+ ``,
43
+ ].join('\n'),
44
+ );
45
+ process.exit(0);
46
+ }
47
+
3
48
  const { runMcpStdioCli } = require('../dist/index.cjs');
4
49
 
5
50
  runMcpStdioCli().then(