@agentwonderland/mcp 0.1.29 → 0.1.31
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/ows-adapter.d.ts +17 -0
- package/dist/core/ows-adapter.js +75 -0
- package/dist/tools/wallet.js +52 -18
- package/package.json +1 -1
- package/src/core/ows-adapter.ts +91 -0
- package/src/tools/wallet.ts +64 -17
|
@@ -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 glibc prebuilds for {darwin, linux, win32} x {x64, arm64}.
|
|
19
|
+
* Musl-libc (Alpine) and other libcs have no 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
|
*
|
package/dist/core/ows-adapter.js
CHANGED
|
@@ -71,6 +71,81 @@ 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 glibc prebuilds for {darwin, linux, win32} x {x64, arm64}.
|
|
77
|
+
* Musl-libc (Alpine) and other libcs have no 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
|
+
if (!ok.has(`${process.platform}-${process.arch}`))
|
|
86
|
+
return false;
|
|
87
|
+
// On Linux, distinguish glibc from musl. musl systems have no prebuilt binary.
|
|
88
|
+
if (process.platform === "linux") {
|
|
89
|
+
try {
|
|
90
|
+
// Node exposes the runtime glibc version when linked against glibc;
|
|
91
|
+
// on musl this field is empty.
|
|
92
|
+
const report = process.report;
|
|
93
|
+
const glibc = report?.getReport?.()?.header?.glibcVersionRuntime;
|
|
94
|
+
if (!glibc)
|
|
95
|
+
return false;
|
|
96
|
+
}
|
|
97
|
+
catch {
|
|
98
|
+
return false;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
return true;
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Attempt to install @open-wallet-standard/core into the MCP package's own
|
|
105
|
+
* node_modules. Avoids sudo for npx-based installs; may require sudo for a
|
|
106
|
+
* global MCP install.
|
|
107
|
+
*/
|
|
108
|
+
export async function installOws() {
|
|
109
|
+
if (await isOwsAvailable()) {
|
|
110
|
+
return { ok: true, message: "OWS is already installed." };
|
|
111
|
+
}
|
|
112
|
+
if (!platformSupportsOws()) {
|
|
113
|
+
const label = process.platform === "linux"
|
|
114
|
+
? `${process.platform}-${process.arch} (likely musl/Alpine)`
|
|
115
|
+
: `${process.platform}-${process.arch}`;
|
|
116
|
+
return {
|
|
117
|
+
ok: false,
|
|
118
|
+
message: `OWS does not ship a prebuilt binary for ${label}. Plaintext storage is the only option on this system. To build from source you'd need glibc + Node build tools (python3, make, node-gyp).`,
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
const { execFileSync } = await import("node:child_process");
|
|
122
|
+
const { dirname, resolve } = await import("node:path");
|
|
123
|
+
const { fileURLToPath } = await import("node:url");
|
|
124
|
+
// dist/core/ows-adapter.js → package root is ../../
|
|
125
|
+
const pkgRoot = resolve(dirname(fileURLToPath(import.meta.url)), "..", "..");
|
|
126
|
+
try {
|
|
127
|
+
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 });
|
|
128
|
+
}
|
|
129
|
+
catch (err) {
|
|
130
|
+
const e = err;
|
|
131
|
+
const stderr = e.stderr?.toString() ?? "";
|
|
132
|
+
const hint = stderr.includes("EACCES")
|
|
133
|
+
? " (permission denied — try: sudo npm install -g @open-wallet-standard/core)"
|
|
134
|
+
: "";
|
|
135
|
+
return {
|
|
136
|
+
ok: false,
|
|
137
|
+
message: `npm install failed: ${(e.message ?? "unknown error").split("\n")[0]}${hint}`,
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
if (!(await isOwsAvailable())) {
|
|
141
|
+
return {
|
|
142
|
+
ok: false,
|
|
143
|
+
message: "OWS installed but the module could not be loaded. Restart Claude Code (or whichever MCP host) and retry.",
|
|
144
|
+
needsRestart: true,
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
return { ok: true, message: "OWS installed and ready." };
|
|
148
|
+
}
|
|
74
149
|
/**
|
|
75
150
|
* Build a viem-compatible `LocalAccount` backed by an OWS wallet.
|
|
76
151
|
*
|
package/dist/tools/wallet.js
CHANGED
|
@@ -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
|
|
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
|
|
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'.");
|
|
@@ -206,16 +234,18 @@ export function registerWalletTools(server) {
|
|
|
206
234
|
label: walletName,
|
|
207
235
|
});
|
|
208
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.";
|
|
209
240
|
return text([
|
|
210
|
-
"Wallet created
|
|
241
|
+
"Wallet created:",
|
|
211
242
|
` Address: ${kp.publicKey.toBase58()}`,
|
|
212
243
|
` Name: ${walletName}`,
|
|
213
244
|
` Chains: solana`,
|
|
214
245
|
` Consumer principal: ${principal}`,
|
|
215
246
|
"",
|
|
216
247
|
"Fund this address with USDC on Solana to start using agents.",
|
|
217
|
-
|
|
218
|
-
].join("\n"));
|
|
248
|
+
].join("\n") + owsNudge);
|
|
219
249
|
}
|
|
220
250
|
const { generatePrivateKey, privateKeyToAccount } = await import("viem/accounts");
|
|
221
251
|
const privateKey = generatePrivateKey();
|
|
@@ -229,16 +259,18 @@ export function registerWalletTools(server) {
|
|
|
229
259
|
label: walletName,
|
|
230
260
|
});
|
|
231
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.";
|
|
232
265
|
return text([
|
|
233
|
-
"Wallet created
|
|
266
|
+
"Wallet created:",
|
|
234
267
|
` Address: ${account.address}`,
|
|
235
268
|
` Name: ${walletName}`,
|
|
236
269
|
` Chains: tempo, base`,
|
|
237
270
|
` Consumer principal: ${principal}`,
|
|
238
271
|
"",
|
|
239
272
|
`Fund this address with USDC on Tempo or Base to start using agents.`,
|
|
240
|
-
|
|
241
|
-
].join("\n"));
|
|
273
|
+
].join("\n") + owsNudge);
|
|
242
274
|
}
|
|
243
275
|
const walletName = name ?? `aw-${Date.now()}`;
|
|
244
276
|
const result = await createOwsWallet(walletName, preferredOwsChain);
|
|
@@ -304,15 +336,16 @@ export function registerWalletTools(server) {
|
|
|
304
336
|
label: walletName,
|
|
305
337
|
});
|
|
306
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.";
|
|
307
342
|
return text([
|
|
308
|
-
"Key imported
|
|
343
|
+
"Key imported:",
|
|
309
344
|
` Address: ${kp.publicKey.toBase58()}`,
|
|
310
345
|
` Name: ${walletName}`,
|
|
311
346
|
` Chains: solana`,
|
|
312
347
|
` Consumer principal: ${principal}`,
|
|
313
|
-
|
|
314
|
-
"For encrypted storage, install OWS: npm install -g @open-wallet-standard/core",
|
|
315
|
-
].join("\n"));
|
|
348
|
+
].join("\n") + owsNudge);
|
|
316
349
|
}
|
|
317
350
|
catch (err) {
|
|
318
351
|
return text(`Invalid Solana key: ${err instanceof Error ? err.message : String(err)}\n` +
|
|
@@ -334,15 +367,16 @@ export function registerWalletTools(server) {
|
|
|
334
367
|
label: walletName,
|
|
335
368
|
});
|
|
336
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.";
|
|
337
373
|
return text([
|
|
338
|
-
`Key imported
|
|
374
|
+
`Key imported:`,
|
|
339
375
|
` Address: ${account.address}`,
|
|
340
376
|
` Name: ${walletName}`,
|
|
341
377
|
` Chains: ${selectedChains.join(", ")}`,
|
|
342
378
|
` Consumer principal: ${principal}`,
|
|
343
|
-
|
|
344
|
-
"Install OWS for encrypted storage: npm install -g @open-wallet-standard/core",
|
|
345
|
-
].join("\n"));
|
|
379
|
+
].join("\n") + owsNudge);
|
|
346
380
|
}
|
|
347
381
|
catch {
|
|
348
382
|
return text("Error: Invalid private key format.");
|
package/package.json
CHANGED
package/src/core/ows-adapter.ts
CHANGED
|
@@ -169,6 +169,97 @@ 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 glibc prebuilds for {darwin, linux, win32} x {x64, arm64}.
|
|
175
|
+
* Musl-libc (Alpine) and other libcs have no 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
|
+
if (!ok.has(`${process.platform}-${process.arch}`)) return false;
|
|
184
|
+
|
|
185
|
+
// On Linux, distinguish glibc from musl. musl systems have no prebuilt binary.
|
|
186
|
+
if (process.platform === "linux") {
|
|
187
|
+
try {
|
|
188
|
+
// Node exposes the runtime glibc version when linked against glibc;
|
|
189
|
+
// on musl this field is empty.
|
|
190
|
+
const report = (process as NodeJS.Process & { report?: { getReport?: () => { header?: { glibcVersionRuntime?: string } } } }).report;
|
|
191
|
+
const glibc = report?.getReport?.()?.header?.glibcVersionRuntime;
|
|
192
|
+
if (!glibc) return false;
|
|
193
|
+
} catch {
|
|
194
|
+
return false;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
return true;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
export interface OwsInstallResult {
|
|
202
|
+
ok: boolean;
|
|
203
|
+
message: string;
|
|
204
|
+
needsRestart?: boolean;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* Attempt to install @open-wallet-standard/core into the MCP package's own
|
|
209
|
+
* node_modules. Avoids sudo for npx-based installs; may require sudo for a
|
|
210
|
+
* global MCP install.
|
|
211
|
+
*/
|
|
212
|
+
export async function installOws(): Promise<OwsInstallResult> {
|
|
213
|
+
if (await isOwsAvailable()) {
|
|
214
|
+
return { ok: true, message: "OWS is already installed." };
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
if (!platformSupportsOws()) {
|
|
218
|
+
const label = process.platform === "linux"
|
|
219
|
+
? `${process.platform}-${process.arch} (likely musl/Alpine)`
|
|
220
|
+
: `${process.platform}-${process.arch}`;
|
|
221
|
+
return {
|
|
222
|
+
ok: false,
|
|
223
|
+
message: `OWS does not ship a prebuilt binary for ${label}. Plaintext storage is the only option on this system. To build from source you'd need glibc + Node build tools (python3, make, node-gyp).`,
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
const { execFileSync } = await import("node:child_process");
|
|
228
|
+
const { dirname, resolve } = await import("node:path");
|
|
229
|
+
const { fileURLToPath } = await import("node:url");
|
|
230
|
+
|
|
231
|
+
// dist/core/ows-adapter.js → package root is ../../
|
|
232
|
+
const pkgRoot = resolve(dirname(fileURLToPath(import.meta.url)), "..", "..");
|
|
233
|
+
|
|
234
|
+
try {
|
|
235
|
+
execFileSync(
|
|
236
|
+
process.platform === "win32" ? "npm.cmd" : "npm",
|
|
237
|
+
["install", "@open-wallet-standard/core", "--no-save", "--no-audit", "--no-fund", "--prefer-offline", "--loglevel=error"],
|
|
238
|
+
{ cwd: pkgRoot, stdio: ["ignore", "pipe", "pipe"], timeout: 120_000 },
|
|
239
|
+
);
|
|
240
|
+
} catch (err: unknown) {
|
|
241
|
+
const e = err as { stderr?: Buffer; message?: string; code?: string };
|
|
242
|
+
const stderr = e.stderr?.toString() ?? "";
|
|
243
|
+
const hint = stderr.includes("EACCES")
|
|
244
|
+
? " (permission denied — try: sudo npm install -g @open-wallet-standard/core)"
|
|
245
|
+
: "";
|
|
246
|
+
return {
|
|
247
|
+
ok: false,
|
|
248
|
+
message: `npm install failed: ${(e.message ?? "unknown error").split("\n")[0]}${hint}`,
|
|
249
|
+
};
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
if (!(await isOwsAvailable())) {
|
|
253
|
+
return {
|
|
254
|
+
ok: false,
|
|
255
|
+
message: "OWS installed but the module could not be loaded. Restart Claude Code (or whichever MCP host) and retry.",
|
|
256
|
+
needsRestart: true,
|
|
257
|
+
};
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
return { ok: true, message: "OWS installed and ready." };
|
|
261
|
+
}
|
|
262
|
+
|
|
172
263
|
/**
|
|
173
264
|
* Build a viem-compatible `LocalAccount` backed by an OWS wallet.
|
|
174
265
|
*
|
package/src/tools/wallet.ts
CHANGED
|
@@ -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
|
|
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
|
|
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(
|
|
@@ -261,17 +302,19 @@ export function registerWalletTools(server: McpServer): void {
|
|
|
261
302
|
label: walletName,
|
|
262
303
|
});
|
|
263
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.";
|
|
264
308
|
return text(
|
|
265
309
|
[
|
|
266
|
-
"Wallet created
|
|
310
|
+
"Wallet created:",
|
|
267
311
|
` Address: ${kp.publicKey.toBase58()}`,
|
|
268
312
|
` Name: ${walletName}`,
|
|
269
313
|
` Chains: solana`,
|
|
270
314
|
` Consumer principal: ${principal}`,
|
|
271
315
|
"",
|
|
272
316
|
"Fund this address with USDC on Solana to start using agents.",
|
|
273
|
-
|
|
274
|
-
].join("\n"),
|
|
317
|
+
].join("\n") + owsNudge,
|
|
275
318
|
);
|
|
276
319
|
}
|
|
277
320
|
const { generatePrivateKey, privateKeyToAccount } = await import("viem/accounts");
|
|
@@ -286,17 +329,19 @@ export function registerWalletTools(server: McpServer): void {
|
|
|
286
329
|
label: walletName,
|
|
287
330
|
});
|
|
288
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.";
|
|
289
335
|
return text(
|
|
290
336
|
[
|
|
291
|
-
"Wallet created
|
|
337
|
+
"Wallet created:",
|
|
292
338
|
` Address: ${account.address}`,
|
|
293
339
|
` Name: ${walletName}`,
|
|
294
340
|
` Chains: tempo, base`,
|
|
295
341
|
` Consumer principal: ${principal}`,
|
|
296
342
|
"",
|
|
297
343
|
`Fund this address with USDC on Tempo or Base to start using agents.`,
|
|
298
|
-
|
|
299
|
-
].join("\n"),
|
|
344
|
+
].join("\n") + owsNudge,
|
|
300
345
|
);
|
|
301
346
|
}
|
|
302
347
|
|
|
@@ -374,16 +419,17 @@ export function registerWalletTools(server: McpServer): void {
|
|
|
374
419
|
label: walletName,
|
|
375
420
|
});
|
|
376
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.";
|
|
377
425
|
return text(
|
|
378
426
|
[
|
|
379
|
-
"Key imported
|
|
427
|
+
"Key imported:",
|
|
380
428
|
` Address: ${kp.publicKey.toBase58()}`,
|
|
381
429
|
` Name: ${walletName}`,
|
|
382
430
|
` Chains: solana`,
|
|
383
431
|
` Consumer principal: ${principal}`,
|
|
384
|
-
|
|
385
|
-
"For encrypted storage, install OWS: npm install -g @open-wallet-standard/core",
|
|
386
|
-
].join("\n"),
|
|
432
|
+
].join("\n") + owsNudge,
|
|
387
433
|
);
|
|
388
434
|
} catch (err) {
|
|
389
435
|
return text(
|
|
@@ -412,16 +458,17 @@ export function registerWalletTools(server: McpServer): void {
|
|
|
412
458
|
});
|
|
413
459
|
const principal = await ensureConsumerPrincipal();
|
|
414
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.";
|
|
415
464
|
return text(
|
|
416
465
|
[
|
|
417
|
-
`Key imported
|
|
466
|
+
`Key imported:`,
|
|
418
467
|
` Address: ${account.address}`,
|
|
419
468
|
` Name: ${walletName}`,
|
|
420
469
|
` Chains: ${selectedChains.join(", ")}`,
|
|
421
470
|
` Consumer principal: ${principal}`,
|
|
422
|
-
|
|
423
|
-
"Install OWS for encrypted storage: npm install -g @open-wallet-standard/core",
|
|
424
|
-
].join("\n"),
|
|
471
|
+
].join("\n") + owsNudge,
|
|
425
472
|
);
|
|
426
473
|
} catch {
|
|
427
474
|
return text("Error: Invalid private key format.");
|