mpp32-mcp-server 1.2.0 → 1.2.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 +41 -0
- package/dist/index.js +1 -1
- package/dist/x402-signers.d.ts +2 -2
- package/dist/x402-signers.js +141 -38
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,47 @@ All notable changes to `mpp32-mcp-server` are documented here. The format
|
|
|
4
4
|
follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and the
|
|
5
5
|
project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
6
6
|
|
|
7
|
+
## [1.2.2] - 2026-05-11
|
|
8
|
+
|
|
9
|
+
### Fixed
|
|
10
|
+
|
|
11
|
+
* **x402 v2 envelope (the whole third-party catalog).** Every third-party
|
|
12
|
+
x402 provider we tested — Venice (`api.venice.ai`), Exa (`api.exa.ai`),
|
|
13
|
+
Firecrawl, OpenAI's x402 gateway, etc. — ships the **v2** challenge
|
|
14
|
+
shape: `{x402Version: 2, accepts: [{scheme, network, asset, payTo,
|
|
15
|
+
amount, ...}, ...]}`. 1.2.0 and 1.2.1 only understood **v1**
|
|
16
|
+
(`{scheme, network, asset, payTo, maxAmountRequired, ...}` at the top
|
|
17
|
+
level), so every third-party call failed with "missing network field"
|
|
18
|
+
before we ever signed anything. The signer now:
|
|
19
|
+
- Detects v1 vs v2 by the presence of `accepts: [...]`.
|
|
20
|
+
- Picks the first `accepts[]` entry it can pay (prefers EVM when
|
|
21
|
+
`MPP32_PRIVATE_KEY` is set, falls back to SVM when only the Solana
|
|
22
|
+
key is set, falls back to the first entry so the per-network error
|
|
23
|
+
surfaces precisely).
|
|
24
|
+
- Maps v2's `amount` to v1's `maxAmountRequired` internally, so the
|
|
25
|
+
same signing code handles both.
|
|
26
|
+
- Echoes the challenge's `x402Version` back in the outgoing envelope
|
|
27
|
+
so servers that key off it (Venice's facilitator does) accept the
|
|
28
|
+
response. v1 challenges still get v1 back; v2 challenges get v2.
|
|
29
|
+
This unblocks paid access to the entire ~4,500-entry federated
|
|
30
|
+
catalog, which is overwhelmingly v2.
|
|
31
|
+
|
|
32
|
+
## [1.2.1] - 2026-05-11
|
|
33
|
+
|
|
34
|
+
### Fixed
|
|
35
|
+
|
|
36
|
+
* **"Server disconnected" on startup under Claude Desktop's bundled Node.**
|
|
37
|
+
1.2.0 imported `@solana/kit` (and `@solana-program/*`) at the top of the
|
|
38
|
+
payment-signer module. Those packages declare `engines.node: ">=20.18.0"`
|
|
39
|
+
and use Node-20-only WebCrypto Ed25519 APIs at load time. Claude Desktop
|
|
40
|
+
ships a bundled Node that on many installs is still 18.x, so the import
|
|
41
|
+
threw before the MCP server could answer the initialize handshake — the
|
|
42
|
+
process exited and Claude Desktop reported only "Server disconnected"
|
|
43
|
+
with no further diagnostics. All Solana and EVM crypto deps are now
|
|
44
|
+
loaded lazily inside the signer functions. The server boots on any Node
|
|
45
|
+
that supports MCP; only a `solana:*` payment attempt fails on too-old
|
|
46
|
+
Node, and now with a clear actionable error.
|
|
47
|
+
|
|
7
48
|
## [1.2.0] - 2026-05-11
|
|
8
49
|
|
|
9
50
|
This release makes x402 payments actually work end-to-end. Prior versions
|
package/dist/index.js
CHANGED
|
@@ -3,7 +3,7 @@ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
|
3
3
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
4
4
|
import { z } from "zod";
|
|
5
5
|
import { signX402Payment } from "./x402-signers.js";
|
|
6
|
-
const SERVER_VERSION = "1.2.
|
|
6
|
+
const SERVER_VERSION = "1.2.2";
|
|
7
7
|
// ── Env loading: trim and sanitize aggressively ─────────────────────────────
|
|
8
8
|
// Copy-paste from Claude Desktop / Cursor / Windsurf JSON config UIs frequently
|
|
9
9
|
// adds trailing \n, \r, NBSP, BOM, or wraps the value in literal quotes. Any
|
package/dist/x402-signers.d.ts
CHANGED
|
@@ -25,8 +25,8 @@ export interface X402PaymentEnvelope {
|
|
|
25
25
|
}
|
|
26
26
|
export declare function isSvmNetwork(network: string): boolean;
|
|
27
27
|
export declare function isEvmNetwork(network: string): boolean;
|
|
28
|
-
export declare function signX402PaymentSvm(requirements: X402PaymentRequirements, rawKey: string, rpcUrlOverride?: string): Promise<string>;
|
|
29
|
-
export declare function signX402PaymentEvm(requirements: X402PaymentRequirements, rawKey: string): Promise<string>;
|
|
28
|
+
export declare function signX402PaymentSvm(requirements: X402PaymentRequirements, rawKey: string, rpcUrlOverride?: string, echoedVersion?: number): Promise<string>;
|
|
29
|
+
export declare function signX402PaymentEvm(requirements: X402PaymentRequirements, rawKey: string, echoedVersion?: number): Promise<string>;
|
|
30
30
|
export interface SignX402Args {
|
|
31
31
|
paymentRequiredHeader: string;
|
|
32
32
|
solanaKey?: string;
|
package/dist/x402-signers.js
CHANGED
|
@@ -17,12 +17,50 @@
|
|
|
17
17
|
// In both cases the outer envelope is
|
|
18
18
|
// { x402Version: 1, scheme: "exact", network, payload: <scheme payload> }
|
|
19
19
|
// base64-encoded into the `X-Payment` HTTP header.
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
import
|
|
23
|
-
import
|
|
24
|
-
import
|
|
25
|
-
import
|
|
20
|
+
async function loadSvmDeps() {
|
|
21
|
+
const [kit, tokenProgram, computeBudgetProgram, bs58Mod, naclMod] = await Promise.all([
|
|
22
|
+
import("@solana/kit"),
|
|
23
|
+
import("@solana-program/token"),
|
|
24
|
+
import("@solana-program/compute-budget"),
|
|
25
|
+
import("bs58"),
|
|
26
|
+
import("tweetnacl"),
|
|
27
|
+
]).catch((err) => {
|
|
28
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
29
|
+
throw new Error(`Could not load Solana signing libraries: ${msg}. ` +
|
|
30
|
+
`Solana x402 payments require Node 20.18 or newer (Claude Desktop's bundled Node may be older). ` +
|
|
31
|
+
`Upgrade Node, or run mpp32-mcp-server under a system Node 20+ via your MCP config's "command".`);
|
|
32
|
+
});
|
|
33
|
+
return {
|
|
34
|
+
address: kit.address,
|
|
35
|
+
createKeyPairSignerFromBytes: kit.createKeyPairSignerFromBytes,
|
|
36
|
+
createSolanaRpc: kit.createSolanaRpc,
|
|
37
|
+
createTransactionMessage: kit.createTransactionMessage,
|
|
38
|
+
setTransactionMessageFeePayer: kit.setTransactionMessageFeePayer,
|
|
39
|
+
setTransactionMessageLifetimeUsingBlockhash: kit.setTransactionMessageLifetimeUsingBlockhash,
|
|
40
|
+
appendTransactionMessageInstructions: kit.appendTransactionMessageInstructions,
|
|
41
|
+
partiallySignTransactionMessageWithSigners: kit.partiallySignTransactionMessageWithSigners,
|
|
42
|
+
getBase64EncodedWireTransaction: kit.getBase64EncodedWireTransaction,
|
|
43
|
+
pipe: kit.pipe,
|
|
44
|
+
getTransferCheckedInstruction: tokenProgram.getTransferCheckedInstruction,
|
|
45
|
+
findAssociatedTokenPda: tokenProgram.findAssociatedTokenPda,
|
|
46
|
+
TOKEN_PROGRAM_ADDRESS: tokenProgram.TOKEN_PROGRAM_ADDRESS,
|
|
47
|
+
getSetComputeUnitLimitInstruction: computeBudgetProgram.getSetComputeUnitLimitInstruction,
|
|
48
|
+
getSetComputeUnitPriceInstruction: computeBudgetProgram.getSetComputeUnitPriceInstruction,
|
|
49
|
+
bs58: bs58Mod.default ?? bs58Mod,
|
|
50
|
+
nacl: naclMod.default ?? naclMod,
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
async function loadEvmDeps() {
|
|
54
|
+
try {
|
|
55
|
+
const viemAccounts = await import("viem/accounts");
|
|
56
|
+
return { privateKeyToAccount: viemAccounts.privateKeyToAccount };
|
|
57
|
+
}
|
|
58
|
+
catch (err) {
|
|
59
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
60
|
+
throw new Error(`Could not load EVM signing libraries: ${msg}. ` +
|
|
61
|
+
`Base / Ethereum x402 payments require Node 18 or newer. Upgrade Node and retry.`);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
26
64
|
// ── Network classification ──────────────────────────────────────────────────
|
|
27
65
|
const SOLANA_MAINNET = "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp";
|
|
28
66
|
const SOLANA_DEVNET = "solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1";
|
|
@@ -46,8 +84,7 @@ function chainSpecFor(network) {
|
|
|
46
84
|
}
|
|
47
85
|
throw new Error(`Unsupported EVM network "${network}". x402 EVM payments currently support: base, base-sepolia, ethereum.`);
|
|
48
86
|
}
|
|
49
|
-
|
|
50
|
-
function decodeSolanaSecret(raw) {
|
|
87
|
+
function decodeSolanaSecret(raw, deps) {
|
|
51
88
|
if (raw.startsWith("[")) {
|
|
52
89
|
const arr = JSON.parse(raw);
|
|
53
90
|
if (!Array.isArray(arr))
|
|
@@ -57,24 +94,24 @@ function decodeSolanaSecret(raw) {
|
|
|
57
94
|
if (/^[0-9a-fA-F]+$/.test(raw) && raw.length % 2 === 0) {
|
|
58
95
|
return new Uint8Array(Buffer.from(raw, "hex"));
|
|
59
96
|
}
|
|
60
|
-
return bs58.decode(raw);
|
|
97
|
+
return deps.bs58.decode(raw);
|
|
61
98
|
}
|
|
62
|
-
async function buildSolanaSigner(rawKey) {
|
|
63
|
-
let bytes = decodeSolanaSecret(rawKey);
|
|
99
|
+
async function buildSolanaSigner(rawKey, deps) {
|
|
100
|
+
let bytes = decodeSolanaSecret(rawKey, deps);
|
|
64
101
|
if (bytes.length === 32) {
|
|
65
102
|
// 32-byte seed — kit's createKeyPairSignerFromBytes wants the 64-byte
|
|
66
103
|
// expanded key. Derive via tweetnacl.
|
|
67
|
-
const kp = nacl.sign.keyPair.fromSeed(bytes);
|
|
104
|
+
const kp = deps.nacl.sign.keyPair.fromSeed(bytes);
|
|
68
105
|
bytes = kp.secretKey;
|
|
69
106
|
}
|
|
70
107
|
else if (bytes.length !== 64) {
|
|
71
108
|
throw new Error(`Solana private key must be a 32-byte seed or a 64-byte expanded key; got ${bytes.length} bytes.`);
|
|
72
109
|
}
|
|
73
|
-
return await createKeyPairSignerFromBytes(bytes);
|
|
110
|
+
return await deps.createKeyPairSignerFromBytes(bytes);
|
|
74
111
|
}
|
|
75
112
|
// ── SVM signer ──────────────────────────────────────────────────────────────
|
|
76
113
|
const DEFAULT_SOLANA_RPC = "https://api.mainnet-beta.solana.com";
|
|
77
|
-
export async function signX402PaymentSvm(requirements, rawKey, rpcUrlOverride) {
|
|
114
|
+
export async function signX402PaymentSvm(requirements, rawKey, rpcUrlOverride, echoedVersion = 1) {
|
|
78
115
|
if (requirements.scheme !== "exact") {
|
|
79
116
|
throw new Error(`SVM x402 scheme "${requirements.scheme}" not implemented; only "exact" is supported.`);
|
|
80
117
|
}
|
|
@@ -86,33 +123,34 @@ export async function signX402PaymentSvm(requirements, rawKey, rpcUrlOverride) {
|
|
|
86
123
|
const amount = BigInt(requirements.maxAmountRequired);
|
|
87
124
|
if (amount <= 0n)
|
|
88
125
|
throw new Error(`Invalid maxAmountRequired: ${requirements.maxAmountRequired}`);
|
|
89
|
-
const
|
|
126
|
+
const deps = await loadSvmDeps();
|
|
127
|
+
const signer = await buildSolanaSigner(rawKey, deps);
|
|
90
128
|
const payerAddress = signer.address;
|
|
91
|
-
const mintAddress = address(requirements.asset);
|
|
92
|
-
const recipientAddress = address(requirements.payTo);
|
|
93
|
-
const feePayerAddress = address(requirements.extra.feePayer);
|
|
129
|
+
const mintAddress = deps.address(requirements.asset);
|
|
130
|
+
const recipientAddress = deps.address(requirements.payTo);
|
|
131
|
+
const feePayerAddress = deps.address(requirements.extra.feePayer);
|
|
94
132
|
// Derive both sides' associated token accounts (classic SPL Token program).
|
|
95
133
|
const [sourceAtaTuple, destinationAtaTuple] = await Promise.all([
|
|
96
|
-
findAssociatedTokenPda({
|
|
134
|
+
deps.findAssociatedTokenPda({
|
|
97
135
|
owner: payerAddress,
|
|
98
136
|
mint: mintAddress,
|
|
99
|
-
tokenProgram: TOKEN_PROGRAM_ADDRESS,
|
|
137
|
+
tokenProgram: deps.TOKEN_PROGRAM_ADDRESS,
|
|
100
138
|
}),
|
|
101
|
-
findAssociatedTokenPda({
|
|
139
|
+
deps.findAssociatedTokenPda({
|
|
102
140
|
owner: recipientAddress,
|
|
103
141
|
mint: mintAddress,
|
|
104
|
-
tokenProgram: TOKEN_PROGRAM_ADDRESS,
|
|
142
|
+
tokenProgram: deps.TOKEN_PROGRAM_ADDRESS,
|
|
105
143
|
}),
|
|
106
144
|
]);
|
|
107
145
|
const sourceAta = sourceAtaTuple[0];
|
|
108
146
|
const destinationAta = destinationAtaTuple[0];
|
|
109
147
|
const rpcUrl = rpcUrlOverride && rpcUrlOverride.length > 0 ? rpcUrlOverride : DEFAULT_SOLANA_RPC;
|
|
110
|
-
const rpc = createSolanaRpc(rpcUrl);
|
|
148
|
+
const rpc = deps.createSolanaRpc(rpcUrl);
|
|
111
149
|
const { value: latestBlockhash } = await rpc.getLatestBlockhash({ commitment: "confirmed" }).send();
|
|
112
150
|
const instructions = [
|
|
113
|
-
getSetComputeUnitLimitInstruction({ units: 150_000 }),
|
|
114
|
-
getSetComputeUnitPriceInstruction({ microLamports: 1000n }),
|
|
115
|
-
getTransferCheckedInstruction({
|
|
151
|
+
deps.getSetComputeUnitLimitInstruction({ units: 150_000 }),
|
|
152
|
+
deps.getSetComputeUnitPriceInstruction({ microLamports: 1000n }),
|
|
153
|
+
deps.getTransferCheckedInstruction({
|
|
116
154
|
source: sourceAta,
|
|
117
155
|
mint: mintAddress,
|
|
118
156
|
destination: destinationAta,
|
|
@@ -121,13 +159,13 @@ export async function signX402PaymentSvm(requirements, rawKey, rpcUrlOverride) {
|
|
|
121
159
|
decimals,
|
|
122
160
|
}),
|
|
123
161
|
];
|
|
124
|
-
const message = pipe(createTransactionMessage({ version: 0 }), (m) => setTransactionMessageFeePayer(feePayerAddress, m), (m) => setTransactionMessageLifetimeUsingBlockhash(latestBlockhash, m), (m) => appendTransactionMessageInstructions(instructions, m));
|
|
162
|
+
const message = deps.pipe(deps.createTransactionMessage({ version: 0 }), (m) => deps.setTransactionMessageFeePayer(feePayerAddress, m), (m) => deps.setTransactionMessageLifetimeUsingBlockhash(latestBlockhash, m), (m) => deps.appendTransactionMessageInstructions(instructions, m));
|
|
125
163
|
// Partially sign — fills the payer's signature slot, leaves the fee payer's
|
|
126
164
|
// slot empty for the facilitator to fill in at /settle time.
|
|
127
|
-
const partiallySigned = await partiallySignTransactionMessageWithSigners(message);
|
|
128
|
-
const base64Tx = getBase64EncodedWireTransaction(partiallySigned);
|
|
165
|
+
const partiallySigned = await deps.partiallySignTransactionMessageWithSigners(message);
|
|
166
|
+
const base64Tx = deps.getBase64EncodedWireTransaction(partiallySigned);
|
|
129
167
|
const envelope = {
|
|
130
|
-
x402Version:
|
|
168
|
+
x402Version: echoedVersion,
|
|
131
169
|
scheme: "exact",
|
|
132
170
|
network: requirements.network,
|
|
133
171
|
payload: { transaction: base64Tx },
|
|
@@ -141,7 +179,7 @@ function randomHex32() {
|
|
|
141
179
|
buf[i] = Math.floor(Math.random() * 256);
|
|
142
180
|
return ("0x" + buf.toString("hex"));
|
|
143
181
|
}
|
|
144
|
-
export async function signX402PaymentEvm(requirements, rawKey) {
|
|
182
|
+
export async function signX402PaymentEvm(requirements, rawKey, echoedVersion = 1) {
|
|
145
183
|
if (requirements.scheme !== "exact") {
|
|
146
184
|
throw new Error(`EVM x402 scheme "${requirements.scheme}" not implemented; only "exact" is supported.`);
|
|
147
185
|
}
|
|
@@ -163,6 +201,7 @@ export async function signX402PaymentEvm(requirements, rawKey) {
|
|
|
163
201
|
if (!/^0x[0-9a-fA-F]{64}$/.test(keyHex)) {
|
|
164
202
|
throw new Error("MPP32_PRIVATE_KEY must be a 64-character hex EVM private key (0x-prefixed or bare).");
|
|
165
203
|
}
|
|
204
|
+
const { privateKeyToAccount } = await loadEvmDeps();
|
|
166
205
|
const account = privateKeyToAccount(keyHex);
|
|
167
206
|
const now = Math.floor(Date.now() / 1000);
|
|
168
207
|
const validAfter = BigInt(0);
|
|
@@ -199,7 +238,7 @@ export async function signX402PaymentEvm(requirements, rawKey) {
|
|
|
199
238
|
message: messageObj,
|
|
200
239
|
});
|
|
201
240
|
const envelope = {
|
|
202
|
-
x402Version:
|
|
241
|
+
x402Version: echoedVersion,
|
|
203
242
|
scheme: "exact",
|
|
204
243
|
network: requirements.network,
|
|
205
244
|
payload: {
|
|
@@ -216,29 +255,93 @@ export async function signX402PaymentEvm(requirements, rawKey) {
|
|
|
216
255
|
};
|
|
217
256
|
return Buffer.from(JSON.stringify(envelope)).toString("base64");
|
|
218
257
|
}
|
|
258
|
+
function isV2Challenge(decoded) {
|
|
259
|
+
return (!!decoded &&
|
|
260
|
+
typeof decoded === "object" &&
|
|
261
|
+
Array.isArray(decoded.accepts));
|
|
262
|
+
}
|
|
263
|
+
// Normalize a v2 `accepts[i]` to our internal requirements shape. v2 uses
|
|
264
|
+
// `amount`, v1 uses `maxAmountRequired` — we map both into the same field so
|
|
265
|
+
// the downstream signers don't have to know which version produced the input.
|
|
266
|
+
function normalizeRequirements(raw) {
|
|
267
|
+
const amount = raw.maxAmountRequired ?? raw.amount ?? "";
|
|
268
|
+
return {
|
|
269
|
+
scheme: String(raw.scheme ?? "exact"),
|
|
270
|
+
network: String(raw.network ?? ""),
|
|
271
|
+
maxAmountRequired: amount,
|
|
272
|
+
resource: String(raw.resource ?? ""),
|
|
273
|
+
description: raw.description,
|
|
274
|
+
mimeType: raw.mimeType,
|
|
275
|
+
payTo: String(raw.payTo ?? ""),
|
|
276
|
+
maxTimeoutSeconds: raw.maxTimeoutSeconds,
|
|
277
|
+
asset: String(raw.asset ?? ""),
|
|
278
|
+
outputSchema: raw.outputSchema,
|
|
279
|
+
extra: raw.extra,
|
|
280
|
+
};
|
|
281
|
+
}
|
|
282
|
+
// Pick the first entry in `accepts` we can actually sign. Preference order:
|
|
283
|
+
// 1. EVM entries when an EVM key is available (Base is the dominant chain).
|
|
284
|
+
// 2. SVM entries when a Solana key is available.
|
|
285
|
+
// If no entry matches the keys we hold, fall back to the first entry and let
|
|
286
|
+
// the per-network signer throw a precise "you need MPP32_X_PRIVATE_KEY" error.
|
|
287
|
+
function pickRequirements(accepts, haveSvm, haveEvm) {
|
|
288
|
+
if (accepts.length === 0) {
|
|
289
|
+
throw new Error("x402 v2 challenge has empty `accepts` array — nothing to pay.");
|
|
290
|
+
}
|
|
291
|
+
if (haveEvm) {
|
|
292
|
+
const evm = accepts.find((a) => isEvmNetwork(a.network));
|
|
293
|
+
if (evm)
|
|
294
|
+
return evm;
|
|
295
|
+
}
|
|
296
|
+
if (haveSvm) {
|
|
297
|
+
const svm = accepts.find((a) => isSvmNetwork(a.network));
|
|
298
|
+
if (svm)
|
|
299
|
+
return svm;
|
|
300
|
+
}
|
|
301
|
+
return accepts[0];
|
|
302
|
+
}
|
|
219
303
|
export async function signX402Payment(args) {
|
|
220
|
-
let
|
|
304
|
+
let decoded;
|
|
221
305
|
try {
|
|
222
306
|
const json = Buffer.from(args.paymentRequiredHeader, "base64").toString("utf-8");
|
|
223
|
-
|
|
307
|
+
decoded = JSON.parse(json);
|
|
224
308
|
}
|
|
225
309
|
catch (err) {
|
|
226
310
|
throw new Error(`Could not decode Payment-Required header as base64 JSON: ${err instanceof Error ? err.message : String(err)}`);
|
|
227
311
|
}
|
|
312
|
+
// Resolve v1 vs v2. v2's `x402Version` field tells the server which envelope
|
|
313
|
+
// shape to expect back — we mirror whichever the challenge used.
|
|
314
|
+
let requirements;
|
|
315
|
+
let echoedVersion;
|
|
316
|
+
if (isV2Challenge(decoded)) {
|
|
317
|
+
const accepts = decoded.accepts
|
|
318
|
+
.filter((a) => !!a && typeof a === "object")
|
|
319
|
+
.map(normalizeRequirements);
|
|
320
|
+
requirements = pickRequirements(accepts, !!args.solanaKey, !!args.evmKey);
|
|
321
|
+
echoedVersion = decoded.x402Version || 2;
|
|
322
|
+
}
|
|
323
|
+
else if (decoded && typeof decoded === "object") {
|
|
324
|
+
requirements = normalizeRequirements(decoded);
|
|
325
|
+
echoedVersion = decoded.x402Version || 1;
|
|
326
|
+
}
|
|
327
|
+
else {
|
|
328
|
+
throw new Error("Decoded Payment-Required is not a JSON object.");
|
|
329
|
+
}
|
|
228
330
|
if (!requirements.network)
|
|
229
331
|
throw new Error("x402 payment requirements missing 'network'");
|
|
230
332
|
if (!requirements.asset)
|
|
231
333
|
throw new Error("x402 payment requirements missing 'asset'");
|
|
232
334
|
if (!requirements.payTo)
|
|
233
335
|
throw new Error("x402 payment requirements missing 'payTo'");
|
|
234
|
-
if (!requirements.maxAmountRequired)
|
|
235
|
-
throw new Error("x402 payment requirements missing 'maxAmountRequired'");
|
|
336
|
+
if (!requirements.maxAmountRequired) {
|
|
337
|
+
throw new Error("x402 payment requirements missing 'maxAmountRequired'/'amount'");
|
|
338
|
+
}
|
|
236
339
|
if (isSvmNetwork(requirements.network)) {
|
|
237
340
|
if (!args.solanaKey) {
|
|
238
341
|
throw new Error(`Provider requires SVM payment on ${requirements.network}, but MPP32_SOLANA_PRIVATE_KEY is not configured. ` +
|
|
239
342
|
`Set it in your MCP config to enable USDC-on-Solana payments.`);
|
|
240
343
|
}
|
|
241
|
-
const header = await signX402PaymentSvm(requirements, args.solanaKey, args.solanaRpcUrl);
|
|
344
|
+
const header = await signX402PaymentSvm(requirements, args.solanaKey, args.solanaRpcUrl, echoedVersion);
|
|
242
345
|
return {
|
|
243
346
|
xPaymentHeader: header,
|
|
244
347
|
network: requirements.network,
|
|
@@ -251,7 +354,7 @@ export async function signX402Payment(args) {
|
|
|
251
354
|
throw new Error(`Provider requires EVM payment on ${requirements.network}, but MPP32_PRIVATE_KEY is not configured. ` +
|
|
252
355
|
`Set it in your MCP config to enable USDC-on-Base payments.`);
|
|
253
356
|
}
|
|
254
|
-
const header = await signX402PaymentEvm(requirements, args.evmKey);
|
|
357
|
+
const header = await signX402PaymentEvm(requirements, args.evmKey, echoedVersion);
|
|
255
358
|
return {
|
|
256
359
|
xPaymentHeader: header,
|
|
257
360
|
network: requirements.network,
|
package/package.json
CHANGED