@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.
- package/dist/core/__tests__/api-client.test.d.ts +1 -0
- package/dist/core/__tests__/api-client.test.js +51 -0
- package/dist/core/__tests__/formatters.test.js +10 -0
- package/dist/core/__tests__/passes-api.test.d.ts +1 -0
- package/dist/core/__tests__/passes-api.test.js +27 -0
- package/dist/core/__tests__/payments.test.js +10 -6
- package/dist/core/__tests__/principal.test.js +41 -4
- package/dist/core/__tests__/solana-charge.test.d.ts +1 -0
- package/dist/core/__tests__/solana-charge.test.js +50 -0
- package/dist/core/api-client.d.ts +1 -0
- package/dist/core/api-client.js +8 -3
- package/dist/core/balances.d.ts +1 -0
- package/dist/core/balances.js +56 -0
- package/dist/core/base-charge.js +13 -6
- package/dist/core/formatters.d.ts +3 -2
- package/dist/core/formatters.js +7 -1
- package/dist/core/passes.d.ts +1 -1
- package/dist/core/passes.js +5 -2
- package/dist/core/payments.d.ts +1 -0
- package/dist/core/payments.js +20 -7
- package/dist/core/principal.d.ts +3 -0
- package/dist/core/principal.js +29 -1
- package/dist/core/settings.d.ts +20 -0
- package/dist/core/settings.js +19 -0
- package/dist/core/solana-charge.d.ts +5 -0
- package/dist/core/solana-charge.js +29 -7
- package/dist/core/tempo-charge.d.ts +7 -0
- package/dist/core/tempo-charge.js +84 -0
- package/dist/index.js +5 -7
- package/dist/prompts/index.js +1 -1
- package/dist/tools/__tests__/jobs.test.d.ts +1 -0
- package/dist/tools/__tests__/jobs.test.js +71 -0
- package/dist/tools/__tests__/run.test.d.ts +1 -0
- package/dist/tools/__tests__/run.test.js +149 -0
- package/dist/tools/__tests__/solve.test.d.ts +1 -0
- package/dist/tools/__tests__/solve.test.js +158 -0
- package/dist/tools/__tests__/wallet.test.d.ts +1 -0
- package/dist/tools/__tests__/wallet.test.js +230 -0
- package/dist/tools/_payment-confirmation.js +1 -1
- package/dist/tools/agent-info.js +14 -28
- package/dist/tools/jobs.js +82 -16
- package/dist/tools/passes.js +30 -14
- package/dist/tools/run.js +35 -20
- package/dist/tools/search.js +9 -8
- package/dist/tools/solve.js +45 -25
- package/dist/tools/wallet.js +35 -15
- package/package.json +2 -2
- package/src/core/__tests__/api-client.test.ts +78 -0
- package/src/core/__tests__/formatters.test.ts +12 -0
- package/src/core/__tests__/passes-api.test.ts +33 -0
- package/src/core/__tests__/payments.test.ts +17 -6
- package/src/core/__tests__/principal.test.ts +49 -4
- package/src/core/__tests__/solana-charge.test.ts +59 -0
- package/src/core/api-client.ts +16 -3
- package/src/core/balances.ts +63 -0
- package/src/core/base-charge.ts +13 -6
- package/src/core/formatters.ts +10 -3
- package/src/core/passes.ts +5 -2
- package/src/core/payments.ts +22 -7
- package/src/core/principal.ts +42 -1
- package/src/core/settings.ts +36 -0
- package/src/core/solana-charge.ts +43 -9
- package/src/core/tempo-charge.ts +104 -0
- package/src/index.ts +5 -7
- package/src/prompts/index.ts +1 -1
- package/src/tools/__tests__/jobs.test.ts +89 -0
- package/src/tools/__tests__/run.test.ts +176 -0
- package/src/tools/__tests__/solve.test.ts +186 -0
- package/src/tools/__tests__/wallet.test.ts +289 -0
- package/src/tools/_payment-confirmation.ts +1 -1
- package/src/tools/agent-info.ts +15 -38
- package/src/tools/jobs.ts +79 -17
- package/src/tools/passes.ts +30 -14
- package/src/tools/run.ts +38 -20
- package/src/tools/search.ts +10 -9
- package/src/tools/solve.ts +48 -25
- package/src/tools/wallet.ts +33 -17
- 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("
|
|
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
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
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
|
-
|
|
152
|
-
|
|
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,
|
package/src/tools/search.ts
CHANGED
|
@@ -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
|
-
//
|
|
33
|
-
|
|
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
|
|
62
|
-
const
|
|
63
|
-
|
|
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
|
|
package/src/tools/solve.ts
CHANGED
|
@@ -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
|
-
.
|
|
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
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
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
|
-
|
|
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
|
|
216
|
+
const status =
|
|
204
217
|
err instanceof Error &&
|
|
205
|
-
"status" in err
|
|
206
|
-
|
|
207
|
-
|
|
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
|
-
|
|
241
|
-
|
|
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
|
-
|
|
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,
|
package/src/tools/wallet.ts
CHANGED
|
@@ -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
|
|
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
|
|
60
|
+
const chainLines = await Promise.all(
|
|
57
61
|
w.chains.map(async (chainName) => {
|
|
58
62
|
const addr = await getWalletAddress(chainName);
|
|
59
|
-
return `${chainName}:
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
104
|
+
// ── wallet_setup ────────────────────────────────────────────────
|
|
92
105
|
server.tool(
|
|
93
106
|
"wallet_setup",
|
|
94
|
-
"Set up or manage payment
|
|
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("'
|
|
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).
|
|
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
|
-
`
|
|
422
|
+
`Local spending policy set for wallet "${wallet_id}":`,
|
|
407
423
|
...policies.map((p) => ` ${p}`),
|
|
408
424
|
"",
|
|
409
|
-
"Policy
|
|
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
|
-
}
|