@agentwonderland/mcp 0.1.25 → 0.1.27

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.
Files changed (78) hide show
  1. package/dist/core/__tests__/api-client.test.d.ts +1 -0
  2. package/dist/core/__tests__/api-client.test.js +51 -0
  3. package/dist/core/__tests__/formatters.test.js +10 -0
  4. package/dist/core/__tests__/passes-api.test.d.ts +1 -0
  5. package/dist/core/__tests__/passes-api.test.js +27 -0
  6. package/dist/core/__tests__/payments.test.js +10 -6
  7. package/dist/core/__tests__/principal.test.js +41 -4
  8. package/dist/core/__tests__/solana-charge.test.d.ts +1 -0
  9. package/dist/core/__tests__/solana-charge.test.js +50 -0
  10. package/dist/core/api-client.d.ts +1 -0
  11. package/dist/core/api-client.js +8 -3
  12. package/dist/core/balances.d.ts +1 -0
  13. package/dist/core/balances.js +56 -0
  14. package/dist/core/base-charge.js +13 -6
  15. package/dist/core/formatters.d.ts +3 -2
  16. package/dist/core/formatters.js +7 -1
  17. package/dist/core/passes.d.ts +1 -1
  18. package/dist/core/passes.js +5 -2
  19. package/dist/core/payments.d.ts +1 -0
  20. package/dist/core/payments.js +20 -7
  21. package/dist/core/principal.d.ts +3 -0
  22. package/dist/core/principal.js +29 -1
  23. package/dist/core/settings.d.ts +20 -0
  24. package/dist/core/settings.js +19 -0
  25. package/dist/core/solana-charge.d.ts +5 -0
  26. package/dist/core/solana-charge.js +29 -7
  27. package/dist/core/tempo-charge.d.ts +7 -0
  28. package/dist/core/tempo-charge.js +84 -0
  29. package/dist/index.js +5 -7
  30. package/dist/prompts/index.js +1 -1
  31. package/dist/tools/__tests__/jobs.test.d.ts +1 -0
  32. package/dist/tools/__tests__/jobs.test.js +71 -0
  33. package/dist/tools/__tests__/run.test.d.ts +1 -0
  34. package/dist/tools/__tests__/run.test.js +149 -0
  35. package/dist/tools/__tests__/solve.test.d.ts +1 -0
  36. package/dist/tools/__tests__/solve.test.js +158 -0
  37. package/dist/tools/__tests__/wallet.test.d.ts +1 -0
  38. package/dist/tools/__tests__/wallet.test.js +230 -0
  39. package/dist/tools/_payment-confirmation.js +1 -1
  40. package/dist/tools/agent-info.js +14 -28
  41. package/dist/tools/jobs.js +82 -16
  42. package/dist/tools/passes.js +30 -14
  43. package/dist/tools/run.js +35 -20
  44. package/dist/tools/search.js +9 -8
  45. package/dist/tools/solve.js +45 -25
  46. package/dist/tools/wallet.js +35 -15
  47. package/package.json +2 -2
  48. package/src/core/__tests__/api-client.test.ts +78 -0
  49. package/src/core/__tests__/formatters.test.ts +12 -0
  50. package/src/core/__tests__/passes-api.test.ts +33 -0
  51. package/src/core/__tests__/payments.test.ts +17 -6
  52. package/src/core/__tests__/principal.test.ts +49 -4
  53. package/src/core/__tests__/solana-charge.test.ts +59 -0
  54. package/src/core/api-client.ts +16 -3
  55. package/src/core/balances.ts +63 -0
  56. package/src/core/base-charge.ts +13 -6
  57. package/src/core/formatters.ts +10 -3
  58. package/src/core/passes.ts +5 -2
  59. package/src/core/payments.ts +22 -7
  60. package/src/core/principal.ts +42 -1
  61. package/src/core/settings.ts +36 -0
  62. package/src/core/solana-charge.ts +43 -9
  63. package/src/core/tempo-charge.ts +104 -0
  64. package/src/index.ts +5 -7
  65. package/src/prompts/index.ts +1 -1
  66. package/src/tools/__tests__/jobs.test.ts +89 -0
  67. package/src/tools/__tests__/run.test.ts +176 -0
  68. package/src/tools/__tests__/solve.test.ts +186 -0
  69. package/src/tools/__tests__/wallet.test.ts +289 -0
  70. package/src/tools/_payment-confirmation.ts +1 -1
  71. package/src/tools/agent-info.ts +15 -38
  72. package/src/tools/jobs.ts +79 -17
  73. package/src/tools/passes.ts +30 -14
  74. package/src/tools/run.ts +38 -20
  75. package/src/tools/search.ts +10 -9
  76. package/src/tools/solve.ts +48 -25
  77. package/src/tools/wallet.ts +33 -17
  78. package/src/tools/observability.ts +0 -43
package/src/tools/run.ts CHANGED
@@ -14,6 +14,7 @@ import {
14
14
  hasWalletConfigured,
15
15
  getWalletAddress,
16
16
  normalizePaymentMethod,
17
+ isCardPaymentEnabled,
17
18
  } from "../core/payments.js";
18
19
  import { requiresSpendConfirmation, getDefaultTipAmount } from "../core/config.js";
19
20
  import { formatRunResult } from "../core/formatters.js";
@@ -32,9 +33,10 @@ const POLL_MAX_MS = 120000;
32
33
 
33
34
  async function pollJobUntilDone(
34
35
  jobId: string,
36
+ paymentMethod?: string,
35
37
  ): Promise<{ status: string; output?: unknown; error_code?: string }> {
36
38
  const deadline = Date.now() + POLL_MAX_MS;
37
- const walletAddress = await getWalletAddress();
39
+ const walletAddress = await getWalletAddress(paymentMethod);
38
40
  const walletParam = walletAddress ? `?wallet=${walletAddress}` : "";
39
41
 
40
42
  while (Date.now() < deadline) {
@@ -100,21 +102,30 @@ export function registerRunTools(server: McpServer): void {
100
102
  {
101
103
  agent_id: z.string().describe("Agent ID (UUID, slug, or name)"),
102
104
  input: z.record(z.unknown()).describe("Input payload for the agent"),
103
- pay_with: z.string().trim().min(1).describe("Required payment method — wallet ID, chain name (tempo, base, etc.), or 'card'. Use wallet_status to see configured options."),
105
+ pay_with: z.string().trim().min(1).optional().describe("Payment method — wallet ID, chain name (tempo, base, etc.), or 'card'. Auto-detected if omitted."),
104
106
  confirmed: z.boolean().optional().describe("Set to true to confirm spending after seeing the price quote."),
105
107
  },
106
108
  async ({ agent_id, input, pay_with, confirmed }) => {
107
109
  if (!hasWalletConfigured()) {
108
- try {
109
- const { url } = await getOrCreatePendingCardSetup();
110
- return multiText(...formatCardSetupBlocks(url));
111
- } catch {
112
- return text(
113
- "No payment method configured.\n\n" +
114
- "To add a credit card: wallet_setup({ action: \"add-card\" })\n" +
115
- "To use crypto: wallet_setup({ action: \"create\" })",
116
- );
110
+ if (isCardPaymentEnabled()) {
111
+ try {
112
+ const { url } = await getOrCreatePendingCardSetup();
113
+ return multiText(...formatCardSetupBlocks(url));
114
+ } catch {
115
+ // Fall through to the setup message below.
116
+ }
117
+ }
118
+ const setupLines = [
119
+ "No payment method configured.",
120
+ "",
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.",
124
+ ];
125
+ if (isCardPaymentEnabled()) {
126
+ setupLines.push("", "Or wallet_setup({ action: \"add-card\" }) to connect a credit card.");
117
127
  }
128
+ return text(setupLines.join("\n"));
118
129
  }
119
130
 
120
131
  let agent: AgentRecord;
@@ -126,21 +137,21 @@ export function registerRunTools(server: McpServer): void {
126
137
 
127
138
  const price = parseFloat(agent.pricePerRunUsd ?? "0.01");
128
139
  const agentName = agent.name ?? agent_id;
129
- const creditPackInventory = await getCreditPackInventory(agent.id);
130
- const activeCreditPack = getActiveCreditPack(creditPackInventory);
131
140
  const compatibleMethods = getCompatiblePaymentMethods(agent, getConfiguredMethods());
132
141
  const pending = pendingRuns.get(agent.id);
133
- const requestedMethod = pay_with;
134
- const normalizedRequestedMethod = normalizePaymentMethod(requestedMethod);
142
+ const requestedMethod = pay_with ?? pending?.method;
143
+ const normalizedRequestedMethod = requestedMethod ? normalizePaymentMethod(requestedMethod) : null;
144
+ const creditPackInventory = await getCreditPackInventory(agent.id, requestedMethod);
145
+ const activeCreditPack = getActiveCreditPack(creditPackInventory);
135
146
 
136
- if (!normalizedRequestedMethod) {
147
+ if (requestedMethod && !normalizedRequestedMethod) {
137
148
  return text(
138
149
  `Payment method "${requestedMethod}" is not configured.\n\n` +
139
150
  "Use wallet_status to review your current payment methods.",
140
151
  );
141
152
  }
142
153
 
143
- if (!compatibleMethods.includes(normalizedRequestedMethod)) {
154
+ if (normalizedRequestedMethod && !compatibleMethods.includes(normalizedRequestedMethod)) {
144
155
  return text(
145
156
  `This agent cannot be paid with "${requestedMethod}".\n\n` +
146
157
  `Available payment methods for this agent: ${compatibleMethods.join(", ") || "none"}.\n` +
@@ -148,8 +159,15 @@ export function registerRunTools(server: McpServer): void {
148
159
  );
149
160
  }
150
161
 
151
- const method = resolveConfirmationMethod(pay_with, pending?.method, compatibleMethods);
152
- const spendCheckMethod = method ?? normalizedRequestedMethod;
162
+ if (!activeCreditPack && compatibleMethods.length === 0) {
163
+ return text(
164
+ `No compatible payment methods are configured for ${agentName}.\n\n` +
165
+ "Use wallet_status to review your current payment methods.",
166
+ );
167
+ }
168
+
169
+ const method = resolveConfirmationMethod(requestedMethod, pending?.method, compatibleMethods);
170
+ const spendCheckMethod = method ?? normalizedRequestedMethod ?? compatibleMethods[0];
153
171
 
154
172
  if (!activeCreditPack) {
155
173
  const spendCheck = canSpend({
@@ -278,7 +296,7 @@ export function registerRunTools(server: McpServer): void {
278
296
  const usedCreditPack = result.consumption_mode === "credit_pack";
279
297
 
280
298
  if (status === "processing") {
281
- const pollResult = await pollJobUntilDone(jobId);
299
+ const pollResult = await pollJobUntilDone(jobId, method);
282
300
  if (pollResult.status === "completed") {
283
301
  const asyncFormatted = formatRunResult({
284
302
  ...result,
@@ -24,13 +24,14 @@ export function registerSearchTools(server: McpServer): void {
24
24
  .describe("Sort results by: relevance (default), price, rating, popularity, or newest"),
25
25
  },
26
26
  async ({ query, tag, limit, max_price, min_rating, sort }) => {
27
- const requestedLimit = limit ?? 10;
27
+ const requestedLimit = Math.max(1, Math.min(50, limit ?? 10));
28
28
  const params = new URLSearchParams();
29
29
  if (query) params.set("q", query);
30
30
  if (tag) params.set("tag", tag);
31
31
 
32
- // When filtering by min_rating client-side, request more results
33
- const apiLimit = min_rating ? requestedLimit * 3 : requestedLimit;
32
+ // min_rating is filtered client-side on avgRating. Request extra candidates
33
+ // so the post-filter result still has a chance of meeting requestedLimit.
34
+ const apiLimit = min_rating ? Math.min(100, requestedLimit * 3) : requestedLimit;
34
35
  params.set("limit", String(apiLimit));
35
36
 
36
37
  if (max_price) params.set("price_max", String(max_price));
@@ -56,13 +57,13 @@ export function registerSearchTools(server: McpServer): void {
56
57
 
57
58
  let agents = await apiGet<AgentRecord[]>(`/agents?${params}`);
58
59
 
59
- // Client-side min_rating filter
60
+ // Client-side min_rating filter — avgRating is the 1-5 user-facing score.
61
+ // Agents with no ratings yet (avgRating == null) are excluded when a
62
+ // minimum is requested, matching user expectation of "at least N stars".
60
63
  if (min_rating) {
61
- agents = agents.filter((a: any) => {
62
- const rating = a.reputationScore ?? a.avgRating ?? 0;
63
- // Convert 0-1 reputation to 1-5 scale if needed
64
- const stars = rating <= 1 ? rating * 5 : rating;
65
- return stars >= min_rating;
64
+ agents = agents.filter((a) => {
65
+ const avg = a.avgRating ?? (a.stats as { avgRating?: number | null } | undefined)?.avgRating;
66
+ return typeof avg === "number" && avg >= min_rating;
66
67
  });
67
68
  }
68
69
 
@@ -10,6 +10,7 @@ import {
10
10
  getWalletAddress,
11
11
  normalizePaymentMethod,
12
12
  toRegistryPaymentMethod,
13
+ isCardPaymentEnabled,
13
14
  } from "../core/payments.js";
14
15
  import { requiresSpendConfirmation, getDefaultTipAmount } from "../core/config.js";
15
16
  import { agentList, formatRunResult } from "../core/formatters.js";
@@ -30,9 +31,10 @@ const POLL_MAX_MS = 120000;
30
31
 
31
32
  async function pollSolveJob(
32
33
  jobId: string,
34
+ paymentMethod?: string,
33
35
  ): Promise<{ status: string; output?: unknown; error_code?: string }> {
34
36
  const deadline = Date.now() + POLL_MAX_MS;
35
- const walletAddress = await getWalletAddress();
37
+ const walletAddress = await getWalletAddress(paymentMethod);
36
38
  const walletParam = walletAddress ? `?wallet=${walletAddress}` : "";
37
39
 
38
40
  while (Date.now() < deadline) {
@@ -139,7 +141,8 @@ export function registerSolveTools(server: McpServer): void {
139
141
  .string()
140
142
  .trim()
141
143
  .min(1)
142
- .describe("Required payment method — wallet ID, chain name (tempo, base, etc.), or 'card'. Use wallet_status to see configured options."),
144
+ .optional()
145
+ .describe("Payment method — wallet ID, chain name (tempo, base, etc.), or 'card'. Auto-detected if omitted."),
143
146
  confirmed: z
144
147
  .boolean()
145
148
  .optional()
@@ -147,25 +150,34 @@ export function registerSolveTools(server: McpServer): void {
147
150
  },
148
151
  async ({ intent, input, budget, pay_with, confirmed }) => {
149
152
  if (!hasWalletConfigured()) {
150
- try {
151
- const { url } = await getOrCreatePendingCardSetup();
152
- return multiText(...formatCardSetupBlocks(url));
153
- } catch {
154
- return text(
155
- "No payment method configured.\n\n" +
156
- "To add a credit card: wallet_setup({ action: \"add-card\" })\n" +
157
- "To use crypto: wallet_setup({ action: \"create\" })",
158
- );
153
+ if (isCardPaymentEnabled()) {
154
+ try {
155
+ const { url } = await getOrCreatePendingCardSetup();
156
+ return multiText(...formatCardSetupBlocks(url));
157
+ } catch {
158
+ // Fall through to the setup message below.
159
+ }
160
+ }
161
+ const setupLines = [
162
+ "No payment method configured.",
163
+ "",
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.",
167
+ ];
168
+ if (isCardPaymentEnabled()) {
169
+ setupLines.push("", "Or wallet_setup({ action: \"add-card\" }) to connect a credit card.");
159
170
  }
171
+ return text(setupLines.join("\n"));
160
172
  }
161
173
 
162
174
  const pendingKey = makeSolvePendingKey(intent, input, budget);
163
175
  const pending = pendingSolves.get(pendingKey);
164
176
  const configuredMethods = getConfiguredMethods();
165
- const requestedMethod = pay_with;
166
- const normalizedRequestedMethod = normalizePaymentMethod(requestedMethod);
177
+ const requestedMethod = pay_with ?? pending?.method;
178
+ const normalizedRequestedMethod = requestedMethod ? normalizePaymentMethod(requestedMethod) : null;
167
179
 
168
- if (!normalizedRequestedMethod) {
180
+ if (requestedMethod && !normalizedRequestedMethod) {
169
181
  return text(
170
182
  `Payment method "${requestedMethod}" is not configured.\n\n` +
171
183
  "Use wallet_status to review your current payment methods.",
@@ -198,13 +210,16 @@ export function registerSolveTools(server: McpServer): void {
198
210
  const cost = (result as Record<string, unknown>).cost as number | undefined;
199
211
  const usedCreditPack = (result as Record<string, unknown>).consumption_mode === "credit_pack";
200
212
  const tipMsg = result.feedback_token ? await autoTip(jobId, agentId, agentName, result.feedback_token as string) : "";
201
- return multiText(formatRunResult(result), feedbackAsk(jobId, agentId, usedCreditPack ? undefined : cost, tipMsg));
213
+ const header = agentName ? [`Matched agent: ${agentName}`, ""].join("\n") : "";
214
+ return multiText(header + formatRunResult(result), feedbackAsk(jobId, agentId, usedCreditPack ? undefined : cost, tipMsg));
202
215
  } catch (err: unknown) {
203
- const isAuthError =
216
+ const status =
204
217
  err instanceof Error &&
205
- "status" in err &&
206
- (err as { status: number }).status === 401;
207
- if (!isAuthError || !hasWalletConfigured()) throw err;
218
+ "status" in err
219
+ ? (err as { status: number }).status
220
+ : undefined;
221
+ const isRecoverableDirectSolveError = status === 401 || status === 402;
222
+ if (!isRecoverableDirectSolveError || !hasWalletConfigured()) throw err;
208
223
  }
209
224
 
210
225
  const params = new URLSearchParams({ q: intent, limit: "5" });
@@ -226,10 +241,10 @@ export function registerSolveTools(server: McpServer): void {
226
241
  return price <= budget;
227
242
  });
228
243
  const selected = affordable[0] ?? agents[0];
229
- const activeCreditPack = await getCreditPackInventory(selected.id).then(getActiveCreditPack);
244
+ const activeCreditPack = await getCreditPackInventory(selected.id, requestedMethod).then(getActiveCreditPack);
230
245
  const compatibleMethods = getCompatiblePaymentMethods(selected, configuredMethods);
231
246
 
232
- if (!compatibleMethods.includes(normalizedRequestedMethod)) {
247
+ if (normalizedRequestedMethod && !compatibleMethods.includes(normalizedRequestedMethod)) {
233
248
  return text(
234
249
  `The best matching agent cannot be paid with "${requestedMethod}".\n\n` +
235
250
  `Available payment methods for ${selected.name}: ${compatibleMethods.join(", ") || "none"}.\n` +
@@ -237,8 +252,15 @@ export function registerSolveTools(server: McpServer): void {
237
252
  );
238
253
  }
239
254
 
240
- const method = resolveConfirmationMethod(pay_with, pending?.method, compatibleMethods);
241
- const spendCheckMethod = method ?? normalizedRequestedMethod;
255
+ if (!activeCreditPack && compatibleMethods.length === 0) {
256
+ return text(
257
+ `No compatible payment methods are configured for ${selected.name}.\n\n` +
258
+ "Use wallet_status to review your current payment methods.",
259
+ );
260
+ }
261
+
262
+ const method = resolveConfirmationMethod(requestedMethod, pending?.method, compatibleMethods);
263
+ const spendCheckMethod = method ?? normalizedRequestedMethod ?? compatibleMethods[0];
242
264
 
243
265
  const selectedPrice = parseFloat(selected.pricePerRunUsd ?? "0.01");
244
266
  const estimatedCost = selectedPrice;
@@ -307,9 +329,10 @@ export function registerSolveTools(server: McpServer): void {
307
329
  } catch (err: unknown) {
308
330
  const apiErr = err as { status?: number; message?: string };
309
331
  if (apiErr?.status === 402) {
332
+ const reason = apiErr.message ? `Payment failed: ${apiErr.message}` : "Payment failed — wallet may not have enough funds or the selected method was rejected.";
310
333
  return text(
311
334
  [
312
- "Payment failed — your wallet may not have enough funds or the selected method was rejected.",
335
+ reason,
313
336
  "",
314
337
  "Check your balance and try again.",
315
338
  "Use wallet_status to check your current payment methods.",
@@ -334,7 +357,7 @@ export function registerSolveTools(server: McpServer): void {
334
357
  }
335
358
 
336
359
  if (status === "processing") {
337
- const pollResult = await pollSolveJob(jobId);
360
+ const pollResult = await pollSolveJob(jobId, method);
338
361
  if (pollResult.status === "completed") {
339
362
  const asyncFormatted = formatRunResult({
340
363
  ...result,
@@ -9,7 +9,9 @@ import {
9
9
  getSpendPolicy,
10
10
  setSpendPolicy,
11
11
  } from "../core/config.js";
12
- import { getWalletAddress } from "../core/payments.js";
12
+ import { getWalletAddress, isCardPaymentEnabled } from "../core/payments.js";
13
+ import { fetchUsdcBalance } from "../core/balances.js";
14
+ import { getSettings } from "../core/settings.js";
13
15
  import {
14
16
  getOrCreatePendingCardSetup,
15
17
  formatCardSetupBlocks,
@@ -47,25 +49,36 @@ export function registerWalletTools(server: McpServer): void {
47
49
  );
48
50
  }
49
51
 
50
- const lines = ["Payment methods:"];
52
+ const settings = await getSettings();
53
+ const networkLabel = settings ? ` (${settings.network})` : "";
54
+ const lines = [`Payment methods${networkLabel}:`];
51
55
 
52
56
  for (const w of wallets) {
53
57
  const label = w.label ? ` (${w.label})` : "";
54
58
  const storage =
55
59
  w.keyType === "ows" ? " [encrypted]" : " [plaintext]";
56
- const chainAddresses = await Promise.all(
60
+ const chainLines = await Promise.all(
57
61
  w.chains.map(async (chainName) => {
58
62
  const addr = await getWalletAddress(chainName);
59
- return `${chainName}: ${addr ?? "unknown"}`;
63
+ if (!addr) return `${chainName}: unknown`;
64
+ let balanceStr = "";
65
+ if (chainName === "tempo" || chainName === "base" || chainName === "solana") {
66
+ const balance = await fetchUsdcBalance(chainName, addr);
67
+ if (balance !== null) {
68
+ const num = Number(balance);
69
+ balanceStr = ` ${Number.isFinite(num) ? num.toFixed(4).replace(/\.?0+$/, "") : balance} USDC`;
70
+ }
71
+ }
72
+ return `${chainName}: ${addr}${balanceStr}`;
60
73
  }),
61
74
  );
62
- lines.push(
63
- ` ${w.id}${label}${storage}: ${chainAddresses.join(" | ")}`,
64
- );
75
+ lines.push(` ${w.id}${label}${storage}:`);
76
+ for (const line of chainLines) lines.push(` ${line}`);
65
77
  }
66
78
 
67
- if (card) {
68
- lines.push(` Card: ${card.brand} ****${card.last4}`);
79
+ if (card && isCardPaymentEnabled()) {
80
+ const stripeMode = settings?.stripe.mode === "test" ? " [Stripe test mode]" : "";
81
+ lines.push(` Card: ${card.brand} ****${card.last4}${stripeMode}`);
69
82
  const capabilities = await getCardCapabilities();
70
83
  if (capabilities.spt_status === "enabled") {
71
84
  lines.push(" Card MPP: ready");
@@ -88,14 +101,14 @@ export function registerWalletTools(server: McpServer): void {
88
101
  },
89
102
  );
90
103
 
91
- // ── wallet_setup (NEW) ──────────────────────────────────────────
104
+ // ── wallet_setup ────────────────────────────────────────────────
92
105
  server.tool(
93
106
  "wallet_setup",
94
- "Set up or manage payment methods. Options: 'add-card' to connect a credit/debit card (recommended), 'remove-card' to disconnect a card, 'create' a crypto wallet, or 'import' an existing key. For crypto wallet changes (removal, key rotation), direct users to edit their config files manually never handle private keys programmatically.",
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.",
95
108
  {
96
109
  action: z
97
110
  .enum(["create", "import", "add-card", "remove-card"])
98
- .describe("'add-card' to connect a card, 'remove-card' to disconnect it, 'create' a crypto wallet, or 'import' an existing key"),
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)"),
99
112
  name: z
100
113
  .string()
101
114
  .optional()
@@ -107,11 +120,14 @@ export function registerWalletTools(server: McpServer): void {
107
120
  "Private key hex string (required for 'import', ignored for 'create')",
108
121
  ),
109
122
  chain: z.enum(["tempo", "base", "solana"]).optional()
110
- .describe("Primary chain (default: tempo). Solana uses an OWS wallet plus Stripe deposit-mode USDC."),
123
+ .describe("Primary chain (default: tempo). Tempo/Base use a shared EVM wallet; Solana uses a separate OWS wallet."),
111
124
  },
112
125
  async ({ action, name, key, chain }) => {
113
126
  // ── Card setup flow ──────────────────────────────────────
114
127
  if (action === "add-card") {
128
+ if (!isCardPaymentEnabled()) {
129
+ return text("Card payments are temporarily unavailable. Use wallet_setup({ action: \"create\" }) for a crypto wallet instead.");
130
+ }
115
131
  const existing = getCardConfig();
116
132
  if (existing) {
117
133
  return text(`Card already connected: ${existing.brand} ****${existing.last4}\n\nTo replace it, remove the current card first.`);
@@ -341,9 +357,9 @@ export function registerWalletTools(server: McpServer): void {
341
357
  // ── wallet_set_policy (NEW) ─────────────────────────────────────
342
358
  server.tool(
343
359
  "wallet_set_policy",
344
- "Set spending limits on a wallet to control agent costs. Limits reset daily.",
360
+ "Set client-side spending limits on a wallet to control agent costs in this MCP client. Limits reset daily.",
345
361
  {
346
- wallet_id: z.string().describe("Wallet ID to set policy on"),
362
+ wallet_id: z.string().describe("Wallet ID to set local policy on"),
347
363
  max_per_tx: z
348
364
  .number()
349
365
  .positive()
@@ -403,10 +419,10 @@ export function registerWalletTools(server: McpServer): void {
403
419
  }
404
420
  return text(
405
421
  [
406
- `Spending policy set for wallet "${wallet_id}":`,
422
+ `Local spending policy set for wallet "${wallet_id}":`,
407
423
  ...policies.map((p) => ` ${p}`),
408
424
  "",
409
- "Policy will be enforced on all future transactions from this wallet.",
425
+ "Policy is stored in this MCP client's local config and enforced on future transactions from this wallet on this client.",
410
426
  ].join("\n"),
411
427
  );
412
428
  },
@@ -1,43 +0,0 @@
1
- import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
- import { apiPost } from "../core/api-client.js";
3
-
4
- function text(t: string) {
5
- return { content: [{ type: "text" as const, text: t }] };
6
- }
7
-
8
- export function registerObservabilityTools(server: McpServer): void {
9
- server.tool(
10
- "open_observability_dashboard",
11
- "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.",
12
- {},
13
- async () => {
14
- const result = await apiPost<{
15
- url: string;
16
- expires_at: string;
17
- consumer_principal?: string;
18
- }>(
19
- "/observability/link",
20
- {},
21
- { ensureConsumerPrincipal: true },
22
- );
23
-
24
- const lines = [
25
- "Your secure observability link is ready:",
26
- result.url,
27
- "",
28
- `Expires: ${result.expires_at}`,
29
- ];
30
-
31
- if (result.consumer_principal) {
32
- lines.push(`Consumer principal: ${result.consumer_principal}`);
33
- }
34
-
35
- lines.push(
36
- "",
37
- "Open the link in your browser to view usage metrics, spend, rebates, and recent runs.",
38
- );
39
-
40
- return text(lines.join("\n"));
41
- },
42
- );
43
- }