@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 +82 -0
- package/dist/broker.cjs +2 -2
- package/dist/broker.js +2 -2
- package/dist/index.cjs +7 -3
- package/dist/index.js +7 -3
- package/manifest.json +1 -1
- package/package.json +1 -1
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 =
|
|
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.
|
|
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 =
|
|
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.
|
|
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 =
|
|
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
|
|
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.
|
|
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 =
|
|
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
|
|
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.
|
|
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.
|
|
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.
|
|
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": {
|