@agentwonderland/mcp 0.1.24 → 0.1.26
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__/amount-utils.test.d.ts +1 -0
- package/dist/core/__tests__/amount-utils.test.js +11 -0
- 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 +59 -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/__tests__/spend-policy.test.d.ts +1 -0
- package/dist/core/__tests__/spend-policy.test.js +40 -0
- package/dist/core/amount-utils.d.ts +1 -0
- package/dist/core/amount-utils.js +4 -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 +16 -8
- package/dist/core/config.d.ts +19 -0
- package/dist/core/config.js +22 -0
- package/dist/core/formatters.d.ts +5 -5
- package/dist/core/formatters.js +12 -8
- 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 +32 -9
- 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 +31 -8
- package/dist/core/spend-policy.d.ts +12 -0
- package/dist/core/spend-policy.js +53 -0
- package/dist/core/tempo-charge.d.ts +7 -0
- package/dist/core/tempo-charge.js +84 -0
- package/dist/core/types.d.ts +1 -2
- package/dist/index.js +9 -5
- package/dist/prompts/index.js +4 -2
- package/dist/resources/agents.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 +2 -2
- package/dist/tools/favorites.js +1 -1
- package/dist/tools/jobs.js +8 -1
- package/dist/tools/observability.d.ts +2 -0
- package/dist/tools/observability.js +20 -0
- package/dist/tools/passes.js +11 -6
- package/dist/tools/run.js +45 -29
- package/dist/tools/solve.js +53 -40
- package/dist/tools/wallet.js +58 -22
- package/package.json +2 -2
- package/src/core/__tests__/amount-utils.test.ts +13 -0
- 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 +79 -6
- package/src/core/__tests__/principal.test.ts +49 -4
- package/src/core/__tests__/solana-charge.test.ts +59 -0
- package/src/core/__tests__/spend-policy.test.ts +58 -0
- package/src/core/amount-utils.ts +5 -0
- package/src/core/api-client.ts +16 -3
- package/src/core/balances.ts +63 -0
- package/src/core/base-charge.ts +16 -8
- package/src/core/config.ts +45 -0
- package/src/core/formatters.ts +16 -11
- package/src/core/passes.ts +5 -2
- package/src/core/payments.ts +37 -9
- package/src/core/principal.ts +42 -1
- package/src/core/settings.ts +36 -0
- package/src/core/solana-charge.ts +45 -10
- package/src/core/spend-policy.ts +69 -0
- package/src/core/tempo-charge.ts +104 -0
- package/src/core/types.ts +1 -2
- package/src/index.ts +9 -5
- package/src/prompts/index.ts +4 -2
- package/src/resources/agents.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 +2 -2
- package/src/tools/favorites.ts +1 -4
- package/src/tools/jobs.ts +10 -1
- package/src/tools/observability.ts +43 -0
- package/src/tools/passes.ts +12 -12
- package/src/tools/run.ts +50 -41
- package/src/tools/solve.ts +58 -52
- package/src/tools/wallet.ts +60 -24
package/src/tools/solve.ts
CHANGED
|
@@ -13,11 +13,11 @@ import {
|
|
|
13
13
|
} from "../core/payments.js";
|
|
14
14
|
import { requiresSpendConfirmation, getDefaultTipAmount } from "../core/config.js";
|
|
15
15
|
import { agentList, formatRunResult } from "../core/formatters.js";
|
|
16
|
+
import { canSpend, recordSpend, requiresPolicyConfirmation } from "../core/spend-policy.js";
|
|
16
17
|
import { uploadLocalFiles } from "../core/file-upload.js";
|
|
17
18
|
import type { AgentRecord } from "../core/types.js";
|
|
18
19
|
import { storeFeedbackToken } from "./_token-cache.js";
|
|
19
20
|
import {
|
|
20
|
-
formatPaymentChoicePrompt,
|
|
21
21
|
formatPaymentLabel,
|
|
22
22
|
formatSolveConfirmationCommand,
|
|
23
23
|
makeSolvePendingKey,
|
|
@@ -30,9 +30,10 @@ const POLL_MAX_MS = 120000;
|
|
|
30
30
|
|
|
31
31
|
async function pollSolveJob(
|
|
32
32
|
jobId: string,
|
|
33
|
+
paymentMethod?: string,
|
|
33
34
|
): Promise<{ status: string; output?: unknown; error_code?: string }> {
|
|
34
35
|
const deadline = Date.now() + POLL_MAX_MS;
|
|
35
|
-
const walletAddress = await getWalletAddress();
|
|
36
|
+
const walletAddress = await getWalletAddress(paymentMethod);
|
|
36
37
|
const walletParam = walletAddress ? `?wallet=${walletAddress}` : "";
|
|
37
38
|
|
|
38
39
|
while (Date.now() < deadline) {
|
|
@@ -137,6 +138,8 @@ export function registerSolveTools(server: McpServer): void {
|
|
|
137
138
|
.describe("Maximum budget in USD"),
|
|
138
139
|
pay_with: z
|
|
139
140
|
.string()
|
|
141
|
+
.trim()
|
|
142
|
+
.min(1)
|
|
140
143
|
.optional()
|
|
141
144
|
.describe("Payment method — wallet ID, chain name (tempo, base, etc.), or 'card'. Auto-detected if omitted."),
|
|
142
145
|
confirmed: z
|
|
@@ -199,11 +202,13 @@ export function registerSolveTools(server: McpServer): void {
|
|
|
199
202
|
const tipMsg = result.feedback_token ? await autoTip(jobId, agentId, agentName, result.feedback_token as string) : "";
|
|
200
203
|
return multiText(formatRunResult(result), feedbackAsk(jobId, agentId, usedCreditPack ? undefined : cost, tipMsg));
|
|
201
204
|
} catch (err: unknown) {
|
|
202
|
-
const
|
|
205
|
+
const status =
|
|
203
206
|
err instanceof Error &&
|
|
204
|
-
"status" in err
|
|
205
|
-
|
|
206
|
-
|
|
207
|
+
"status" in err
|
|
208
|
+
? (err as { status: number }).status
|
|
209
|
+
: undefined;
|
|
210
|
+
const isRecoverableDirectSolveError = status === 401 || status === 402;
|
|
211
|
+
if (!isRecoverableDirectSolveError || !hasWalletConfigured()) throw err;
|
|
207
212
|
}
|
|
208
213
|
|
|
209
214
|
const params = new URLSearchParams({ q: intent, limit: "5" });
|
|
@@ -220,17 +225,15 @@ export function registerSolveTools(server: McpServer): void {
|
|
|
220
225
|
}
|
|
221
226
|
|
|
222
227
|
const discovery = agentList(agents, intent);
|
|
223
|
-
const inputTokens = Math.ceil(JSON.stringify(input).length / 4);
|
|
224
228
|
const affordable = agents.filter((agent) => {
|
|
225
|
-
const price = parseFloat(agent.
|
|
226
|
-
|
|
227
|
-
return cost <= budget;
|
|
229
|
+
const price = parseFloat(agent.pricePerRunUsd ?? "0.01");
|
|
230
|
+
return price <= budget;
|
|
228
231
|
});
|
|
229
232
|
const selected = affordable[0] ?? agents[0];
|
|
230
|
-
const activeCreditPack = await getCreditPackInventory(selected.id).then(getActiveCreditPack);
|
|
233
|
+
const activeCreditPack = await getCreditPackInventory(selected.id, requestedMethod).then(getActiveCreditPack);
|
|
231
234
|
const compatibleMethods = getCompatiblePaymentMethods(selected, configuredMethods);
|
|
232
235
|
|
|
233
|
-
if (
|
|
236
|
+
if (normalizedRequestedMethod && !compatibleMethods.includes(normalizedRequestedMethod)) {
|
|
234
237
|
return text(
|
|
235
238
|
`The best matching agent cannot be paid with "${requestedMethod}".\n\n` +
|
|
236
239
|
`Available payment methods for ${selected.name}: ${compatibleMethods.join(", ") || "none"}.\n` +
|
|
@@ -238,39 +241,21 @@ export function registerSolveTools(server: McpServer): void {
|
|
|
238
241
|
);
|
|
239
242
|
}
|
|
240
243
|
|
|
241
|
-
if (!activeCreditPack &&
|
|
244
|
+
if (!activeCreditPack && compatibleMethods.length === 0) {
|
|
242
245
|
return text(
|
|
243
246
|
`No compatible payment methods are configured for ${selected.name}.\n\n` +
|
|
244
|
-
|
|
245
|
-
`Agent accepts: ${selected.payment?.accepted_payments?.join(", ") || "unknown"}\n` +
|
|
246
|
-
"Use wallet_status to review your current setup.",
|
|
247
|
+
"Use wallet_status to review your current payment methods.",
|
|
247
248
|
);
|
|
248
249
|
}
|
|
249
250
|
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
discovery,
|
|
253
|
-
"",
|
|
254
|
-
formatPaymentChoicePrompt(
|
|
255
|
-
selected.name ?? selected.id,
|
|
256
|
-
compatibleMethods,
|
|
257
|
-
compatibleMethods.map((method) =>
|
|
258
|
-
formatSolveConfirmationCommand(intent, budget, method).replace(", confirmed: true", ""),
|
|
259
|
-
),
|
|
260
|
-
),
|
|
261
|
-
].join("\n"));
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
const method = activeCreditPack
|
|
265
|
-
? undefined
|
|
266
|
-
: resolveConfirmationMethod(pay_with, pending?.method, compatibleMethods);
|
|
251
|
+
const method = resolveConfirmationMethod(requestedMethod, pending?.method, compatibleMethods);
|
|
252
|
+
const spendCheckMethod = method ?? normalizedRequestedMethod ?? compatibleMethods[0];
|
|
267
253
|
|
|
268
|
-
const selectedPrice = parseFloat(selected.
|
|
269
|
-
const estimatedCost =
|
|
270
|
-
? selectedPrice
|
|
271
|
-
: (inputTokens / 1000) * selectedPrice;
|
|
254
|
+
const selectedPrice = parseFloat(selected.pricePerRunUsd ?? "0.01");
|
|
255
|
+
const estimatedCost = selectedPrice;
|
|
272
256
|
|
|
273
|
-
|
|
257
|
+
const needsPolicyConfirmation = !activeCreditPack && requiresPolicyConfirmation(spendCheckMethod, estimatedCost);
|
|
258
|
+
if (!activeCreditPack && (requiresSpendConfirmation() || needsPolicyConfirmation) && !confirmed) {
|
|
274
259
|
pendingSolves.set(pendingKey, { method });
|
|
275
260
|
return text([
|
|
276
261
|
discovery,
|
|
@@ -291,26 +276,47 @@ export function registerSolveTools(server: McpServer): void {
|
|
|
291
276
|
}
|
|
292
277
|
|
|
293
278
|
let result: Record<string, unknown>;
|
|
279
|
+
if (!activeCreditPack) {
|
|
280
|
+
const spendCheck = canSpend({
|
|
281
|
+
method: spendCheckMethod,
|
|
282
|
+
amountUsd: estimatedCost,
|
|
283
|
+
});
|
|
284
|
+
if (!spendCheck.ok) {
|
|
285
|
+
return text(spendCheck.message);
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
|
|
294
289
|
try {
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
290
|
+
let usedPaidMethod = !activeCreditPack;
|
|
291
|
+
if (activeCreditPack) {
|
|
292
|
+
try {
|
|
293
|
+
result = await apiPost<Record<string, unknown>>(
|
|
294
|
+
`/agents/${selected.id}/run`,
|
|
295
|
+
{ input: processedInput },
|
|
296
|
+
{ ensureConsumerPrincipal: true },
|
|
297
|
+
);
|
|
298
|
+
} catch (packErr) {
|
|
299
|
+
const packApiErr = packErr as { status?: number };
|
|
300
|
+
if (packApiErr?.status !== 402) throw packErr;
|
|
301
|
+
result = await apiPostWithPayment<Record<string, unknown>>(
|
|
302
|
+
`/agents/${selected.id}/run`,
|
|
303
|
+
{ input: processedInput },
|
|
304
|
+
method,
|
|
305
|
+
);
|
|
306
|
+
usedPaidMethod = true;
|
|
307
|
+
}
|
|
308
|
+
} else {
|
|
309
|
+
result = await apiPostWithPayment<Record<string, unknown>>(
|
|
302
310
|
`/agents/${selected.id}/run`,
|
|
303
311
|
{ input: processedInput },
|
|
304
312
|
method,
|
|
305
313
|
);
|
|
314
|
+
}
|
|
315
|
+
if (usedPaidMethod) {
|
|
316
|
+
recordSpend(spendCheckMethod, estimatedCost);
|
|
317
|
+
}
|
|
306
318
|
} catch (err: unknown) {
|
|
307
319
|
const apiErr = err as { status?: number; message?: string };
|
|
308
|
-
if (activeCreditPack && apiErr?.status === 402) {
|
|
309
|
-
return text(
|
|
310
|
-
`Your available credit packs for ${selected.name} could not cover this run.\n\n` +
|
|
311
|
-
"Use list_agent_credit_packs to inspect your balances, or retry with a payment method.",
|
|
312
|
-
);
|
|
313
|
-
}
|
|
314
320
|
if (apiErr?.status === 402) {
|
|
315
321
|
return text(
|
|
316
322
|
[
|
|
@@ -339,7 +345,7 @@ export function registerSolveTools(server: McpServer): void {
|
|
|
339
345
|
}
|
|
340
346
|
|
|
341
347
|
if (status === "processing") {
|
|
342
|
-
const pollResult = await pollSolveJob(jobId);
|
|
348
|
+
const pollResult = await pollSolveJob(jobId, method);
|
|
343
349
|
if (pollResult.status === "completed") {
|
|
344
350
|
const asyncFormatted = formatRunResult({
|
|
345
351
|
...result,
|
package/src/tools/wallet.ts
CHANGED
|
@@ -6,8 +6,12 @@ import {
|
|
|
6
6
|
setCardConfig,
|
|
7
7
|
addWallet,
|
|
8
8
|
getPendingCardSetupToken,
|
|
9
|
+
getSpendPolicy,
|
|
10
|
+
setSpendPolicy,
|
|
9
11
|
} from "../core/config.js";
|
|
10
|
-
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";
|
|
11
15
|
import {
|
|
12
16
|
getOrCreatePendingCardSetup,
|
|
13
17
|
formatCardSetupBlocks,
|
|
@@ -45,25 +49,36 @@ export function registerWalletTools(server: McpServer): void {
|
|
|
45
49
|
);
|
|
46
50
|
}
|
|
47
51
|
|
|
48
|
-
const
|
|
52
|
+
const settings = await getSettings();
|
|
53
|
+
const networkLabel = settings ? ` (${settings.network})` : "";
|
|
54
|
+
const lines = [`Payment methods${networkLabel}:`];
|
|
49
55
|
|
|
50
56
|
for (const w of wallets) {
|
|
51
57
|
const label = w.label ? ` (${w.label})` : "";
|
|
52
58
|
const storage =
|
|
53
59
|
w.keyType === "ows" ? " [encrypted]" : " [plaintext]";
|
|
54
|
-
const
|
|
60
|
+
const chainLines = await Promise.all(
|
|
55
61
|
w.chains.map(async (chainName) => {
|
|
56
62
|
const addr = await getWalletAddress(chainName);
|
|
57
|
-
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}`;
|
|
58
73
|
}),
|
|
59
74
|
);
|
|
60
|
-
lines.push(
|
|
61
|
-
|
|
62
|
-
);
|
|
75
|
+
lines.push(` ${w.id}${label}${storage}:`);
|
|
76
|
+
for (const line of chainLines) lines.push(` ${line}`);
|
|
63
77
|
}
|
|
64
78
|
|
|
65
|
-
if (card) {
|
|
66
|
-
|
|
79
|
+
if (card && isCardPaymentEnabled()) {
|
|
80
|
+
const stripeMode = settings?.stripe.mode === "test" ? " [Stripe test mode]" : "";
|
|
81
|
+
lines.push(` Card: ${card.brand} ****${card.last4}${stripeMode}`);
|
|
67
82
|
const capabilities = await getCardCapabilities();
|
|
68
83
|
if (capabilities.spt_status === "enabled") {
|
|
69
84
|
lines.push(" Card MPP: ready");
|
|
@@ -89,7 +104,7 @@ export function registerWalletTools(server: McpServer): void {
|
|
|
89
104
|
// ── wallet_setup (NEW) ──────────────────────────────────────────
|
|
90
105
|
server.tool(
|
|
91
106
|
"wallet_setup",
|
|
92
|
-
"Set up or manage payment methods. Options: 'add-card' to connect a credit/debit card
|
|
107
|
+
"Set up or manage payment methods. Options: 'add-card' to connect a credit/debit card, 'remove-card' to disconnect a card, 'create' a crypto wallet, or 'import' an existing key. After card setup, use wallet_status to confirm whether card-backed MPP is ready. For crypto wallet changes (removal, key rotation), direct users to edit their config files manually — never handle private keys programmatically.",
|
|
93
108
|
{
|
|
94
109
|
action: z
|
|
95
110
|
.enum(["create", "import", "add-card", "remove-card"])
|
|
@@ -110,6 +125,9 @@ export function registerWalletTools(server: McpServer): void {
|
|
|
110
125
|
async ({ action, name, key, chain }) => {
|
|
111
126
|
// ── Card setup flow ──────────────────────────────────────
|
|
112
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
|
+
}
|
|
113
131
|
const existing = getCardConfig();
|
|
114
132
|
if (existing) {
|
|
115
133
|
return text(`Card already connected: ${existing.brand} ****${existing.last4}\n\nTo replace it, remove the current card first.`);
|
|
@@ -339,9 +357,9 @@ export function registerWalletTools(server: McpServer): void {
|
|
|
339
357
|
// ── wallet_set_policy (NEW) ─────────────────────────────────────
|
|
340
358
|
server.tool(
|
|
341
359
|
"wallet_set_policy",
|
|
342
|
-
"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.",
|
|
343
361
|
{
|
|
344
|
-
wallet_id: z.string().describe("Wallet ID to set policy on"),
|
|
362
|
+
wallet_id: z.string().describe("Wallet ID to set local policy on"),
|
|
345
363
|
max_per_tx: z
|
|
346
364
|
.number()
|
|
347
365
|
.positive()
|
|
@@ -352,8 +370,13 @@ export function registerWalletTools(server: McpServer): void {
|
|
|
352
370
|
.positive()
|
|
353
371
|
.optional()
|
|
354
372
|
.describe("Maximum USD per day across all transactions"),
|
|
373
|
+
require_confirmation_above: z
|
|
374
|
+
.number()
|
|
375
|
+
.positive()
|
|
376
|
+
.optional()
|
|
377
|
+
.describe("Require manual confirmation above this USD amount even when auto-spend is enabled"),
|
|
355
378
|
},
|
|
356
|
-
async ({ wallet_id, max_per_tx, max_per_day }) => {
|
|
379
|
+
async ({ wallet_id, max_per_tx, max_per_day, require_confirmation_above }) => {
|
|
357
380
|
const wallets = getWallets();
|
|
358
381
|
const wallet = wallets.find((w) => w.id === wallet_id);
|
|
359
382
|
|
|
@@ -364,29 +387,42 @@ export function registerWalletTools(server: McpServer): void {
|
|
|
364
387
|
);
|
|
365
388
|
}
|
|
366
389
|
|
|
367
|
-
if (
|
|
390
|
+
if (
|
|
391
|
+
max_per_tx == null &&
|
|
392
|
+
max_per_day == null &&
|
|
393
|
+
require_confirmation_above == null
|
|
394
|
+
) {
|
|
368
395
|
return text(
|
|
369
|
-
"No policy changes specified. Provide
|
|
396
|
+
"No policy changes specified. Provide at least one policy field.",
|
|
370
397
|
);
|
|
371
398
|
}
|
|
372
399
|
|
|
400
|
+
const existing = getSpendPolicy(wallet_id) ?? {};
|
|
401
|
+
const nextPolicy = {
|
|
402
|
+
...existing,
|
|
403
|
+
...(max_per_tx != null ? { maxPerTxUsd: max_per_tx } : {}),
|
|
404
|
+
...(max_per_day != null ? { maxPerDayUsd: max_per_day } : {}),
|
|
405
|
+
...(require_confirmation_above != null ? { requireConfirmationAboveUsd: require_confirmation_above } : {}),
|
|
406
|
+
};
|
|
407
|
+
setSpendPolicy(wallet_id, nextPolicy);
|
|
408
|
+
|
|
373
409
|
// Build policy summary
|
|
374
410
|
const policies: string[] = [];
|
|
375
|
-
if (
|
|
376
|
-
policies.push(`Max per transaction: $${
|
|
411
|
+
if (nextPolicy.maxPerTxUsd != null) {
|
|
412
|
+
policies.push(`Max per transaction: $${nextPolicy.maxPerTxUsd.toFixed(2)}`);
|
|
377
413
|
}
|
|
378
|
-
if (
|
|
379
|
-
policies.push(`Max per day: $${
|
|
414
|
+
if (nextPolicy.maxPerDayUsd != null) {
|
|
415
|
+
policies.push(`Max per day: $${nextPolicy.maxPerDayUsd.toFixed(2)}`);
|
|
416
|
+
}
|
|
417
|
+
if (nextPolicy.requireConfirmationAboveUsd != null) {
|
|
418
|
+
policies.push(`Require confirmation above: $${nextPolicy.requireConfirmationAboveUsd.toFixed(2)}`);
|
|
380
419
|
}
|
|
381
|
-
|
|
382
|
-
// Note: actual persistence depends on the config module supporting policy fields.
|
|
383
|
-
// For now, we report what would be set. The core/config module will handle storage.
|
|
384
420
|
return text(
|
|
385
421
|
[
|
|
386
|
-
`
|
|
422
|
+
`Local spending policy set for wallet "${wallet_id}":`,
|
|
387
423
|
...policies.map((p) => ` ${p}`),
|
|
388
424
|
"",
|
|
389
|
-
"Policy
|
|
425
|
+
"Policy is stored in this MCP client's local config and enforced on future transactions from this wallet on this client.",
|
|
390
426
|
].join("\n"),
|
|
391
427
|
);
|
|
392
428
|
},
|