@muhaven/mcp 0.1.2 → 0.1.4

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,205 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.1.4] — 2026-05-17
11
+
12
+ Adds the one-shot `muhaven-broker setup` subcommand so a fresh install
13
+ goes from `npm install -g @muhaven/mcp` straight to a working MCP host
14
+ in two commands. Surfaced during the Wave 4 demo-recording prep — the
15
+ prior five-line manual ritual (env exports + session-key mint +
16
+ background daemon + login) was the longest opaque block in the demo
17
+ script. Also adds `--version` / `--help` to both `muhaven-broker` and
18
+ `muhaven-mcp` bins.
19
+
20
+ ### Added
21
+
22
+ - **`muhaven-broker setup` subcommand** — orchestrates env defaulting +
23
+ session-key minting + detached daemon spawn + login in a single
24
+ invocation. Flags:
25
+ - `--foreground` / `-f`: keep the daemon attached to the current
26
+ shell (useful when systemd/launchd will supervise instead of the
27
+ backgrounded child).
28
+ - `--skip-login`: spawn the daemon but defer the device-code flow.
29
+ - `--no-launch-browser`: pass-through to the embedded `login` step.
30
+ - `--broker-endpoint`, `--backend-base-url`, `--dashboard-base-url`:
31
+ same overrides as `login`.
32
+
33
+ Env defaults applied (only when the var is unset):
34
+ - `MUHAVEN_BACKEND_URL=https://api.muhaven.app`
35
+ - `MUHAVEN_DASHBOARD_URL=https://muhaven.app`
36
+ - `MUHAVEN_KEYRING=file` (auto-applied on Windows / WSL2 /
37
+ devcontainer / GitHub Codespace / SSH — same heuristic as
38
+ `muhaven-broker doctor`'s environment detector). Native macOS +
39
+ Linux desktop leave the value unset so the OS keychain remains
40
+ the default.
41
+
42
+ Idempotent: re-running `setup` against an already-up daemon detects
43
+ the existing JWT and short-circuits to `Login: skipped — JWT already
44
+ in keystore.`. Against a daemon that's up but unauthenticated, it
45
+ skips the spawn and only runs the login step.
46
+
47
+ Closing summary always surfaces the broker endpoint and a
48
+ platform-specific "Stop daemon" command (`kill <pid>` on POSIX,
49
+ `Stop-Process -Id <pid>` on Windows). Sign-out is explicitly
50
+ documented as separate from daemon shutdown — `muhaven-broker logout`
51
+ clears the JWT but leaves the daemon running.
52
+
53
+ - **`muhaven-broker --version` / `-v`** — prints `muhaven-broker
54
+ @muhaven/mcp@<version>` and exits 0. Wired into the dispatcher
55
+ alongside the existing `--help` / `-h`. Reads the package version
56
+ from the tsup-injected `__SERVER_VERSION__` constant.
57
+
58
+ - **`muhaven-mcp --version` / `-v` and `--help` / `-h`** — bin shim
59
+ short-circuits before requiring `dist/index.cjs`, so the flags exit
60
+ cleanly without spinning up the broker IPC + tool registry. Reads
61
+ the version from the sibling `package.json` directly.
62
+
63
+ ### Security
64
+
65
+ - **Session key never lands in `process.env`** — the orchestrator
66
+ builds a local `effectiveEnv` snapshot and passes the minted
67
+ session key only to the spawned daemon's env. Prior version
68
+ mutated `process.env.MUHAVEN_BROKER_SESSION_KEY` so any subsequent
69
+ child of the operator's shell would inherit the key. Foreground
70
+ mode brackets its required `process.env` mutation in a try/finally
71
+ that restores the original values on exit.
72
+
73
+ - **Spawned daemon strips `NODE_OPTIONS` / `NODE_TLS_REJECT_UNAUTHORIZED`
74
+ / `NODE_EXTRA_CA_CERTS` / `NODE_PATH`** from inherited env so a
75
+ same-user attacker who set those in the operator's shell can't
76
+ hijack the daemon's execution to exfiltrate the session key.
77
+
78
+ - **URL flag validation** — `--backend-base-url` / `--dashboard-base-url`
79
+ must be `https://` (with `http://localhost` / `127.0.0.1` /
80
+ `[::1]` dev carve-out). Rejects `javascript:`, `file:`, `data:`,
81
+ and plain `http:` to non-loopback BEFORE the spawn — defense
82
+ against the OAuth-device-flow phishing vector where a malicious
83
+ `--backend-base-url` would ship the JWT to an attacker host.
84
+
85
+ - **`--broker-endpoint` path validation** — must be a `\\.\pipe\…`
86
+ path on Windows or an absolute path on POSIX. Rejects relative
87
+ paths + flag-injection (e.g. `--broker-endpoint --from-daemon` is
88
+ parsed but rejected at validation, preventing the spawned daemon
89
+ from being bound to an attacker-controlled location).
90
+
91
+ - **Preserved env values not echoed** — `Env preserved: NAME (set in
92
+ your shell)` only — values stay opaque. Prior version printed
93
+ `Env preserved: NAME=value` which would leak operator-supplied
94
+ values to shell history / CI logs.
95
+
96
+ - **Session key minted via viem's `generatePrivateKey`** — guarantees
97
+ the result is in the valid secp256k1 scalar range. Prior version
98
+ used raw `crypto.randomBytes(32)`, which had a (negligible but
99
+ nonzero) probability of returning an out-of-range value that the
100
+ signer would reject as invalid much later in the flow.
101
+
102
+ - **Bin path resolved via `__dirname`** — `resolveBrokerBinPath` walks
103
+ from the bundled `dist/broker.cjs` to the sibling
104
+ `bin/muhaven-broker.cjs` deterministically, so Windows global-npm
105
+ shim wrappers (`.cmd` / `.ps1` in `process.argv[1]`) don't end up
106
+ as the spawn target.
107
+
108
+ - **`detectMcpHost` no longer falls through to `npm_lifecycle_event`**
109
+ — that var is the npm script name, not an MCP-host identity. The
110
+ device-flow `/link` page's "requesting client" panel would have
111
+ displayed "setup" for operators running via `npm run setup`,
112
+ misleading the passkey ceremony.
113
+
114
+ ### Tests
115
+
116
+ - 197 vitest pass (up from 134 in 0.1.3). Net +58 cases in
117
+ `__tests__/setup.test.ts` (+22 over the initial +36 after the
118
+ parallel agent security review) + 5 in `__tests__/cli-version-flag.test.ts`:
119
+ - **+10** `applyEnvDefaults` — defaults applied on empty env;
120
+ backend/dashboard preserved when set; KEYRING auto-applied on
121
+ win32/WSL2/SSH/devcontainer/Codespaces; left unset on native
122
+ macOS/Linux desktop; explicit `MUHAVEN_KEYRING=os` preserved on
123
+ Windows; empty-string vars treated as unset.
124
+ - **+2** `mintSessionKey` — 0x-prefixed 32-byte hex shape;
125
+ non-deterministic across calls.
126
+ - **+3** `decideSetupAction` — spawn-and-login / login-only /
127
+ already-ready decision tree.
128
+ - **+6** `parseSetupFlags` — defaults; `--foreground` and `-f`
129
+ aliases; `--skip-login`; `--no-launch-browser` pass-through; value
130
+ flag parsing; unknown-flag rejection.
131
+ - **+3** `waitForBroker` — first-call success; retry-until-success
132
+ with virtual clock; timeout throws with last error in message.
133
+ - **+12** `runSetup` orchestrator — flag-error path returns 2;
134
+ foreground mode short-circuits; spawn_and_login happy path;
135
+ login_only path; already_ready path; `--skip-login`; login-failure
136
+ bubbles exit code + leaves daemon running; wait timeout returns 1;
137
+ `--no-launch-browser` pass-through; value-flag pass-through;
138
+ session key minted vs preserved.
139
+
140
+ ## [0.1.3] — 2026-05-16
141
+
142
+ Q2 fix bundle from the post-§4 queue closing four findings from §3e⁶
143
+ (broker-session-key-required-for-reads, broker-env-divergence,
144
+ mcp-serverinfo-version-stale) and unblocking the openclaw-skill ClawScan
145
+ fix (the `noExternal: ['@muhaven/mcp']` inline bundle requires this
146
+ version on npm before the skill can be republished).
147
+
148
+ ### Added
149
+
150
+ - **Read-only daemon posture**: the broker daemon now boots WITHOUT
151
+ `MUHAVEN_BROKER_SESSION_KEY`. In that mode the daemon still serves
152
+ `hello` + the JWT verbs (so `muhaven.read.*` tools work end-to-end via
153
+ the standalone `@muhaven/mcp` install), but any `sign_hash` request
154
+ returns the new `session_key_unavailable` broker error so write paths
155
+ fail with a clear remediation message instead of the daemon dying at
156
+ startup. Closes §3e⁶ F-broker-session-key-required-for-reads.
157
+ - **`muhaven-broker login --from-daemon` flag**: resolves backend +
158
+ dashboard URLs from the running daemon's `hello.effectiveConfig`
159
+ rather than the login CLI's env. Solves the daemon-vs-CLI env-divergence
160
+ problem when the two processes inherit different shell environments
161
+ (e.g. the daemon was launched by systemd/launchd, the CLI by ssh).
162
+ Mutually exclusive with explicit `--backend-base-url` /
163
+ `--dashboard-base-url`. Closes §3e⁶ F-broker-env-divergence.
164
+ - **`muhaven-broker doctor` surfaces the daemon's effective config**
165
+ and read-only-posture status — the operator can verify which backend
166
+ URL is actually in play before driving a login.
167
+
168
+ ### Changed
169
+
170
+ - **Broker protocol bumped 0.2.0 → 0.3.0** (additive — pre-0.3.0 clients
171
+ remain compatible):
172
+ - `hello.hasSessionKey` (optional `boolean`) — absence implies `true`
173
+ for back-compat.
174
+ - `hello.effectiveConfig` (optional `{ backendBaseUrl, dashboardBaseUrl }`).
175
+ - New `session_key_unavailable` broker error code.
176
+ - **`serverInfo.version`** in the MCP server's `initialize` response is
177
+ now build-time injected from `package.json#version` (tsup `define` on
178
+ `__SERVER_VERSION__`) rather than the previously hardcoded `'0.1.0'`
179
+ string in `src/server.ts`. Closes §3e⁶ F-mcp-serverinfo-version-stale.
180
+
181
+ ### Tests
182
+
183
+ - 134 vitest pass (up from 101 in 0.1.2). Net +33 cases:
184
+ - **+6** `config.test.ts` — `loadBrokerConfig` lazy-validation
185
+ (no key, empty string, valid key, malformed key) + env-driven backend
186
+ + dashboard URL surface.
187
+ - **+5** `daemon-handler.test.ts` — 0.3.0 protocol: `hello` surfaces
188
+ `hasSessionKey` + `effectiveConfig` from options, defaults `true`
189
+ when omitted, reflects `false` when set; `sign_hash` with
190
+ `NullSigner` returns `session_key_unavailable`; re-throws non-Missing
191
+ errors verbatim.
192
+ - **+9** `cli-parse-login-flags.test.ts` — flag parser unit cases
193
+ incl. `--from-daemon` mutual-exclusion guard.
194
+ - **+8** `session-key-required.test.ts` — `signEnvelope` probe of
195
+ `hello.hasSessionKey`; short-circuit returns `SESSION_KEY_REQUIRED`
196
+ for buy + claim with mint-URL pointing at `dashboardBaseUrl`;
197
+ safety-net mapping of inner `session_key_unavailable`;
198
+ probe-cache reuse; concurrent-call coalescing (one hello round-trip
199
+ for N callers); retry-after-rejection (eager cache clear); trailing-slash
200
+ mintUrl strip.
201
+ - **+2** `server-version.test.ts` — runtime fallback returns
202
+ `package.json#version`; matches `manifest.json#version`.
203
+ - **+2** `build-artifacts.test.ts` — hostname-migration guard + new
204
+ `__SERVER_VERSION__` literal grep in bundled dist.
205
+ - **+1** `daemon-lifecycle.test.ts` — read-only-posture boot test
206
+ replaces the prior "exits on missing key" assertion; added
207
+ "exits on a malformed session key" as the second negative-path test.
208
+
10
209
  ## [0.1.2] — 2026-05-11
11
210
 
12
211
  Re-roll of the `0.1.1` workflow-validation cut. `0.1.1` never reached npm:
@@ -181,7 +380,8 @@ Workstream H)
181
380
  muhaven-mcp-0.1.0.tgz
182
381
  ```
183
382
 
184
- [Unreleased]: https://github.com/hasToDev/muhaven/compare/mcp-v0.1.2...HEAD
383
+ [Unreleased]: https://github.com/hasToDev/muhaven/compare/mcp-v0.1.3...HEAD
384
+ [0.1.3]: https://github.com/hasToDev/muhaven/releases/tag/mcp-v0.1.3
185
385
  [0.1.2]: https://github.com/hasToDev/muhaven/releases/tag/mcp-v0.1.2
186
386
  [0.1.1]: https://github.com/hasToDev/muhaven/releases/tag/mcp-v0.1.1
187
387
  [0.1.0]: https://github.com/hasToDev/muhaven/releases/tag/mcp-v0.1.0
@@ -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(