@agentwonderland/mcp 0.1.23 → 0.1.25
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__/card-setup.test.d.ts +1 -0
- package/dist/core/__tests__/card-setup.test.js +99 -0
- package/dist/core/__tests__/formatters.test.d.ts +1 -0
- package/dist/core/__tests__/formatters.test.js +15 -0
- package/dist/core/__tests__/passes.test.d.ts +1 -0
- package/dist/core/__tests__/passes.test.js +82 -0
- package/dist/core/__tests__/payments.test.d.ts +1 -0
- package/dist/core/__tests__/payments.test.js +101 -0
- package/dist/core/__tests__/principal.test.d.ts +1 -0
- package/dist/core/__tests__/principal.test.js +67 -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 +9 -4
- package/dist/core/api-client.js +52 -22
- package/dist/core/base-charge.js +3 -2
- package/dist/core/card-setup.d.ts +20 -13
- package/dist/core/card-setup.js +85 -29
- package/dist/core/config.d.ts +22 -0
- package/dist/core/config.js +46 -2
- package/dist/core/formatters.d.ts +4 -3
- package/dist/core/formatters.js +10 -8
- package/dist/core/index.d.ts +2 -0
- package/dist/core/index.js +2 -0
- package/dist/core/ows-adapter.d.ts +10 -2
- package/dist/core/ows-adapter.js +54 -10
- package/dist/core/passes.d.ts +40 -0
- package/dist/core/passes.js +32 -0
- package/dist/core/payments.d.ts +8 -0
- package/dist/core/payments.js +111 -17
- package/dist/core/principal.d.ts +2 -0
- package/dist/core/principal.js +109 -0
- package/dist/core/solana-charge.d.ts +9 -0
- package/dist/core/solana-charge.js +96 -0
- package/dist/core/spend-policy.d.ts +12 -0
- package/dist/core/spend-policy.js +53 -0
- package/dist/core/types.d.ts +11 -2
- package/dist/index.js +11 -3
- package/dist/prompts/index.js +4 -2
- package/dist/resources/agents.js +1 -1
- package/dist/resources/wallet.js +8 -1
- package/dist/tools/__tests__/_payment-confirmation.test.d.ts +1 -0
- package/dist/tools/__tests__/_payment-confirmation.test.js +30 -0
- package/dist/tools/_payment-confirmation.d.ts +6 -0
- package/dist/tools/_payment-confirmation.js +28 -0
- package/dist/tools/agent-info.js +16 -2
- package/dist/tools/favorites.js +1 -1
- package/dist/tools/index.d.ts +1 -0
- package/dist/tools/index.js +1 -0
- package/dist/tools/observability.d.ts +2 -0
- package/dist/tools/observability.js +20 -0
- package/dist/tools/passes.d.ts +2 -0
- package/dist/tools/passes.js +157 -0
- package/dist/tools/run.js +127 -53
- package/dist/tools/solve.js +115 -51
- package/dist/tools/wallet.js +110 -59
- package/package.json +3 -1
- package/src/core/__tests__/amount-utils.test.ts +13 -0
- package/src/core/__tests__/card-setup.test.ts +118 -0
- package/src/core/__tests__/formatters.test.ts +17 -0
- package/src/core/__tests__/passes.test.ts +94 -0
- package/src/core/__tests__/payments.test.ts +122 -0
- package/src/core/__tests__/principal.test.ts +87 -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 +70 -23
- package/src/core/base-charge.ts +3 -2
- package/src/core/card-setup.ts +109 -34
- package/src/core/config.ts +74 -3
- package/src/core/formatters.ts +13 -9
- package/src/core/index.ts +2 -0
- package/src/core/ows-adapter.ts +74 -8
- package/src/core/passes.ts +74 -0
- package/src/core/payments.ts +130 -17
- package/src/core/principal.ts +128 -0
- package/src/core/solana-charge.ts +150 -0
- package/src/core/spend-policy.ts +69 -0
- package/src/core/types.ts +11 -2
- package/src/index.ts +11 -3
- package/src/prompts/index.ts +4 -2
- package/src/resources/agents.ts +1 -1
- package/src/resources/wallet.ts +8 -1
- package/src/tools/__tests__/_payment-confirmation.test.ts +45 -0
- package/src/tools/_payment-confirmation.ts +52 -0
- package/src/tools/agent-info.ts +25 -2
- package/src/tools/favorites.ts +1 -4
- package/src/tools/index.ts +1 -0
- package/src/tools/observability.ts +43 -0
- package/src/tools/passes.ts +228 -0
- package/src/tools/run.ts +174 -57
- package/src/tools/solve.ts +147 -59
- package/src/tools/wallet.ts +132 -62
package/dist/core/payments.d.ts
CHANGED
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
* Users can configure multiple wallets with different chains and select
|
|
10
10
|
* which to use per-request via `--pay-with <wallet-id|chain|card>`.
|
|
11
11
|
*/
|
|
12
|
+
import type { AgentRecord } from "./types.js";
|
|
12
13
|
/**
|
|
13
14
|
* Returns a payment-aware fetch for a specific method, or the best
|
|
14
15
|
* available method if none is specified.
|
|
@@ -21,6 +22,11 @@ export declare function getPaymentFetch(method?: string): Promise<typeof fetch>;
|
|
|
21
22
|
* (with default wallet's default chain first) + "card" if configured.
|
|
22
23
|
*/
|
|
23
24
|
export declare function getConfiguredMethods(): string[];
|
|
25
|
+
/**
|
|
26
|
+
* Normalize a requested payment method into the MCP-visible method identifier.
|
|
27
|
+
* Accepts chain names, wallet IDs, or "card".
|
|
28
|
+
*/
|
|
29
|
+
export declare function normalizePaymentMethod(method: string): string | null;
|
|
24
30
|
/**
|
|
25
31
|
* Human-friendly display name for a payment method identifier.
|
|
26
32
|
*/
|
|
@@ -31,6 +37,8 @@ export declare function paymentMethodDisplayName(method: string): string;
|
|
|
31
37
|
* (tempo_usdc, base_usdc, stripe_card).
|
|
32
38
|
*/
|
|
33
39
|
export declare function getAcceptedPaymentMethods(): string[];
|
|
40
|
+
export declare function toRegistryPaymentMethod(method: string): string | null;
|
|
41
|
+
export declare function getCompatiblePaymentMethods(agent: Pick<AgentRecord, "payment"> | null | undefined, configuredMethods?: string[]): string[];
|
|
34
42
|
/**
|
|
35
43
|
* Check whether any payment method is configured.
|
|
36
44
|
*/
|
package/dist/core/payments.js
CHANGED
|
@@ -12,6 +12,18 @@
|
|
|
12
12
|
import { getConfig, getWallets, getDefaultWallet, getCardConfig, resolveWalletAndChain, getApiUrl, } from "./config.js";
|
|
13
13
|
// Cache per wallet+chain combo to avoid re-initializing
|
|
14
14
|
const fetchCache = new Map();
|
|
15
|
+
const REGISTRY_METHOD_MAP = {
|
|
16
|
+
tempo: "tempo_usdc",
|
|
17
|
+
base: "base_usdc",
|
|
18
|
+
solana: "solana_usdc",
|
|
19
|
+
card: "stripe_card",
|
|
20
|
+
};
|
|
21
|
+
const METHOD_REGISTRY_MAP = {
|
|
22
|
+
tempo_usdc: "tempo",
|
|
23
|
+
base_usdc: "base",
|
|
24
|
+
solana_usdc: "solana",
|
|
25
|
+
stripe_card: "card",
|
|
26
|
+
};
|
|
15
27
|
// ── Helpers ─────────────────────────────────────────────────────
|
|
16
28
|
function normalizeKey(key) {
|
|
17
29
|
return (key.startsWith("0x") ? key : `0x${key}`);
|
|
@@ -19,8 +31,21 @@ function normalizeKey(key) {
|
|
|
19
31
|
function cacheKey(walletId, chain) {
|
|
20
32
|
return `${walletId}:${chain}`;
|
|
21
33
|
}
|
|
34
|
+
function cardCacheKey() {
|
|
35
|
+
const card = getCardConfig();
|
|
36
|
+
if (!card)
|
|
37
|
+
return null;
|
|
38
|
+
return `card:${getApiUrl()}:${card.consumerToken}:${card.paymentMethodId ?? ""}`;
|
|
39
|
+
}
|
|
40
|
+
function clearStaleCardCache(activeKey) {
|
|
41
|
+
for (const key of fetchCache.keys()) {
|
|
42
|
+
if (key.startsWith("card:") && key !== activeKey) {
|
|
43
|
+
fetchCache.delete(key);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
22
47
|
// ── Per-protocol initializers ───────────────────────────────────
|
|
23
|
-
async function
|
|
48
|
+
async function initEvmMppForChain(wallet, chain) {
|
|
24
49
|
try {
|
|
25
50
|
const { Mppx, tempo } = await import("mppx/client");
|
|
26
51
|
let account;
|
|
@@ -35,8 +60,31 @@ async function initMpp(wallet) {
|
|
|
35
60
|
else {
|
|
36
61
|
return null;
|
|
37
62
|
}
|
|
38
|
-
const
|
|
39
|
-
|
|
63
|
+
const methods = [];
|
|
64
|
+
if (chain === "tempo") {
|
|
65
|
+
methods.push(tempo({ account }));
|
|
66
|
+
}
|
|
67
|
+
else {
|
|
68
|
+
const { baseChargeClient } = await import("./base-charge.js");
|
|
69
|
+
methods.push(baseChargeClient({ account }));
|
|
70
|
+
}
|
|
71
|
+
const mppx = Mppx.create({ methods: methods });
|
|
72
|
+
return mppx.fetch.bind(mppx);
|
|
73
|
+
}
|
|
74
|
+
catch {
|
|
75
|
+
return null;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
async function initSolanaMpp(wallet) {
|
|
79
|
+
try {
|
|
80
|
+
if (wallet.keyType !== "ows" && !wallet.key) {
|
|
81
|
+
return null;
|
|
82
|
+
}
|
|
83
|
+
const { Mppx } = await import("mppx/client");
|
|
84
|
+
const { solanaChargeClient } = await import("./solana-charge.js");
|
|
85
|
+
const mppx = Mppx.create({
|
|
86
|
+
methods: [solanaChargeClient({ wallet })],
|
|
87
|
+
});
|
|
40
88
|
return mppx.fetch.bind(mppx);
|
|
41
89
|
}
|
|
42
90
|
catch {
|
|
@@ -82,8 +130,14 @@ async function initCard() {
|
|
|
82
130
|
/**
|
|
83
131
|
* Initialize a payment-aware fetch for a given wallet + chain.
|
|
84
132
|
*/
|
|
85
|
-
async function initForChain(wallet,
|
|
86
|
-
|
|
133
|
+
async function initForChain(wallet, chain) {
|
|
134
|
+
if (chain === "solana") {
|
|
135
|
+
return initSolanaMpp(wallet);
|
|
136
|
+
}
|
|
137
|
+
if (chain === "tempo" || chain === "base") {
|
|
138
|
+
return initEvmMppForChain(wallet, chain);
|
|
139
|
+
}
|
|
140
|
+
return null;
|
|
87
141
|
}
|
|
88
142
|
// ── Public API ──────────────────────────────────────────────────
|
|
89
143
|
/**
|
|
@@ -95,7 +149,11 @@ async function initForChain(wallet, _chain) {
|
|
|
95
149
|
export async function getPaymentFetch(method) {
|
|
96
150
|
// Card payment
|
|
97
151
|
if (method === "card") {
|
|
98
|
-
const ck =
|
|
152
|
+
const ck = cardCacheKey();
|
|
153
|
+
clearStaleCardCache(ck ?? undefined);
|
|
154
|
+
if (!ck) {
|
|
155
|
+
throw new Error('Payment method "card" is not configured. Use the wallet_setup tool to configure a wallet');
|
|
156
|
+
}
|
|
99
157
|
if (fetchCache.has(ck))
|
|
100
158
|
return fetchCache.get(ck);
|
|
101
159
|
const pf = await initCard();
|
|
@@ -126,7 +184,16 @@ export async function getPaymentFetch(method) {
|
|
|
126
184
|
const defaultMethod = getConfig().defaultPaymentMethod;
|
|
127
185
|
for (const m of configured) {
|
|
128
186
|
if (m === "card") {
|
|
129
|
-
const ck =
|
|
187
|
+
const ck = cardCacheKey();
|
|
188
|
+
clearStaleCardCache(ck ?? undefined);
|
|
189
|
+
if (!ck) {
|
|
190
|
+
if (m === defaultMethod) {
|
|
191
|
+
const others = configured.filter((x) => x !== m);
|
|
192
|
+
const altText = others.length > 0 ? ` Available alternatives: ${others.join(", ")}` : "";
|
|
193
|
+
throw new Error(`Card payment failed to initialize. Check your card with wallet_status.${altText}`);
|
|
194
|
+
}
|
|
195
|
+
continue;
|
|
196
|
+
}
|
|
130
197
|
if (fetchCache.has(ck))
|
|
131
198
|
return fetchCache.get(ck);
|
|
132
199
|
const pf = await initCard();
|
|
@@ -205,6 +272,16 @@ export function getConfiguredMethods() {
|
|
|
205
272
|
}
|
|
206
273
|
return methods;
|
|
207
274
|
}
|
|
275
|
+
/**
|
|
276
|
+
* Normalize a requested payment method into the MCP-visible method identifier.
|
|
277
|
+
* Accepts chain names, wallet IDs, or "card".
|
|
278
|
+
*/
|
|
279
|
+
export function normalizePaymentMethod(method) {
|
|
280
|
+
if (method === "card")
|
|
281
|
+
return "card";
|
|
282
|
+
const resolved = resolveWalletAndChain(method);
|
|
283
|
+
return resolved?.chain ?? null;
|
|
284
|
+
}
|
|
208
285
|
/**
|
|
209
286
|
* Human-friendly display name for a payment method identifier.
|
|
210
287
|
*/
|
|
@@ -224,13 +301,25 @@ export function paymentMethodDisplayName(method) {
|
|
|
224
301
|
*/
|
|
225
302
|
export function getAcceptedPaymentMethods() {
|
|
226
303
|
const methods = getConfiguredMethods();
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
304
|
+
return methods
|
|
305
|
+
.map((m) => REGISTRY_METHOD_MAP[m])
|
|
306
|
+
.filter(Boolean);
|
|
307
|
+
}
|
|
308
|
+
export function toRegistryPaymentMethod(method) {
|
|
309
|
+
const normalized = normalizePaymentMethod(method);
|
|
310
|
+
if (!normalized)
|
|
311
|
+
return null;
|
|
312
|
+
return REGISTRY_METHOD_MAP[normalized] ?? null;
|
|
313
|
+
}
|
|
314
|
+
export function getCompatiblePaymentMethods(agent, configuredMethods = getConfiguredMethods()) {
|
|
315
|
+
const acceptedPayments = agent?.payment?.accepted_payments;
|
|
316
|
+
if (!Array.isArray(acceptedPayments) || acceptedPayments.length === 0) {
|
|
317
|
+
return [...configuredMethods];
|
|
318
|
+
}
|
|
319
|
+
const acceptedMethods = new Set(acceptedPayments
|
|
320
|
+
.map((payment) => METHOD_REGISTRY_MAP[payment])
|
|
321
|
+
.filter(Boolean));
|
|
322
|
+
return configuredMethods.filter((method) => acceptedMethods.has(method));
|
|
234
323
|
}
|
|
235
324
|
/**
|
|
236
325
|
* Check whether any payment method is configured.
|
|
@@ -242,27 +331,32 @@ export function hasWalletConfigured() {
|
|
|
242
331
|
* Get address for a specific method, or the first configured one.
|
|
243
332
|
*/
|
|
244
333
|
export async function getWalletAddress(method) {
|
|
334
|
+
let chain;
|
|
245
335
|
let wallet;
|
|
246
336
|
if (method && method !== "card") {
|
|
247
337
|
const resolved = resolveWalletAndChain(method);
|
|
248
338
|
wallet = resolved?.wallet;
|
|
339
|
+
chain = resolved?.chain;
|
|
249
340
|
}
|
|
250
341
|
else if (!method) {
|
|
251
342
|
wallet = getDefaultWallet();
|
|
343
|
+
chain = wallet?.defaultChain ?? wallet?.chains[0];
|
|
252
344
|
}
|
|
253
345
|
if (!wallet)
|
|
254
346
|
return null;
|
|
255
347
|
// OWS-managed wallet: derive address via the adapter
|
|
256
348
|
if (wallet.keyType === "ows" && wallet.owsWalletId) {
|
|
257
349
|
try {
|
|
258
|
-
const {
|
|
259
|
-
|
|
260
|
-
return account.address;
|
|
350
|
+
const { getOwsWalletAddress } = await import("./ows-adapter.js");
|
|
351
|
+
return await getOwsWalletAddress(wallet.owsWalletId, chain === "solana" ? "solana" : "evm");
|
|
261
352
|
}
|
|
262
353
|
catch {
|
|
263
354
|
return null;
|
|
264
355
|
}
|
|
265
356
|
}
|
|
357
|
+
if (chain === "solana") {
|
|
358
|
+
return null;
|
|
359
|
+
}
|
|
266
360
|
// Raw EVM key path
|
|
267
361
|
if (!wallet.key)
|
|
268
362
|
return null;
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { addWallet, getDefaultWallet, getWallets } from "./config.js";
|
|
2
|
+
import { getWalletAddress } from "./payments.js";
|
|
3
|
+
import { createOwsWallet, isOwsAvailable, listOwsWallets, } from "./ows-adapter.js";
|
|
4
|
+
const BASE_CHAIN_ID = "8453";
|
|
5
|
+
const SOLANA_CHAIN_ID = "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp";
|
|
6
|
+
const AUTO_IDENTITY_LABEL = "AW Identity";
|
|
7
|
+
function principalFromAddress(address, type) {
|
|
8
|
+
if (type === "evm") {
|
|
9
|
+
return `did:pkh:eip155:${BASE_CHAIN_ID}:${address.toLowerCase()}`;
|
|
10
|
+
}
|
|
11
|
+
return `did:pkh:${SOLANA_CHAIN_ID}:${address}`;
|
|
12
|
+
}
|
|
13
|
+
function walletSupportsEvm(wallet) {
|
|
14
|
+
return wallet.chains.some((chain) => chain !== "solana");
|
|
15
|
+
}
|
|
16
|
+
function walletSupportsSolana(wallet) {
|
|
17
|
+
return wallet.chains.includes("solana");
|
|
18
|
+
}
|
|
19
|
+
async function walletPrincipal(wallet) {
|
|
20
|
+
if (walletSupportsEvm(wallet)) {
|
|
21
|
+
const address = await getWalletAddress(wallet.id);
|
|
22
|
+
return address ? principalFromAddress(address, "evm") : null;
|
|
23
|
+
}
|
|
24
|
+
if (walletSupportsSolana(wallet)) {
|
|
25
|
+
const address = await getWalletAddress(wallet.id);
|
|
26
|
+
return address ? principalFromAddress(address, "solana") : null;
|
|
27
|
+
}
|
|
28
|
+
return null;
|
|
29
|
+
}
|
|
30
|
+
function preferredWallets() {
|
|
31
|
+
const wallets = getWallets();
|
|
32
|
+
const defaultWallet = getDefaultWallet();
|
|
33
|
+
const ordered = defaultWallet
|
|
34
|
+
? [defaultWallet, ...wallets.filter((wallet) => wallet.id !== defaultWallet.id)]
|
|
35
|
+
: wallets;
|
|
36
|
+
return ordered.sort((a, b) => {
|
|
37
|
+
const aScore = walletSupportsEvm(a) ? 0 : walletSupportsSolana(a) ? 1 : 2;
|
|
38
|
+
const bScore = walletSupportsEvm(b) ? 0 : walletSupportsSolana(b) ? 1 : 2;
|
|
39
|
+
return aScore - bScore;
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
async function createFallbackIdentityWallet() {
|
|
43
|
+
const { generatePrivateKey } = await import("viem/accounts");
|
|
44
|
+
const walletName = `aw-identity-${Date.now()}`;
|
|
45
|
+
const entry = {
|
|
46
|
+
id: walletName,
|
|
47
|
+
keyType: "evm",
|
|
48
|
+
key: generatePrivateKey(),
|
|
49
|
+
chains: ["tempo", "base"],
|
|
50
|
+
defaultChain: "base",
|
|
51
|
+
label: AUTO_IDENTITY_LABEL,
|
|
52
|
+
};
|
|
53
|
+
addWallet(entry);
|
|
54
|
+
return entry;
|
|
55
|
+
}
|
|
56
|
+
async function ensureIdentityWallet() {
|
|
57
|
+
const wallets = preferredWallets();
|
|
58
|
+
const existing = wallets.find((wallet) => walletSupportsEvm(wallet) || walletSupportsSolana(wallet));
|
|
59
|
+
if (existing)
|
|
60
|
+
return existing;
|
|
61
|
+
if (await isOwsAvailable()) {
|
|
62
|
+
const existingOwsWallets = await listOwsWallets();
|
|
63
|
+
const linked = existingOwsWallets[0];
|
|
64
|
+
if (linked) {
|
|
65
|
+
const entry = {
|
|
66
|
+
id: linked.name,
|
|
67
|
+
keyType: "ows",
|
|
68
|
+
owsWalletId: linked.id,
|
|
69
|
+
chains: ["tempo", "base"],
|
|
70
|
+
defaultChain: "base",
|
|
71
|
+
label: linked.name,
|
|
72
|
+
};
|
|
73
|
+
addWallet(entry);
|
|
74
|
+
return entry;
|
|
75
|
+
}
|
|
76
|
+
const walletName = `aw-identity-${Date.now()}`;
|
|
77
|
+
const created = await createOwsWallet(walletName, "evm");
|
|
78
|
+
const entry = {
|
|
79
|
+
id: walletName,
|
|
80
|
+
keyType: "ows",
|
|
81
|
+
owsWalletId: created.walletId,
|
|
82
|
+
chains: ["tempo", "base"],
|
|
83
|
+
defaultChain: "base",
|
|
84
|
+
label: AUTO_IDENTITY_LABEL,
|
|
85
|
+
};
|
|
86
|
+
addWallet(entry);
|
|
87
|
+
return entry;
|
|
88
|
+
}
|
|
89
|
+
return createFallbackIdentityWallet();
|
|
90
|
+
}
|
|
91
|
+
export async function getConsumerPrincipal() {
|
|
92
|
+
for (const wallet of preferredWallets()) {
|
|
93
|
+
const principal = await walletPrincipal(wallet);
|
|
94
|
+
if (principal)
|
|
95
|
+
return principal;
|
|
96
|
+
}
|
|
97
|
+
return null;
|
|
98
|
+
}
|
|
99
|
+
export async function ensureConsumerPrincipal() {
|
|
100
|
+
const existing = await getConsumerPrincipal();
|
|
101
|
+
if (existing)
|
|
102
|
+
return existing;
|
|
103
|
+
const identityWallet = await ensureIdentityWallet();
|
|
104
|
+
const principal = await walletPrincipal(identityWallet);
|
|
105
|
+
if (!principal) {
|
|
106
|
+
throw new Error("Could not derive a consumer principal from the configured identity wallet.");
|
|
107
|
+
}
|
|
108
|
+
return principal;
|
|
109
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { WalletEntry } from "./config.js";
|
|
2
|
+
export declare const SOLANA_USDC_MINT: "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v";
|
|
3
|
+
export declare const SOLANA_CHAIN_ID: "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp";
|
|
4
|
+
interface SolanaChargeClientConfig {
|
|
5
|
+
wallet: WalletEntry;
|
|
6
|
+
rpcUrl?: string;
|
|
7
|
+
}
|
|
8
|
+
export declare function solanaChargeClient(config: SolanaChargeClientConfig): any;
|
|
9
|
+
export {};
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Custom MPP payment method: Solana USDC charge (client-side).
|
|
3
|
+
*
|
|
4
|
+
* Sends an SPL Token transfer on Solana mainnet and returns the transaction
|
|
5
|
+
* signature as the payment credential.
|
|
6
|
+
*/
|
|
7
|
+
import { Credential, Method, z } from "mppx";
|
|
8
|
+
import { Connection, Keypair, PublicKey, Transaction, sendAndConfirmTransaction } from "@solana/web3.js";
|
|
9
|
+
import { ASSOCIATED_TOKEN_PROGRAM_ID, TOKEN_PROGRAM_ID, createTransferCheckedInstruction, getAssociatedTokenAddressSync, } from "@solana/spl-token";
|
|
10
|
+
import { toAtomicAmount } from "./amount-utils.js";
|
|
11
|
+
export const SOLANA_USDC_MINT = "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v";
|
|
12
|
+
export const SOLANA_CHAIN_ID = "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp";
|
|
13
|
+
const SOLANA_RPC = "https://api.mainnet-beta.solana.com";
|
|
14
|
+
const solanaChargeMethod = Method.from({
|
|
15
|
+
name: "solana",
|
|
16
|
+
intent: "charge",
|
|
17
|
+
schema: {
|
|
18
|
+
credential: {
|
|
19
|
+
payload: z.object({
|
|
20
|
+
signature: z.string(),
|
|
21
|
+
type: z.literal("signature"),
|
|
22
|
+
}),
|
|
23
|
+
},
|
|
24
|
+
request: z.pipe(z.object({
|
|
25
|
+
amount: z.string(),
|
|
26
|
+
currency: z.string(),
|
|
27
|
+
recipient: z.string(),
|
|
28
|
+
chainId: z.optional(z.string()),
|
|
29
|
+
decimals: z.optional(z.number()),
|
|
30
|
+
}), z.transform((value) => ({
|
|
31
|
+
...value,
|
|
32
|
+
methodDetails: { chainId: value.chainId ?? SOLANA_CHAIN_ID },
|
|
33
|
+
}))),
|
|
34
|
+
},
|
|
35
|
+
});
|
|
36
|
+
function keypairFromPrivateKeyHex(privateKeyHex) {
|
|
37
|
+
const normalized = privateKeyHex.startsWith("0x")
|
|
38
|
+
? privateKeyHex.slice(2)
|
|
39
|
+
: privateKeyHex;
|
|
40
|
+
const bytes = Uint8Array.from(Buffer.from(normalized, "hex"));
|
|
41
|
+
if (bytes.length === 32) {
|
|
42
|
+
return Keypair.fromSeed(bytes);
|
|
43
|
+
}
|
|
44
|
+
if (bytes.length === 64) {
|
|
45
|
+
return Keypair.fromSecretKey(bytes);
|
|
46
|
+
}
|
|
47
|
+
throw new Error(`Unsupported Solana private key length: ${bytes.length} bytes.`);
|
|
48
|
+
}
|
|
49
|
+
async function getKeypair(wallet) {
|
|
50
|
+
if (wallet.keyType === "ows" && wallet.owsWalletId) {
|
|
51
|
+
const { owsSolanaKeypairFromWalletId } = await import("./ows-adapter.js");
|
|
52
|
+
return owsSolanaKeypairFromWalletId(wallet.owsWalletId);
|
|
53
|
+
}
|
|
54
|
+
if (wallet.key) {
|
|
55
|
+
return keypairFromPrivateKeyHex(wallet.key);
|
|
56
|
+
}
|
|
57
|
+
throw new Error(`Wallet "${wallet.id}" cannot sign Solana transactions.`);
|
|
58
|
+
}
|
|
59
|
+
async function resolveRecipientTokenAccount(connection, mint, recipient) {
|
|
60
|
+
const accountInfo = await connection.getParsedAccountInfo(recipient, "confirmed");
|
|
61
|
+
const owner = accountInfo.value?.owner;
|
|
62
|
+
if (owner && owner.toBase58() === TOKEN_PROGRAM_ID.toBase58()) {
|
|
63
|
+
return recipient;
|
|
64
|
+
}
|
|
65
|
+
return getAssociatedTokenAddressSync(mint, recipient, false, TOKEN_PROGRAM_ID, ASSOCIATED_TOKEN_PROGRAM_ID);
|
|
66
|
+
}
|
|
67
|
+
export function solanaChargeClient(config) {
|
|
68
|
+
return Method.toClient(solanaChargeMethod, {
|
|
69
|
+
async createCredential({ challenge }) {
|
|
70
|
+
const { request } = challenge;
|
|
71
|
+
const amount = toAtomicAmount(request.amount, request.decimals ?? 6);
|
|
72
|
+
const decimals = request.decimals ?? 6;
|
|
73
|
+
const mint = new PublicKey(request.currency ?? SOLANA_USDC_MINT);
|
|
74
|
+
const recipient = new PublicKey(request.recipient);
|
|
75
|
+
const keypair = await getKeypair(config.wallet);
|
|
76
|
+
const owner = keypair.publicKey;
|
|
77
|
+
const connection = new Connection(config.rpcUrl ?? SOLANA_RPC, "confirmed");
|
|
78
|
+
const sourceTokenAccount = getAssociatedTokenAddressSync(mint, owner, false, TOKEN_PROGRAM_ID, ASSOCIATED_TOKEN_PROGRAM_ID);
|
|
79
|
+
const destinationTokenAccount = await resolveRecipientTokenAccount(connection, mint, recipient);
|
|
80
|
+
const instruction = createTransferCheckedInstruction(sourceTokenAccount, mint, destinationTokenAccount, owner, amount, decimals);
|
|
81
|
+
const { blockhash } = await connection.getLatestBlockhash("confirmed");
|
|
82
|
+
const transaction = new Transaction({
|
|
83
|
+
feePayer: owner,
|
|
84
|
+
recentBlockhash: blockhash,
|
|
85
|
+
}).add(instruction);
|
|
86
|
+
const signature = await sendAndConfirmTransaction(connection, transaction, [keypair], {
|
|
87
|
+
commitment: "confirmed",
|
|
88
|
+
});
|
|
89
|
+
return Credential.serialize({
|
|
90
|
+
challenge,
|
|
91
|
+
payload: { signature, type: "signature" },
|
|
92
|
+
source: `did:pkh:${SOLANA_CHAIN_ID}:${owner.toBase58()}`,
|
|
93
|
+
});
|
|
94
|
+
},
|
|
95
|
+
});
|
|
96
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export declare function getDailySpend(method: string, now?: Date): number;
|
|
2
|
+
export declare function canSpend(params: {
|
|
3
|
+
method: string;
|
|
4
|
+
amountUsd: number;
|
|
5
|
+
}): {
|
|
6
|
+
ok: true;
|
|
7
|
+
} | {
|
|
8
|
+
ok: false;
|
|
9
|
+
message: string;
|
|
10
|
+
};
|
|
11
|
+
export declare function requiresPolicyConfirmation(method: string, amountUsd: number): boolean;
|
|
12
|
+
export declare function recordSpend(method: string, amountUsd: number, now?: Date): void;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { getSpendLedger, getSpendPolicy, saveSpendLedger, } from "./config.js";
|
|
2
|
+
const LEDGER_RETENTION_DAYS = 35;
|
|
3
|
+
function utcDay(now) {
|
|
4
|
+
return now.toISOString().slice(0, 10);
|
|
5
|
+
}
|
|
6
|
+
function pruneLedger(entries, now) {
|
|
7
|
+
const cutoff = new Date(now);
|
|
8
|
+
cutoff.setUTCDate(cutoff.getUTCDate() - LEDGER_RETENTION_DAYS);
|
|
9
|
+
return entries.filter((entry) => new Date(entry.timestamp) >= cutoff);
|
|
10
|
+
}
|
|
11
|
+
export function getDailySpend(method, now = new Date()) {
|
|
12
|
+
const day = utcDay(now);
|
|
13
|
+
return getSpendLedger()
|
|
14
|
+
.filter((entry) => entry.method === method && entry.day === day)
|
|
15
|
+
.reduce((sum, entry) => sum + entry.amountUsd, 0);
|
|
16
|
+
}
|
|
17
|
+
export function canSpend(params) {
|
|
18
|
+
const policy = getSpendPolicy(params.method);
|
|
19
|
+
if (!policy)
|
|
20
|
+
return { ok: true };
|
|
21
|
+
if (policy.maxPerTxUsd != null && params.amountUsd > policy.maxPerTxUsd) {
|
|
22
|
+
return {
|
|
23
|
+
ok: false,
|
|
24
|
+
message: `Transaction blocked by spend policy: $${params.amountUsd.toFixed(2)} exceeds max_per_tx of $${policy.maxPerTxUsd.toFixed(2)} for "${params.method}".`,
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
if (policy.maxPerDayUsd != null) {
|
|
28
|
+
const spentToday = getDailySpend(params.method);
|
|
29
|
+
if (spentToday + params.amountUsd > policy.maxPerDayUsd) {
|
|
30
|
+
return {
|
|
31
|
+
ok: false,
|
|
32
|
+
message: `Transaction blocked by spend policy: daily spend would be $${(spentToday + params.amountUsd).toFixed(2)} > $${policy.maxPerDayUsd.toFixed(2)} for "${params.method}".`,
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
return { ok: true };
|
|
37
|
+
}
|
|
38
|
+
export function requiresPolicyConfirmation(method, amountUsd) {
|
|
39
|
+
const policy = getSpendPolicy(method);
|
|
40
|
+
if (!policy?.requireConfirmationAboveUsd)
|
|
41
|
+
return false;
|
|
42
|
+
return amountUsd >= policy.requireConfirmationAboveUsd;
|
|
43
|
+
}
|
|
44
|
+
export function recordSpend(method, amountUsd, now = new Date()) {
|
|
45
|
+
const entries = pruneLedger(getSpendLedger(), now);
|
|
46
|
+
entries.push({
|
|
47
|
+
method,
|
|
48
|
+
amountUsd,
|
|
49
|
+
day: utcDay(now),
|
|
50
|
+
timestamp: now.toISOString(),
|
|
51
|
+
});
|
|
52
|
+
saveSpendLedger(entries);
|
|
53
|
+
}
|
package/dist/core/types.d.ts
CHANGED
|
@@ -5,8 +5,7 @@ export interface AgentRecord {
|
|
|
5
5
|
id: string;
|
|
6
6
|
name: string;
|
|
7
7
|
description?: string;
|
|
8
|
-
|
|
9
|
-
pricingModel?: string;
|
|
8
|
+
pricePerRunUsd?: string;
|
|
10
9
|
avgRating?: number | null;
|
|
11
10
|
totalExecutions?: number;
|
|
12
11
|
successRate?: number | string | null;
|
|
@@ -21,6 +20,16 @@ export interface AgentRecord {
|
|
|
21
20
|
};
|
|
22
21
|
payment?: {
|
|
23
22
|
pricing?: Record<string, unknown>;
|
|
23
|
+
accepted_payments?: string[];
|
|
24
|
+
credit_packs?: {
|
|
25
|
+
unit_type?: string;
|
|
26
|
+
packs?: Array<{
|
|
27
|
+
key?: string;
|
|
28
|
+
name?: string;
|
|
29
|
+
included_units?: number;
|
|
30
|
+
price_usd?: string;
|
|
31
|
+
}>;
|
|
32
|
+
} | null;
|
|
24
33
|
[key: string]: unknown;
|
|
25
34
|
};
|
|
26
35
|
[key: string]: unknown;
|
package/dist/index.js
CHANGED
|
@@ -11,6 +11,8 @@ import { registerRateTools } from "./tools/rate.js";
|
|
|
11
11
|
import { registerWalletTools } from "./tools/wallet.js";
|
|
12
12
|
import { registerFavoriteTools } from "./tools/favorites.js";
|
|
13
13
|
import { registerTipTools } from "./tools/tip.js";
|
|
14
|
+
import { registerPassTools } from "./tools/passes.js";
|
|
15
|
+
import { registerObservabilityTools } from "./tools/observability.js";
|
|
14
16
|
// ── Resources ────────────────────────────────────────────────────
|
|
15
17
|
import { registerAgentResources } from "./resources/agents.js";
|
|
16
18
|
import { registerWalletResources } from "./resources/wallet.js";
|
|
@@ -33,14 +35,18 @@ export async function startMcpServer() {
|
|
|
33
35
|
"1. search_agents() or solve() — find agents for the task",
|
|
34
36
|
"2. get_agent() — check required input fields before running",
|
|
35
37
|
"3. run_agent() or solve() with ALL required fields",
|
|
36
|
-
"
|
|
38
|
+
"3a. If the agent offers discounted credit packs, use buy_agent_credit_pack() and list_agent_credit_packs() when helpful",
|
|
39
|
+
"4. If no payment method is set up, run_agent returns a setup page to connect a credit card — present it to the user",
|
|
37
40
|
"5. Ask user to rate or tip after a successful run",
|
|
38
41
|
"",
|
|
39
42
|
"PAYMENT:",
|
|
40
|
-
"- Credit/debit card is the default and easiest way to pay —
|
|
41
|
-
"- Crypto wallets (Tempo USDC, Base USDC) are available for advanced users.",
|
|
43
|
+
"- Credit/debit card is the default and easiest way to pay — open the setup page to connect, no funding needed.",
|
|
44
|
+
"- Crypto wallets (Tempo USDC, Base USDC, Solana USDC) are available for advanced users.",
|
|
42
45
|
"- Card and crypto are SEPARATE payment methods. Card charges a credit card; crypto sends USDC on-chain.",
|
|
43
46
|
"- Card is set as the default payment method when configured.",
|
|
47
|
+
"- run_agent() and solve() require pay_with explicitly.",
|
|
48
|
+
"- Use wallet_status to see the exact payment methods the user has configured before calling a paid tool.",
|
|
49
|
+
"- Use open_observability_dashboard() to open a secure web usage dashboard for runs/spend/rebates.",
|
|
44
50
|
"- Payment is automatic once configured. Users are never charged for failed runs.",
|
|
45
51
|
"- Do NOT ask the user to set up payment before they try to run an agent. Let them explore freely.",
|
|
46
52
|
"- If a specific payment method fails, report the error clearly. Do NOT silently fall back to a different method.",
|
|
@@ -65,6 +71,8 @@ export async function startMcpServer() {
|
|
|
65
71
|
registerWalletTools(server);
|
|
66
72
|
registerFavoriteTools(server);
|
|
67
73
|
registerTipTools(server);
|
|
74
|
+
registerPassTools(server);
|
|
75
|
+
registerObservabilityTools(server);
|
|
68
76
|
// Register resources
|
|
69
77
|
registerAgentResources(server);
|
|
70
78
|
registerWalletResources(server);
|
package/dist/prompts/index.js
CHANGED
|
@@ -12,7 +12,7 @@ export function registerPrompts(server) {
|
|
|
12
12
|
"1. Check my wallet status with wallet_status",
|
|
13
13
|
"2. If no wallet is configured, help me create one with wallet_setup",
|
|
14
14
|
"3. Show me some popular agents with search_agents",
|
|
15
|
-
"4. Briefly explain how solve, run_agent, rating, and tipping work",
|
|
15
|
+
"4. Briefly explain how solve, run_agent, buy_agent_credit_pack, pay_with selection, rating, and tipping work",
|
|
16
16
|
].join("\n"),
|
|
17
17
|
},
|
|
18
18
|
}],
|
|
@@ -62,7 +62,7 @@ export function registerPrompts(server) {
|
|
|
62
62
|
text: [
|
|
63
63
|
`Complete this task on Agent Wonderland within $${budget || "1.00"}: "${task}"`,
|
|
64
64
|
"",
|
|
65
|
-
"Use the solve tool with the budget parameter. Show me what agent was selected and why.",
|
|
65
|
+
"Use the solve tool with the budget parameter and an explicit pay_with method. Show me what agent was selected and why.",
|
|
66
66
|
].join("\n"),
|
|
67
67
|
},
|
|
68
68
|
}],
|
|
@@ -78,6 +78,8 @@ export function registerPrompts(server) {
|
|
|
78
78
|
text: [
|
|
79
79
|
`Run agent ${agent_id} with this input: ${input}`,
|
|
80
80
|
"",
|
|
81
|
+
"Include an explicit pay_with method when you call run_agent.",
|
|
82
|
+
"",
|
|
81
83
|
"After getting the result:",
|
|
82
84
|
"1. Summarize the output",
|
|
83
85
|
"2. Assess the quality",
|
package/dist/resources/agents.js
CHANGED
|
@@ -7,7 +7,7 @@ export function registerAgentResources(server) {
|
|
|
7
7
|
const lines = agents.map(a => {
|
|
8
8
|
const rating = stars(a.reputationScore);
|
|
9
9
|
const jobs = compactNumber(a.totalExecutions);
|
|
10
|
-
const price = formatPrice(a.
|
|
10
|
+
const price = formatPrice(a.pricePerRunUsd);
|
|
11
11
|
return `${a.name} ${rating} ${jobs} jobs | ${price}/req — ${a.description || ""}`;
|
|
12
12
|
});
|
|
13
13
|
return {
|
package/dist/resources/wallet.js
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
|
-
import { getWallets, getCardConfig } from "../core/config.js";
|
|
1
|
+
import { getWallets, getCardConfig, getPendingCardSetupToken } from "../core/config.js";
|
|
2
|
+
import { getCardCapabilities } from "../core/card-setup.js";
|
|
2
3
|
import { getWalletAddress, getConfiguredMethods } from "../core/payments.js";
|
|
3
4
|
export function registerWalletResources(server) {
|
|
4
5
|
server.resource("wallet-config", "aw://wallet", async () => {
|
|
5
6
|
const wallets = getWallets();
|
|
6
7
|
const card = getCardConfig();
|
|
8
|
+
const pendingCardSetupToken = getPendingCardSetupToken();
|
|
7
9
|
const methods = getConfiguredMethods();
|
|
8
10
|
const lines = ["Wallet Configuration", ""];
|
|
9
11
|
for (const w of wallets) {
|
|
@@ -13,6 +15,11 @@ export function registerWalletResources(server) {
|
|
|
13
15
|
}
|
|
14
16
|
if (card) {
|
|
15
17
|
lines.push(`Card: ${card.brand} ****${card.last4}`);
|
|
18
|
+
const capabilities = await getCardCapabilities();
|
|
19
|
+
lines.push(`Card MPP: ${capabilities.spt_status}${capabilities.message ? ` — ${capabilities.message}` : ""}`);
|
|
20
|
+
}
|
|
21
|
+
if (pendingCardSetupToken) {
|
|
22
|
+
lines.push("Card setup: pending confirmation");
|
|
16
23
|
}
|
|
17
24
|
lines.push("", `Configured methods: ${methods.join(", ") || "none"}`);
|
|
18
25
|
return {
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|