@agentwonderland/mcp 0.1.41 → 0.1.42
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/dist/core/__tests__/api-client.test.js +1 -1
- package/dist/core/api-client.js +1 -1
- package/dist/core/ows-adapter.js +58 -3
- package/dist/core/payments.js +52 -41
- package/dist/index.js +1 -1
- package/dist/tools/wallet.js +14 -0
- package/package.json +2 -1
- package/src/core/__tests__/api-client.test.ts +1 -1
- package/src/core/api-client.ts +1 -1
- package/src/core/ows-adapter.ts +67 -3
- package/src/core/payments.ts +52 -40
- package/src/index.ts +1 -1
- package/src/tools/wallet.ts +19 -5
|
@@ -46,7 +46,7 @@ describe("api-client headers", () => {
|
|
|
46
46
|
"X-AW-Consumer-Principal": "did:pkh:solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp:42W2HfLfveSm1T5et9WTLp2CZ2QXdF2EYCUvyJ2gPpxF",
|
|
47
47
|
"X-AW-Rebate-Principal": "did:pkh:eip155:8453:0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
|
|
48
48
|
"X-AW-Surface": "mcp",
|
|
49
|
-
"X-AW-MCP-Version": "0.1.
|
|
49
|
+
"X-AW-MCP-Version": "0.1.42",
|
|
50
50
|
"X-AW-MCP-Tool": "run_agent",
|
|
51
51
|
"X-AW-MCP-Action": "execute",
|
|
52
52
|
}),
|
package/dist/core/api-client.js
CHANGED
|
@@ -12,7 +12,7 @@ export class ApiError extends Error {
|
|
|
12
12
|
this.name = "ApiError";
|
|
13
13
|
}
|
|
14
14
|
}
|
|
15
|
-
const MCP_VERSION = "0.1.
|
|
15
|
+
const MCP_VERSION = "0.1.42";
|
|
16
16
|
function inferToolHeaders(path, method) {
|
|
17
17
|
if (path === "/solve") {
|
|
18
18
|
return { "X-AW-MCP-Tool": "solve", "X-AW-MCP-Action": method === "POST" ? "execute" : "view" };
|
package/dist/core/ows-adapter.js
CHANGED
|
@@ -7,6 +7,9 @@
|
|
|
7
7
|
* The OWS SDK (`@open-wallet-standard/core`) is a NAPI native module.
|
|
8
8
|
* ALL imports are dynamic so the MCP server works without OWS installed.
|
|
9
9
|
*/
|
|
10
|
+
import { createHmac } from "node:crypto";
|
|
11
|
+
import { mnemonicToSeedSync, validateMnemonic } from "@scure/bip39";
|
|
12
|
+
import { wordlist } from "@scure/bip39/wordlists/english";
|
|
10
13
|
// ── Helpers ──────────────────────────────────────────────────────
|
|
11
14
|
const OWS_INSTALL_HINT = "OWS is not installed. Install with: npm install -g @open-wallet-standard/core";
|
|
12
15
|
async function loadOws() {
|
|
@@ -58,6 +61,51 @@ function keypairFromEd25519Hex(privateKeyHex, KeypairCtor) {
|
|
|
58
61
|
}
|
|
59
62
|
throw new Error(`Unsupported ed25519 key length: ${bytes.length} bytes.`);
|
|
60
63
|
}
|
|
64
|
+
function parseExportedSecret(exported) {
|
|
65
|
+
const secret = exported.trim();
|
|
66
|
+
try {
|
|
67
|
+
const parsed = JSON.parse(secret);
|
|
68
|
+
if (parsed.secp256k1 || parsed.ed25519 || parsed.mnemonic)
|
|
69
|
+
return parsed;
|
|
70
|
+
}
|
|
71
|
+
catch {
|
|
72
|
+
// OWS-created wallets export a BIP-39 mnemonic, not a JSON key object.
|
|
73
|
+
}
|
|
74
|
+
const hex = secret.startsWith("0x") ? secret.slice(2) : secret;
|
|
75
|
+
if (/^[0-9a-fA-F]+$/.test(hex)) {
|
|
76
|
+
if (hex.length === 64)
|
|
77
|
+
return { secp256k1: hex, ed25519: hex };
|
|
78
|
+
if (hex.length === 128)
|
|
79
|
+
return { ed25519: hex };
|
|
80
|
+
}
|
|
81
|
+
if (validateMnemonic(secret, wordlist)) {
|
|
82
|
+
return { mnemonic: secret };
|
|
83
|
+
}
|
|
84
|
+
return {};
|
|
85
|
+
}
|
|
86
|
+
function deriveSolanaSeedFromMnemonic(mnemonic, path = "m/44'/501'/0'/0'") {
|
|
87
|
+
const seed = mnemonicToSeedSync(mnemonic);
|
|
88
|
+
let digest = createHmac("sha512", "ed25519 seed").update(seed).digest();
|
|
89
|
+
let key = digest.subarray(0, 32);
|
|
90
|
+
let chainCode = digest.subarray(32);
|
|
91
|
+
for (const part of path.split("/").slice(1)) {
|
|
92
|
+
if (!part.endsWith("'")) {
|
|
93
|
+
throw new Error(`Unsupported non-hardened ed25519 path segment: ${part}`);
|
|
94
|
+
}
|
|
95
|
+
const index = Number(part.slice(0, -1));
|
|
96
|
+
if (!Number.isInteger(index) || index < 0) {
|
|
97
|
+
throw new Error(`Unsupported ed25519 path segment: ${part}`);
|
|
98
|
+
}
|
|
99
|
+
const data = Buffer.alloc(37);
|
|
100
|
+
data[0] = 0;
|
|
101
|
+
Buffer.from(key).copy(data, 1);
|
|
102
|
+
data.writeUInt32BE(index + 0x80000000, 33);
|
|
103
|
+
digest = createHmac("sha512", chainCode).update(data).digest();
|
|
104
|
+
key = digest.subarray(0, 32);
|
|
105
|
+
chainCode = digest.subarray(32);
|
|
106
|
+
}
|
|
107
|
+
return Uint8Array.from(key);
|
|
108
|
+
}
|
|
61
109
|
// ── Public API ───────────────────────────────────────────────────
|
|
62
110
|
/**
|
|
63
111
|
* Check whether the OWS native module can be loaded.
|
|
@@ -160,7 +208,11 @@ export async function owsAccountFromWalletId(walletId, _chain) {
|
|
|
160
208
|
// (EIP-5806 delegate calls) which OWS's native signing can't serialize.
|
|
161
209
|
// OWS provides encrypted storage at rest; viem handles signing in memory.
|
|
162
210
|
const exported = ows.exportWallet(walletId);
|
|
163
|
-
const keys =
|
|
211
|
+
const keys = parseExportedSecret(exported);
|
|
212
|
+
if (keys.mnemonic) {
|
|
213
|
+
const { mnemonicToAccount } = await import("viem/accounts");
|
|
214
|
+
return mnemonicToAccount(keys.mnemonic);
|
|
215
|
+
}
|
|
164
216
|
if (!keys.secp256k1) {
|
|
165
217
|
throw new Error(`Wallet "${wallet.name}" has no secp256k1 key for EVM signing.`);
|
|
166
218
|
}
|
|
@@ -172,11 +224,14 @@ export async function owsSolanaKeypairFromWalletId(walletId) {
|
|
|
172
224
|
const wallet = ows.getWallet(walletId);
|
|
173
225
|
findSolanaAccount(wallet);
|
|
174
226
|
const exported = ows.exportWallet(walletId);
|
|
175
|
-
const keys =
|
|
227
|
+
const keys = parseExportedSecret(exported);
|
|
228
|
+
const { Keypair } = await import("@solana/web3.js");
|
|
229
|
+
if (keys.mnemonic) {
|
|
230
|
+
return Keypair.fromSeed(deriveSolanaSeedFromMnemonic(keys.mnemonic));
|
|
231
|
+
}
|
|
176
232
|
if (!keys.ed25519) {
|
|
177
233
|
throw new Error(`Wallet "${wallet.name}" has no ed25519 key for Solana signing.`);
|
|
178
234
|
}
|
|
179
|
-
const { Keypair } = await import("@solana/web3.js");
|
|
180
235
|
return keypairFromEd25519Hex(keys.ed25519, Keypair);
|
|
181
236
|
}
|
|
182
237
|
export async function getOwsWalletAddress(walletId, chain = "evm") {
|
package/dist/core/payments.js
CHANGED
|
@@ -53,52 +53,42 @@ function clearStaleCardCache(activeKey) {
|
|
|
53
53
|
}
|
|
54
54
|
// ── Per-protocol initializers ───────────────────────────────────
|
|
55
55
|
async function initEvmMppForChain(wallet, chain) {
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
account = await owsAccountFromWalletId(wallet.owsWalletId);
|
|
62
|
-
}
|
|
63
|
-
else if (wallet.key) {
|
|
64
|
-
const { privateKeyToAccount } = await import("viem/accounts");
|
|
65
|
-
account = privateKeyToAccount(normalizeKey(wallet.key));
|
|
66
|
-
}
|
|
67
|
-
else {
|
|
68
|
-
return null;
|
|
69
|
-
}
|
|
70
|
-
const methods = [];
|
|
71
|
-
if (chain === "tempo") {
|
|
72
|
-
const { tempoChargeClient } = await import("./tempo-charge.js");
|
|
73
|
-
methods.push(tempoChargeClient({ account }));
|
|
74
|
-
}
|
|
75
|
-
else {
|
|
76
|
-
const { baseChargeClient } = await import("./base-charge.js");
|
|
77
|
-
methods.push(baseChargeClient({ account }));
|
|
78
|
-
}
|
|
79
|
-
const mppx = Mppx.create({ methods: methods, polyfill: false });
|
|
80
|
-
return mppx.fetch.bind(mppx);
|
|
56
|
+
const { Mppx } = await import("./mpp-client.js");
|
|
57
|
+
let account;
|
|
58
|
+
if (wallet.keyType === "ows" && wallet.owsWalletId) {
|
|
59
|
+
const { owsAccountFromWalletId } = await import("./ows-adapter.js");
|
|
60
|
+
account = await owsAccountFromWalletId(wallet.owsWalletId);
|
|
81
61
|
}
|
|
82
|
-
|
|
62
|
+
else if (wallet.key) {
|
|
63
|
+
const { privateKeyToAccount } = await import("viem/accounts");
|
|
64
|
+
account = privateKeyToAccount(normalizeKey(wallet.key));
|
|
65
|
+
}
|
|
66
|
+
else {
|
|
83
67
|
return null;
|
|
84
68
|
}
|
|
69
|
+
const methods = [];
|
|
70
|
+
if (chain === "tempo") {
|
|
71
|
+
const { tempoChargeClient } = await import("./tempo-charge.js");
|
|
72
|
+
methods.push(tempoChargeClient({ account }));
|
|
73
|
+
}
|
|
74
|
+
else {
|
|
75
|
+
const { baseChargeClient } = await import("./base-charge.js");
|
|
76
|
+
methods.push(baseChargeClient({ account }));
|
|
77
|
+
}
|
|
78
|
+
const mppx = Mppx.create({ methods: methods, polyfill: false });
|
|
79
|
+
return mppx.fetch.bind(mppx);
|
|
85
80
|
}
|
|
86
81
|
async function initSolanaMpp(wallet) {
|
|
87
|
-
|
|
88
|
-
if (wallet.keyType !== "ows" && !wallet.key) {
|
|
89
|
-
return null;
|
|
90
|
-
}
|
|
91
|
-
const { Mppx } = await import("./mpp-client.js");
|
|
92
|
-
const { solanaChargeClient } = await import("./solana-charge.js");
|
|
93
|
-
const mppx = Mppx.create({
|
|
94
|
-
methods: [solanaChargeClient({ wallet })],
|
|
95
|
-
polyfill: false,
|
|
96
|
-
});
|
|
97
|
-
return mppx.fetch.bind(mppx);
|
|
98
|
-
}
|
|
99
|
-
catch {
|
|
82
|
+
if (wallet.keyType !== "ows" && !wallet.key) {
|
|
100
83
|
return null;
|
|
101
84
|
}
|
|
85
|
+
const { Mppx } = await import("./mpp-client.js");
|
|
86
|
+
const { solanaChargeClient } = await import("./solana-charge.js");
|
|
87
|
+
const mppx = Mppx.create({
|
|
88
|
+
methods: [solanaChargeClient({ wallet })],
|
|
89
|
+
polyfill: false,
|
|
90
|
+
});
|
|
91
|
+
return mppx.fetch.bind(mppx);
|
|
102
92
|
}
|
|
103
93
|
async function initCard() {
|
|
104
94
|
const cardConfig = getCardConfig();
|
|
@@ -149,6 +139,10 @@ async function initForChain(wallet, chain) {
|
|
|
149
139
|
}
|
|
150
140
|
return null;
|
|
151
141
|
}
|
|
142
|
+
function initFailureMessage(method, wallet, chain, err) {
|
|
143
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
144
|
+
return `Payment method "${method}" is configured as ${chain} on wallet "${wallet.id}", but the signer could not initialize: ${reason}`;
|
|
145
|
+
}
|
|
152
146
|
// ── Public API ──────────────────────────────────────────────────
|
|
153
147
|
/**
|
|
154
148
|
* Returns a payment-aware fetch for a specific method, or the best
|
|
@@ -185,7 +179,13 @@ export async function getPaymentFetch(method) {
|
|
|
185
179
|
const ck = cacheKey(resolved.wallet.id, resolved.chain);
|
|
186
180
|
if (fetchCache.has(ck))
|
|
187
181
|
return fetchCache.get(ck);
|
|
188
|
-
|
|
182
|
+
let pf;
|
|
183
|
+
try {
|
|
184
|
+
pf = await initForChain(resolved.wallet, resolved.chain);
|
|
185
|
+
}
|
|
186
|
+
catch (err) {
|
|
187
|
+
throw new Error(initFailureMessage(method, resolved.wallet, resolved.chain, err));
|
|
188
|
+
}
|
|
189
189
|
if (pf) {
|
|
190
190
|
fetchCache.set(ck, pf);
|
|
191
191
|
return pf;
|
|
@@ -234,7 +234,18 @@ export async function getPaymentFetch(method) {
|
|
|
234
234
|
const ck = cacheKey(resolved.wallet.id, resolved.chain);
|
|
235
235
|
if (fetchCache.has(ck))
|
|
236
236
|
return fetchCache.get(ck);
|
|
237
|
-
|
|
237
|
+
let pf;
|
|
238
|
+
try {
|
|
239
|
+
pf = await initForChain(resolved.wallet, resolved.chain);
|
|
240
|
+
}
|
|
241
|
+
catch (err) {
|
|
242
|
+
if (m === defaultMethod) {
|
|
243
|
+
const others = configured.filter((x) => x !== m);
|
|
244
|
+
const altText = others.length > 0 ? ` Alternatives: ${others.join(", ")}` : "";
|
|
245
|
+
throw new Error(`${initFailureMessage(m, resolved.wallet, resolved.chain, err)}.${altText}`);
|
|
246
|
+
}
|
|
247
|
+
continue;
|
|
248
|
+
}
|
|
238
249
|
if (pf) {
|
|
239
250
|
fetchCache.set(ck, pf);
|
|
240
251
|
return pf;
|
package/dist/index.js
CHANGED
|
@@ -22,7 +22,7 @@ import { registerJobResources } from "./resources/jobs.js";
|
|
|
22
22
|
// ── Prompts ──────────────────────────────────────────────────────
|
|
23
23
|
import { registerPrompts } from "./prompts/index.js";
|
|
24
24
|
import { runSetupCli } from "./setup.js";
|
|
25
|
-
const PACKAGE_VERSION = "0.1.
|
|
25
|
+
const PACKAGE_VERSION = "0.1.42";
|
|
26
26
|
export async function startMcpServer() {
|
|
27
27
|
const server = new McpServer({
|
|
28
28
|
name: "agentwonderland",
|
package/dist/tools/wallet.js
CHANGED
|
@@ -213,6 +213,8 @@ export function registerWalletTools(server) {
|
|
|
213
213
|
"",
|
|
214
214
|
chainStatus,
|
|
215
215
|
"",
|
|
216
|
+
"No MCP restart is required for wallet changes.",
|
|
217
|
+
"",
|
|
216
218
|
`Fund this address with USDC on ${defaultCh === "solana" ? "Solana" : "Tempo"} to start using agents.`,
|
|
217
219
|
].join("\n"));
|
|
218
220
|
}
|
|
@@ -244,6 +246,8 @@ export function registerWalletTools(server) {
|
|
|
244
246
|
` Chains: solana`,
|
|
245
247
|
` Consumer principal: ${principal}`,
|
|
246
248
|
"",
|
|
249
|
+
"No MCP restart is required for wallet changes.",
|
|
250
|
+
"",
|
|
247
251
|
"Fund this address with USDC on Solana to start using agents.",
|
|
248
252
|
].join("\n") + owsNudge);
|
|
249
253
|
}
|
|
@@ -269,6 +273,8 @@ export function registerWalletTools(server) {
|
|
|
269
273
|
` Chains: tempo, base`,
|
|
270
274
|
` Consumer principal: ${principal}`,
|
|
271
275
|
"",
|
|
276
|
+
"No MCP restart is required for wallet changes.",
|
|
277
|
+
"",
|
|
272
278
|
`Fund this address with USDC on Tempo or Base to start using agents.`,
|
|
273
279
|
].join("\n") + owsNudge);
|
|
274
280
|
}
|
|
@@ -293,6 +299,8 @@ export function registerWalletTools(server) {
|
|
|
293
299
|
` Storage: ~/.ows/ (AES-256-GCM encrypted)`,
|
|
294
300
|
` Consumer principal: ${principal}`,
|
|
295
301
|
"",
|
|
302
|
+
"No MCP restart is required for wallet changes.",
|
|
303
|
+
"",
|
|
296
304
|
`Fund this address with USDC on ${defaultCh === "solana" ? "Solana" : "Tempo"} to start using agents.`,
|
|
297
305
|
"For testnet: npx mppx account fund",
|
|
298
306
|
].join("\n"));
|
|
@@ -317,6 +325,8 @@ export function registerWalletTools(server) {
|
|
|
317
325
|
` Name: ${walletName}`,
|
|
318
326
|
` Chains: ${selectedChains.join(", ")}`,
|
|
319
327
|
` Consumer principal: ${principal}`,
|
|
328
|
+
"",
|
|
329
|
+
"No MCP restart is required for wallet changes.",
|
|
320
330
|
].join("\n"));
|
|
321
331
|
}
|
|
322
332
|
if (defaultCh === "solana") {
|
|
@@ -345,6 +355,8 @@ export function registerWalletTools(server) {
|
|
|
345
355
|
` Name: ${walletName}`,
|
|
346
356
|
` Chains: solana`,
|
|
347
357
|
` Consumer principal: ${principal}`,
|
|
358
|
+
"",
|
|
359
|
+
"No MCP restart is required for wallet changes.",
|
|
348
360
|
].join("\n") + owsNudge);
|
|
349
361
|
}
|
|
350
362
|
catch (err) {
|
|
@@ -376,6 +388,8 @@ export function registerWalletTools(server) {
|
|
|
376
388
|
` Name: ${walletName}`,
|
|
377
389
|
` Chains: ${selectedChains.join(", ")}`,
|
|
378
390
|
` Consumer principal: ${principal}`,
|
|
391
|
+
"",
|
|
392
|
+
"No MCP restart is required for wallet changes.",
|
|
379
393
|
].join("\n") + owsNudge);
|
|
380
394
|
}
|
|
381
395
|
catch {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agentwonderland/mcp",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.42",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "MCP server for the Agent Wonderland AI agent marketplace",
|
|
6
6
|
"bin": {
|
|
@@ -23,6 +23,7 @@
|
|
|
23
23
|
},
|
|
24
24
|
"dependencies": {
|
|
25
25
|
"@modelcontextprotocol/sdk": "^1.12.1",
|
|
26
|
+
"@scure/bip39": "^1.6.0",
|
|
26
27
|
"@solana/spl-token": "^0.4.14",
|
|
27
28
|
"@solana/web3.js": "1.95.8",
|
|
28
29
|
"qrcode": "^1.5.4",
|
|
@@ -72,7 +72,7 @@ describe("api-client headers", () => {
|
|
|
72
72
|
"X-AW-Rebate-Principal":
|
|
73
73
|
"did:pkh:eip155:8453:0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
|
|
74
74
|
"X-AW-Surface": "mcp",
|
|
75
|
-
"X-AW-MCP-Version": "0.1.
|
|
75
|
+
"X-AW-MCP-Version": "0.1.42",
|
|
76
76
|
"X-AW-MCP-Tool": "run_agent",
|
|
77
77
|
"X-AW-MCP-Action": "execute",
|
|
78
78
|
}),
|
package/src/core/api-client.ts
CHANGED
package/src/core/ows-adapter.ts
CHANGED
|
@@ -11,6 +11,9 @@
|
|
|
11
11
|
import type { LocalAccount } from "viem/accounts";
|
|
12
12
|
import type { Hex } from "viem";
|
|
13
13
|
import type { Keypair } from "@solana/web3.js";
|
|
14
|
+
import { createHmac } from "node:crypto";
|
|
15
|
+
import { mnemonicToSeedSync, validateMnemonic } from "@scure/bip39";
|
|
16
|
+
import { wordlist } from "@scure/bip39/wordlists/english";
|
|
14
17
|
|
|
15
18
|
// Note: OWS provides encrypted key storage at rest (~/.ows/, AES-256-GCM).
|
|
16
19
|
// For EVM signing, we export the secp256k1 key and use viem's native
|
|
@@ -155,6 +158,56 @@ function keypairFromEd25519Hex(privateKeyHex: string, KeypairCtor: typeof import
|
|
|
155
158
|
throw new Error(`Unsupported ed25519 key length: ${bytes.length} bytes.`);
|
|
156
159
|
}
|
|
157
160
|
|
|
161
|
+
function parseExportedSecret(exported: string): { secp256k1?: string; ed25519?: string; mnemonic?: string } {
|
|
162
|
+
const secret = exported.trim();
|
|
163
|
+
try {
|
|
164
|
+
const parsed = JSON.parse(secret) as { secp256k1?: string; ed25519?: string; mnemonic?: string };
|
|
165
|
+
if (parsed.secp256k1 || parsed.ed25519 || parsed.mnemonic) return parsed;
|
|
166
|
+
} catch {
|
|
167
|
+
// OWS-created wallets export a BIP-39 mnemonic, not a JSON key object.
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
const hex = secret.startsWith("0x") ? secret.slice(2) : secret;
|
|
171
|
+
if (/^[0-9a-fA-F]+$/.test(hex)) {
|
|
172
|
+
if (hex.length === 64) return { secp256k1: hex, ed25519: hex };
|
|
173
|
+
if (hex.length === 128) return { ed25519: hex };
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
if (validateMnemonic(secret, wordlist)) {
|
|
177
|
+
return { mnemonic: secret };
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
return {};
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
function deriveSolanaSeedFromMnemonic(mnemonic: string, path = "m/44'/501'/0'/0'"): Uint8Array {
|
|
184
|
+
const seed = mnemonicToSeedSync(mnemonic);
|
|
185
|
+
let digest = createHmac("sha512", "ed25519 seed").update(seed).digest();
|
|
186
|
+
let key = digest.subarray(0, 32);
|
|
187
|
+
let chainCode = digest.subarray(32);
|
|
188
|
+
|
|
189
|
+
for (const part of path.split("/").slice(1)) {
|
|
190
|
+
if (!part.endsWith("'")) {
|
|
191
|
+
throw new Error(`Unsupported non-hardened ed25519 path segment: ${part}`);
|
|
192
|
+
}
|
|
193
|
+
const index = Number(part.slice(0, -1));
|
|
194
|
+
if (!Number.isInteger(index) || index < 0) {
|
|
195
|
+
throw new Error(`Unsupported ed25519 path segment: ${part}`);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
const data = Buffer.alloc(37);
|
|
199
|
+
data[0] = 0;
|
|
200
|
+
Buffer.from(key).copy(data, 1);
|
|
201
|
+
data.writeUInt32BE(index + 0x80000000, 33);
|
|
202
|
+
|
|
203
|
+
digest = createHmac("sha512", chainCode).update(data).digest();
|
|
204
|
+
key = digest.subarray(0, 32);
|
|
205
|
+
chainCode = digest.subarray(32);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
return Uint8Array.from(key);
|
|
209
|
+
}
|
|
210
|
+
|
|
158
211
|
// ── Public API ───────────────────────────────────────────────────
|
|
159
212
|
|
|
160
213
|
/**
|
|
@@ -278,7 +331,13 @@ export async function owsAccountFromWalletId(
|
|
|
278
331
|
// (EIP-5806 delegate calls) which OWS's native signing can't serialize.
|
|
279
332
|
// OWS provides encrypted storage at rest; viem handles signing in memory.
|
|
280
333
|
const exported = ows.exportWallet(walletId);
|
|
281
|
-
const keys =
|
|
334
|
+
const keys = parseExportedSecret(exported);
|
|
335
|
+
|
|
336
|
+
if (keys.mnemonic) {
|
|
337
|
+
const { mnemonicToAccount } = await import("viem/accounts");
|
|
338
|
+
return mnemonicToAccount(keys.mnemonic);
|
|
339
|
+
}
|
|
340
|
+
|
|
282
341
|
if (!keys.secp256k1) {
|
|
283
342
|
throw new Error(`Wallet "${wallet.name}" has no secp256k1 key for EVM signing.`);
|
|
284
343
|
}
|
|
@@ -295,12 +354,17 @@ export async function owsSolanaKeypairFromWalletId(
|
|
|
295
354
|
findSolanaAccount(wallet);
|
|
296
355
|
|
|
297
356
|
const exported = ows.exportWallet(walletId);
|
|
298
|
-
const keys =
|
|
357
|
+
const keys = parseExportedSecret(exported);
|
|
358
|
+
const { Keypair } = await import("@solana/web3.js");
|
|
359
|
+
|
|
360
|
+
if (keys.mnemonic) {
|
|
361
|
+
return Keypair.fromSeed(deriveSolanaSeedFromMnemonic(keys.mnemonic));
|
|
362
|
+
}
|
|
363
|
+
|
|
299
364
|
if (!keys.ed25519) {
|
|
300
365
|
throw new Error(`Wallet "${wallet.name}" has no ed25519 key for Solana signing.`);
|
|
301
366
|
}
|
|
302
367
|
|
|
303
|
-
const { Keypair } = await import("@solana/web3.js");
|
|
304
368
|
return keypairFromEd25519Hex(keys.ed25519, Keypair);
|
|
305
369
|
}
|
|
306
370
|
|
package/src/core/payments.ts
CHANGED
|
@@ -76,52 +76,44 @@ async function initEvmMppForChain(
|
|
|
76
76
|
wallet: WalletEntry,
|
|
77
77
|
chain: "tempo" | "base",
|
|
78
78
|
): Promise<typeof fetch | null> {
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
let account;
|
|
82
|
-
|
|
83
|
-
if (wallet.keyType === "ows" && wallet.owsWalletId) {
|
|
84
|
-
const { owsAccountFromWalletId } = await import("./ows-adapter.js");
|
|
85
|
-
account = await owsAccountFromWalletId(wallet.owsWalletId);
|
|
86
|
-
} else if (wallet.key) {
|
|
87
|
-
const { privateKeyToAccount } = await import("viem/accounts");
|
|
88
|
-
account = privateKeyToAccount(normalizeKey(wallet.key));
|
|
89
|
-
} else {
|
|
90
|
-
return null;
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
const methods = [];
|
|
94
|
-
if (chain === "tempo") {
|
|
95
|
-
const { tempoChargeClient } = await import("./tempo-charge.js");
|
|
96
|
-
methods.push(tempoChargeClient({ account }));
|
|
97
|
-
} else {
|
|
98
|
-
const { baseChargeClient } = await import("./base-charge.js");
|
|
99
|
-
methods.push(baseChargeClient({ account }));
|
|
100
|
-
}
|
|
79
|
+
const { Mppx } = await import("./mpp-client.js");
|
|
80
|
+
let account;
|
|
101
81
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
82
|
+
if (wallet.keyType === "ows" && wallet.owsWalletId) {
|
|
83
|
+
const { owsAccountFromWalletId } = await import("./ows-adapter.js");
|
|
84
|
+
account = await owsAccountFromWalletId(wallet.owsWalletId);
|
|
85
|
+
} else if (wallet.key) {
|
|
86
|
+
const { privateKeyToAccount } = await import("viem/accounts");
|
|
87
|
+
account = privateKeyToAccount(normalizeKey(wallet.key));
|
|
88
|
+
} else {
|
|
105
89
|
return null;
|
|
106
90
|
}
|
|
91
|
+
|
|
92
|
+
const methods = [];
|
|
93
|
+
if (chain === "tempo") {
|
|
94
|
+
const { tempoChargeClient } = await import("./tempo-charge.js");
|
|
95
|
+
methods.push(tempoChargeClient({ account }));
|
|
96
|
+
} else {
|
|
97
|
+
const { baseChargeClient } = await import("./base-charge.js");
|
|
98
|
+
methods.push(baseChargeClient({ account }));
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const mppx = Mppx.create({ methods: methods as any, polyfill: false });
|
|
102
|
+
return mppx.fetch.bind(mppx) as typeof fetch;
|
|
107
103
|
}
|
|
108
104
|
|
|
109
105
|
async function initSolanaMpp(wallet: WalletEntry): Promise<typeof fetch | null> {
|
|
110
|
-
|
|
111
|
-
if (wallet.keyType !== "ows" && !wallet.key) {
|
|
112
|
-
return null;
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
const { Mppx } = await import("./mpp-client.js");
|
|
116
|
-
const { solanaChargeClient } = await import("./solana-charge.js");
|
|
117
|
-
const mppx = Mppx.create({
|
|
118
|
-
methods: [solanaChargeClient({ wallet })] as any,
|
|
119
|
-
polyfill: false,
|
|
120
|
-
});
|
|
121
|
-
return mppx.fetch.bind(mppx) as typeof fetch;
|
|
122
|
-
} catch {
|
|
106
|
+
if (wallet.keyType !== "ows" && !wallet.key) {
|
|
123
107
|
return null;
|
|
124
108
|
}
|
|
109
|
+
|
|
110
|
+
const { Mppx } = await import("./mpp-client.js");
|
|
111
|
+
const { solanaChargeClient } = await import("./solana-charge.js");
|
|
112
|
+
const mppx = Mppx.create({
|
|
113
|
+
methods: [solanaChargeClient({ wallet })] as any,
|
|
114
|
+
polyfill: false,
|
|
115
|
+
});
|
|
116
|
+
return mppx.fetch.bind(mppx) as typeof fetch;
|
|
125
117
|
}
|
|
126
118
|
|
|
127
119
|
async function initCard(): Promise<typeof fetch | null> {
|
|
@@ -178,6 +170,11 @@ async function initForChain(wallet: WalletEntry, chain: string): Promise<typeof
|
|
|
178
170
|
return null;
|
|
179
171
|
}
|
|
180
172
|
|
|
173
|
+
function initFailureMessage(method: string, wallet: WalletEntry, chain: string, err: unknown): string {
|
|
174
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
175
|
+
return `Payment method "${method}" is configured as ${chain} on wallet "${wallet.id}", but the signer could not initialize: ${reason}`;
|
|
176
|
+
}
|
|
177
|
+
|
|
181
178
|
// ── Public API ──────────────────────────────────────────────────
|
|
182
179
|
|
|
183
180
|
/**
|
|
@@ -214,7 +211,12 @@ export async function getPaymentFetch(method?: string): Promise<typeof fetch> {
|
|
|
214
211
|
}
|
|
215
212
|
const ck = cacheKey(resolved.wallet.id, resolved.chain);
|
|
216
213
|
if (fetchCache.has(ck)) return fetchCache.get(ck)!;
|
|
217
|
-
|
|
214
|
+
let pf: typeof fetch | null;
|
|
215
|
+
try {
|
|
216
|
+
pf = await initForChain(resolved.wallet, resolved.chain);
|
|
217
|
+
} catch (err) {
|
|
218
|
+
throw new Error(initFailureMessage(method, resolved.wallet, resolved.chain, err));
|
|
219
|
+
}
|
|
218
220
|
if (pf) {
|
|
219
221
|
fetchCache.set(ck, pf);
|
|
220
222
|
return pf;
|
|
@@ -264,7 +266,17 @@ export async function getPaymentFetch(method?: string): Promise<typeof fetch> {
|
|
|
264
266
|
}
|
|
265
267
|
const ck = cacheKey(resolved.wallet.id, resolved.chain);
|
|
266
268
|
if (fetchCache.has(ck)) return fetchCache.get(ck)!;
|
|
267
|
-
|
|
269
|
+
let pf: typeof fetch | null;
|
|
270
|
+
try {
|
|
271
|
+
pf = await initForChain(resolved.wallet, resolved.chain);
|
|
272
|
+
} catch (err) {
|
|
273
|
+
if (m === defaultMethod) {
|
|
274
|
+
const others = configured.filter((x) => x !== m);
|
|
275
|
+
const altText = others.length > 0 ? ` Alternatives: ${others.join(", ")}` : "";
|
|
276
|
+
throw new Error(`${initFailureMessage(m, resolved.wallet, resolved.chain, err)}.${altText}`);
|
|
277
|
+
}
|
|
278
|
+
continue;
|
|
279
|
+
}
|
|
268
280
|
if (pf) {
|
|
269
281
|
fetchCache.set(ck, pf);
|
|
270
282
|
return pf;
|
package/src/index.ts
CHANGED
|
@@ -27,7 +27,7 @@ import { registerJobResources } from "./resources/jobs.js";
|
|
|
27
27
|
import { registerPrompts } from "./prompts/index.js";
|
|
28
28
|
import { runSetupCli } from "./setup.js";
|
|
29
29
|
|
|
30
|
-
const PACKAGE_VERSION = "0.1.
|
|
30
|
+
const PACKAGE_VERSION = "0.1.42";
|
|
31
31
|
|
|
32
32
|
export async function startMcpServer(): Promise<void> {
|
|
33
33
|
const server = new McpServer(
|
package/src/tools/wallet.ts
CHANGED
|
@@ -280,6 +280,8 @@ export function registerWalletTools(server: McpServer): void {
|
|
|
280
280
|
"",
|
|
281
281
|
chainStatus,
|
|
282
282
|
"",
|
|
283
|
+
"No MCP restart is required for wallet changes.",
|
|
284
|
+
"",
|
|
283
285
|
`Fund this address with USDC on ${defaultCh === "solana" ? "Solana" : "Tempo"} to start using agents.`,
|
|
284
286
|
].join("\n"));
|
|
285
287
|
}
|
|
@@ -313,6 +315,8 @@ export function registerWalletTools(server: McpServer): void {
|
|
|
313
315
|
` Chains: solana`,
|
|
314
316
|
` Consumer principal: ${principal}`,
|
|
315
317
|
"",
|
|
318
|
+
"No MCP restart is required for wallet changes.",
|
|
319
|
+
"",
|
|
316
320
|
"Fund this address with USDC on Solana to start using agents.",
|
|
317
321
|
].join("\n") + owsNudge,
|
|
318
322
|
);
|
|
@@ -340,6 +344,8 @@ export function registerWalletTools(server: McpServer): void {
|
|
|
340
344
|
` Chains: tempo, base`,
|
|
341
345
|
` Consumer principal: ${principal}`,
|
|
342
346
|
"",
|
|
347
|
+
"No MCP restart is required for wallet changes.",
|
|
348
|
+
"",
|
|
343
349
|
`Fund this address with USDC on Tempo or Base to start using agents.`,
|
|
344
350
|
].join("\n") + owsNudge,
|
|
345
351
|
);
|
|
@@ -369,6 +375,8 @@ export function registerWalletTools(server: McpServer): void {
|
|
|
369
375
|
` Storage: ~/.ows/ (AES-256-GCM encrypted)`,
|
|
370
376
|
` Consumer principal: ${principal}`,
|
|
371
377
|
"",
|
|
378
|
+
"No MCP restart is required for wallet changes.",
|
|
379
|
+
"",
|
|
372
380
|
`Fund this address with USDC on ${defaultCh === "solana" ? "Solana" : "Tempo"} to start using agents.`,
|
|
373
381
|
"For testnet: npx mppx account fund",
|
|
374
382
|
].join("\n"),
|
|
@@ -395,11 +403,13 @@ export function registerWalletTools(server: McpServer): void {
|
|
|
395
403
|
`Key imported to OWS [encrypted]:`,
|
|
396
404
|
` ID: ${result.walletId}`,
|
|
397
405
|
` Address: ${result.address}`,
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
406
|
+
` Name: ${walletName}`,
|
|
407
|
+
` Chains: ${selectedChains.join(", ")}`,
|
|
408
|
+
` Consumer principal: ${principal}`,
|
|
409
|
+
"",
|
|
410
|
+
"No MCP restart is required for wallet changes.",
|
|
411
|
+
].join("\n"),
|
|
412
|
+
);
|
|
403
413
|
}
|
|
404
414
|
|
|
405
415
|
if (defaultCh === "solana") {
|
|
@@ -429,6 +439,8 @@ export function registerWalletTools(server: McpServer): void {
|
|
|
429
439
|
` Name: ${walletName}`,
|
|
430
440
|
` Chains: solana`,
|
|
431
441
|
` Consumer principal: ${principal}`,
|
|
442
|
+
"",
|
|
443
|
+
"No MCP restart is required for wallet changes.",
|
|
432
444
|
].join("\n") + owsNudge,
|
|
433
445
|
);
|
|
434
446
|
} catch (err) {
|
|
@@ -468,6 +480,8 @@ export function registerWalletTools(server: McpServer): void {
|
|
|
468
480
|
` Name: ${walletName}`,
|
|
469
481
|
` Chains: ${selectedChains.join(", ")}`,
|
|
470
482
|
` Consumer principal: ${principal}`,
|
|
483
|
+
"",
|
|
484
|
+
"No MCP restart is required for wallet changes.",
|
|
471
485
|
].join("\n") + owsNudge,
|
|
472
486
|
);
|
|
473
487
|
} catch {
|