@agentwonderland/mcp 0.1.46 → 0.1.48

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.
@@ -3,10 +3,15 @@ import { z } from "zod";
3
3
  import {
4
4
  getWallets,
5
5
  getCardConfig,
6
+ getLinkConfig,
7
+ getPendingLinkSetup,
8
+ setPendingLinkSetup,
9
+ setLinkConfig,
6
10
  setCardConfig,
7
11
  addWallet,
8
12
  getPendingCardSetupToken,
9
13
  getSpendPolicy,
14
+ setDefaultPaymentMethod,
10
15
  setSpendPolicy,
11
16
  } from "../core/config.js";
12
17
  import { getWalletAddress, isCardPaymentEnabled } from "../core/payments.js";
@@ -18,6 +23,12 @@ import {
18
23
  getCardCapabilities,
19
24
  pollCardSetup,
20
25
  } from "../core/card-setup.js";
26
+ import {
27
+ getLinkCliAuthStatus,
28
+ listLinkPaymentMethods,
29
+ openLinkPaymentMethodAdd,
30
+ startLinkCliLogin,
31
+ } from "../core/link-cli.js";
21
32
  import {
22
33
  isOwsAvailable,
23
34
  createOwsWallet,
@@ -34,6 +45,49 @@ function text(t: string) {
34
45
  return { content: [{ type: "text" as const, text: t }] };
35
46
  }
36
47
 
48
+ function formatLinkApprovalPrompt(pending: { verificationUrl: string; phrase: string }): string {
49
+ return [
50
+ "Approve Agent Wonderland in Link:",
51
+ "",
52
+ pending.verificationUrl,
53
+ "",
54
+ `Verification phrase: ${pending.phrase}`,
55
+ "",
56
+ "After approving, run wallet_setup({ action: \"add-link\" }) again and I will finish connecting the payment method.",
57
+ ].join("\n");
58
+ }
59
+
60
+ function isFreshLinkSetup(pending: { createdAt: string }): boolean {
61
+ const createdAt = Date.parse(pending.createdAt);
62
+ if (!Number.isFinite(createdAt)) return false;
63
+ return Date.now() - createdAt < 10 * 60 * 1000;
64
+ }
65
+
66
+ function formatPaymentSetupMenu(): string {
67
+ return [
68
+ "Choose a payment method to set up:",
69
+ "",
70
+ "1. Link card or bank account (recommended)",
71
+ " wallet_setup({ action: \"add-link\" })",
72
+ "",
73
+ "2. Tempo USDC",
74
+ " wallet_setup({ action: \"create\", chain: \"tempo\" })",
75
+ "",
76
+ "3. Base USDC",
77
+ " wallet_setup({ action: \"create\", chain: \"base\" })",
78
+ "",
79
+ "4. Solana USDC",
80
+ " wallet_setup({ action: \"create\", chain: \"solana\" })",
81
+ "",
82
+ "5. Import an existing wallet",
83
+ " wallet_setup({ action: \"import\", chain: \"base\", key: \"<private key>\" })",
84
+ ].join("\n");
85
+ }
86
+
87
+ function setDefaultCryptoPaymentMethod(chain: string): void {
88
+ setDefaultPaymentMethod(chain === "solana" ? "solana" : chain === "base" ? "base" : "tempo");
89
+ }
90
+
37
91
  export function registerWalletTools(server: McpServer): void {
38
92
  // ── wallet_status (extracted from check_wallet) ─────────────────
39
93
  server.tool(
@@ -43,12 +97,13 @@ export function registerWalletTools(server: McpServer): void {
43
97
  async () => {
44
98
  const wallets = getWallets();
45
99
  const card = getCardConfig();
100
+ const link = getLinkConfig();
46
101
  const pendingCardSetupToken = getPendingCardSetupToken();
47
102
  const consumerPrincipal = await getConsumerPrincipal();
48
103
 
49
- if (wallets.length === 0 && !card && !pendingCardSetupToken) {
104
+ if (wallets.length === 0 && !card && !link && !pendingCardSetupToken) {
50
105
  return text(
51
- "No payment methods configured.\nUse wallet_setup to create or import a wallet.",
106
+ `No payment methods configured.\n\n${formatPaymentSetupMenu()}`,
52
107
  );
53
108
  }
54
109
 
@@ -95,6 +150,15 @@ export function registerWalletTools(server: McpServer): void {
95
150
  }
96
151
  }
97
152
 
153
+ if (link) {
154
+ const auth = await getLinkCliAuthStatus();
155
+ const label = link.label ? ` (${link.label})` : "";
156
+ lines.push(` Link${label}: ${link.paymentMethodId}`);
157
+ lines.push(auth.authenticated
158
+ ? " Link CLI: authenticated"
159
+ : " Link CLI: not authenticated — run npx @stripe/link-cli auth login");
160
+ }
161
+
98
162
  if (pendingCardSetupToken) {
99
163
  lines.push(" Card setup: pending confirmation");
100
164
  }
@@ -121,11 +185,11 @@ export function registerWalletTools(server: McpServer): void {
121
185
  // ── wallet_setup ────────────────────────────────────────────────
122
186
  server.tool(
123
187
  "wallet_setup",
124
- "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.",
188
+ "Set up or manage an Agent Wonderland payment method. Use 'start' for a guided setup menu. Link card/bank is recommended for most users. 'add-link' connects a Link card or bank account for agent payments. '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.",
125
189
  {
126
190
  action: z
127
- .enum(["create", "import", "add-card", "remove-card", "enable-ows"])
128
- .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)"),
191
+ .enum(["start", "create", "import", "add-card", "remove-card", "add-link", "remove-link", "enable-ows"])
192
+ .describe("'start' shows the guided payment setup menu, 'add-link' connects Link card/bank, 'create' makes a crypto wallet, 'import' imports an existing key, 'enable-ows' installs encrypted key storage"),
129
193
  name: z
130
194
  .string()
131
195
  .optional()
@@ -138,8 +202,160 @@ export function registerWalletTools(server: McpServer): void {
138
202
  ),
139
203
  chain: z.enum(["tempo", "base", "solana"]).optional()
140
204
  .describe("Primary chain (default: tempo). Tempo/Base use a shared EVM wallet; Solana uses a separate OWS wallet."),
205
+ link_payment_method_id: z.string().optional()
206
+ .describe("Link payment method ID from `npx @stripe/link-cli payment-methods list`; used with action 'add-link'."),
207
+ link_payment_method: z.string().optional()
208
+ .describe("Friendly Link payment method selector, such as a card/bank name or last4; used with action 'add-link'."),
141
209
  },
142
- async ({ action, name, key, chain }) => {
210
+ async ({ action, name, key, chain, link_payment_method_id, link_payment_method }) => {
211
+ if (action === "start") {
212
+ return text(formatPaymentSetupMenu());
213
+ }
214
+
215
+ // ── Link setup flow ──────────────────────────────────────
216
+ if (action === "add-link") {
217
+ let auth = await getLinkCliAuthStatus();
218
+ if (!auth.authenticated) {
219
+ const pending = getPendingLinkSetup();
220
+ if (pending && isFreshLinkSetup(pending)) {
221
+ return text(formatLinkApprovalPrompt(pending));
222
+ }
223
+ if (pending) {
224
+ setPendingLinkSetup(null);
225
+ }
226
+
227
+ try {
228
+ const login = await startLinkCliLogin();
229
+ setPendingLinkSetup({
230
+ verificationUrl: login.verificationUrl,
231
+ phrase: login.phrase,
232
+ createdAt: new Date().toISOString(),
233
+ });
234
+ auth = await getLinkCliAuthStatus();
235
+ if (!auth.authenticated) {
236
+ return text(formatLinkApprovalPrompt({
237
+ verificationUrl: login.verificationUrl,
238
+ phrase: login.phrase,
239
+ }));
240
+ }
241
+ } catch (err) {
242
+ return text(
243
+ [
244
+ "Link authentication could not start from the MCP session.",
245
+ `Reason: ${err instanceof Error ? err.message : String(err)}`,
246
+ "",
247
+ "Run wallet_setup({ action: \"add-link\" }) again to retry.",
248
+ ].join("\n"),
249
+ );
250
+ }
251
+ }
252
+ setPendingLinkSetup(null);
253
+
254
+ const explicitLinkSelector = link_payment_method_id ?? link_payment_method;
255
+ if (explicitLinkSelector) {
256
+ const methods = await listLinkPaymentMethods();
257
+ const normalizedSelector = explicitLinkSelector.toLowerCase().trim();
258
+ const exact = methods.find((method) => method.id === explicitLinkSelector);
259
+ const matches = exact
260
+ ? [exact]
261
+ : methods.filter((method) => method.searchText?.includes(normalizedSelector));
262
+ if (matches.length === 0 && !link_payment_method_id) {
263
+ return text(
264
+ [
265
+ `No Link payment method matched "${explicitLinkSelector}".`,
266
+ "",
267
+ "Available methods:",
268
+ ...methods.map((method) => ` ${method.label ?? method.id}${method.label ? ` (${method.id})` : ""}`),
269
+ ].join("\n"),
270
+ );
271
+ }
272
+ if (matches.length > 1) {
273
+ return text(
274
+ [
275
+ `Multiple Link payment methods matched "${explicitLinkSelector}". Choose one:`,
276
+ ...matches.map((method) => ` ${method.label ?? method.id}${method.label ? ` (${method.id})` : ""}`),
277
+ "",
278
+ "Then run:",
279
+ " wallet_setup({ action: \"add-link\", link_payment_method: \"<name or last4>\" })",
280
+ ].join("\n"),
281
+ );
282
+ }
283
+ const selected = matches[0] ?? { id: link_payment_method_id!, label: name };
284
+ setLinkConfig({
285
+ paymentMethodId: selected.id,
286
+ label: name ?? selected.label,
287
+ });
288
+ const principal = await ensureConsumerPrincipal();
289
+ return text(
290
+ [
291
+ "Link payment method connected.",
292
+ ` Payment method: ${selected.label ?? selected.id}${selected.label ? ` (${selected.id})` : ""}`,
293
+ ` Consumer principal: ${principal}`,
294
+ "",
295
+ "Use pay_with: \"link\" for agent runs.",
296
+ ].join("\n"),
297
+ );
298
+ }
299
+
300
+ let methods = await listLinkPaymentMethods();
301
+ if (methods.length === 0) {
302
+ try {
303
+ await openLinkPaymentMethodAdd();
304
+ methods = await listLinkPaymentMethods();
305
+ } catch (err) {
306
+ return text(
307
+ [
308
+ "No Link payment methods are available yet.",
309
+ `Reason: ${err instanceof Error ? err.message : String(err)}`,
310
+ "",
311
+ "Run wallet_setup({ action: \"add-link\" }) again after adding a payment method in Link.",
312
+ ].join("\n"),
313
+ );
314
+ }
315
+ if (methods.length === 0) {
316
+ return text("Link payment-method setup was started. Run wallet_setup({ action: \"add-link\" }) again after adding a payment method in Link.");
317
+ }
318
+ }
319
+
320
+ if (methods.length === 1) {
321
+ const method = methods[0];
322
+ setLinkConfig({
323
+ paymentMethodId: method.id,
324
+ label: method.label,
325
+ });
326
+ const principal = await ensureConsumerPrincipal();
327
+ return text(
328
+ [
329
+ "Link payment method connected.",
330
+ ` Payment method: ${method.id}${method.label ? ` (${method.label})` : ""}`,
331
+ ` Consumer principal: ${principal}`,
332
+ "",
333
+ "Use pay_with: \"link\" for agent runs.",
334
+ ].join("\n"),
335
+ );
336
+ }
337
+
338
+ return text(
339
+ [
340
+ "Multiple Link payment methods found. Choose one:",
341
+ ...methods.map((method) => ` ${method.label ?? method.id}${method.label ? ` (${method.id})` : ""}`),
342
+ "",
343
+ "Then run:",
344
+ " wallet_setup({ action: \"add-link\", link_payment_method: \"<name or last4>\" })",
345
+ ].join("\n"),
346
+ );
347
+ }
348
+
349
+ if (action === "remove-link") {
350
+ const existing = getLinkConfig();
351
+ if (!existing) {
352
+ return text("No Link payment method is currently connected.");
353
+ }
354
+
355
+ setLinkConfig(null);
356
+ return text(`Removed Link payment method ${existing.paymentMethodId}.`);
357
+ }
358
+
143
359
  // ── Card setup flow ──────────────────────────────────────
144
360
  if (action === "add-card") {
145
361
  if (!isCardPaymentEnabled()) {
@@ -273,6 +489,7 @@ export function registerWalletTools(server: McpServer): void {
273
489
  chainStatus = "Already linked to Agent Wonderland.";
274
490
  }
275
491
  }
492
+ setDefaultCryptoPaymentMethod(defaultCh);
276
493
  const principal = await ensureConsumerPrincipal();
277
494
  return text([
278
495
  "Found existing OWS wallet:",
@@ -307,6 +524,7 @@ export function registerWalletTools(server: McpServer): void {
307
524
  defaultChain: "solana",
308
525
  label: walletName,
309
526
  });
527
+ setDefaultCryptoPaymentMethod("solana");
310
528
  const principal = await ensureConsumerPrincipal();
311
529
  const owsNudge = platformSupportsOws()
312
530
  ? "\n\n⚠ Key stored in plaintext at ~/.agentwonderland/config.json. Run wallet_setup({ action: \"enable-ows\" }) to install encrypted at-rest storage — takes ~30s."
@@ -336,6 +554,7 @@ export function registerWalletTools(server: McpServer): void {
336
554
  defaultChain: defaultCh === "solana" ? "tempo" : defaultCh,
337
555
  label: walletName,
338
556
  });
557
+ setDefaultCryptoPaymentMethod(defaultCh);
339
558
  const principal = await ensureConsumerPrincipal();
340
559
  const owsNudge = platformSupportsOws()
341
560
  ? "\n\n⚠ Key stored in plaintext at ~/.agentwonderland/config.json. Run wallet_setup({ action: \"enable-ows\" }) to install encrypted at-rest storage — takes ~30s."
@@ -367,6 +586,7 @@ export function registerWalletTools(server: McpServer): void {
367
586
  defaultChain: defaultCh,
368
587
  label: walletName,
369
588
  });
589
+ setDefaultCryptoPaymentMethod(defaultCh);
370
590
  const principal = await ensureConsumerPrincipal();
371
591
 
372
592
  return text(
@@ -400,6 +620,7 @@ export function registerWalletTools(server: McpServer): void {
400
620
  defaultChain: defaultCh,
401
621
  label: walletName,
402
622
  });
623
+ setDefaultCryptoPaymentMethod(defaultCh);
403
624
  const principal = await ensureConsumerPrincipal();
404
625
 
405
626
  return text(
@@ -432,6 +653,7 @@ export function registerWalletTools(server: McpServer): void {
432
653
  defaultChain: "solana",
433
654
  label: walletName,
434
655
  });
656
+ setDefaultCryptoPaymentMethod("solana");
435
657
  const principal = await ensureConsumerPrincipal();
436
658
  const owsNudge = platformSupportsOws()
437
659
  ? "\n\n⚠ Key stored in plaintext at ~/.agentwonderland/config.json. Run wallet_setup({ action: \"enable-ows\" }) to install encrypted at-rest storage — takes ~30s."
@@ -472,6 +694,7 @@ export function registerWalletTools(server: McpServer): void {
472
694
  defaultChain: defaultCh,
473
695
  label: walletName,
474
696
  });
697
+ setDefaultCryptoPaymentMethod(defaultCh);
475
698
  const principal = await ensureConsumerPrincipal();
476
699
 
477
700
  const owsNudge = platformSupportsOws()
@@ -1,2 +0,0 @@
1
- import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
- export declare function registerObservabilityTools(server: McpServer): void;
@@ -1,20 +0,0 @@
1
- import { apiPost } from "../core/api-client.js";
2
- function text(t) {
3
- return { content: [{ type: "text", text: t }] };
4
- }
5
- export function registerObservabilityTools(server) {
6
- server.tool("open_observability_dashboard", "Generate a secure one-click sign-in URL for the Agent Wonderland web observability dashboard. The dashboard shows your agent runs, spend, rebates, and recent activity.", {}, async () => {
7
- const result = await apiPost("/observability/link", {}, { ensureConsumerPrincipal: true });
8
- const lines = [
9
- "Your secure observability link is ready:",
10
- result.url,
11
- "",
12
- `Expires: ${result.expires_at}`,
13
- ];
14
- if (result.consumer_principal) {
15
- lines.push(`Consumer principal: ${result.consumer_principal}`);
16
- }
17
- lines.push("", "Open the link in your browser to view usage metrics, spend, rebates, and recent runs.");
18
- return text(lines.join("\n"));
19
- });
20
- }