@muhaven/mcp 0.2.2 → 0.2.3

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,30 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.2.3] — 2026-05-23
11
+
12
+ ### Fixed
13
+
14
+ - **`BundlerClient` now sends an `Origin` header on every RPC.** ZeroDev
15
+ bundler URLs gate access via an IP+domain allowlist; browser requests
16
+ from `https://muhaven.app` pass because the project's allowlist
17
+ accepts that domain, but Node `fetch` (the MCP server's transport)
18
+ sends no `Origin` by default and so hit a `403 "Neither IP nor domain
19
+ is on the allowlist"` on every `eth_call` / `eth_gasPrice`. The MCP
20
+ server then surfaced this as `pathDFallbackReason:
21
+ bundler_setup_failed` and degraded to Path C. Fix: stamp `Origin:
22
+ <MUHAVEN_DASHBOARD_URL>` on every bundler RPC (defaults to
23
+ `https://muhaven.app`, threaded through from
24
+ `buildMcpServer({ dashboardBaseUrl })`). Mirrors how ethers.js + viem
25
+ stamp default Origins against EVM RPC providers; surfaces a single
26
+ knob (`MUHAVEN_DASHBOARD_URL`) for operators on a custom domain.
27
+ Diagnosed 2026-05-23 via direct curl reproduction (bare → 403; with
28
+ `Origin: https://muhaven.app` → `result: 0x66eee`).
29
+
30
+ Added 3 regression tests pinning the contract (Origin sent when set;
31
+ omitted when undefined; omitted when explicitly empty for test
32
+ injection).
33
+
10
34
  ## [0.2.2] — 2026-05-23
11
35
 
12
36
  ### Fixed
package/dist/broker.cjs CHANGED
@@ -2783,7 +2783,7 @@ function printUsage() {
2783
2783
  }
2784
2784
  function getBrokerPackageVersion() {
2785
2785
  {
2786
- return "0.2.2";
2786
+ return "0.2.3";
2787
2787
  }
2788
2788
  }
2789
2789
  function printVersion() {
package/dist/broker.js CHANGED
@@ -2785,7 +2785,7 @@ function printUsage() {
2785
2785
  }
2786
2786
  function getBrokerPackageVersion() {
2787
2787
  {
2788
- return "0.2.2";
2788
+ return "0.2.3";
2789
2789
  }
2790
2790
  }
2791
2791
  function printVersion() {
package/dist/index.cjs CHANGED
@@ -1194,9 +1194,16 @@ var BundlerClient = class {
1194
1194
  const timer = setTimeout(() => ctrl.abort(), this.options.requestTimeoutMs);
1195
1195
  let res;
1196
1196
  try {
1197
+ const headers = {
1198
+ "content-type": "application/json",
1199
+ accept: "application/json"
1200
+ };
1201
+ if (this.options.originHeader) {
1202
+ headers["origin"] = this.options.originHeader;
1203
+ }
1197
1204
  res = await this.fetchImpl(this.options.endpoint, {
1198
1205
  method: "POST",
1199
- headers: { "content-type": "application/json", accept: "application/json" },
1206
+ headers,
1200
1207
  body,
1201
1208
  signal: ctrl.signal
1202
1209
  });
@@ -3025,7 +3032,7 @@ var SERVER_NAME = "@muhaven/mcp";
3025
3032
  var SERVER_VERSION = resolveServerVersion();
3026
3033
  function resolveServerVersion() {
3027
3034
  {
3028
- return "0.2.2";
3035
+ return "0.2.3";
3029
3036
  }
3030
3037
  }
3031
3038
  function toJsonInputSchema(schema) {
@@ -3166,7 +3173,13 @@ async function runMcpStdioCli(opts = {}) {
3166
3173
  const bundler = config.bundlerUrl ? new BundlerClient({
3167
3174
  endpoint: config.bundlerUrl,
3168
3175
  requestTimeoutMs: config.bundlerTimeoutMs,
3169
- expectedChainId: config.chainId
3176
+ expectedChainId: config.chainId,
3177
+ // Wave 5 Path D 0.2.3 — Origin defaults to the dashboard URL
3178
+ // so ZeroDev's domain-allowlist accepts the MCP server's RPC
3179
+ // traffic. The dashboard URL is the natural match because it's
3180
+ // also the SIWE / passkey origin the project already trusts
3181
+ // for browser-side traffic.
3182
+ originHeader: config.dashboardBaseUrl
3170
3183
  }) : void 0;
3171
3184
  const baseRegistry = selectRegistry(config.readOnly);
3172
3185
  const registry = opts.filterRegistry ? opts.filterRegistry(baseRegistry) : baseRegistry;
package/dist/index.d.cts CHANGED
@@ -753,6 +753,24 @@ interface BundlerClientOptions {
753
753
  /** Expected chain id (Arb Sepolia = 421614). When set, `assertChainId()`
754
754
  * refuses to proceed if the bundler reports a different chain. */
755
755
  readonly expectedChainId?: number;
756
+ /**
757
+ * Wave 5 Path D 0.2.3 — `Origin` header sent on every bundler RPC.
758
+ *
759
+ * Why: ZeroDev's bundler URLs gate access via an IP+domain allowlist.
760
+ * Browser requests from `https://muhaven.app` pass because the
761
+ * project's allowlist includes that domain; Node `fetch` (the MCP
762
+ * server's transport) sends no `Origin` header by default and so
763
+ * hits a 403 "Neither IP nor domain is on the allowlist". Sending
764
+ * an `Origin` matching the project's allowlisted domain unblocks
765
+ * the MCP server without requiring an operator-side ZeroDev
766
+ * dashboard edit. Mirrors how ethers.js + viem's HTTP transports
767
+ * stamp a default `Origin` against EVM RPC providers.
768
+ *
769
+ * Defaults to `https://muhaven.app` at the call site
770
+ * (`server.ts::buildMcpServer`) — operators on a custom dashboard
771
+ * URL override via `MUHAVEN_DASHBOARD_URL`.
772
+ */
773
+ readonly originHeader?: string;
756
774
  /** Inject for tests. */
757
775
  readonly fetchImpl?: typeof fetch;
758
776
  }
package/dist/index.d.ts CHANGED
@@ -753,6 +753,24 @@ interface BundlerClientOptions {
753
753
  /** Expected chain id (Arb Sepolia = 421614). When set, `assertChainId()`
754
754
  * refuses to proceed if the bundler reports a different chain. */
755
755
  readonly expectedChainId?: number;
756
+ /**
757
+ * Wave 5 Path D 0.2.3 — `Origin` header sent on every bundler RPC.
758
+ *
759
+ * Why: ZeroDev's bundler URLs gate access via an IP+domain allowlist.
760
+ * Browser requests from `https://muhaven.app` pass because the
761
+ * project's allowlist includes that domain; Node `fetch` (the MCP
762
+ * server's transport) sends no `Origin` header by default and so
763
+ * hits a 403 "Neither IP nor domain is on the allowlist". Sending
764
+ * an `Origin` matching the project's allowlisted domain unblocks
765
+ * the MCP server without requiring an operator-side ZeroDev
766
+ * dashboard edit. Mirrors how ethers.js + viem's HTTP transports
767
+ * stamp a default `Origin` against EVM RPC providers.
768
+ *
769
+ * Defaults to `https://muhaven.app` at the call site
770
+ * (`server.ts::buildMcpServer`) — operators on a custom dashboard
771
+ * URL override via `MUHAVEN_DASHBOARD_URL`.
772
+ */
773
+ readonly originHeader?: string;
756
774
  /** Inject for tests. */
757
775
  readonly fetchImpl?: typeof fetch;
758
776
  }
package/dist/index.js CHANGED
@@ -1190,9 +1190,16 @@ var BundlerClient = class {
1190
1190
  const timer = setTimeout(() => ctrl.abort(), this.options.requestTimeoutMs);
1191
1191
  let res;
1192
1192
  try {
1193
+ const headers = {
1194
+ "content-type": "application/json",
1195
+ accept: "application/json"
1196
+ };
1197
+ if (this.options.originHeader) {
1198
+ headers["origin"] = this.options.originHeader;
1199
+ }
1193
1200
  res = await this.fetchImpl(this.options.endpoint, {
1194
1201
  method: "POST",
1195
- headers: { "content-type": "application/json", accept: "application/json" },
1202
+ headers,
1196
1203
  body,
1197
1204
  signal: ctrl.signal
1198
1205
  });
@@ -3021,7 +3028,7 @@ var SERVER_NAME = "@muhaven/mcp";
3021
3028
  var SERVER_VERSION = resolveServerVersion();
3022
3029
  function resolveServerVersion() {
3023
3030
  {
3024
- return "0.2.2";
3031
+ return "0.2.3";
3025
3032
  }
3026
3033
  }
3027
3034
  function toJsonInputSchema(schema) {
@@ -3162,7 +3169,13 @@ async function runMcpStdioCli(opts = {}) {
3162
3169
  const bundler = config.bundlerUrl ? new BundlerClient({
3163
3170
  endpoint: config.bundlerUrl,
3164
3171
  requestTimeoutMs: config.bundlerTimeoutMs,
3165
- expectedChainId: config.chainId
3172
+ expectedChainId: config.chainId,
3173
+ // Wave 5 Path D 0.2.3 — Origin defaults to the dashboard URL
3174
+ // so ZeroDev's domain-allowlist accepts the MCP server's RPC
3175
+ // traffic. The dashboard URL is the natural match because it's
3176
+ // also the SIWE / passkey origin the project already trusts
3177
+ // for browser-side traffic.
3178
+ originHeader: config.dashboardBaseUrl
3166
3179
  }) : void 0;
3167
3180
  const baseRegistry = selectRegistry(config.readOnly);
3168
3181
  const registry = opts.filterRegistry ? opts.filterRegistry(baseRegistry) : baseRegistry;
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.2.2",
6
+ "version": "0.2.3",
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.2.2",
3
+ "version": "0.2.3",
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": {