@agentwonderland/mcp 0.1.15 → 0.1.17
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/card-setup.d.ts +21 -0
- package/dist/core/card-setup.js +63 -0
- package/dist/core/config.d.ts +4 -0
- package/dist/core/config.js +6 -0
- package/dist/index.js +8 -8
- package/dist/tools/run.js +10 -3
- package/dist/tools/solve.js +10 -3
- package/dist/tools/wallet.js +39 -3
- package/package.json +3 -1
- package/src/core/card-setup.ts +85 -0
- package/src/core/config.ts +7 -0
- package/src/index.ts +8 -8
- package/src/tools/run.ts +11 -5
- package/src/tools/solve.ts +11 -5
- package/src/tools/wallet.ts +45 -4
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Initiate card setup — creates a Stripe Checkout session and returns
|
|
3
|
+
* QR code + short URL for the user to scan.
|
|
4
|
+
*/
|
|
5
|
+
export declare function initiateCardSetup(): Promise<{
|
|
6
|
+
qr: string;
|
|
7
|
+
url: string;
|
|
8
|
+
token: string;
|
|
9
|
+
}>;
|
|
10
|
+
/**
|
|
11
|
+
* Format the card setup prompt shown to the user.
|
|
12
|
+
*/
|
|
13
|
+
export declare function formatCardSetupPrompt(qr: string, url: string): string;
|
|
14
|
+
/**
|
|
15
|
+
* Poll for card setup completion. Returns the card details or null on timeout.
|
|
16
|
+
*/
|
|
17
|
+
export declare function pollCardSetup(token: string, timeoutMs?: number): Promise<{
|
|
18
|
+
last4: string;
|
|
19
|
+
brand: string;
|
|
20
|
+
consumerToken: string;
|
|
21
|
+
} | null>;
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import QRCode from "qrcode";
|
|
2
|
+
import { apiPost, apiGet } from "./api-client.js";
|
|
3
|
+
import { getApiUrl, setCardConfig } from "./config.js";
|
|
4
|
+
/**
|
|
5
|
+
* Initiate card setup — creates a Stripe Checkout session and returns
|
|
6
|
+
* QR code + short URL for the user to scan.
|
|
7
|
+
*/
|
|
8
|
+
export async function initiateCardSetup() {
|
|
9
|
+
const { url: _checkoutUrl, token } = await apiPost("/card/setup", {});
|
|
10
|
+
// Short redirect URL keeps the QR code small and scannable
|
|
11
|
+
const apiUrl = getApiUrl();
|
|
12
|
+
const shortUrl = `${apiUrl}/card/link/${token}`;
|
|
13
|
+
const qr = await QRCode.toString(shortUrl, {
|
|
14
|
+
type: "utf8",
|
|
15
|
+
errorCorrectionLevel: "L",
|
|
16
|
+
margin: 2,
|
|
17
|
+
});
|
|
18
|
+
return { qr, url: shortUrl, token };
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Format the card setup prompt shown to the user.
|
|
22
|
+
*/
|
|
23
|
+
export function formatCardSetupPrompt(qr, url) {
|
|
24
|
+
return [
|
|
25
|
+
"Connect a payment card to get started:\n",
|
|
26
|
+
qr,
|
|
27
|
+
`Or open: ${url}`,
|
|
28
|
+
"",
|
|
29
|
+
"We also support crypto wallets (Base USDC, Tempo USDC) — use wallet_setup({ action: \"create\" }) if you prefer.",
|
|
30
|
+
"",
|
|
31
|
+
"IMPORTANT: Present the QR code and link above to the user. After they enter their card, call wallet_setup({ action: \"add-card\" }) to confirm the connection.",
|
|
32
|
+
].join("\n");
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Poll for card setup completion. Returns the card details or null on timeout.
|
|
36
|
+
*/
|
|
37
|
+
export async function pollCardSetup(token, timeoutMs = 120_000) {
|
|
38
|
+
const deadline = Date.now() + timeoutMs;
|
|
39
|
+
while (Date.now() < deadline) {
|
|
40
|
+
await new Promise((r) => setTimeout(r, 3000));
|
|
41
|
+
try {
|
|
42
|
+
const status = await apiGet(`/card/status?token=${token}`);
|
|
43
|
+
if (status.status === "complete" && status.consumer_token) {
|
|
44
|
+
const card = {
|
|
45
|
+
last4: status.card_last4 ?? "????",
|
|
46
|
+
brand: status.card_brand ?? "card",
|
|
47
|
+
consumerToken: status.consumer_token,
|
|
48
|
+
};
|
|
49
|
+
// Persist to config
|
|
50
|
+
setCardConfig({
|
|
51
|
+
consumerToken: card.consumerToken,
|
|
52
|
+
last4: card.last4,
|
|
53
|
+
brand: card.brand,
|
|
54
|
+
});
|
|
55
|
+
return card;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
catch {
|
|
59
|
+
// Keep polling
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
return null;
|
|
63
|
+
}
|
package/dist/core/config.d.ts
CHANGED
|
@@ -68,6 +68,10 @@ export declare function removeWallet(id: string): void;
|
|
|
68
68
|
* Get card configuration.
|
|
69
69
|
*/
|
|
70
70
|
export declare function getCardConfig(): CardConfig | null;
|
|
71
|
+
/**
|
|
72
|
+
* Save card configuration after setup.
|
|
73
|
+
*/
|
|
74
|
+
export declare function setCardConfig(card: CardConfig | null): void;
|
|
71
75
|
/**
|
|
72
76
|
* Resolve a payment method string to a wallet + chain.
|
|
73
77
|
* Accepts: wallet ID, chain name, or "card".
|
package/dist/core/config.js
CHANGED
|
@@ -259,6 +259,12 @@ export function removeWallet(id) {
|
|
|
259
259
|
export function getCardConfig() {
|
|
260
260
|
return getConfig().card;
|
|
261
261
|
}
|
|
262
|
+
/**
|
|
263
|
+
* Save card configuration after setup.
|
|
264
|
+
*/
|
|
265
|
+
export function setCardConfig(card) {
|
|
266
|
+
saveConfig({ card });
|
|
267
|
+
}
|
|
262
268
|
/**
|
|
263
269
|
* Resolve a payment method string to a wallet + chain.
|
|
264
270
|
* Accepts: wallet ID, chain name, or "card".
|
package/dist/index.js
CHANGED
|
@@ -30,17 +30,17 @@ export async function startMcpServer() {
|
|
|
30
30
|
"The MCP auto-uploads to cloud storage and replaces with a URL. Never base64-encode.",
|
|
31
31
|
"",
|
|
32
32
|
"WORKFLOW:",
|
|
33
|
-
"1.
|
|
34
|
-
"2.
|
|
35
|
-
"3.
|
|
36
|
-
"4. run_agent
|
|
33
|
+
"1. search_agents() or solve() — find agents for the task",
|
|
34
|
+
"2. get_agent() — check required input fields before running",
|
|
35
|
+
"3. run_agent() or solve() with ALL required fields",
|
|
36
|
+
"4. If no payment method is set up, run_agent returns a QR code to connect a credit card — present it to the user",
|
|
37
37
|
"5. Ask user to rate or tip after a successful run",
|
|
38
38
|
"",
|
|
39
39
|
"PAYMENT:",
|
|
40
|
-
"-
|
|
41
|
-
"-
|
|
42
|
-
"-
|
|
43
|
-
"-
|
|
40
|
+
"- Credit/debit card is the easiest way to pay — just scan a QR code to connect, no funding needed.",
|
|
41
|
+
"- Crypto wallets (Tempo USDC, Base USDC) are also supported for advanced users.",
|
|
42
|
+
"- Payment is automatic once configured. Users are never charged for failed runs.",
|
|
43
|
+
"- Do NOT ask the user to set up payment before they try to run an agent. Let them explore freely — payment setup is handled inline when they're ready to pay.",
|
|
44
44
|
"",
|
|
45
45
|
"REQUIRED FIELDS:",
|
|
46
46
|
"Always check the agent's input schema (via get_agent) before calling run_agent.",
|
package/dist/tools/run.js
CHANGED
|
@@ -5,6 +5,7 @@ import { getConfiguredMethods, hasWalletConfigured, getWalletAddress } from "../
|
|
|
5
5
|
import { requiresSpendConfirmation, getDefaultTipAmount } from "../core/config.js";
|
|
6
6
|
import { formatRunResult } from "../core/formatters.js";
|
|
7
7
|
import { storeFeedbackToken } from "./_token-cache.js";
|
|
8
|
+
import { initiateCardSetup, formatCardSetupPrompt } from "../core/card-setup.js";
|
|
8
9
|
const POLL_INTERVAL_MS = 3000;
|
|
9
10
|
const POLL_MAX_MS = 120000; // 2 minutes
|
|
10
11
|
async function pollJobUntilDone(jobId, agentName) {
|
|
@@ -45,9 +46,15 @@ export function registerRunTools(server) {
|
|
|
45
46
|
confirmed: z.boolean().optional().describe("Set to true to confirm spending after seeing the price quote."),
|
|
46
47
|
}, async ({ agent_id, input, pay_with, confirmed }) => {
|
|
47
48
|
if (!hasWalletConfigured()) {
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
49
|
+
try {
|
|
50
|
+
const { qr, url } = await initiateCardSetup();
|
|
51
|
+
return text(formatCardSetupPrompt(qr, url));
|
|
52
|
+
}
|
|
53
|
+
catch {
|
|
54
|
+
return text("No payment method configured.\n\n" +
|
|
55
|
+
"To add a credit card: wallet_setup({ action: \"add-card\" })\n" +
|
|
56
|
+
"To use crypto: wallet_setup({ action: \"create\" })");
|
|
57
|
+
}
|
|
51
58
|
}
|
|
52
59
|
// Resolve agent and fetch details
|
|
53
60
|
let agent;
|
package/dist/tools/solve.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { apiGet, apiPost, apiPostWithPayment } from "../core/api-client.js";
|
|
3
|
+
import { initiateCardSetup, formatCardSetupPrompt } from "../core/card-setup.js";
|
|
3
4
|
import { hasWalletConfigured, getConfiguredMethods, getAcceptedPaymentMethods, getWalletAddress, } from "../core/payments.js";
|
|
4
5
|
import { requiresSpendConfirmation, getDefaultTipAmount } from "../core/config.js";
|
|
5
6
|
import { agentList, formatRunResult } from "../core/formatters.js";
|
|
@@ -93,9 +94,15 @@ export function registerSolveTools(server) {
|
|
|
93
94
|
.describe("Set to true to confirm spending after seeing the price quote."),
|
|
94
95
|
}, async ({ intent, input, budget, pay_with, confirmed }) => {
|
|
95
96
|
if (!hasWalletConfigured()) {
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
97
|
+
try {
|
|
98
|
+
const { qr, url } = await initiateCardSetup();
|
|
99
|
+
return text(formatCardSetupPrompt(qr, url));
|
|
100
|
+
}
|
|
101
|
+
catch {
|
|
102
|
+
return text("No payment method configured.\n\n" +
|
|
103
|
+
"To add a credit card: wallet_setup({ action: \"add-card\" })\n" +
|
|
104
|
+
"To use crypto: wallet_setup({ action: \"create\" })");
|
|
105
|
+
}
|
|
99
106
|
}
|
|
100
107
|
const method = pay_with ?? getConfiguredMethods()[0];
|
|
101
108
|
// Path 1: If authenticated, use the platform /solve route
|
package/dist/tools/wallet.js
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { getWallets, getCardConfig, addWallet } from "../core/config.js";
|
|
3
3
|
import { getWalletAddress } from "../core/payments.js";
|
|
4
|
+
import { initiateCardSetup, pollCardSetup } from "../core/card-setup.js";
|
|
4
5
|
import { isOwsAvailable, createOwsWallet, importKeyToOws, listOwsWallets, } from "../core/ows-adapter.js";
|
|
5
6
|
function text(t) {
|
|
6
7
|
return { content: [{ type: "text", text: t }] };
|
|
7
8
|
}
|
|
9
|
+
// Pending card setup — stored between the QR display call and the confirmation poll
|
|
10
|
+
let pendingCardSetup = null;
|
|
8
11
|
export function registerWalletTools(server) {
|
|
9
12
|
// ── wallet_status (extracted from check_wallet) ─────────────────
|
|
10
13
|
server.tool("wallet_status", "Check payment readiness. Shows all configured wallets, their chains, addresses, and card status.", {}, async () => {
|
|
@@ -26,10 +29,10 @@ export function registerWalletTools(server) {
|
|
|
26
29
|
return text(lines.join("\n"));
|
|
27
30
|
});
|
|
28
31
|
// ── wallet_setup (NEW) ──────────────────────────────────────────
|
|
29
|
-
server.tool("wallet_setup", "
|
|
32
|
+
server.tool("wallet_setup", "Set up a payment method for running agents. Options: 'create' a crypto wallet, 'import' an existing key, or 'add-card' to connect a credit/debit card via QR code. Card setup shows a scannable QR code — call again after the user enters their card to confirm.", {
|
|
30
33
|
action: z
|
|
31
|
-
.enum(["create", "import"])
|
|
32
|
-
.describe("'create' a new wallet
|
|
34
|
+
.enum(["create", "import", "add-card"])
|
|
35
|
+
.describe("'create' a new crypto wallet, 'import' an existing private key, or 'add-card' to connect a credit/debit card"),
|
|
33
36
|
name: z
|
|
34
37
|
.string()
|
|
35
38
|
.optional()
|
|
@@ -41,6 +44,39 @@ export function registerWalletTools(server) {
|
|
|
41
44
|
chain: z.enum(["tempo", "base", "solana"]).optional()
|
|
42
45
|
.describe("Primary chain (default: tempo). Solana support is via Stripe deposit mode."),
|
|
43
46
|
}, async ({ action, name, key, chain }) => {
|
|
47
|
+
// ── Card setup flow ──────────────────────────────────────
|
|
48
|
+
if (action === "add-card") {
|
|
49
|
+
const existing = getCardConfig();
|
|
50
|
+
if (existing) {
|
|
51
|
+
return text(`Card already connected: ${existing.brand} ****${existing.last4}\n\nTo replace it, remove the current card first.`);
|
|
52
|
+
}
|
|
53
|
+
// Check if there's a pending setup to complete (user said they're done)
|
|
54
|
+
if (pendingCardSetup) {
|
|
55
|
+
const pendingToken = pendingCardSetup.token;
|
|
56
|
+
const result = await pollCardSetup(pendingToken);
|
|
57
|
+
pendingCardSetup = null;
|
|
58
|
+
if (result) {
|
|
59
|
+
return text(`Connected! ${result.brand} ****${result.last4} is ready for payments.`);
|
|
60
|
+
}
|
|
61
|
+
return text("Card setup timed out. Run wallet_setup({ action: \"add-card\" }) to try again.");
|
|
62
|
+
}
|
|
63
|
+
// Create new card setup session with QR code
|
|
64
|
+
try {
|
|
65
|
+
const { qr, url, token } = await initiateCardSetup();
|
|
66
|
+
pendingCardSetup = { token };
|
|
67
|
+
return text([
|
|
68
|
+
"Scan to connect a payment card:\n",
|
|
69
|
+
qr,
|
|
70
|
+
`Or open: ${url}`,
|
|
71
|
+
"",
|
|
72
|
+
"After entering your card, tell me and I'll confirm the connection.",
|
|
73
|
+
].join("\n"));
|
|
74
|
+
}
|
|
75
|
+
catch {
|
|
76
|
+
return text("Error: Could not create card setup session. Try again later.");
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
// ── Crypto wallet setup ──────────────────────────────────
|
|
44
80
|
if (action === "import" && !key) {
|
|
45
81
|
return text("Error: 'key' parameter is required when action is 'import'.");
|
|
46
82
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agentwonderland/mcp",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.17",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "MCP server for the Agent Wonderland AI agent marketplace",
|
|
6
6
|
"bin": {
|
|
@@ -24,6 +24,7 @@
|
|
|
24
24
|
"dependencies": {
|
|
25
25
|
"@modelcontextprotocol/sdk": "^1.12.1",
|
|
26
26
|
"mppx": "^0.4.9",
|
|
27
|
+
"qrcode": "^1.5.4",
|
|
27
28
|
"viem": "^2.47.6",
|
|
28
29
|
"zod": "^3.24.0"
|
|
29
30
|
},
|
|
@@ -36,6 +37,7 @@
|
|
|
36
37
|
}
|
|
37
38
|
},
|
|
38
39
|
"devDependencies": {
|
|
40
|
+
"@types/qrcode": "^1.5.6",
|
|
39
41
|
"tsx": "^4.0.0",
|
|
40
42
|
"typescript": "^5.7.0"
|
|
41
43
|
},
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import QRCode from "qrcode";
|
|
2
|
+
import { apiPost, apiGet } from "./api-client.js";
|
|
3
|
+
import { getApiUrl, getCardConfig, setCardConfig } from "./config.js";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Initiate card setup — creates a Stripe Checkout session and returns
|
|
7
|
+
* QR code + short URL for the user to scan.
|
|
8
|
+
*/
|
|
9
|
+
export async function initiateCardSetup(): Promise<{
|
|
10
|
+
qr: string;
|
|
11
|
+
url: string;
|
|
12
|
+
token: string;
|
|
13
|
+
}> {
|
|
14
|
+
const { url: _checkoutUrl, token } = await apiPost<{ url: string; token: string }>("/card/setup", {});
|
|
15
|
+
|
|
16
|
+
// Short redirect URL keeps the QR code small and scannable
|
|
17
|
+
const apiUrl = getApiUrl();
|
|
18
|
+
const shortUrl = `${apiUrl}/card/link/${token}`;
|
|
19
|
+
|
|
20
|
+
const qr = await QRCode.toString(shortUrl, {
|
|
21
|
+
type: "utf8",
|
|
22
|
+
errorCorrectionLevel: "L",
|
|
23
|
+
margin: 2,
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
return { qr, url: shortUrl, token };
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Format the card setup prompt shown to the user.
|
|
31
|
+
*/
|
|
32
|
+
export function formatCardSetupPrompt(qr: string, url: string): string {
|
|
33
|
+
return [
|
|
34
|
+
"Connect a payment card to get started:\n",
|
|
35
|
+
qr,
|
|
36
|
+
`Or open: ${url}`,
|
|
37
|
+
"",
|
|
38
|
+
"We also support crypto wallets (Base USDC, Tempo USDC) — use wallet_setup({ action: \"create\" }) if you prefer.",
|
|
39
|
+
"",
|
|
40
|
+
"IMPORTANT: Present the QR code and link above to the user. After they enter their card, call wallet_setup({ action: \"add-card\" }) to confirm the connection.",
|
|
41
|
+
].join("\n");
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Poll for card setup completion. Returns the card details or null on timeout.
|
|
46
|
+
*/
|
|
47
|
+
export async function pollCardSetup(
|
|
48
|
+
token: string,
|
|
49
|
+
timeoutMs = 120_000,
|
|
50
|
+
): Promise<{ last4: string; brand: string; consumerToken: string } | null> {
|
|
51
|
+
const deadline = Date.now() + timeoutMs;
|
|
52
|
+
|
|
53
|
+
while (Date.now() < deadline) {
|
|
54
|
+
await new Promise((r) => setTimeout(r, 3000));
|
|
55
|
+
try {
|
|
56
|
+
const status = await apiGet<{
|
|
57
|
+
status: string;
|
|
58
|
+
card_last4?: string;
|
|
59
|
+
card_brand?: string;
|
|
60
|
+
consumer_token?: string;
|
|
61
|
+
}>(`/card/status?token=${token}`);
|
|
62
|
+
|
|
63
|
+
if (status.status === "complete" && status.consumer_token) {
|
|
64
|
+
const card = {
|
|
65
|
+
last4: status.card_last4 ?? "????",
|
|
66
|
+
brand: status.card_brand ?? "card",
|
|
67
|
+
consumerToken: status.consumer_token,
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
// Persist to config
|
|
71
|
+
setCardConfig({
|
|
72
|
+
consumerToken: card.consumerToken,
|
|
73
|
+
last4: card.last4,
|
|
74
|
+
brand: card.brand,
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
return card;
|
|
78
|
+
}
|
|
79
|
+
} catch {
|
|
80
|
+
// Keep polling
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
return null;
|
|
85
|
+
}
|
package/src/core/config.ts
CHANGED
|
@@ -346,6 +346,13 @@ export function getCardConfig(): CardConfig | null {
|
|
|
346
346
|
return getConfig().card;
|
|
347
347
|
}
|
|
348
348
|
|
|
349
|
+
/**
|
|
350
|
+
* Save card configuration after setup.
|
|
351
|
+
*/
|
|
352
|
+
export function setCardConfig(card: CardConfig | null): void {
|
|
353
|
+
saveConfig({ card });
|
|
354
|
+
}
|
|
355
|
+
|
|
349
356
|
/**
|
|
350
357
|
* Resolve a payment method string to a wallet + chain.
|
|
351
358
|
* Accepts: wallet ID, chain name, or "card".
|
package/src/index.ts
CHANGED
|
@@ -37,17 +37,17 @@ export async function startMcpServer(): Promise<void> {
|
|
|
37
37
|
"The MCP auto-uploads to cloud storage and replaces with a URL. Never base64-encode.",
|
|
38
38
|
"",
|
|
39
39
|
"WORKFLOW:",
|
|
40
|
-
"1.
|
|
41
|
-
"2.
|
|
42
|
-
"3.
|
|
43
|
-
"4. run_agent
|
|
40
|
+
"1. search_agents() or solve() — find agents for the task",
|
|
41
|
+
"2. get_agent() — check required input fields before running",
|
|
42
|
+
"3. run_agent() or solve() with ALL required fields",
|
|
43
|
+
"4. If no payment method is set up, run_agent returns a QR code to connect a credit card — present it to the user",
|
|
44
44
|
"5. Ask user to rate or tip after a successful run",
|
|
45
45
|
"",
|
|
46
46
|
"PAYMENT:",
|
|
47
|
-
"-
|
|
48
|
-
"-
|
|
49
|
-
"-
|
|
50
|
-
"-
|
|
47
|
+
"- Credit/debit card is the easiest way to pay — just scan a QR code to connect, no funding needed.",
|
|
48
|
+
"- Crypto wallets (Tempo USDC, Base USDC) are also supported for advanced users.",
|
|
49
|
+
"- Payment is automatic once configured. Users are never charged for failed runs.",
|
|
50
|
+
"- Do NOT ask the user to set up payment before they try to run an agent. Let them explore freely — payment setup is handled inline when they're ready to pay.",
|
|
51
51
|
"",
|
|
52
52
|
"REQUIRED FIELDS:",
|
|
53
53
|
"Always check the agent's input schema (via get_agent) before calling run_agent.",
|
package/src/tools/run.ts
CHANGED
|
@@ -6,6 +6,7 @@ import { getConfiguredMethods, hasWalletConfigured, getWalletAddress } from "../
|
|
|
6
6
|
import { requiresSpendConfirmation, getDefaultTipAmount } from "../core/config.js";
|
|
7
7
|
import { formatRunResult } from "../core/formatters.js";
|
|
8
8
|
import { storeFeedbackToken, getFeedbackToken } from "./_token-cache.js";
|
|
9
|
+
import { initiateCardSetup, formatCardSetupPrompt } from "../core/card-setup.js";
|
|
9
10
|
|
|
10
11
|
const POLL_INTERVAL_MS = 3000;
|
|
11
12
|
const POLL_MAX_MS = 120000; // 2 minutes
|
|
@@ -69,11 +70,16 @@ export function registerRunTools(server: McpServer): void {
|
|
|
69
70
|
},
|
|
70
71
|
async ({ agent_id, input, pay_with, confirmed }) => {
|
|
71
72
|
if (!hasWalletConfigured()) {
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
73
|
+
try {
|
|
74
|
+
const { qr, url } = await initiateCardSetup();
|
|
75
|
+
return text(formatCardSetupPrompt(qr, url));
|
|
76
|
+
} catch {
|
|
77
|
+
return text(
|
|
78
|
+
"No payment method configured.\n\n" +
|
|
79
|
+
"To add a credit card: wallet_setup({ action: \"add-card\" })\n" +
|
|
80
|
+
"To use crypto: wallet_setup({ action: \"create\" })"
|
|
81
|
+
);
|
|
82
|
+
}
|
|
77
83
|
}
|
|
78
84
|
|
|
79
85
|
// Resolve agent and fetch details
|
package/src/tools/solve.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
2
|
import { z } from "zod";
|
|
3
3
|
import { apiGet, apiPost, apiPostWithPayment } from "../core/api-client.js";
|
|
4
|
+
import { initiateCardSetup, formatCardSetupPrompt } from "../core/card-setup.js";
|
|
4
5
|
import {
|
|
5
6
|
hasWalletConfigured,
|
|
6
7
|
getConfiguredMethods,
|
|
@@ -119,11 +120,16 @@ export function registerSolveTools(server: McpServer): void {
|
|
|
119
120
|
},
|
|
120
121
|
async ({ intent, input, budget, pay_with, confirmed }) => {
|
|
121
122
|
if (!hasWalletConfigured()) {
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
123
|
+
try {
|
|
124
|
+
const { qr, url } = await initiateCardSetup();
|
|
125
|
+
return text(formatCardSetupPrompt(qr, url));
|
|
126
|
+
} catch {
|
|
127
|
+
return text(
|
|
128
|
+
"No payment method configured.\n\n" +
|
|
129
|
+
"To add a credit card: wallet_setup({ action: \"add-card\" })\n" +
|
|
130
|
+
"To use crypto: wallet_setup({ action: \"create\" })"
|
|
131
|
+
);
|
|
132
|
+
}
|
|
127
133
|
}
|
|
128
134
|
|
|
129
135
|
const method = pay_with ?? getConfiguredMethods()[0];
|
package/src/tools/wallet.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
2
|
import { z } from "zod";
|
|
3
|
-
import { getWallets, getCardConfig, addWallet } from "../core/config.js";
|
|
3
|
+
import { getWallets, getCardConfig, setCardConfig, addWallet } from "../core/config.js";
|
|
4
4
|
import { getWalletAddress } from "../core/payments.js";
|
|
5
|
+
import { initiateCardSetup, pollCardSetup } from "../core/card-setup.js";
|
|
5
6
|
import {
|
|
6
7
|
isOwsAvailable,
|
|
7
8
|
createOwsWallet,
|
|
@@ -13,6 +14,9 @@ function text(t: string) {
|
|
|
13
14
|
return { content: [{ type: "text" as const, text: t }] };
|
|
14
15
|
}
|
|
15
16
|
|
|
17
|
+
// Pending card setup — stored between the QR display call and the confirmation poll
|
|
18
|
+
let pendingCardSetup: { token: string } | null = null;
|
|
19
|
+
|
|
16
20
|
export function registerWalletTools(server: McpServer): void {
|
|
17
21
|
// ── wallet_status (extracted from check_wallet) ─────────────────
|
|
18
22
|
server.tool(
|
|
@@ -52,11 +56,11 @@ export function registerWalletTools(server: McpServer): void {
|
|
|
52
56
|
// ── wallet_setup (NEW) ──────────────────────────────────────────
|
|
53
57
|
server.tool(
|
|
54
58
|
"wallet_setup",
|
|
55
|
-
"
|
|
59
|
+
"Set up a payment method for running agents. Options: 'create' a crypto wallet, 'import' an existing key, or 'add-card' to connect a credit/debit card via QR code. Card setup shows a scannable QR code — call again after the user enters their card to confirm.",
|
|
56
60
|
{
|
|
57
61
|
action: z
|
|
58
|
-
.enum(["create", "import"])
|
|
59
|
-
.describe("'create' a new wallet
|
|
62
|
+
.enum(["create", "import", "add-card"])
|
|
63
|
+
.describe("'create' a new crypto wallet, 'import' an existing private key, or 'add-card' to connect a credit/debit card"),
|
|
60
64
|
name: z
|
|
61
65
|
.string()
|
|
62
66
|
.optional()
|
|
@@ -71,6 +75,43 @@ export function registerWalletTools(server: McpServer): void {
|
|
|
71
75
|
.describe("Primary chain (default: tempo). Solana support is via Stripe deposit mode."),
|
|
72
76
|
},
|
|
73
77
|
async ({ action, name, key, chain }) => {
|
|
78
|
+
// ── Card setup flow ──────────────────────────────────────
|
|
79
|
+
if (action === "add-card") {
|
|
80
|
+
const existing = getCardConfig();
|
|
81
|
+
if (existing) {
|
|
82
|
+
return text(`Card already connected: ${existing.brand} ****${existing.last4}\n\nTo replace it, remove the current card first.`);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Check if there's a pending setup to complete (user said they're done)
|
|
86
|
+
if (pendingCardSetup) {
|
|
87
|
+
const pendingToken = pendingCardSetup.token;
|
|
88
|
+
const result = await pollCardSetup(pendingToken);
|
|
89
|
+
pendingCardSetup = null;
|
|
90
|
+
|
|
91
|
+
if (result) {
|
|
92
|
+
return text(`Connected! ${result.brand} ****${result.last4} is ready for payments.`);
|
|
93
|
+
}
|
|
94
|
+
return text("Card setup timed out. Run wallet_setup({ action: \"add-card\" }) to try again.");
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// Create new card setup session with QR code
|
|
98
|
+
try {
|
|
99
|
+
const { qr, url, token } = await initiateCardSetup();
|
|
100
|
+
pendingCardSetup = { token };
|
|
101
|
+
|
|
102
|
+
return text([
|
|
103
|
+
"Scan to connect a payment card:\n",
|
|
104
|
+
qr,
|
|
105
|
+
`Or open: ${url}`,
|
|
106
|
+
"",
|
|
107
|
+
"After entering your card, tell me and I'll confirm the connection.",
|
|
108
|
+
].join("\n"));
|
|
109
|
+
} catch {
|
|
110
|
+
return text("Error: Could not create card setup session. Try again later.");
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// ── Crypto wallet setup ──────────────────────────────────
|
|
74
115
|
if (action === "import" && !key) {
|
|
75
116
|
return text(
|
|
76
117
|
"Error: 'key' parameter is required when action is 'import'.",
|