@muhaven/mcp 0.2.4 → 0.2.6

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,88 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.2.6] — 2026-05-23
11
+
12
+ ### Fixed
13
+
14
+ - **`PLACEHOLDER_SIGNATURE` uses exact `@zerodev/sdk::DUMMY_ECDSA_SIG`
15
+ bytes** — NOT random `0xfe`-filled high-entropy bytes (the 0.2.5
16
+ regression). Per `@zerodev/permissions::toPermissionValidator.js`,
17
+ the canonical stub signature for paymaster simulation is:
18
+
19
+ concat(["0xff", signer.getDummySignature()])
20
+ ↓ ↓
21
+ "use root permission" DUMMY_ECDSA_SIG = "0xfffffff...7aa...aaa...1c"
22
+
23
+ The DUMMY_ECDSA_SIG is a CRAFTED 65-byte pattern (r is high-end of
24
+ secp256k1's field, s is `7aa...aaa`, v is `0x1c`) that the
25
+ PermissionValidator's `validateUserOp` simulation path recognizes
26
+ as a stub and skips real ecrecover. 0.2.5 had the right length (66
27
+ bytes) and the right `0xff` prefix, but filled the trailing 65
28
+ bytes with random `0xfe` — the validator ecrecovers them as if
29
+ real, gets a garbage address that doesn't match the bound session-
30
+ key, reverts with `AA23` → paymaster returns rpc_error → MCP maps
31
+ to `paymaster_rejected`.
32
+
33
+ The new `pathDFallbackDetail` echo (0.2.5) made this trivially
34
+ diagnosable on the very next smoke iteration — the surfaced
35
+ message was `zd_sponsorUserOperation → HTTP 400 → AA23 reverted`
36
+ which pinned the validator-revert layer.
37
+
38
+ Verified 2026-05-23 against `@zerodev/sdk@5.5.10`'s
39
+ `_cjs/constants.js::DUMMY_ECDSA_SIG` and
40
+ `@zerodev/permissions/_cjs/toPermissionValidator.js::getStubSignature`.
41
+
42
+ Regression tests pin: byte length (66), `0xff` prefix, trailing
43
+ 65-byte byte-for-byte match against DUMMY_ECDSA_SIG, v=0x1c,
44
+ s-component magic pattern (rejecting the 0.2.5 `0xfe`-filled
45
+ shape).
46
+
47
+ ## [0.2.5] — 2026-05-23
48
+
49
+ ### Fixed
50
+
51
+ - **`PLACEHOLDER_SIGNATURE` size 86 bytes → 66 bytes** to match the
52
+ real Kernel v3.1 PermissionValidator signature shape that
53
+ `buildKernelSessionKeySignature` produces:
54
+
55
+ byte 0 — 0xff (PermissionValidator "use root permission" sentinel)
56
+ bytes 1..65 — 65-byte ECDSA
57
+ = 66 bytes total
58
+
59
+ Pre-0.2.5 the placeholder was 86 bytes — the OLD enable-mode shape
60
+ (1 byte prefix + 20 bytes validator + 65 bytes ECDSA). The paymaster
61
+ simulated the validator with the wrong-length signature, the
62
+ validator reverted with `AA23 reverted`, and
63
+ `zd_sponsorUserOperation` returned rpc_error → MCP mapped to
64
+ `paymaster_rejected`. This is the load-bearing piece that 0.2.4 did
65
+ NOT close.
66
+
67
+ ### Added
68
+
69
+ - **`pathDFallbackDetail` in the `muhaven.position.buy` echo.** Every
70
+ Path D fallback (`bundler_setup_failed`, `paymaster_rejected`,
71
+ `encrypt_shares_server_error`, etc.) now carries the underlying
72
+ error message in addition to the structured reason code. Pre-0.2.5
73
+ the message was dropped → every new gate required curl repro to
74
+ find the actual error class (cost ~2 publish cycles during the
75
+ 2026-05-23 smoke). Future fallback iterations are self-diagnosing.
76
+ Untrusted-network input (bundler RPC error messages) is sanitized
77
+ server-side before crossing into the echo (existing
78
+ `sanitizeRpcMessageForLlmContext` boundary).
79
+
80
+ ### Changed
81
+
82
+ - **`DEFAULT_REQUEST_TIMEOUT_MS` 15s → 75s.** The cold-start FHE
83
+ encrypt at `/api/v1/agent/path-d/encrypt-shares` costs ~25s on
84
+ first call after fhe-worker container boot (CoFHE verifier-
85
+ signature handshake). The 15s default cut the MCP-side fetch
86
+ before the backend's reply arrived → spurious
87
+ `encrypt_shares_server_error`. Operators on warm setups can
88
+ tighten via `MUHAVEN_REQUEST_TIMEOUT_MS`. Subsequent encrypts
89
+ after warm-up are sub-second; the 75s ceiling is defensive
90
+ headroom, not steady-state latency.
91
+
10
92
  ## [0.2.4] — 2026-05-23
11
93
 
12
94
  ### Fixed
package/dist/broker.cjs CHANGED
@@ -10,7 +10,7 @@ var crypto = require('crypto');
10
10
 
11
11
  var DEFAULT_BACKEND_URL = "https://api.muhaven.app";
12
12
  var DEFAULT_DASHBOARD_URL = "https://muhaven.app";
13
- var DEFAULT_REQUEST_TIMEOUT_MS = 15e3;
13
+ var DEFAULT_REQUEST_TIMEOUT_MS = 75e3;
14
14
  var DEFAULT_BROKER_TIMEOUT_MS = 5e3;
15
15
  var DEFAULT_BROKER_MAX_BYTES = 64 * 1024;
16
16
  var DEFAULT_JWT_CACHE_TTL_SEC = 30;
@@ -2783,7 +2783,7 @@ function printUsage() {
2783
2783
  }
2784
2784
  function getBrokerPackageVersion() {
2785
2785
  {
2786
- return "0.2.4";
2786
+ return "0.2.6";
2787
2787
  }
2788
2788
  }
2789
2789
  function printVersion() {
package/dist/broker.js CHANGED
@@ -12,7 +12,7 @@ var getDirname = () => path.dirname(getFilename());
12
12
  var __dirname$1 = /* @__PURE__ */ getDirname();
13
13
  var DEFAULT_BACKEND_URL = "https://api.muhaven.app";
14
14
  var DEFAULT_DASHBOARD_URL = "https://muhaven.app";
15
- var DEFAULT_REQUEST_TIMEOUT_MS = 15e3;
15
+ var DEFAULT_REQUEST_TIMEOUT_MS = 75e3;
16
16
  var DEFAULT_BROKER_TIMEOUT_MS = 5e3;
17
17
  var DEFAULT_BROKER_MAX_BYTES = 64 * 1024;
18
18
  var DEFAULT_JWT_CACHE_TTL_SEC = 30;
@@ -2785,7 +2785,7 @@ function printUsage() {
2785
2785
  }
2786
2786
  function getBrokerPackageVersion() {
2787
2787
  {
2788
- return "0.2.4";
2788
+ return "0.2.6";
2789
2789
  }
2790
2790
  }
2791
2791
  function printVersion() {
package/dist/index.cjs CHANGED
@@ -22,7 +22,7 @@ var getImportMetaUrl = () => typeof document === "undefined" ? new URL(`file:${_
22
22
  var importMetaUrl = /* @__PURE__ */ getImportMetaUrl();
23
23
  var DEFAULT_BACKEND_URL = "https://api.muhaven.app";
24
24
  var DEFAULT_DASHBOARD_URL = "https://muhaven.app";
25
- var DEFAULT_REQUEST_TIMEOUT_MS = 15e3;
25
+ var DEFAULT_REQUEST_TIMEOUT_MS = 75e3;
26
26
  var DEFAULT_BROKER_TIMEOUT_MS = 5e3;
27
27
  var DEFAULT_BROKER_MAX_BYTES = 64 * 1024;
28
28
  var DEFAULT_JWT_CACHE_TTL_SEC = 30;
@@ -1923,7 +1923,8 @@ var SUBSCRIPTION_PURCHASE_SELECTOR = viem.toFunctionSelector(
1923
1923
  var SUBSCRIPTION_PURCHASE_ABI = viem.parseAbi([
1924
1924
  "function purchase(address token, (uint256 ctHash, uint8 securityZone, uint8 utype, bytes signature) encShares, uint128 maxSharesHint, address ephemeralEOA)"
1925
1925
  ]);
1926
- var PLACEHOLDER_SIGNATURE = "0x" + "fe".repeat(86);
1926
+ var ZERODEV_DUMMY_ECDSA_SIG = "0xfffffffffffffffffffffffffffffff0000000000000000000000000000000007aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa1c";
1927
+ var PLACEHOLDER_SIGNATURE = `0xff${ZERODEV_DUMMY_ECDSA_SIG.slice(2)}`;
1927
1928
  function ok(data) {
1928
1929
  return { ok: true, data };
1929
1930
  }
@@ -2667,6 +2668,7 @@ async function positionBuy(input, deps) {
2667
2668
  const navDisplay = formatUsd6AsDecimal(navUsd6);
2668
2669
  const sharesStr = shares.toString();
2669
2670
  let pathDFallbackReason;
2671
+ let pathDFallbackDetail;
2670
2672
  let pathDSubmittedUserOpHash;
2671
2673
  const pathD = await attemptPathD(
2672
2674
  { shares, tokenAddress: token.address, tokenSymbol: token.symbol },
@@ -2677,6 +2679,7 @@ async function positionBuy(input, deps) {
2677
2679
  }
2678
2680
  if (pathD.kind === "fallback") {
2679
2681
  pathDFallbackReason = pathD.reason;
2682
+ pathDFallbackDetail = pathD.message;
2680
2683
  if (pathD.submittedUserOpHash) {
2681
2684
  pathDSubmittedUserOpHash = pathD.submittedUserOpHash;
2682
2685
  }
@@ -2702,6 +2705,7 @@ ${dashboardUrl}`,
2702
2705
  effectiveNotionalUsd6: effectiveNotionalUsd6.toString(),
2703
2706
  navUsd6: navUsd6.toString(),
2704
2707
  ...pathDFallbackReason ? { pathDFallbackReason } : {},
2708
+ ...pathDFallbackDetail ? { pathDFallbackDetail } : {},
2705
2709
  ...pathDSubmittedUserOpHash ? { pathDSubmittedUserOpHash } : {}
2706
2710
  }
2707
2711
  });
@@ -3053,7 +3057,7 @@ var SERVER_NAME = "@muhaven/mcp";
3053
3057
  var SERVER_VERSION = resolveServerVersion();
3054
3058
  function resolveServerVersion() {
3055
3059
  {
3056
- return "0.2.4";
3060
+ return "0.2.6";
3057
3061
  }
3058
3062
  }
3059
3063
  function toJsonInputSchema(schema) {
package/dist/index.js CHANGED
@@ -18,7 +18,7 @@ import { privateKeyToAccount } from 'viem/accounts';
18
18
  // src/server.ts
19
19
  var DEFAULT_BACKEND_URL = "https://api.muhaven.app";
20
20
  var DEFAULT_DASHBOARD_URL = "https://muhaven.app";
21
- var DEFAULT_REQUEST_TIMEOUT_MS = 15e3;
21
+ var DEFAULT_REQUEST_TIMEOUT_MS = 75e3;
22
22
  var DEFAULT_BROKER_TIMEOUT_MS = 5e3;
23
23
  var DEFAULT_BROKER_MAX_BYTES = 64 * 1024;
24
24
  var DEFAULT_JWT_CACHE_TTL_SEC = 30;
@@ -1919,7 +1919,8 @@ var SUBSCRIPTION_PURCHASE_SELECTOR = toFunctionSelector(
1919
1919
  var SUBSCRIPTION_PURCHASE_ABI = parseAbi([
1920
1920
  "function purchase(address token, (uint256 ctHash, uint8 securityZone, uint8 utype, bytes signature) encShares, uint128 maxSharesHint, address ephemeralEOA)"
1921
1921
  ]);
1922
- var PLACEHOLDER_SIGNATURE = "0x" + "fe".repeat(86);
1922
+ var ZERODEV_DUMMY_ECDSA_SIG = "0xfffffffffffffffffffffffffffffff0000000000000000000000000000000007aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa1c";
1923
+ var PLACEHOLDER_SIGNATURE = `0xff${ZERODEV_DUMMY_ECDSA_SIG.slice(2)}`;
1923
1924
  function ok(data) {
1924
1925
  return { ok: true, data };
1925
1926
  }
@@ -2663,6 +2664,7 @@ async function positionBuy(input, deps) {
2663
2664
  const navDisplay = formatUsd6AsDecimal(navUsd6);
2664
2665
  const sharesStr = shares.toString();
2665
2666
  let pathDFallbackReason;
2667
+ let pathDFallbackDetail;
2666
2668
  let pathDSubmittedUserOpHash;
2667
2669
  const pathD = await attemptPathD(
2668
2670
  { shares, tokenAddress: token.address, tokenSymbol: token.symbol },
@@ -2673,6 +2675,7 @@ async function positionBuy(input, deps) {
2673
2675
  }
2674
2676
  if (pathD.kind === "fallback") {
2675
2677
  pathDFallbackReason = pathD.reason;
2678
+ pathDFallbackDetail = pathD.message;
2676
2679
  if (pathD.submittedUserOpHash) {
2677
2680
  pathDSubmittedUserOpHash = pathD.submittedUserOpHash;
2678
2681
  }
@@ -2698,6 +2701,7 @@ ${dashboardUrl}`,
2698
2701
  effectiveNotionalUsd6: effectiveNotionalUsd6.toString(),
2699
2702
  navUsd6: navUsd6.toString(),
2700
2703
  ...pathDFallbackReason ? { pathDFallbackReason } : {},
2704
+ ...pathDFallbackDetail ? { pathDFallbackDetail } : {},
2701
2705
  ...pathDSubmittedUserOpHash ? { pathDSubmittedUserOpHash } : {}
2702
2706
  }
2703
2707
  });
@@ -3049,7 +3053,7 @@ var SERVER_NAME = "@muhaven/mcp";
3049
3053
  var SERVER_VERSION = resolveServerVersion();
3050
3054
  function resolveServerVersion() {
3051
3055
  {
3052
- return "0.2.4";
3056
+ return "0.2.6";
3053
3057
  }
3054
3058
  }
3055
3059
  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.2.4",
6
+ "version": "0.2.6",
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.4",
3
+ "version": "0.2.6",
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": {