@agentwonderland/mcp 0.1.41 → 0.1.43

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.
@@ -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.41",
49
+ "X-AW-MCP-Version": "0.1.43",
50
50
  "X-AW-MCP-Tool": "run_agent",
51
51
  "X-AW-MCP-Action": "execute",
52
52
  }),
@@ -20,7 +20,7 @@ describe("MCP setup", () => {
20
20
  expect(next.mcpServers.existing.command).toBe("node");
21
21
  expect(next.mcpServers.agentwonderland).toEqual({
22
22
  command: "npx",
23
- args: ["-y", "@agentwonderland/mcp"],
23
+ args: ["-y", "@agentwonderland/mcp@latest"],
24
24
  });
25
25
  });
26
26
  it("replaces an existing Codex TOML block without disturbing other servers", () => {
@@ -40,7 +40,7 @@ describe("MCP setup", () => {
40
40
  expect(next).toContain("[mcp_servers.other]");
41
41
  expect(next).toContain("[mcp_servers.agentwonderland]");
42
42
  expect(next).toContain('command = "npx"');
43
- expect(next).toContain('args = ["-y", "@agentwonderland/mcp"]');
43
+ expect(next).toContain('args = ["-y", "@agentwonderland/mcp@latest"]');
44
44
  expect(next).not.toContain('command = "old"');
45
45
  expect(next).toContain("[model_provider.openai]");
46
46
  });
@@ -61,7 +61,7 @@ describe("MCP setup", () => {
61
61
  const written = JSON.parse(await readFile(configPath, "utf8"));
62
62
  expect(result.status).toBe("updated");
63
63
  expect(result.backupPath).toBeTruthy();
64
- expect(written.mcpServers.agentwonderland.args).toEqual(["-y", "@agentwonderland/mcp"]);
64
+ expect(written.mcpServers.agentwonderland.args).toEqual(["-y", "@agentwonderland/mcp@latest"]);
65
65
  });
66
66
  it("parses client selection flags", () => {
67
67
  expect(parseSetupArgs(["--clients", "codex,cursor", "--yes"])).toEqual({
@@ -1,6 +1,7 @@
1
1
  import { getApiUrl, getApiKey } from "./config.js";
2
2
  import { getPaymentFetch } from "./payments.js";
3
3
  import { getBaseRebatePrincipal, ensureConsumerPrincipalForMethod, getConsumerPrincipalForMethod, } from "./principal.js";
4
+ import { MCP_PACKAGE_VERSION } from "./version.js";
4
5
  // ── Error class ────────────────────────────────────────────────────
5
6
  export class ApiError extends Error {
6
7
  status;
@@ -12,7 +13,6 @@ export class ApiError extends Error {
12
13
  this.name = "ApiError";
13
14
  }
14
15
  }
15
- const MCP_VERSION = "0.1.41";
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" };
@@ -36,7 +36,7 @@ async function buildHeaders(path, method, options) {
36
36
  "Content-Type": "application/json",
37
37
  Accept: "application/json",
38
38
  "X-AW-Surface": "mcp",
39
- "X-AW-MCP-Version": MCP_VERSION,
39
+ "X-AW-MCP-Version": MCP_PACKAGE_VERSION,
40
40
  ...inferToolHeaders(path, method),
41
41
  };
42
42
  const apiKey = getApiKey();
@@ -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 = JSON.parse(exported);
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 = JSON.parse(exported);
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") {
@@ -53,52 +53,42 @@ function clearStaleCardCache(activeKey) {
53
53
  }
54
54
  // ── Per-protocol initializers ───────────────────────────────────
55
55
  async function initEvmMppForChain(wallet, chain) {
56
- try {
57
- const { Mppx } = await import("./mpp-client.js");
58
- let account;
59
- if (wallet.keyType === "ows" && wallet.owsWalletId) {
60
- const { owsAccountFromWalletId } = await import("./ows-adapter.js");
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
- catch {
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
- try {
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
- const pf = await initForChain(resolved.wallet, resolved.chain);
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
- const pf = await initForChain(resolved.wallet, resolved.chain);
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;
@@ -0,0 +1 @@
1
+ export declare const MCP_PACKAGE_VERSION = "0.1.43";
@@ -0,0 +1 @@
1
+ export const MCP_PACKAGE_VERSION = "0.1.43";
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.41";
25
+ import { MCP_PACKAGE_VERSION } from "./core/version.js";
26
26
  export async function startMcpServer() {
27
27
  const server = new McpServer({
28
28
  name: "agentwonderland",
@@ -115,7 +115,7 @@ if (isCli) {
115
115
  "in a terminal it waits silently for MCP JSON-RPC messages on stdin.",
116
116
  "",
117
117
  "MCP client config:",
118
- ' { "mcpServers": { "agentwonderland": { "command": "npx", "args": ["-y", "@agentwonderland/mcp"] } } }',
118
+ ' { "mcpServers": { "agentwonderland": { "command": "npx", "args": ["-y", "@agentwonderland/mcp@latest"] } } }',
119
119
  "",
120
120
  "Options:",
121
121
  " -h, --help Show this help",
@@ -124,7 +124,7 @@ if (isCli) {
124
124
  process.exit(0);
125
125
  }
126
126
  else if (args.includes("--version") || args.includes("-v")) {
127
- console.log(PACKAGE_VERSION);
127
+ console.log(MCP_PACKAGE_VERSION);
128
128
  process.exit(0);
129
129
  }
130
130
  else {
package/dist/setup.js CHANGED
@@ -8,7 +8,7 @@ import { execFileSync } from "node:child_process";
8
8
  export const SERVER_NAME = "agentwonderland";
9
9
  export const SERVER_CONFIG = {
10
10
  command: "npx",
11
- args: ["-y", "@agentwonderland/mcp"],
11
+ args: ["-y", "@agentwonderland/mcp@latest"],
12
12
  };
13
13
  const CLIENT_ORDER = [
14
14
  "codex",
@@ -272,7 +272,7 @@ export function mergeCodexTomlConfig(existing) {
272
272
  const block = [
273
273
  `[mcp_servers.${SERVER_NAME}]`,
274
274
  `command = "npx"`,
275
- `args = ["-y", "@agentwonderland/mcp"]`,
275
+ `args = ["-y", "@agentwonderland/mcp@latest"]`,
276
276
  "",
277
277
  ].join("\n");
278
278
  const pattern = new RegExp(`\\n?\\[mcp_servers\\.${SERVER_NAME.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}\\][\\s\\S]*?(?=\\n\\[[^\\]]+\\]|$)`);
@@ -6,6 +6,7 @@ import { getSettings } from "../core/settings.js";
6
6
  import { getOrCreatePendingCardSetup, formatCardSetupBlocks, getCardCapabilities, pollCardSetup, } from "../core/card-setup.js";
7
7
  import { isOwsAvailable, createOwsWallet, importKeyToOws, listOwsWallets, listOwsWalletsByChain, installOws, platformSupportsOws, } from "../core/ows-adapter.js";
8
8
  import { ensureConsumerPrincipal, getConsumerPrincipal } from "../core/principal.js";
9
+ import { MCP_PACKAGE_VERSION } from "../core/version.js";
9
10
  function text(t) {
10
11
  return { content: [{ type: "text", text: t }] };
11
12
  }
@@ -21,7 +22,10 @@ export function registerWalletTools(server) {
21
22
  }
22
23
  const settings = await getSettings();
23
24
  const networkLabel = settings ? ` (${settings.network})` : "";
24
- const lines = [`Payment methods${networkLabel}:`];
25
+ const lines = [
26
+ `Agent Wonderland MCP: ${MCP_PACKAGE_VERSION}`,
27
+ `Payment methods${networkLabel}:`,
28
+ ];
25
29
  for (const w of wallets) {
26
30
  const label = w.label ? ` (${w.label})` : "";
27
31
  const storage = w.keyType === "ows" ? " [encrypted]" : " [plaintext]";
@@ -213,6 +217,8 @@ export function registerWalletTools(server) {
213
217
  "",
214
218
  chainStatus,
215
219
  "",
220
+ "No MCP restart is required for wallet changes.",
221
+ "",
216
222
  `Fund this address with USDC on ${defaultCh === "solana" ? "Solana" : "Tempo"} to start using agents.`,
217
223
  ].join("\n"));
218
224
  }
@@ -244,6 +250,8 @@ export function registerWalletTools(server) {
244
250
  ` Chains: solana`,
245
251
  ` Consumer principal: ${principal}`,
246
252
  "",
253
+ "No MCP restart is required for wallet changes.",
254
+ "",
247
255
  "Fund this address with USDC on Solana to start using agents.",
248
256
  ].join("\n") + owsNudge);
249
257
  }
@@ -269,6 +277,8 @@ export function registerWalletTools(server) {
269
277
  ` Chains: tempo, base`,
270
278
  ` Consumer principal: ${principal}`,
271
279
  "",
280
+ "No MCP restart is required for wallet changes.",
281
+ "",
272
282
  `Fund this address with USDC on Tempo or Base to start using agents.`,
273
283
  ].join("\n") + owsNudge);
274
284
  }
@@ -293,6 +303,8 @@ export function registerWalletTools(server) {
293
303
  ` Storage: ~/.ows/ (AES-256-GCM encrypted)`,
294
304
  ` Consumer principal: ${principal}`,
295
305
  "",
306
+ "No MCP restart is required for wallet changes.",
307
+ "",
296
308
  `Fund this address with USDC on ${defaultCh === "solana" ? "Solana" : "Tempo"} to start using agents.`,
297
309
  "For testnet: npx mppx account fund",
298
310
  ].join("\n"));
@@ -317,6 +329,8 @@ export function registerWalletTools(server) {
317
329
  ` Name: ${walletName}`,
318
330
  ` Chains: ${selectedChains.join(", ")}`,
319
331
  ` Consumer principal: ${principal}`,
332
+ "",
333
+ "No MCP restart is required for wallet changes.",
320
334
  ].join("\n"));
321
335
  }
322
336
  if (defaultCh === "solana") {
@@ -345,6 +359,8 @@ export function registerWalletTools(server) {
345
359
  ` Name: ${walletName}`,
346
360
  ` Chains: solana`,
347
361
  ` Consumer principal: ${principal}`,
362
+ "",
363
+ "No MCP restart is required for wallet changes.",
348
364
  ].join("\n") + owsNudge);
349
365
  }
350
366
  catch (err) {
@@ -376,6 +392,8 @@ export function registerWalletTools(server) {
376
392
  ` Name: ${walletName}`,
377
393
  ` Chains: ${selectedChains.join(", ")}`,
378
394
  ` Consumer principal: ${principal}`,
395
+ "",
396
+ "No MCP restart is required for wallet changes.",
379
397
  ].join("\n") + owsNudge);
380
398
  }
381
399
  catch {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agentwonderland/mcp",
3
- "version": "0.1.41",
3
+ "version": "0.1.43",
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.41",
75
+ "X-AW-MCP-Version": "0.1.43",
76
76
  "X-AW-MCP-Tool": "run_agent",
77
77
  "X-AW-MCP-Action": "execute",
78
78
  }),
@@ -30,7 +30,7 @@ describe("MCP setup", () => {
30
30
  expect(next.mcpServers.existing.command).toBe("node");
31
31
  expect(next.mcpServers.agentwonderland).toEqual({
32
32
  command: "npx",
33
- args: ["-y", "@agentwonderland/mcp"],
33
+ args: ["-y", "@agentwonderland/mcp@latest"],
34
34
  });
35
35
  });
36
36
 
@@ -52,7 +52,7 @@ describe("MCP setup", () => {
52
52
  expect(next).toContain("[mcp_servers.other]");
53
53
  expect(next).toContain("[mcp_servers.agentwonderland]");
54
54
  expect(next).toContain('command = "npx"');
55
- expect(next).toContain('args = ["-y", "@agentwonderland/mcp"]');
55
+ expect(next).toContain('args = ["-y", "@agentwonderland/mcp@latest"]');
56
56
  expect(next).not.toContain('command = "old"');
57
57
  expect(next).toContain("[model_provider.openai]");
58
58
  });
@@ -76,7 +76,7 @@ describe("MCP setup", () => {
76
76
 
77
77
  expect(result.status).toBe("updated");
78
78
  expect(result.backupPath).toBeTruthy();
79
- expect(written.mcpServers.agentwonderland.args).toEqual(["-y", "@agentwonderland/mcp"]);
79
+ expect(written.mcpServers.agentwonderland.args).toEqual(["-y", "@agentwonderland/mcp@latest"]);
80
80
  });
81
81
 
82
82
  it("parses client selection flags", () => {
@@ -7,6 +7,7 @@ import {
7
7
  getConsumerPrincipal,
8
8
  getConsumerPrincipalForMethod,
9
9
  } from "./principal.js";
10
+ import { MCP_PACKAGE_VERSION } from "./version.js";
10
11
 
11
12
  // ── Error class ────────────────────────────────────────────────────
12
13
 
@@ -29,8 +30,6 @@ interface RequestOptions {
29
30
  extraHeaders?: Record<string, string>;
30
31
  }
31
32
 
32
- const MCP_VERSION = "0.1.41";
33
-
34
33
  function inferToolHeaders(path: string, method: string): Record<string, string> {
35
34
  if (path === "/solve") {
36
35
  return { "X-AW-MCP-Tool": "solve", "X-AW-MCP-Action": method === "POST" ? "execute" : "view" };
@@ -55,7 +54,7 @@ async function buildHeaders(path: string, method: string, options?: RequestOptio
55
54
  "Content-Type": "application/json",
56
55
  Accept: "application/json",
57
56
  "X-AW-Surface": "mcp",
58
- "X-AW-MCP-Version": MCP_VERSION,
57
+ "X-AW-MCP-Version": MCP_PACKAGE_VERSION,
59
58
  ...inferToolHeaders(path, method),
60
59
  };
61
60
 
@@ -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 = JSON.parse(exported) as { secp256k1?: string; ed25519?: string };
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 = JSON.parse(exported) as { secp256k1?: string; ed25519?: string };
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
 
@@ -76,52 +76,44 @@ async function initEvmMppForChain(
76
76
  wallet: WalletEntry,
77
77
  chain: "tempo" | "base",
78
78
  ): Promise<typeof fetch | null> {
79
- try {
80
- const { Mppx } = await import("./mpp-client.js");
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
- const mppx = Mppx.create({ methods: methods as any, polyfill: false });
103
- return mppx.fetch.bind(mppx) as typeof fetch;
104
- } catch {
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
- try {
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
- const pf = await initForChain(resolved.wallet, resolved.chain);
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
- const pf = await initForChain(resolved.wallet, resolved.chain);
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;
@@ -0,0 +1 @@
1
+ export const MCP_PACKAGE_VERSION = "0.1.43";
package/src/index.ts CHANGED
@@ -26,8 +26,7 @@ import { registerJobResources } from "./resources/jobs.js";
26
26
  // ── Prompts ──────────────────────────────────────────────────────
27
27
  import { registerPrompts } from "./prompts/index.js";
28
28
  import { runSetupCli } from "./setup.js";
29
-
30
- const PACKAGE_VERSION = "0.1.41";
29
+ import { MCP_PACKAGE_VERSION } from "./core/version.js";
31
30
 
32
31
  export async function startMcpServer(): Promise<void> {
33
32
  const server = new McpServer(
@@ -131,7 +130,7 @@ if (isCli) {
131
130
  "in a terminal it waits silently for MCP JSON-RPC messages on stdin.",
132
131
  "",
133
132
  "MCP client config:",
134
- ' { "mcpServers": { "agentwonderland": { "command": "npx", "args": ["-y", "@agentwonderland/mcp"] } } }',
133
+ ' { "mcpServers": { "agentwonderland": { "command": "npx", "args": ["-y", "@agentwonderland/mcp@latest"] } } }',
135
134
  "",
136
135
  "Options:",
137
136
  " -h, --help Show this help",
@@ -139,7 +138,7 @@ if (isCli) {
139
138
  ].join("\n"));
140
139
  process.exit(0);
141
140
  } else if (args.includes("--version") || args.includes("-v")) {
142
- console.log(PACKAGE_VERSION);
141
+ console.log(MCP_PACKAGE_VERSION);
143
142
  process.exit(0);
144
143
  } else {
145
144
  if (process.stdin.isTTY && process.stdout.isTTY) {
package/src/setup.ts CHANGED
@@ -9,7 +9,7 @@ import { execFileSync } from "node:child_process";
9
9
  export const SERVER_NAME = "agentwonderland";
10
10
  export const SERVER_CONFIG = {
11
11
  command: "npx",
12
- args: ["-y", "@agentwonderland/mcp"],
12
+ args: ["-y", "@agentwonderland/mcp@latest"],
13
13
  };
14
14
 
15
15
  export type SetupClientId =
@@ -330,7 +330,7 @@ export function mergeCodexTomlConfig(existing: string): string {
330
330
  const block = [
331
331
  `[mcp_servers.${SERVER_NAME}]`,
332
332
  `command = "npx"`,
333
- `args = ["-y", "@agentwonderland/mcp"]`,
333
+ `args = ["-y", "@agentwonderland/mcp@latest"]`,
334
334
  "",
335
335
  ].join("\n");
336
336
 
@@ -28,6 +28,7 @@ import {
28
28
  platformSupportsOws,
29
29
  } from "../core/ows-adapter.js";
30
30
  import { ensureConsumerPrincipal, getConsumerPrincipal } from "../core/principal.js";
31
+ import { MCP_PACKAGE_VERSION } from "../core/version.js";
31
32
 
32
33
  function text(t: string) {
33
34
  return { content: [{ type: "text" as const, text: t }] };
@@ -53,7 +54,10 @@ export function registerWalletTools(server: McpServer): void {
53
54
 
54
55
  const settings = await getSettings();
55
56
  const networkLabel = settings ? ` (${settings.network})` : "";
56
- const lines = [`Payment methods${networkLabel}:`];
57
+ const lines = [
58
+ `Agent Wonderland MCP: ${MCP_PACKAGE_VERSION}`,
59
+ `Payment methods${networkLabel}:`,
60
+ ];
57
61
 
58
62
  for (const w of wallets) {
59
63
  const label = w.label ? ` (${w.label})` : "";
@@ -280,6 +284,8 @@ export function registerWalletTools(server: McpServer): void {
280
284
  "",
281
285
  chainStatus,
282
286
  "",
287
+ "No MCP restart is required for wallet changes.",
288
+ "",
283
289
  `Fund this address with USDC on ${defaultCh === "solana" ? "Solana" : "Tempo"} to start using agents.`,
284
290
  ].join("\n"));
285
291
  }
@@ -313,6 +319,8 @@ export function registerWalletTools(server: McpServer): void {
313
319
  ` Chains: solana`,
314
320
  ` Consumer principal: ${principal}`,
315
321
  "",
322
+ "No MCP restart is required for wallet changes.",
323
+ "",
316
324
  "Fund this address with USDC on Solana to start using agents.",
317
325
  ].join("\n") + owsNudge,
318
326
  );
@@ -340,6 +348,8 @@ export function registerWalletTools(server: McpServer): void {
340
348
  ` Chains: tempo, base`,
341
349
  ` Consumer principal: ${principal}`,
342
350
  "",
351
+ "No MCP restart is required for wallet changes.",
352
+ "",
343
353
  `Fund this address with USDC on Tempo or Base to start using agents.`,
344
354
  ].join("\n") + owsNudge,
345
355
  );
@@ -369,6 +379,8 @@ export function registerWalletTools(server: McpServer): void {
369
379
  ` Storage: ~/.ows/ (AES-256-GCM encrypted)`,
370
380
  ` Consumer principal: ${principal}`,
371
381
  "",
382
+ "No MCP restart is required for wallet changes.",
383
+ "",
372
384
  `Fund this address with USDC on ${defaultCh === "solana" ? "Solana" : "Tempo"} to start using agents.`,
373
385
  "For testnet: npx mppx account fund",
374
386
  ].join("\n"),
@@ -395,11 +407,13 @@ export function registerWalletTools(server: McpServer): void {
395
407
  `Key imported to OWS [encrypted]:`,
396
408
  ` ID: ${result.walletId}`,
397
409
  ` Address: ${result.address}`,
398
- ` Name: ${walletName}`,
399
- ` Chains: ${selectedChains.join(", ")}`,
400
- ` Consumer principal: ${principal}`,
401
- ].join("\n"),
402
- );
410
+ ` Name: ${walletName}`,
411
+ ` Chains: ${selectedChains.join(", ")}`,
412
+ ` Consumer principal: ${principal}`,
413
+ "",
414
+ "No MCP restart is required for wallet changes.",
415
+ ].join("\n"),
416
+ );
403
417
  }
404
418
 
405
419
  if (defaultCh === "solana") {
@@ -429,6 +443,8 @@ export function registerWalletTools(server: McpServer): void {
429
443
  ` Name: ${walletName}`,
430
444
  ` Chains: solana`,
431
445
  ` Consumer principal: ${principal}`,
446
+ "",
447
+ "No MCP restart is required for wallet changes.",
432
448
  ].join("\n") + owsNudge,
433
449
  );
434
450
  } catch (err) {
@@ -468,6 +484,8 @@ export function registerWalletTools(server: McpServer): void {
468
484
  ` Name: ${walletName}`,
469
485
  ` Chains: ${selectedChains.join(", ")}`,
470
486
  ` Consumer principal: ${principal}`,
487
+ "",
488
+ "No MCP restart is required for wallet changes.",
471
489
  ].join("\n") + owsNudge,
472
490
  );
473
491
  } catch {