@muhaven/mcp 0.4.0 → 0.4.2
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 +50 -0
- package/dist/broker.cjs +48 -8
- package/dist/broker.js +48 -8
- package/dist/index.cjs +19 -3
- package/dist/index.js +19 -3
- package/manifest.json +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,56 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.4.2] — 2026-05-24
|
|
11
|
+
|
|
12
|
+
### Fixed
|
|
13
|
+
|
|
14
|
+
- **Revoke kill-switch — MCP-side gate (defense-in-depth).** A revoked
|
|
15
|
+
Scoped session let an already-running broker keep buying autonomously
|
|
16
|
+
(the broker signs from a local snapshot with no "revoked" concept and the
|
|
17
|
+
on-chain validator stays installed). `position.buy` Path D now hard-gates
|
|
18
|
+
on the backend mirror: when the broker hands over an active snapshot but
|
|
19
|
+
`GET /agent/policy/scoped-session` reports NO active session (= revoked or
|
|
20
|
+
expired on the dashboard), the buy refuses with the new
|
|
21
|
+
`pathDFallbackReason: 'session_revoked'`, best-effort calls the broker's
|
|
22
|
+
`clear_policy_snapshot` to purge the dormant key-backed snapshot, and
|
|
23
|
+
falls back to the Path C deep-link. A transient mirror-fetch ERROR is
|
|
24
|
+
still treated as best-effort (not "revoked") so a backend blip doesn't
|
|
25
|
+
break Path D. The authoritative enforcement is the server-side
|
|
26
|
+
`encrypt-shares` gate (backend change, same release window); this MCP
|
|
27
|
+
check is the fast, clear fail before the encrypt round-trip. SecEng
|
|
28
|
+
investigation 2026-05-24.
|
|
29
|
+
|
|
30
|
+
## [0.4.1] — 2026-05-24
|
|
31
|
+
|
|
32
|
+
### Added
|
|
33
|
+
|
|
34
|
+
- **Default broker chain RPC + `--broker-rpc-url` override (OPEN-D
|
|
35
|
+
follow-up).** The broker daemon needs a chain RPC URL for the Path D
|
|
36
|
+
`currentNonce()` pre-check. Previously, with neither
|
|
37
|
+
`MUHAVEN_BROKER_RPC_URL` nor `MUHAVEN_BUNDLER_URL` set, `chainRpcUrl`
|
|
38
|
+
was undefined → the `current_nonce` IPC returned `chain_rpc_failed` and
|
|
39
|
+
Path D fell back to the deep-link. Now:
|
|
40
|
+
- `loadBrokerConfig` **defaults `chainRpcUrl` to the public Arb Sepolia
|
|
41
|
+
RPC** (`https://sepolia-rollup.arbitrum.io/rpc`) when no RPC env is
|
|
42
|
+
set, so Path D works out-of-the-box. Resolution order is unchanged
|
|
43
|
+
where set: `MUHAVEN_BROKER_RPC_URL` → `MUHAVEN_BUNDLER_URL` →
|
|
44
|
+
default. (The broker's only use is a read-only `eth_call`, which the
|
|
45
|
+
public RPC serves.)
|
|
46
|
+
- `muhaven-broker start` / `update` / `setup` gained a
|
|
47
|
+
**`--broker-rpc-url <URL>`** flag to point the spawned daemon at a
|
|
48
|
+
private/faster RPC without exporting an env var. Validated by the same
|
|
49
|
+
https-or-loopback rule as the other URL flags; forwarded into the
|
|
50
|
+
spawned daemon's child env. When omitted, the daemon inherits a
|
|
51
|
+
shell-set value or falls back to the default above.
|
|
52
|
+
|
|
53
|
+
### Changed
|
|
54
|
+
|
|
55
|
+
- `BrokerRuntimeConfig.chainRpcUrl` is now always populated (default
|
|
56
|
+
applied) rather than possibly `undefined`. The type stays optional for
|
|
57
|
+
test-injected configs; the `current_nonce` handler's
|
|
58
|
+
`chain_rpc_failed`-when-unset path remains as a backstop.
|
|
59
|
+
|
|
10
60
|
## [0.4.0] — 2026-05-24
|
|
11
61
|
|
|
12
62
|
### Added
|
package/dist/broker.cjs
CHANGED
|
@@ -18,6 +18,7 @@ var DEFAULT_BROKER_MAX_BYTES = 64 * 1024;
|
|
|
18
18
|
var DEFAULT_JWT_CACHE_TTL_SEC = 30;
|
|
19
19
|
var DEFAULT_BUNDLER_TIMEOUT_MS = 2e4;
|
|
20
20
|
var DEFAULT_CHAIN_ID = 421614;
|
|
21
|
+
var DEFAULT_BROKER_RPC_URL = "https://sepolia-rollup.arbitrum.io/rpc";
|
|
21
22
|
var DEFAULT_ENTRY_POINT_ADDRESS = "0x0000000071727De22E5E9d8BAf0edAc6f37da032";
|
|
22
23
|
var ADDRESS_HEX_RE = /^0x[0-9a-fA-F]{40}$/;
|
|
23
24
|
function defaultBrokerEndpoint() {
|
|
@@ -163,8 +164,8 @@ function loadBrokerConfig(env = process.env) {
|
|
|
163
164
|
env.MUHAVEN_DASHBOARD_URL,
|
|
164
165
|
DEFAULT_DASHBOARD_URL
|
|
165
166
|
);
|
|
166
|
-
const chainRpcUrlRaw = readEnv("MUHAVEN_BROKER_RPC_URL", env) ?? readEnv("MUHAVEN_BUNDLER_URL", env);
|
|
167
|
-
const chainRpcUrl =
|
|
167
|
+
const chainRpcUrlRaw = readEnv("MUHAVEN_BROKER_RPC_URL", env) ?? readEnv("MUHAVEN_BUNDLER_URL", env) ?? DEFAULT_BROKER_RPC_URL;
|
|
168
|
+
const chainRpcUrl = resolvePublicUrlEnv(
|
|
168
169
|
"MUHAVEN_BROKER_RPC_URL",
|
|
169
170
|
chainRpcUrlRaw,
|
|
170
171
|
chainRpcUrlRaw
|
|
@@ -2568,6 +2569,7 @@ function parseSetupFlags(argv) {
|
|
|
2568
2569
|
let brokerEndpoint;
|
|
2569
2570
|
let backendBaseUrl;
|
|
2570
2571
|
let dashboardBaseUrl;
|
|
2572
|
+
let brokerRpcUrl;
|
|
2571
2573
|
let skipLogin = false;
|
|
2572
2574
|
const register = [];
|
|
2573
2575
|
let registerScope = "user";
|
|
@@ -2579,6 +2581,7 @@ function parseSetupFlags(argv) {
|
|
|
2579
2581
|
else if (a === "--broker-endpoint" && i + 1 < argv.length) brokerEndpoint = argv[++i];
|
|
2580
2582
|
else if (a === "--backend-base-url" && i + 1 < argv.length) backendBaseUrl = argv[++i];
|
|
2581
2583
|
else if (a === "--dashboard-base-url" && i + 1 < argv.length) dashboardBaseUrl = argv[++i];
|
|
2584
|
+
else if (a === "--broker-rpc-url" && i + 1 < argv.length) brokerRpcUrl = argv[++i];
|
|
2582
2585
|
else if (a === "--register" && i + 1 < argv.length) {
|
|
2583
2586
|
const value = argv[++i];
|
|
2584
2587
|
for (const raw of value.split(",")) {
|
|
@@ -2609,6 +2612,7 @@ function parseSetupFlags(argv) {
|
|
|
2609
2612
|
brokerEndpoint,
|
|
2610
2613
|
backendBaseUrl,
|
|
2611
2614
|
dashboardBaseUrl,
|
|
2615
|
+
brokerRpcUrl,
|
|
2612
2616
|
skipLogin,
|
|
2613
2617
|
register,
|
|
2614
2618
|
registerScope
|
|
@@ -2732,7 +2736,7 @@ async function runSetup(argv, deps) {
|
|
|
2732
2736
|
} catch (err) {
|
|
2733
2737
|
deps.printErr(`error: ${err.message}`);
|
|
2734
2738
|
deps.printErr(
|
|
2735
|
-
"usage: muhaven-broker setup [--foreground|-f] [--no-launch-browser] [--skip-login]\n [--broker-endpoint PATH] [--backend-base-url URL]\n [--dashboard-base-url URL]\n [--register HOST[,HOST...]] [--register-scope user|project|local]"
|
|
2739
|
+
"usage: muhaven-broker setup [--foreground|-f] [--no-launch-browser] [--skip-login]\n [--broker-endpoint PATH] [--backend-base-url URL]\n [--dashboard-base-url URL] [--broker-rpc-url URL]\n [--register HOST[,HOST...]] [--register-scope user|project|local]"
|
|
2736
2740
|
);
|
|
2737
2741
|
return 2;
|
|
2738
2742
|
}
|
|
@@ -2757,6 +2761,13 @@ async function runSetup(argv, deps) {
|
|
|
2757
2761
|
return 2;
|
|
2758
2762
|
}
|
|
2759
2763
|
}
|
|
2764
|
+
if (flags.brokerRpcUrl) {
|
|
2765
|
+
const err = validateHttpUrlFlag("--broker-rpc-url", flags.brokerRpcUrl);
|
|
2766
|
+
if (err) {
|
|
2767
|
+
deps.printErr(`error: ${err}`);
|
|
2768
|
+
return 2;
|
|
2769
|
+
}
|
|
2770
|
+
}
|
|
2760
2771
|
const overrides = applyEnvDefaults({
|
|
2761
2772
|
env: deps.env,
|
|
2762
2773
|
platformId: deps.platformId,
|
|
@@ -2772,6 +2783,7 @@ async function runSetup(argv, deps) {
|
|
|
2772
2783
|
if (flags.brokerEndpoint) effectiveEnv.MUHAVEN_BROKER_ENDPOINT = flags.brokerEndpoint;
|
|
2773
2784
|
if (flags.backendBaseUrl) effectiveEnv.MUHAVEN_BACKEND_URL = flags.backendBaseUrl;
|
|
2774
2785
|
if (flags.dashboardBaseUrl) effectiveEnv.MUHAVEN_DASHBOARD_URL = flags.dashboardBaseUrl;
|
|
2786
|
+
if (flags.brokerRpcUrl) effectiveEnv.MUHAVEN_BROKER_RPC_URL = flags.brokerRpcUrl;
|
|
2775
2787
|
for (const name of overrides.preserved) {
|
|
2776
2788
|
deps.print(`Env preserved: ${name} (set in your shell)`);
|
|
2777
2789
|
}
|
|
@@ -2854,7 +2866,11 @@ async function runSetup(argv, deps) {
|
|
|
2854
2866
|
MUHAVEN_BROKER_ENDPOINT: config.brokerEndpoint,
|
|
2855
2867
|
MUHAVEN_BACKEND_URL: effectiveEnv.MUHAVEN_BACKEND_URL,
|
|
2856
2868
|
MUHAVEN_DASHBOARD_URL: effectiveEnv.MUHAVEN_DASHBOARD_URL,
|
|
2857
|
-
MUHAVEN_BROKER_SESSION_KEY: sessionKey
|
|
2869
|
+
MUHAVEN_BROKER_SESSION_KEY: sessionKey,
|
|
2870
|
+
// Forward the chain RPC URL only when resolved (flag or shell env);
|
|
2871
|
+
// absent → the daemon's loadBrokerConfig applies the public Arb
|
|
2872
|
+
// Sepolia default.
|
|
2873
|
+
...effectiveEnv.MUHAVEN_BROKER_RPC_URL ? { MUHAVEN_BROKER_RPC_URL: effectiveEnv.MUHAVEN_BROKER_RPC_URL } : {}
|
|
2858
2874
|
}
|
|
2859
2875
|
});
|
|
2860
2876
|
try {
|
|
@@ -3047,6 +3063,7 @@ function parseBringUpFlags(argv) {
|
|
|
3047
3063
|
let brokerEndpoint;
|
|
3048
3064
|
let backendBaseUrl;
|
|
3049
3065
|
let dashboardBaseUrl;
|
|
3066
|
+
let brokerRpcUrl;
|
|
3050
3067
|
for (let i = 0; i < argv.length; i++) {
|
|
3051
3068
|
const a = argv[i];
|
|
3052
3069
|
if (a === "--no-launch-browser") noLaunchBrowser = true;
|
|
@@ -3063,14 +3080,23 @@ function parseBringUpFlags(argv) {
|
|
|
3063
3080
|
} else if (a === "--broker-endpoint" && i + 1 < argv.length) brokerEndpoint = argv[++i];
|
|
3064
3081
|
else if (a === "--backend-base-url" && i + 1 < argv.length) backendBaseUrl = argv[++i];
|
|
3065
3082
|
else if (a === "--dashboard-base-url" && i + 1 < argv.length) dashboardBaseUrl = argv[++i];
|
|
3083
|
+
else if (a === "--broker-rpc-url" && i + 1 < argv.length) brokerRpcUrl = argv[++i];
|
|
3066
3084
|
else throw new Error(`unknown flag: ${a}`);
|
|
3067
3085
|
}
|
|
3068
|
-
return {
|
|
3086
|
+
return {
|
|
3087
|
+
session,
|
|
3088
|
+
noLaunchBrowser,
|
|
3089
|
+
skipLogin,
|
|
3090
|
+
brokerEndpoint,
|
|
3091
|
+
backendBaseUrl,
|
|
3092
|
+
dashboardBaseUrl,
|
|
3093
|
+
brokerRpcUrl
|
|
3094
|
+
};
|
|
3069
3095
|
}
|
|
3070
3096
|
function usageLine(mode) {
|
|
3071
3097
|
return `usage: muhaven-broker ${mode} --session <key|-> [--no-launch-browser] [--skip-login]
|
|
3072
3098
|
[--broker-endpoint PATH] [--backend-base-url URL]
|
|
3073
|
-
[--dashboard-base-url URL]
|
|
3099
|
+
[--dashboard-base-url URL] [--broker-rpc-url URL]
|
|
3074
3100
|
(omit --session to be asked interactively; pipe the key with \`--session -\`)`;
|
|
3075
3101
|
}
|
|
3076
3102
|
async function runBringUp(mode, argv, deps) {
|
|
@@ -3103,6 +3129,13 @@ async function runBringUp(mode, argv, deps) {
|
|
|
3103
3129
|
return 2;
|
|
3104
3130
|
}
|
|
3105
3131
|
}
|
|
3132
|
+
if (flags.brokerRpcUrl) {
|
|
3133
|
+
const e = validateHttpUrlFlag("--broker-rpc-url", flags.brokerRpcUrl);
|
|
3134
|
+
if (e) {
|
|
3135
|
+
deps.printErr(`error: ${e}`);
|
|
3136
|
+
return 2;
|
|
3137
|
+
}
|
|
3138
|
+
}
|
|
3106
3139
|
const resolution = await resolveSessionKey({
|
|
3107
3140
|
sessionFlag: flags.session,
|
|
3108
3141
|
policy: "require",
|
|
@@ -3127,6 +3160,7 @@ async function runBringUp(mode, argv, deps) {
|
|
|
3127
3160
|
if (flags.brokerEndpoint) effectiveEnv.MUHAVEN_BROKER_ENDPOINT = flags.brokerEndpoint;
|
|
3128
3161
|
if (flags.backendBaseUrl) effectiveEnv.MUHAVEN_BACKEND_URL = flags.backendBaseUrl;
|
|
3129
3162
|
if (flags.dashboardBaseUrl) effectiveEnv.MUHAVEN_DASHBOARD_URL = flags.dashboardBaseUrl;
|
|
3163
|
+
if (flags.brokerRpcUrl) effectiveEnv.MUHAVEN_BROKER_RPC_URL = flags.brokerRpcUrl;
|
|
3130
3164
|
for (const name of overrides.preserved) deps.print(`Env preserved: ${name} (set in your shell)`);
|
|
3131
3165
|
for (const [k, v] of Object.entries(overrides.toSet)) deps.print(`Env defaulted: ${k}=${v}`);
|
|
3132
3166
|
const config = loadMcpConfig(effectiveEnv);
|
|
@@ -3167,7 +3201,11 @@ async function runBringUp(mode, argv, deps) {
|
|
|
3167
3201
|
MUHAVEN_BROKER_ENDPOINT: config.brokerEndpoint,
|
|
3168
3202
|
MUHAVEN_BACKEND_URL: effectiveEnv.MUHAVEN_BACKEND_URL,
|
|
3169
3203
|
MUHAVEN_DASHBOARD_URL: effectiveEnv.MUHAVEN_DASHBOARD_URL,
|
|
3170
|
-
MUHAVEN_BROKER_SESSION_KEY: sessionKey
|
|
3204
|
+
MUHAVEN_BROKER_SESSION_KEY: sessionKey,
|
|
3205
|
+
// Forward the chain RPC URL only when resolved (flag or shell env).
|
|
3206
|
+
// When absent, the daemon's loadBrokerConfig applies the public
|
|
3207
|
+
// Arb Sepolia default — no need to inject it here.
|
|
3208
|
+
...effectiveEnv.MUHAVEN_BROKER_RPC_URL ? { MUHAVEN_BROKER_RPC_URL: effectiveEnv.MUHAVEN_BROKER_RPC_URL } : {}
|
|
3171
3209
|
}
|
|
3172
3210
|
});
|
|
3173
3211
|
let ready;
|
|
@@ -3538,8 +3576,10 @@ function printUsage() {
|
|
|
3538
3576
|
print(" start Bring the daemon up on a DASHBOARD-minted session key (daemon NOT running)");
|
|
3539
3577
|
print(" --session <key|-> the key (or `-` to read it from stdin); omit to be");
|
|
3540
3578
|
print(" asked interactively. [--skip-login] [--no-launch-browser]");
|
|
3579
|
+
print(" [--broker-rpc-url URL] chain RPC for Path D (default: public Arb Sepolia)");
|
|
3541
3580
|
print(" update Rotate the session key on a running daemon (stop \u2192 swap \u2192 restart,");
|
|
3542
3581
|
print(" reusing the existing JWT). --session <key|-> (or interactive).");
|
|
3582
|
+
print(" [--broker-rpc-url URL] override the daemon chain RPC for Path D");
|
|
3543
3583
|
print(" stop Cleanly stop a running daemon (SIGTERM with SIGKILL fallback");
|
|
3544
3584
|
print(" after 5s). Also clears the keystore JWT as a best effort.");
|
|
3545
3585
|
print(" login Acquire a JWT via the device-code flow + store in keystore");
|
|
@@ -3551,7 +3591,7 @@ function printUsage() {
|
|
|
3551
3591
|
}
|
|
3552
3592
|
function getBrokerPackageVersion() {
|
|
3553
3593
|
{
|
|
3554
|
-
return "0.4.
|
|
3594
|
+
return "0.4.2";
|
|
3555
3595
|
}
|
|
3556
3596
|
}
|
|
3557
3597
|
function printVersion() {
|
package/dist/broker.js
CHANGED
|
@@ -20,6 +20,7 @@ var DEFAULT_BROKER_MAX_BYTES = 64 * 1024;
|
|
|
20
20
|
var DEFAULT_JWT_CACHE_TTL_SEC = 30;
|
|
21
21
|
var DEFAULT_BUNDLER_TIMEOUT_MS = 2e4;
|
|
22
22
|
var DEFAULT_CHAIN_ID = 421614;
|
|
23
|
+
var DEFAULT_BROKER_RPC_URL = "https://sepolia-rollup.arbitrum.io/rpc";
|
|
23
24
|
var DEFAULT_ENTRY_POINT_ADDRESS = "0x0000000071727De22E5E9d8BAf0edAc6f37da032";
|
|
24
25
|
var ADDRESS_HEX_RE = /^0x[0-9a-fA-F]{40}$/;
|
|
25
26
|
function defaultBrokerEndpoint() {
|
|
@@ -165,8 +166,8 @@ function loadBrokerConfig(env = process.env) {
|
|
|
165
166
|
env.MUHAVEN_DASHBOARD_URL,
|
|
166
167
|
DEFAULT_DASHBOARD_URL
|
|
167
168
|
);
|
|
168
|
-
const chainRpcUrlRaw = readEnv("MUHAVEN_BROKER_RPC_URL", env) ?? readEnv("MUHAVEN_BUNDLER_URL", env);
|
|
169
|
-
const chainRpcUrl =
|
|
169
|
+
const chainRpcUrlRaw = readEnv("MUHAVEN_BROKER_RPC_URL", env) ?? readEnv("MUHAVEN_BUNDLER_URL", env) ?? DEFAULT_BROKER_RPC_URL;
|
|
170
|
+
const chainRpcUrl = resolvePublicUrlEnv(
|
|
170
171
|
"MUHAVEN_BROKER_RPC_URL",
|
|
171
172
|
chainRpcUrlRaw,
|
|
172
173
|
chainRpcUrlRaw
|
|
@@ -2570,6 +2571,7 @@ function parseSetupFlags(argv) {
|
|
|
2570
2571
|
let brokerEndpoint;
|
|
2571
2572
|
let backendBaseUrl;
|
|
2572
2573
|
let dashboardBaseUrl;
|
|
2574
|
+
let brokerRpcUrl;
|
|
2573
2575
|
let skipLogin = false;
|
|
2574
2576
|
const register = [];
|
|
2575
2577
|
let registerScope = "user";
|
|
@@ -2581,6 +2583,7 @@ function parseSetupFlags(argv) {
|
|
|
2581
2583
|
else if (a === "--broker-endpoint" && i + 1 < argv.length) brokerEndpoint = argv[++i];
|
|
2582
2584
|
else if (a === "--backend-base-url" && i + 1 < argv.length) backendBaseUrl = argv[++i];
|
|
2583
2585
|
else if (a === "--dashboard-base-url" && i + 1 < argv.length) dashboardBaseUrl = argv[++i];
|
|
2586
|
+
else if (a === "--broker-rpc-url" && i + 1 < argv.length) brokerRpcUrl = argv[++i];
|
|
2584
2587
|
else if (a === "--register" && i + 1 < argv.length) {
|
|
2585
2588
|
const value = argv[++i];
|
|
2586
2589
|
for (const raw of value.split(",")) {
|
|
@@ -2611,6 +2614,7 @@ function parseSetupFlags(argv) {
|
|
|
2611
2614
|
brokerEndpoint,
|
|
2612
2615
|
backendBaseUrl,
|
|
2613
2616
|
dashboardBaseUrl,
|
|
2617
|
+
brokerRpcUrl,
|
|
2614
2618
|
skipLogin,
|
|
2615
2619
|
register,
|
|
2616
2620
|
registerScope
|
|
@@ -2734,7 +2738,7 @@ async function runSetup(argv, deps) {
|
|
|
2734
2738
|
} catch (err) {
|
|
2735
2739
|
deps.printErr(`error: ${err.message}`);
|
|
2736
2740
|
deps.printErr(
|
|
2737
|
-
"usage: muhaven-broker setup [--foreground|-f] [--no-launch-browser] [--skip-login]\n [--broker-endpoint PATH] [--backend-base-url URL]\n [--dashboard-base-url URL]\n [--register HOST[,HOST...]] [--register-scope user|project|local]"
|
|
2741
|
+
"usage: muhaven-broker setup [--foreground|-f] [--no-launch-browser] [--skip-login]\n [--broker-endpoint PATH] [--backend-base-url URL]\n [--dashboard-base-url URL] [--broker-rpc-url URL]\n [--register HOST[,HOST...]] [--register-scope user|project|local]"
|
|
2738
2742
|
);
|
|
2739
2743
|
return 2;
|
|
2740
2744
|
}
|
|
@@ -2759,6 +2763,13 @@ async function runSetup(argv, deps) {
|
|
|
2759
2763
|
return 2;
|
|
2760
2764
|
}
|
|
2761
2765
|
}
|
|
2766
|
+
if (flags.brokerRpcUrl) {
|
|
2767
|
+
const err = validateHttpUrlFlag("--broker-rpc-url", flags.brokerRpcUrl);
|
|
2768
|
+
if (err) {
|
|
2769
|
+
deps.printErr(`error: ${err}`);
|
|
2770
|
+
return 2;
|
|
2771
|
+
}
|
|
2772
|
+
}
|
|
2762
2773
|
const overrides = applyEnvDefaults({
|
|
2763
2774
|
env: deps.env,
|
|
2764
2775
|
platformId: deps.platformId,
|
|
@@ -2774,6 +2785,7 @@ async function runSetup(argv, deps) {
|
|
|
2774
2785
|
if (flags.brokerEndpoint) effectiveEnv.MUHAVEN_BROKER_ENDPOINT = flags.brokerEndpoint;
|
|
2775
2786
|
if (flags.backendBaseUrl) effectiveEnv.MUHAVEN_BACKEND_URL = flags.backendBaseUrl;
|
|
2776
2787
|
if (flags.dashboardBaseUrl) effectiveEnv.MUHAVEN_DASHBOARD_URL = flags.dashboardBaseUrl;
|
|
2788
|
+
if (flags.brokerRpcUrl) effectiveEnv.MUHAVEN_BROKER_RPC_URL = flags.brokerRpcUrl;
|
|
2777
2789
|
for (const name of overrides.preserved) {
|
|
2778
2790
|
deps.print(`Env preserved: ${name} (set in your shell)`);
|
|
2779
2791
|
}
|
|
@@ -2856,7 +2868,11 @@ async function runSetup(argv, deps) {
|
|
|
2856
2868
|
MUHAVEN_BROKER_ENDPOINT: config.brokerEndpoint,
|
|
2857
2869
|
MUHAVEN_BACKEND_URL: effectiveEnv.MUHAVEN_BACKEND_URL,
|
|
2858
2870
|
MUHAVEN_DASHBOARD_URL: effectiveEnv.MUHAVEN_DASHBOARD_URL,
|
|
2859
|
-
MUHAVEN_BROKER_SESSION_KEY: sessionKey
|
|
2871
|
+
MUHAVEN_BROKER_SESSION_KEY: sessionKey,
|
|
2872
|
+
// Forward the chain RPC URL only when resolved (flag or shell env);
|
|
2873
|
+
// absent → the daemon's loadBrokerConfig applies the public Arb
|
|
2874
|
+
// Sepolia default.
|
|
2875
|
+
...effectiveEnv.MUHAVEN_BROKER_RPC_URL ? { MUHAVEN_BROKER_RPC_URL: effectiveEnv.MUHAVEN_BROKER_RPC_URL } : {}
|
|
2860
2876
|
}
|
|
2861
2877
|
});
|
|
2862
2878
|
try {
|
|
@@ -3049,6 +3065,7 @@ function parseBringUpFlags(argv) {
|
|
|
3049
3065
|
let brokerEndpoint;
|
|
3050
3066
|
let backendBaseUrl;
|
|
3051
3067
|
let dashboardBaseUrl;
|
|
3068
|
+
let brokerRpcUrl;
|
|
3052
3069
|
for (let i = 0; i < argv.length; i++) {
|
|
3053
3070
|
const a = argv[i];
|
|
3054
3071
|
if (a === "--no-launch-browser") noLaunchBrowser = true;
|
|
@@ -3065,14 +3082,23 @@ function parseBringUpFlags(argv) {
|
|
|
3065
3082
|
} else if (a === "--broker-endpoint" && i + 1 < argv.length) brokerEndpoint = argv[++i];
|
|
3066
3083
|
else if (a === "--backend-base-url" && i + 1 < argv.length) backendBaseUrl = argv[++i];
|
|
3067
3084
|
else if (a === "--dashboard-base-url" && i + 1 < argv.length) dashboardBaseUrl = argv[++i];
|
|
3085
|
+
else if (a === "--broker-rpc-url" && i + 1 < argv.length) brokerRpcUrl = argv[++i];
|
|
3068
3086
|
else throw new Error(`unknown flag: ${a}`);
|
|
3069
3087
|
}
|
|
3070
|
-
return {
|
|
3088
|
+
return {
|
|
3089
|
+
session,
|
|
3090
|
+
noLaunchBrowser,
|
|
3091
|
+
skipLogin,
|
|
3092
|
+
brokerEndpoint,
|
|
3093
|
+
backendBaseUrl,
|
|
3094
|
+
dashboardBaseUrl,
|
|
3095
|
+
brokerRpcUrl
|
|
3096
|
+
};
|
|
3071
3097
|
}
|
|
3072
3098
|
function usageLine(mode) {
|
|
3073
3099
|
return `usage: muhaven-broker ${mode} --session <key|-> [--no-launch-browser] [--skip-login]
|
|
3074
3100
|
[--broker-endpoint PATH] [--backend-base-url URL]
|
|
3075
|
-
[--dashboard-base-url URL]
|
|
3101
|
+
[--dashboard-base-url URL] [--broker-rpc-url URL]
|
|
3076
3102
|
(omit --session to be asked interactively; pipe the key with \`--session -\`)`;
|
|
3077
3103
|
}
|
|
3078
3104
|
async function runBringUp(mode, argv, deps) {
|
|
@@ -3105,6 +3131,13 @@ async function runBringUp(mode, argv, deps) {
|
|
|
3105
3131
|
return 2;
|
|
3106
3132
|
}
|
|
3107
3133
|
}
|
|
3134
|
+
if (flags.brokerRpcUrl) {
|
|
3135
|
+
const e = validateHttpUrlFlag("--broker-rpc-url", flags.brokerRpcUrl);
|
|
3136
|
+
if (e) {
|
|
3137
|
+
deps.printErr(`error: ${e}`);
|
|
3138
|
+
return 2;
|
|
3139
|
+
}
|
|
3140
|
+
}
|
|
3108
3141
|
const resolution = await resolveSessionKey({
|
|
3109
3142
|
sessionFlag: flags.session,
|
|
3110
3143
|
policy: "require",
|
|
@@ -3129,6 +3162,7 @@ async function runBringUp(mode, argv, deps) {
|
|
|
3129
3162
|
if (flags.brokerEndpoint) effectiveEnv.MUHAVEN_BROKER_ENDPOINT = flags.brokerEndpoint;
|
|
3130
3163
|
if (flags.backendBaseUrl) effectiveEnv.MUHAVEN_BACKEND_URL = flags.backendBaseUrl;
|
|
3131
3164
|
if (flags.dashboardBaseUrl) effectiveEnv.MUHAVEN_DASHBOARD_URL = flags.dashboardBaseUrl;
|
|
3165
|
+
if (flags.brokerRpcUrl) effectiveEnv.MUHAVEN_BROKER_RPC_URL = flags.brokerRpcUrl;
|
|
3132
3166
|
for (const name of overrides.preserved) deps.print(`Env preserved: ${name} (set in your shell)`);
|
|
3133
3167
|
for (const [k, v] of Object.entries(overrides.toSet)) deps.print(`Env defaulted: ${k}=${v}`);
|
|
3134
3168
|
const config = loadMcpConfig(effectiveEnv);
|
|
@@ -3169,7 +3203,11 @@ async function runBringUp(mode, argv, deps) {
|
|
|
3169
3203
|
MUHAVEN_BROKER_ENDPOINT: config.brokerEndpoint,
|
|
3170
3204
|
MUHAVEN_BACKEND_URL: effectiveEnv.MUHAVEN_BACKEND_URL,
|
|
3171
3205
|
MUHAVEN_DASHBOARD_URL: effectiveEnv.MUHAVEN_DASHBOARD_URL,
|
|
3172
|
-
MUHAVEN_BROKER_SESSION_KEY: sessionKey
|
|
3206
|
+
MUHAVEN_BROKER_SESSION_KEY: sessionKey,
|
|
3207
|
+
// Forward the chain RPC URL only when resolved (flag or shell env).
|
|
3208
|
+
// When absent, the daemon's loadBrokerConfig applies the public
|
|
3209
|
+
// Arb Sepolia default — no need to inject it here.
|
|
3210
|
+
...effectiveEnv.MUHAVEN_BROKER_RPC_URL ? { MUHAVEN_BROKER_RPC_URL: effectiveEnv.MUHAVEN_BROKER_RPC_URL } : {}
|
|
3173
3211
|
}
|
|
3174
3212
|
});
|
|
3175
3213
|
let ready;
|
|
@@ -3540,8 +3578,10 @@ function printUsage() {
|
|
|
3540
3578
|
print(" start Bring the daemon up on a DASHBOARD-minted session key (daemon NOT running)");
|
|
3541
3579
|
print(" --session <key|-> the key (or `-` to read it from stdin); omit to be");
|
|
3542
3580
|
print(" asked interactively. [--skip-login] [--no-launch-browser]");
|
|
3581
|
+
print(" [--broker-rpc-url URL] chain RPC for Path D (default: public Arb Sepolia)");
|
|
3543
3582
|
print(" update Rotate the session key on a running daemon (stop \u2192 swap \u2192 restart,");
|
|
3544
3583
|
print(" reusing the existing JWT). --session <key|-> (or interactive).");
|
|
3584
|
+
print(" [--broker-rpc-url URL] override the daemon chain RPC for Path D");
|
|
3545
3585
|
print(" stop Cleanly stop a running daemon (SIGTERM with SIGKILL fallback");
|
|
3546
3586
|
print(" after 5s). Also clears the keystore JWT as a best effort.");
|
|
3547
3587
|
print(" login Acquire a JWT via the device-code flow + store in keystore");
|
|
@@ -3553,7 +3593,7 @@ function printUsage() {
|
|
|
3553
3593
|
}
|
|
3554
3594
|
function getBrokerPackageVersion() {
|
|
3555
3595
|
{
|
|
3556
|
-
return "0.4.
|
|
3596
|
+
return "0.4.2";
|
|
3557
3597
|
}
|
|
3558
3598
|
}
|
|
3559
3599
|
function printVersion() {
|
package/dist/index.cjs
CHANGED
|
@@ -28,6 +28,7 @@ var DEFAULT_BROKER_MAX_BYTES = 64 * 1024;
|
|
|
28
28
|
var DEFAULT_JWT_CACHE_TTL_SEC = 30;
|
|
29
29
|
var DEFAULT_BUNDLER_TIMEOUT_MS = 2e4;
|
|
30
30
|
var DEFAULT_CHAIN_ID = 421614;
|
|
31
|
+
var DEFAULT_BROKER_RPC_URL = "https://sepolia-rollup.arbitrum.io/rpc";
|
|
31
32
|
var DEFAULT_ENTRY_POINT_ADDRESS = "0x0000000071727De22E5E9d8BAf0edAc6f37da032";
|
|
32
33
|
var ADDRESS_HEX_RE = /^0x[0-9a-fA-F]{40}$/;
|
|
33
34
|
function defaultBrokerEndpoint() {
|
|
@@ -173,8 +174,8 @@ function loadBrokerConfig(env = process.env) {
|
|
|
173
174
|
env.MUHAVEN_DASHBOARD_URL,
|
|
174
175
|
DEFAULT_DASHBOARD_URL
|
|
175
176
|
);
|
|
176
|
-
const chainRpcUrlRaw = readEnv("MUHAVEN_BROKER_RPC_URL", env) ?? readEnv("MUHAVEN_BUNDLER_URL", env);
|
|
177
|
-
const chainRpcUrl =
|
|
177
|
+
const chainRpcUrlRaw = readEnv("MUHAVEN_BROKER_RPC_URL", env) ?? readEnv("MUHAVEN_BUNDLER_URL", env) ?? DEFAULT_BROKER_RPC_URL;
|
|
178
|
+
const chainRpcUrl = resolvePublicUrlEnv(
|
|
178
179
|
"MUHAVEN_BROKER_RPC_URL",
|
|
179
180
|
chainRpcUrlRaw,
|
|
180
181
|
chainRpcUrlRaw
|
|
@@ -2798,18 +2799,33 @@ async function attemptPathD(args, deps) {
|
|
|
2798
2799
|
message: `backend /agent/policy/state lookup failed: ${err2 instanceof Error ? err2.message : String(err2)}`
|
|
2799
2800
|
};
|
|
2800
2801
|
}
|
|
2802
|
+
let mirrorFetchOk = false;
|
|
2803
|
+
let mirrorHadActiveSession = false;
|
|
2801
2804
|
try {
|
|
2802
2805
|
const mirror = await deps.backend.get(
|
|
2803
2806
|
"/api/v1/agent/policy/scoped-session",
|
|
2804
2807
|
{ surface: "mcp" }
|
|
2805
2808
|
);
|
|
2809
|
+
mirrorFetchOk = true;
|
|
2806
2810
|
if (mirror?.session) {
|
|
2811
|
+
mirrorHadActiveSession = true;
|
|
2807
2812
|
mirrorSessionRow = mirror.session;
|
|
2808
2813
|
mirrorEnableStatus = mirror.session.enableStatus ?? null;
|
|
2809
2814
|
mirrorValidatorNonce = mirror.session.validatorNonce ?? null;
|
|
2810
2815
|
}
|
|
2811
2816
|
} catch (err2) {
|
|
2812
2817
|
}
|
|
2818
|
+
if (mirrorFetchOk && !mirrorHadActiveSession) {
|
|
2819
|
+
try {
|
|
2820
|
+
await deps.broker.clearPolicySnapshot(activeId);
|
|
2821
|
+
} catch {
|
|
2822
|
+
}
|
|
2823
|
+
return {
|
|
2824
|
+
kind: "fallback",
|
|
2825
|
+
reason: "session_revoked",
|
|
2826
|
+
message: "the Scoped session was revoked (or expired) on the dashboard \u2014 the broker snapshot is stale; purged it and falling back to Path C. Re-mint a Scoped session to resume autonomous buys."
|
|
2827
|
+
};
|
|
2828
|
+
}
|
|
2813
2829
|
if (!snapshot.permissionId) {
|
|
2814
2830
|
return {
|
|
2815
2831
|
kind: "fallback",
|
|
@@ -3679,7 +3695,7 @@ var SERVER_NAME = "@muhaven/mcp";
|
|
|
3679
3695
|
var SERVER_VERSION = resolveServerVersion();
|
|
3680
3696
|
function resolveServerVersion() {
|
|
3681
3697
|
{
|
|
3682
|
-
return "0.4.
|
|
3698
|
+
return "0.4.2";
|
|
3683
3699
|
}
|
|
3684
3700
|
}
|
|
3685
3701
|
function toJsonInputSchema(schema) {
|
package/dist/index.js
CHANGED
|
@@ -24,6 +24,7 @@ var DEFAULT_BROKER_MAX_BYTES = 64 * 1024;
|
|
|
24
24
|
var DEFAULT_JWT_CACHE_TTL_SEC = 30;
|
|
25
25
|
var DEFAULT_BUNDLER_TIMEOUT_MS = 2e4;
|
|
26
26
|
var DEFAULT_CHAIN_ID = 421614;
|
|
27
|
+
var DEFAULT_BROKER_RPC_URL = "https://sepolia-rollup.arbitrum.io/rpc";
|
|
27
28
|
var DEFAULT_ENTRY_POINT_ADDRESS = "0x0000000071727De22E5E9d8BAf0edAc6f37da032";
|
|
28
29
|
var ADDRESS_HEX_RE = /^0x[0-9a-fA-F]{40}$/;
|
|
29
30
|
function defaultBrokerEndpoint() {
|
|
@@ -169,8 +170,8 @@ function loadBrokerConfig(env = process.env) {
|
|
|
169
170
|
env.MUHAVEN_DASHBOARD_URL,
|
|
170
171
|
DEFAULT_DASHBOARD_URL
|
|
171
172
|
);
|
|
172
|
-
const chainRpcUrlRaw = readEnv("MUHAVEN_BROKER_RPC_URL", env) ?? readEnv("MUHAVEN_BUNDLER_URL", env);
|
|
173
|
-
const chainRpcUrl =
|
|
173
|
+
const chainRpcUrlRaw = readEnv("MUHAVEN_BROKER_RPC_URL", env) ?? readEnv("MUHAVEN_BUNDLER_URL", env) ?? DEFAULT_BROKER_RPC_URL;
|
|
174
|
+
const chainRpcUrl = resolvePublicUrlEnv(
|
|
174
175
|
"MUHAVEN_BROKER_RPC_URL",
|
|
175
176
|
chainRpcUrlRaw,
|
|
176
177
|
chainRpcUrlRaw
|
|
@@ -2794,18 +2795,33 @@ async function attemptPathD(args, deps) {
|
|
|
2794
2795
|
message: `backend /agent/policy/state lookup failed: ${err2 instanceof Error ? err2.message : String(err2)}`
|
|
2795
2796
|
};
|
|
2796
2797
|
}
|
|
2798
|
+
let mirrorFetchOk = false;
|
|
2799
|
+
let mirrorHadActiveSession = false;
|
|
2797
2800
|
try {
|
|
2798
2801
|
const mirror = await deps.backend.get(
|
|
2799
2802
|
"/api/v1/agent/policy/scoped-session",
|
|
2800
2803
|
{ surface: "mcp" }
|
|
2801
2804
|
);
|
|
2805
|
+
mirrorFetchOk = true;
|
|
2802
2806
|
if (mirror?.session) {
|
|
2807
|
+
mirrorHadActiveSession = true;
|
|
2803
2808
|
mirrorSessionRow = mirror.session;
|
|
2804
2809
|
mirrorEnableStatus = mirror.session.enableStatus ?? null;
|
|
2805
2810
|
mirrorValidatorNonce = mirror.session.validatorNonce ?? null;
|
|
2806
2811
|
}
|
|
2807
2812
|
} catch (err2) {
|
|
2808
2813
|
}
|
|
2814
|
+
if (mirrorFetchOk && !mirrorHadActiveSession) {
|
|
2815
|
+
try {
|
|
2816
|
+
await deps.broker.clearPolicySnapshot(activeId);
|
|
2817
|
+
} catch {
|
|
2818
|
+
}
|
|
2819
|
+
return {
|
|
2820
|
+
kind: "fallback",
|
|
2821
|
+
reason: "session_revoked",
|
|
2822
|
+
message: "the Scoped session was revoked (or expired) on the dashboard \u2014 the broker snapshot is stale; purged it and falling back to Path C. Re-mint a Scoped session to resume autonomous buys."
|
|
2823
|
+
};
|
|
2824
|
+
}
|
|
2809
2825
|
if (!snapshot.permissionId) {
|
|
2810
2826
|
return {
|
|
2811
2827
|
kind: "fallback",
|
|
@@ -3675,7 +3691,7 @@ var SERVER_NAME = "@muhaven/mcp";
|
|
|
3675
3691
|
var SERVER_VERSION = resolveServerVersion();
|
|
3676
3692
|
function resolveServerVersion() {
|
|
3677
3693
|
{
|
|
3678
|
-
return "0.4.
|
|
3694
|
+
return "0.4.2";
|
|
3679
3695
|
}
|
|
3680
3696
|
}
|
|
3681
3697
|
function toJsonInputSchema(schema) {
|
package/manifest.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"manifest_version": "0.2",
|
|
4
4
|
"name": "muhaven-mcp",
|
|
5
5
|
"display_name": "MuHaven (RWA portfolio)",
|
|
6
|
-
"version": "0.4.
|
|
6
|
+
"version": "0.4.2",
|
|
7
7
|
"description": "Confidential RWA portfolio management on Fhenix CoFHE. Read your encrypted balances, propose yield claims and policy changes — all signing happens in a sibling broker daemon, the LLM never sees your private key.",
|
|
8
8
|
"long_description": "MuHaven MCP exposes 24 tools across read.* / position.* / policy.* / issuer.* / governance.* groups for managing real-world asset (RWA) tokens with FHE-encrypted balances. Authentication uses a one-time device-code ceremony (run `muhaven-broker login`); subsequent tool calls fetch the JWT from the broker over a Unix socket. Position / governance tools deep-link to the dashboard for passkey signing — they NEVER auto-submit to a bundler. The companion `muhaven-broker` daemon must be running before tools can be invoked. See README for setup.",
|
|
9
9
|
"author": {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@muhaven/mcp",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.2",
|
|
4
4
|
"description": "MuHaven MCP server — read/position/policy toolsets bridging Claude Desktop / Cursor / Claude Code to the MuHaven backend, with a sibling muhaven-broker daemon holding the session-key private half over a local IPC socket",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"repository": {
|