@agentwonderland/mcp 0.1.28 → 0.1.30

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.
@@ -1,6 +1,6 @@
1
1
  export interface WalletEntry {
2
2
  id: string;
3
- keyType: "evm" | "ows";
3
+ keyType: "evm" | "solana" | "ows";
4
4
  key?: string;
5
5
  owsWalletId?: string;
6
6
  chains: string[];
@@ -13,6 +13,23 @@ import type { Keypair } from "@solana/web3.js";
13
13
  * Check whether the OWS native module can be loaded.
14
14
  */
15
15
  export declare function isOwsAvailable(): Promise<boolean>;
16
+ /**
17
+ * Heuristic: is this platform likely to have a prebuilt OWS binary?
18
+ * OWS ships prebuilds for {darwin, linux, win32} x {x64, arm64}.
19
+ * Musl-libc environments (Alpine) often lack prebuilds.
20
+ */
21
+ export declare function platformSupportsOws(): boolean;
22
+ export interface OwsInstallResult {
23
+ ok: boolean;
24
+ message: string;
25
+ needsRestart?: boolean;
26
+ }
27
+ /**
28
+ * Attempt to install @open-wallet-standard/core into the MCP package's own
29
+ * node_modules. Avoids sudo for npx-based installs; may require sudo for a
30
+ * global MCP install.
31
+ */
32
+ export declare function installOws(): Promise<OwsInstallResult>;
16
33
  /**
17
34
  * Build a viem-compatible `LocalAccount` backed by an OWS wallet.
18
35
  *
@@ -71,6 +71,62 @@ export async function isOwsAvailable() {
71
71
  return false;
72
72
  }
73
73
  }
74
+ /**
75
+ * Heuristic: is this platform likely to have a prebuilt OWS binary?
76
+ * OWS ships prebuilds for {darwin, linux, win32} x {x64, arm64}.
77
+ * Musl-libc environments (Alpine) often lack prebuilds.
78
+ */
79
+ export function platformSupportsOws() {
80
+ const ok = new Set([
81
+ "darwin-x64", "darwin-arm64",
82
+ "linux-x64", "linux-arm64",
83
+ "win32-x64", "win32-arm64",
84
+ ]);
85
+ return ok.has(`${process.platform}-${process.arch}`);
86
+ }
87
+ /**
88
+ * Attempt to install @open-wallet-standard/core into the MCP package's own
89
+ * node_modules. Avoids sudo for npx-based installs; may require sudo for a
90
+ * global MCP install.
91
+ */
92
+ export async function installOws() {
93
+ if (await isOwsAvailable()) {
94
+ return { ok: true, message: "OWS is already installed." };
95
+ }
96
+ if (!platformSupportsOws()) {
97
+ return {
98
+ ok: false,
99
+ message: `OWS does not ship a prebuilt binary for ${process.platform}-${process.arch}. Continue with plaintext storage, or install a Node build toolchain (python3, make, node-gyp) and run: npm install -g @open-wallet-standard/core`,
100
+ };
101
+ }
102
+ const { execFileSync } = await import("node:child_process");
103
+ const { dirname, resolve } = await import("node:path");
104
+ const { fileURLToPath } = await import("node:url");
105
+ // dist/core/ows-adapter.js → package root is ../../
106
+ const pkgRoot = resolve(dirname(fileURLToPath(import.meta.url)), "..", "..");
107
+ try {
108
+ execFileSync(process.platform === "win32" ? "npm.cmd" : "npm", ["install", "@open-wallet-standard/core", "--no-save", "--no-audit", "--no-fund", "--prefer-offline", "--loglevel=error"], { cwd: pkgRoot, stdio: ["ignore", "pipe", "pipe"], timeout: 120_000 });
109
+ }
110
+ catch (err) {
111
+ const e = err;
112
+ const stderr = e.stderr?.toString() ?? "";
113
+ const hint = stderr.includes("EACCES")
114
+ ? " (permission denied — try: sudo npm install -g @open-wallet-standard/core)"
115
+ : "";
116
+ return {
117
+ ok: false,
118
+ message: `npm install failed: ${(e.message ?? "unknown error").split("\n")[0]}${hint}`,
119
+ };
120
+ }
121
+ if (!(await isOwsAvailable())) {
122
+ return {
123
+ ok: false,
124
+ message: "OWS installed but the module could not be loaded. Restart Claude Code (or whichever MCP host) and retry.",
125
+ needsRestart: true,
126
+ };
127
+ }
128
+ return { ok: true, message: "OWS installed and ready." };
129
+ }
74
130
  /**
75
131
  * Build a viem-compatible `LocalAccount` backed by an OWS wallet.
76
132
  *
@@ -368,7 +368,19 @@ export async function getWalletAddress(method) {
368
368
  }
369
369
  }
370
370
  if (chain === "solana") {
371
- return null;
371
+ // Raw Solana ed25519 key path (plaintext fallback when OWS is unavailable)
372
+ if (!wallet.key)
373
+ return null;
374
+ try {
375
+ const { Keypair } = await import("@solana/web3.js");
376
+ const hex = wallet.key.startsWith("0x") ? wallet.key.slice(2) : wallet.key;
377
+ const bytes = Uint8Array.from(Buffer.from(hex, "hex"));
378
+ const kp = bytes.length === 32 ? Keypair.fromSeed(bytes) : Keypair.fromSecretKey(bytes);
379
+ return kp.publicKey.toBase58();
380
+ }
381
+ catch {
382
+ return null;
383
+ }
372
384
  }
373
385
  // Raw EVM key path
374
386
  if (!wallet.key)
@@ -52,8 +52,9 @@ export function registerPassTools(server) {
52
52
  "No payment method configured.",
53
53
  "",
54
54
  "Supported rails: Tempo USDC, Base USDC, Solana USDC.",
55
- "Run wallet_setup({ action: \"create\" }) to create a crypto wallet,",
56
- "or wallet_setup({ action: \"import\" }) with an existing key.",
55
+ "Run wallet_setup({ action: \"create\" }) to create a wallet (falls back to plaintext storage",
56
+ "if OWS is not installed install @open-wallet-standard/core globally for encrypted keys).",
57
+ "Or wallet_setup({ action: \"import\", key: \"<hex>\" }) with an existing key.",
57
58
  ];
58
59
  if (isCardPaymentEnabled()) {
59
60
  setupLines.push("", "Or wallet_setup({ action: \"add-card\" }) to connect a credit card.");
package/dist/tools/run.js CHANGED
@@ -78,8 +78,9 @@ export function registerRunTools(server) {
78
78
  "No payment method configured.",
79
79
  "",
80
80
  "Supported rails: Tempo USDC, Base USDC, Solana USDC.",
81
- "Run wallet_setup({ action: \"create\" }) to create a crypto wallet,",
82
- "or wallet_setup({ action: \"import\" }) with an existing key.",
81
+ "Run wallet_setup({ action: \"create\" }) to create a wallet (falls back to plaintext storage",
82
+ "if OWS is not installed install @open-wallet-standard/core globally for encrypted keys).",
83
+ "Or wallet_setup({ action: \"import\", key: \"<hex>\" }) with an existing key.",
83
84
  ];
84
85
  if (isCardPaymentEnabled()) {
85
86
  setupLines.push("", "Or wallet_setup({ action: \"add-card\" }) to connect a credit card.");
@@ -127,8 +127,9 @@ export function registerSolveTools(server) {
127
127
  "No payment method configured.",
128
128
  "",
129
129
  "Supported rails: Tempo USDC, Base USDC, Solana USDC.",
130
- "Run wallet_setup({ action: \"create\" }) to create a crypto wallet,",
131
- "or wallet_setup({ action: \"import\" }) with an existing key.",
130
+ "Run wallet_setup({ action: \"create\" }) to create a wallet (falls back to plaintext storage",
131
+ "if OWS is not installed install @open-wallet-standard/core globally for encrypted keys).",
132
+ "Or wallet_setup({ action: \"import\", key: \"<hex>\" }) with an existing key.",
132
133
  ];
133
134
  if (isCardPaymentEnabled()) {
134
135
  setupLines.push("", "Or wallet_setup({ action: \"add-card\" }) to connect a credit card.");
@@ -4,7 +4,7 @@ import { getWalletAddress, isCardPaymentEnabled } from "../core/payments.js";
4
4
  import { fetchUsdcBalance } from "../core/balances.js";
5
5
  import { getSettings } from "../core/settings.js";
6
6
  import { getOrCreatePendingCardSetup, formatCardSetupBlocks, getCardCapabilities, pollCardSetup, } from "../core/card-setup.js";
7
- import { isOwsAvailable, createOwsWallet, importKeyToOws, listOwsWallets, listOwsWalletsByChain, } from "../core/ows-adapter.js";
7
+ import { isOwsAvailable, createOwsWallet, importKeyToOws, listOwsWallets, listOwsWalletsByChain, installOws, platformSupportsOws, } from "../core/ows-adapter.js";
8
8
  import { ensureConsumerPrincipal, getConsumerPrincipal } from "../core/principal.js";
9
9
  function text(t) {
10
10
  return { content: [{ type: "text", text: t }] };
@@ -63,13 +63,20 @@ export function registerWalletTools(server) {
63
63
  if (consumerPrincipal) {
64
64
  lines.push("", `Consumer principal: ${consumerPrincipal}`);
65
65
  }
66
+ const plaintextWallets = wallets.filter((w) => w.keyType !== "ows");
67
+ if (plaintextWallets.length > 0) {
68
+ const owsAvailable = await isOwsAvailable();
69
+ if (!owsAvailable && platformSupportsOws()) {
70
+ lines.push("", `⚠ ${plaintextWallets.length} wallet(s) stored in plaintext. Run wallet_setup({ action: "enable-ows" }) to upgrade to encrypted storage.`);
71
+ }
72
+ }
66
73
  return text(lines.join("\n"));
67
74
  });
68
75
  // ── wallet_setup ────────────────────────────────────────────────
69
- server.tool("wallet_setup", "Set up or manage a payment wallet. 'create' makes a new encrypted crypto wallet (OWS); 'import' takes an existing private key. Tempo/Base share one EVM key — a single wallet covers both. Solana uses a separate ed25519 key. For crypto wallet deletion or key rotation, direct users to edit ~/.agentwonderland/config.json or ~/.ows/ manually; never handle key material programmatically.", {
76
+ server.tool("wallet_setup", "Set up or manage a payment wallet. 'create' makes a new crypto wallet (encrypted via OWS if available, otherwise plaintext — run 'enable-ows' to upgrade). 'import' takes an existing private key. 'enable-ows' installs the Open Wallet Standard native module for encrypted at-rest storage. Tempo/Base share one EVM key; Solana uses a separate ed25519 key. NEVER delete or rotate keys programmatically; direct users to edit ~/.agentwonderland/config.json or ~/.ows/ manually.", {
70
77
  action: z
71
- .enum(["create", "import", "add-card", "remove-card"])
72
- .describe("'create' a crypto wallet (recommended), 'import' an existing key, 'add-card'/'remove-card' for credit card (card-backed MPP availability depends on Stripe SPT)"),
78
+ .enum(["create", "import", "add-card", "remove-card", "enable-ows"])
79
+ .describe("'create' a wallet, 'import' an existing key, 'enable-ows' to install encrypted key storage, 'add-card'/'remove-card' for credit card (may be disabled depending on Stripe SPT availability)"),
73
80
  name: z
74
81
  .string()
75
82
  .optional()
@@ -131,6 +138,27 @@ export function registerWalletTools(server) {
131
138
  setCardConfig(null);
132
139
  return text(`Removed ${existing.brand} ****${existing.last4}. Card disconnected from Agent Wonderland.`);
133
140
  }
141
+ // ── Install OWS for encrypted key storage ────────────────
142
+ if (action === "enable-ows") {
143
+ if (await isOwsAvailable()) {
144
+ return text("OWS is already installed — your crypto wallets are stored with AES-256-GCM encryption at rest.");
145
+ }
146
+ if (!platformSupportsOws()) {
147
+ return text(`Your platform (${process.platform}-${process.arch}) does not have a prebuilt OWS binary.\n` +
148
+ "Plaintext storage will remain in effect. To install from source you'd need Node build tools (python3, make, node-gyp).");
149
+ }
150
+ const result = await installOws();
151
+ if (result.ok) {
152
+ const plaintextWallets = getWallets().filter((w) => w.keyType !== "ows");
153
+ const migrationHint = plaintextWallets.length > 0
154
+ ? "\n\nExisting plaintext wallets are NOT auto-migrated. To move them to encrypted storage, re-import each via wallet_setup({ action: \"import\", key: \"<hex>\" }) after OWS is active, then delete the plaintext copy from ~/.agentwonderland/config.json."
155
+ : "";
156
+ return text(`${result.message}\n\n` +
157
+ `New wallets created via wallet_setup({ action: "create" }) will now use encrypted storage.${migrationHint}`);
158
+ }
159
+ return text(`Could not install OWS: ${result.message}\n\n` +
160
+ "Plaintext storage still works. Try again later or install manually: npm install -g @open-wallet-standard/core");
161
+ }
134
162
  // ── Crypto wallet setup ──────────────────────────────────
135
163
  if (action === "import" && !key) {
136
164
  return text("Error: 'key' parameter is required when action is 'import'.");
@@ -190,11 +218,59 @@ export function registerWalletTools(server) {
190
218
  }
191
219
  }
192
220
  if (!owsReady) {
193
- return text("Cannot create wallet: OWS (Open Wallet Standard) is not installed.\n" +
194
- "Install with: npm install -g @open-wallet-standard/core\n\n" +
195
- (defaultCh === "solana"
196
- ? "Solana wallets currently require OWS-managed encrypted storage."
197
- : "Alternatively, use action 'import' with an existing private key."));
221
+ // Fallback: generate a plaintext wallet stored in ~/.agentwonderland/config.json.
222
+ // Safe for prototyping; install OWS for encrypted at-rest storage.
223
+ const walletName = name ?? `aw-${Date.now()}`;
224
+ if (preferredOwsChain === "solana") {
225
+ const { Keypair } = await import("@solana/web3.js");
226
+ const kp = Keypair.generate();
227
+ const secretHex = Buffer.from(kp.secretKey).toString("hex");
228
+ addWallet({
229
+ id: walletName,
230
+ keyType: "solana",
231
+ key: secretHex,
232
+ chains: ["solana"],
233
+ defaultChain: "solana",
234
+ label: walletName,
235
+ });
236
+ const principal = await ensureConsumerPrincipal();
237
+ const owsNudge = platformSupportsOws()
238
+ ? "\n\n⚠ Key stored in plaintext at ~/.agentwonderland/config.json. Run wallet_setup({ action: \"enable-ows\" }) to install encrypted at-rest storage — takes ~30s."
239
+ : "\n\n⚠ Key stored in plaintext at ~/.agentwonderland/config.json. OWS encrypted storage isn't available for your platform.";
240
+ return text([
241
+ "Wallet created:",
242
+ ` Address: ${kp.publicKey.toBase58()}`,
243
+ ` Name: ${walletName}`,
244
+ ` Chains: solana`,
245
+ ` Consumer principal: ${principal}`,
246
+ "",
247
+ "Fund this address with USDC on Solana to start using agents.",
248
+ ].join("\n") + owsNudge);
249
+ }
250
+ const { generatePrivateKey, privateKeyToAccount } = await import("viem/accounts");
251
+ const privateKey = generatePrivateKey();
252
+ const account = privateKeyToAccount(privateKey);
253
+ addWallet({
254
+ id: walletName,
255
+ keyType: "evm",
256
+ key: privateKey,
257
+ chains: ["tempo", "base"],
258
+ defaultChain: defaultCh === "solana" ? "tempo" : defaultCh,
259
+ label: walletName,
260
+ });
261
+ const principal = await ensureConsumerPrincipal();
262
+ const owsNudge = platformSupportsOws()
263
+ ? "\n\n⚠ Key stored in plaintext at ~/.agentwonderland/config.json. Run wallet_setup({ action: \"enable-ows\" }) to install encrypted at-rest storage — takes ~30s."
264
+ : "\n\n⚠ Key stored in plaintext at ~/.agentwonderland/config.json. OWS encrypted storage isn't available for your platform.";
265
+ return text([
266
+ "Wallet created:",
267
+ ` Address: ${account.address}`,
268
+ ` Name: ${walletName}`,
269
+ ` Chains: tempo, base`,
270
+ ` Consumer principal: ${principal}`,
271
+ "",
272
+ `Fund this address with USDC on Tempo or Base to start using agents.`,
273
+ ].join("\n") + owsNudge);
198
274
  }
199
275
  const walletName = name ?? `aw-${Date.now()}`;
200
276
  const result = await createOwsWallet(walletName, preferredOwsChain);
@@ -244,8 +320,37 @@ export function registerWalletTools(server) {
244
320
  ].join("\n"));
245
321
  }
246
322
  if (defaultCh === "solana") {
247
- return text("Solana key import currently requires OWS (Open Wallet Standard) so the ed25519 key can be stored safely.\n" +
248
- "Install with: npm install -g @open-wallet-standard/core");
323
+ // Plaintext Solana import fallback.
324
+ try {
325
+ const { Keypair } = await import("@solana/web3.js");
326
+ const raw = key.startsWith("0x") ? key.slice(2) : key;
327
+ const bytes = Uint8Array.from(Buffer.from(raw, "hex"));
328
+ const kp = bytes.length === 32 ? Keypair.fromSeed(bytes) : Keypair.fromSecretKey(bytes);
329
+ const walletName = name ?? `imported-${Date.now()}`;
330
+ addWallet({
331
+ id: walletName,
332
+ keyType: "solana",
333
+ key: raw,
334
+ chains: ["solana"],
335
+ defaultChain: "solana",
336
+ label: walletName,
337
+ });
338
+ const principal = await ensureConsumerPrincipal();
339
+ const owsNudge = platformSupportsOws()
340
+ ? "\n\n⚠ Key stored in plaintext at ~/.agentwonderland/config.json. Run wallet_setup({ action: \"enable-ows\" }) to install encrypted at-rest storage — takes ~30s."
341
+ : "\n\n⚠ Key stored in plaintext at ~/.agentwonderland/config.json. OWS encrypted storage isn't available for your platform.";
342
+ return text([
343
+ "Key imported:",
344
+ ` Address: ${kp.publicKey.toBase58()}`,
345
+ ` Name: ${walletName}`,
346
+ ` Chains: solana`,
347
+ ` Consumer principal: ${principal}`,
348
+ ].join("\n") + owsNudge);
349
+ }
350
+ catch (err) {
351
+ return text(`Invalid Solana key: ${err instanceof Error ? err.message : String(err)}\n` +
352
+ "Expected a 32-byte seed or 64-byte secret key, hex-encoded.");
353
+ }
249
354
  }
250
355
  // No OWS — store plaintext
251
356
  try {
@@ -262,15 +367,16 @@ export function registerWalletTools(server) {
262
367
  label: walletName,
263
368
  });
264
369
  const principal = await ensureConsumerPrincipal();
370
+ const owsNudge = platformSupportsOws()
371
+ ? "\n\n⚠ Key stored in plaintext at ~/.agentwonderland/config.json. Run wallet_setup({ action: \"enable-ows\" }) to install encrypted at-rest storage — takes ~30s."
372
+ : "\n\n⚠ Key stored in plaintext at ~/.agentwonderland/config.json. OWS encrypted storage isn't available for your platform.";
265
373
  return text([
266
- `Key imported [plaintext] \u2014 OWS not available for encrypted storage.`,
374
+ `Key imported:`,
267
375
  ` Address: ${account.address}`,
268
376
  ` Name: ${walletName}`,
269
377
  ` Chains: ${selectedChains.join(", ")}`,
270
378
  ` Consumer principal: ${principal}`,
271
- "",
272
- "Install OWS for encrypted storage: npm install -g @open-wallet-standard/core",
273
- ].join("\n"));
379
+ ].join("\n") + owsNudge);
274
380
  }
275
381
  catch {
276
382
  return text("Error: Invalid private key format.");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agentwonderland/mcp",
3
- "version": "0.1.28",
3
+ "version": "0.1.30",
4
4
  "type": "module",
5
5
  "description": "MCP server for the Agent Wonderland AI agent marketplace",
6
6
  "bin": {
@@ -30,14 +30,9 @@
30
30
  "viem": "^2.47.6",
31
31
  "zod": "^3.24.0"
32
32
  },
33
- "peerDependencies": {
33
+ "optionalDependencies": {
34
34
  "@open-wallet-standard/core": "^1.0.0"
35
35
  },
36
- "peerDependenciesMeta": {
37
- "@open-wallet-standard/core": {
38
- "optional": true
39
- }
40
- },
41
36
  "devDependencies": {
42
37
  "@types/qrcode": "^1.5.6",
43
38
  "tsx": "^4.0.0",
@@ -6,8 +6,8 @@ import { homedir } from "node:os";
6
6
 
7
7
  export interface WalletEntry {
8
8
  id: string; // e.g. "evm-1"
9
- keyType: "evm" | "ows"; // key format: raw EVM key or OWS-managed wallet
10
- key?: string; // private key (legacy / raw EVM only)
9
+ keyType: "evm" | "solana" | "ows"; // key format: raw EVM / raw Solana / OWS-managed
10
+ key?: string; // private key hex (secp256k1 for evm, ed25519 for solana)
11
11
  owsWalletId?: string; // OWS wallet reference (when keyType === "ows")
12
12
  chains: string[]; // enabled chains: "tempo", "base", "solana"
13
13
  defaultChain?: string; // which chain to use by default for this wallet
@@ -169,6 +169,79 @@ export async function isOwsAvailable(): Promise<boolean> {
169
169
  }
170
170
  }
171
171
 
172
+ /**
173
+ * Heuristic: is this platform likely to have a prebuilt OWS binary?
174
+ * OWS ships prebuilds for {darwin, linux, win32} x {x64, arm64}.
175
+ * Musl-libc environments (Alpine) often lack prebuilds.
176
+ */
177
+ export function platformSupportsOws(): boolean {
178
+ const ok = new Set([
179
+ "darwin-x64", "darwin-arm64",
180
+ "linux-x64", "linux-arm64",
181
+ "win32-x64", "win32-arm64",
182
+ ]);
183
+ return ok.has(`${process.platform}-${process.arch}`);
184
+ }
185
+
186
+ export interface OwsInstallResult {
187
+ ok: boolean;
188
+ message: string;
189
+ needsRestart?: boolean;
190
+ }
191
+
192
+ /**
193
+ * Attempt to install @open-wallet-standard/core into the MCP package's own
194
+ * node_modules. Avoids sudo for npx-based installs; may require sudo for a
195
+ * global MCP install.
196
+ */
197
+ export async function installOws(): Promise<OwsInstallResult> {
198
+ if (await isOwsAvailable()) {
199
+ return { ok: true, message: "OWS is already installed." };
200
+ }
201
+
202
+ if (!platformSupportsOws()) {
203
+ return {
204
+ ok: false,
205
+ message: `OWS does not ship a prebuilt binary for ${process.platform}-${process.arch}. Continue with plaintext storage, or install a Node build toolchain (python3, make, node-gyp) and run: npm install -g @open-wallet-standard/core`,
206
+ };
207
+ }
208
+
209
+ const { execFileSync } = await import("node:child_process");
210
+ const { dirname, resolve } = await import("node:path");
211
+ const { fileURLToPath } = await import("node:url");
212
+
213
+ // dist/core/ows-adapter.js → package root is ../../
214
+ const pkgRoot = resolve(dirname(fileURLToPath(import.meta.url)), "..", "..");
215
+
216
+ try {
217
+ execFileSync(
218
+ process.platform === "win32" ? "npm.cmd" : "npm",
219
+ ["install", "@open-wallet-standard/core", "--no-save", "--no-audit", "--no-fund", "--prefer-offline", "--loglevel=error"],
220
+ { cwd: pkgRoot, stdio: ["ignore", "pipe", "pipe"], timeout: 120_000 },
221
+ );
222
+ } catch (err: unknown) {
223
+ const e = err as { stderr?: Buffer; message?: string; code?: string };
224
+ const stderr = e.stderr?.toString() ?? "";
225
+ const hint = stderr.includes("EACCES")
226
+ ? " (permission denied — try: sudo npm install -g @open-wallet-standard/core)"
227
+ : "";
228
+ return {
229
+ ok: false,
230
+ message: `npm install failed: ${(e.message ?? "unknown error").split("\n")[0]}${hint}`,
231
+ };
232
+ }
233
+
234
+ if (!(await isOwsAvailable())) {
235
+ return {
236
+ ok: false,
237
+ message: "OWS installed but the module could not be loaded. Restart Claude Code (or whichever MCP host) and retry.",
238
+ needsRestart: true,
239
+ };
240
+ }
241
+
242
+ return { ok: true, message: "OWS installed and ready." };
243
+ }
244
+
172
245
  /**
173
246
  * Build a viem-compatible `LocalAccount` backed by an OWS wallet.
174
247
  *
@@ -417,7 +417,17 @@ export async function getWalletAddress(method?: string): Promise<string | null>
417
417
  }
418
418
 
419
419
  if (chain === "solana") {
420
- return null;
420
+ // Raw Solana ed25519 key path (plaintext fallback when OWS is unavailable)
421
+ if (!wallet.key) return null;
422
+ try {
423
+ const { Keypair } = await import("@solana/web3.js");
424
+ const hex = wallet.key.startsWith("0x") ? wallet.key.slice(2) : wallet.key;
425
+ const bytes = Uint8Array.from(Buffer.from(hex, "hex"));
426
+ const kp = bytes.length === 32 ? Keypair.fromSeed(bytes) : Keypair.fromSecretKey(bytes);
427
+ return kp.publicKey.toBase58();
428
+ } catch {
429
+ return null;
430
+ }
421
431
  }
422
432
 
423
433
  // Raw EVM key path
@@ -84,8 +84,9 @@ export function registerPassTools(server: McpServer): void {
84
84
  "No payment method configured.",
85
85
  "",
86
86
  "Supported rails: Tempo USDC, Base USDC, Solana USDC.",
87
- "Run wallet_setup({ action: \"create\" }) to create a crypto wallet,",
88
- "or wallet_setup({ action: \"import\" }) with an existing key.",
87
+ "Run wallet_setup({ action: \"create\" }) to create a wallet (falls back to plaintext storage",
88
+ "if OWS is not installed install @open-wallet-standard/core globally for encrypted keys).",
89
+ "Or wallet_setup({ action: \"import\", key: \"<hex>\" }) with an existing key.",
89
90
  ];
90
91
  if (isCardPaymentEnabled()) {
91
92
  setupLines.push("", "Or wallet_setup({ action: \"add-card\" }) to connect a credit card.");
package/src/tools/run.ts CHANGED
@@ -119,8 +119,9 @@ export function registerRunTools(server: McpServer): void {
119
119
  "No payment method configured.",
120
120
  "",
121
121
  "Supported rails: Tempo USDC, Base USDC, Solana USDC.",
122
- "Run wallet_setup({ action: \"create\" }) to create a crypto wallet,",
123
- "or wallet_setup({ action: \"import\" }) with an existing key.",
122
+ "Run wallet_setup({ action: \"create\" }) to create a wallet (falls back to plaintext storage",
123
+ "if OWS is not installed install @open-wallet-standard/core globally for encrypted keys).",
124
+ "Or wallet_setup({ action: \"import\", key: \"<hex>\" }) with an existing key.",
124
125
  ];
125
126
  if (isCardPaymentEnabled()) {
126
127
  setupLines.push("", "Or wallet_setup({ action: \"add-card\" }) to connect a credit card.");
@@ -162,8 +162,9 @@ export function registerSolveTools(server: McpServer): void {
162
162
  "No payment method configured.",
163
163
  "",
164
164
  "Supported rails: Tempo USDC, Base USDC, Solana USDC.",
165
- "Run wallet_setup({ action: \"create\" }) to create a crypto wallet,",
166
- "or wallet_setup({ action: \"import\" }) with an existing key.",
165
+ "Run wallet_setup({ action: \"create\" }) to create a wallet (falls back to plaintext storage",
166
+ "if OWS is not installed install @open-wallet-standard/core globally for encrypted keys).",
167
+ "Or wallet_setup({ action: \"import\", key: \"<hex>\" }) with an existing key.",
167
168
  ];
168
169
  if (isCardPaymentEnabled()) {
169
170
  setupLines.push("", "Or wallet_setup({ action: \"add-card\" }) to connect a credit card.");
@@ -24,6 +24,8 @@ import {
24
24
  importKeyToOws,
25
25
  listOwsWallets,
26
26
  listOwsWalletsByChain,
27
+ installOws,
28
+ platformSupportsOws,
27
29
  } from "../core/ows-adapter.js";
28
30
  import { ensureConsumerPrincipal, getConsumerPrincipal } from "../core/principal.js";
29
31
 
@@ -97,6 +99,17 @@ export function registerWalletTools(server: McpServer): void {
97
99
  lines.push("", `Consumer principal: ${consumerPrincipal}`);
98
100
  }
99
101
 
102
+ const plaintextWallets = wallets.filter((w) => w.keyType !== "ows");
103
+ if (plaintextWallets.length > 0) {
104
+ const owsAvailable = await isOwsAvailable();
105
+ if (!owsAvailable && platformSupportsOws()) {
106
+ lines.push(
107
+ "",
108
+ `⚠ ${plaintextWallets.length} wallet(s) stored in plaintext. Run wallet_setup({ action: "enable-ows" }) to upgrade to encrypted storage.`,
109
+ );
110
+ }
111
+ }
112
+
100
113
  return text(lines.join("\n"));
101
114
  },
102
115
  );
@@ -104,11 +117,11 @@ export function registerWalletTools(server: McpServer): void {
104
117
  // ── wallet_setup ────────────────────────────────────────────────
105
118
  server.tool(
106
119
  "wallet_setup",
107
- "Set up or manage a payment wallet. 'create' makes a new encrypted crypto wallet (OWS); 'import' takes an existing private key. Tempo/Base share one EVM key — a single wallet covers both. Solana uses a separate ed25519 key. For crypto wallet deletion or key rotation, direct users to edit ~/.agentwonderland/config.json or ~/.ows/ manually; never handle key material programmatically.",
120
+ "Set up or manage a payment wallet. 'create' makes a new crypto wallet (encrypted via OWS if available, otherwise plaintext — run 'enable-ows' to upgrade). 'import' takes an existing private key. 'enable-ows' installs the Open Wallet Standard native module for encrypted at-rest storage. Tempo/Base share one EVM key; Solana uses a separate ed25519 key. NEVER delete or rotate keys programmatically; direct users to edit ~/.agentwonderland/config.json or ~/.ows/ manually.",
108
121
  {
109
122
  action: z
110
- .enum(["create", "import", "add-card", "remove-card"])
111
- .describe("'create' a crypto wallet (recommended), 'import' an existing key, 'add-card'/'remove-card' for credit card (card-backed MPP availability depends on Stripe SPT)"),
123
+ .enum(["create", "import", "add-card", "remove-card", "enable-ows"])
124
+ .describe("'create' a wallet, 'import' an existing key, 'enable-ows' to install encrypted key storage, 'add-card'/'remove-card' for credit card (may be disabled depending on Stripe SPT availability)"),
112
125
  name: z
113
126
  .string()
114
127
  .optional()
@@ -181,6 +194,34 @@ export function registerWalletTools(server: McpServer): void {
181
194
  return text(`Removed ${existing.brand} ****${existing.last4}. Card disconnected from Agent Wonderland.`);
182
195
  }
183
196
 
197
+ // ── Install OWS for encrypted key storage ────────────────
198
+ if (action === "enable-ows") {
199
+ if (await isOwsAvailable()) {
200
+ return text("OWS is already installed — your crypto wallets are stored with AES-256-GCM encryption at rest.");
201
+ }
202
+ if (!platformSupportsOws()) {
203
+ return text(
204
+ `Your platform (${process.platform}-${process.arch}) does not have a prebuilt OWS binary.\n` +
205
+ "Plaintext storage will remain in effect. To install from source you'd need Node build tools (python3, make, node-gyp).",
206
+ );
207
+ }
208
+ const result = await installOws();
209
+ if (result.ok) {
210
+ const plaintextWallets = getWallets().filter((w) => w.keyType !== "ows");
211
+ const migrationHint = plaintextWallets.length > 0
212
+ ? "\n\nExisting plaintext wallets are NOT auto-migrated. To move them to encrypted storage, re-import each via wallet_setup({ action: \"import\", key: \"<hex>\" }) after OWS is active, then delete the plaintext copy from ~/.agentwonderland/config.json."
213
+ : "";
214
+ return text(
215
+ `${result.message}\n\n` +
216
+ `New wallets created via wallet_setup({ action: "create" }) will now use encrypted storage.${migrationHint}`,
217
+ );
218
+ }
219
+ return text(
220
+ `Could not install OWS: ${result.message}\n\n` +
221
+ "Plaintext storage still works. Try again later or install manually: npm install -g @open-wallet-standard/core",
222
+ );
223
+ }
224
+
184
225
  // ── Crypto wallet setup ──────────────────────────────────
185
226
  if (action === "import" && !key) {
186
227
  return text(
@@ -245,12 +286,62 @@ export function registerWalletTools(server: McpServer): void {
245
286
  }
246
287
 
247
288
  if (!owsReady) {
289
+ // Fallback: generate a plaintext wallet stored in ~/.agentwonderland/config.json.
290
+ // Safe for prototyping; install OWS for encrypted at-rest storage.
291
+ const walletName = name ?? `aw-${Date.now()}`;
292
+ if (preferredOwsChain === "solana") {
293
+ const { Keypair } = await import("@solana/web3.js");
294
+ const kp = Keypair.generate();
295
+ const secretHex = Buffer.from(kp.secretKey).toString("hex");
296
+ addWallet({
297
+ id: walletName,
298
+ keyType: "solana",
299
+ key: secretHex,
300
+ chains: ["solana"],
301
+ defaultChain: "solana",
302
+ label: walletName,
303
+ });
304
+ const principal = await ensureConsumerPrincipal();
305
+ const owsNudge = platformSupportsOws()
306
+ ? "\n\n⚠ Key stored in plaintext at ~/.agentwonderland/config.json. Run wallet_setup({ action: \"enable-ows\" }) to install encrypted at-rest storage — takes ~30s."
307
+ : "\n\n⚠ Key stored in plaintext at ~/.agentwonderland/config.json. OWS encrypted storage isn't available for your platform.";
308
+ return text(
309
+ [
310
+ "Wallet created:",
311
+ ` Address: ${kp.publicKey.toBase58()}`,
312
+ ` Name: ${walletName}`,
313
+ ` Chains: solana`,
314
+ ` Consumer principal: ${principal}`,
315
+ "",
316
+ "Fund this address with USDC on Solana to start using agents.",
317
+ ].join("\n") + owsNudge,
318
+ );
319
+ }
320
+ const { generatePrivateKey, privateKeyToAccount } = await import("viem/accounts");
321
+ const privateKey = generatePrivateKey();
322
+ const account = privateKeyToAccount(privateKey);
323
+ addWallet({
324
+ id: walletName,
325
+ keyType: "evm",
326
+ key: privateKey,
327
+ chains: ["tempo", "base"],
328
+ defaultChain: defaultCh === "solana" ? "tempo" : defaultCh,
329
+ label: walletName,
330
+ });
331
+ const principal = await ensureConsumerPrincipal();
332
+ const owsNudge = platformSupportsOws()
333
+ ? "\n\n⚠ Key stored in plaintext at ~/.agentwonderland/config.json. Run wallet_setup({ action: \"enable-ows\" }) to install encrypted at-rest storage — takes ~30s."
334
+ : "\n\n⚠ Key stored in plaintext at ~/.agentwonderland/config.json. OWS encrypted storage isn't available for your platform.";
248
335
  return text(
249
- "Cannot create wallet: OWS (Open Wallet Standard) is not installed.\n" +
250
- "Install with: npm install -g @open-wallet-standard/core\n\n" +
251
- (defaultCh === "solana"
252
- ? "Solana wallets currently require OWS-managed encrypted storage."
253
- : "Alternatively, use action 'import' with an existing private key."),
336
+ [
337
+ "Wallet created:",
338
+ ` Address: ${account.address}`,
339
+ ` Name: ${walletName}`,
340
+ ` Chains: tempo, base`,
341
+ ` Consumer principal: ${principal}`,
342
+ "",
343
+ `Fund this address with USDC on Tempo or Base to start using agents.`,
344
+ ].join("\n") + owsNudge,
254
345
  );
255
346
  }
256
347
 
@@ -312,10 +403,40 @@ export function registerWalletTools(server: McpServer): void {
312
403
  }
313
404
 
314
405
  if (defaultCh === "solana") {
315
- return text(
316
- "Solana key import currently requires OWS (Open Wallet Standard) so the ed25519 key can be stored safely.\n" +
317
- "Install with: npm install -g @open-wallet-standard/core",
318
- );
406
+ // Plaintext Solana import fallback.
407
+ try {
408
+ const { Keypair } = await import("@solana/web3.js");
409
+ const raw = key!.startsWith("0x") ? key!.slice(2) : key!;
410
+ const bytes = Uint8Array.from(Buffer.from(raw, "hex"));
411
+ const kp = bytes.length === 32 ? Keypair.fromSeed(bytes) : Keypair.fromSecretKey(bytes);
412
+ const walletName = name ?? `imported-${Date.now()}`;
413
+ addWallet({
414
+ id: walletName,
415
+ keyType: "solana",
416
+ key: raw,
417
+ chains: ["solana"],
418
+ defaultChain: "solana",
419
+ label: walletName,
420
+ });
421
+ const principal = await ensureConsumerPrincipal();
422
+ const owsNudge = platformSupportsOws()
423
+ ? "\n\n⚠ Key stored in plaintext at ~/.agentwonderland/config.json. Run wallet_setup({ action: \"enable-ows\" }) to install encrypted at-rest storage — takes ~30s."
424
+ : "\n\n⚠ Key stored in plaintext at ~/.agentwonderland/config.json. OWS encrypted storage isn't available for your platform.";
425
+ return text(
426
+ [
427
+ "Key imported:",
428
+ ` Address: ${kp.publicKey.toBase58()}`,
429
+ ` Name: ${walletName}`,
430
+ ` Chains: solana`,
431
+ ` Consumer principal: ${principal}`,
432
+ ].join("\n") + owsNudge,
433
+ );
434
+ } catch (err) {
435
+ return text(
436
+ `Invalid Solana key: ${err instanceof Error ? err.message : String(err)}\n` +
437
+ "Expected a 32-byte seed or 64-byte secret key, hex-encoded.",
438
+ );
439
+ }
319
440
  }
320
441
 
321
442
  // No OWS — store plaintext
@@ -337,16 +458,17 @@ export function registerWalletTools(server: McpServer): void {
337
458
  });
338
459
  const principal = await ensureConsumerPrincipal();
339
460
 
461
+ const owsNudge = platformSupportsOws()
462
+ ? "\n\n⚠ Key stored in plaintext at ~/.agentwonderland/config.json. Run wallet_setup({ action: \"enable-ows\" }) to install encrypted at-rest storage — takes ~30s."
463
+ : "\n\n⚠ Key stored in plaintext at ~/.agentwonderland/config.json. OWS encrypted storage isn't available for your platform.";
340
464
  return text(
341
465
  [
342
- `Key imported [plaintext] \u2014 OWS not available for encrypted storage.`,
466
+ `Key imported:`,
343
467
  ` Address: ${account.address}`,
344
468
  ` Name: ${walletName}`,
345
469
  ` Chains: ${selectedChains.join(", ")}`,
346
470
  ` Consumer principal: ${principal}`,
347
- "",
348
- "Install OWS for encrypted storage: npm install -g @open-wallet-standard/core",
349
- ].join("\n"),
471
+ ].join("\n") + owsNudge,
350
472
  );
351
473
  } catch {
352
474
  return text("Error: Invalid private key format.");