@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.
- package/dist/core/config.d.ts +1 -1
- package/dist/core/ows-adapter.d.ts +17 -0
- package/dist/core/ows-adapter.js +56 -0
- package/dist/core/payments.js +13 -1
- package/dist/tools/passes.js +3 -2
- package/dist/tools/run.js +3 -2
- package/dist/tools/solve.js +3 -2
- package/dist/tools/wallet.js +121 -15
- package/package.json +2 -7
- package/src/core/config.ts +2 -2
- package/src/core/ows-adapter.ts +73 -0
- package/src/core/payments.ts +11 -1
- package/src/tools/passes.ts +3 -2
- package/src/tools/run.ts +3 -2
- package/src/tools/solve.ts +3 -2
- package/src/tools/wallet.ts +138 -16
package/dist/core/config.d.ts
CHANGED
|
@@ -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
|
*
|
package/dist/core/ows-adapter.js
CHANGED
|
@@ -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
|
*
|
package/dist/core/payments.js
CHANGED
|
@@ -368,7 +368,19 @@ export async function getWalletAddress(method) {
|
|
|
368
368
|
}
|
|
369
369
|
}
|
|
370
370
|
if (chain === "solana") {
|
|
371
|
-
|
|
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)
|
package/dist/tools/passes.js
CHANGED
|
@@ -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
|
|
56
|
-
"
|
|
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
|
|
82
|
-
"
|
|
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.");
|
package/dist/tools/solve.js
CHANGED
|
@@ -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
|
|
131
|
-
"
|
|
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.");
|
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'.");
|
|
@@ -190,11 +218,59 @@ export function registerWalletTools(server) {
|
|
|
190
218
|
}
|
|
191
219
|
}
|
|
192
220
|
if (!owsReady) {
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
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
|
-
|
|
248
|
-
|
|
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
|
|
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.
|
|
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
|
-
"
|
|
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",
|
package/src/core/config.ts
CHANGED
|
@@ -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
|
|
10
|
-
key?: string; // private key (
|
|
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
|
package/src/core/ows-adapter.ts
CHANGED
|
@@ -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
|
*
|
package/src/core/payments.ts
CHANGED
|
@@ -417,7 +417,17 @@ export async function getWalletAddress(method?: string): Promise<string | null>
|
|
|
417
417
|
}
|
|
418
418
|
|
|
419
419
|
if (chain === "solana") {
|
|
420
|
-
|
|
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
|
package/src/tools/passes.ts
CHANGED
|
@@ -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
|
|
88
|
-
"
|
|
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
|
|
123
|
-
"
|
|
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.");
|
package/src/tools/solve.ts
CHANGED
|
@@ -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
|
|
166
|
-
"
|
|
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.");
|
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(
|
|
@@ -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
|
-
|
|
250
|
-
"
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
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
|
-
|
|
316
|
-
|
|
317
|
-
|
|
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
|
|
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.");
|