@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 +201 -1
- package/bin/muhaven-mcp.cjs +45 -0
- package/dist/broker.cjs +506 -24
- package/dist/broker.d.cts +29 -1
- package/dist/broker.d.ts +29 -1
- package/dist/broker.js +513 -30
- package/dist/index.cjs +124 -20
- package/dist/index.d.cts +143 -13
- package/dist/index.d.ts +143 -13
- package/dist/index.js +120 -21
- package/manifest.json +2 -2
- package/package.json +1 -1
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.
|
|
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
|
package/bin/muhaven-mcp.cjs
CHANGED
|
@@ -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(
|