@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
|
@@ -11,7 +11,7 @@ export declare function isFileOutput(output: unknown): output is {
|
|
|
11
11
|
mime_type?: string;
|
|
12
12
|
size_bytes?: number;
|
|
13
13
|
};
|
|
14
|
-
export declare function formatPrice(
|
|
14
|
+
export declare function formatPrice(pricePerRunUsd?: string | null): string;
|
|
15
15
|
interface AgentLike {
|
|
16
16
|
id?: string;
|
|
17
17
|
name?: string;
|
|
@@ -19,8 +19,7 @@ interface AgentLike {
|
|
|
19
19
|
avgRating?: number | null;
|
|
20
20
|
ratingCount?: number;
|
|
21
21
|
totalExecutions?: number;
|
|
22
|
-
|
|
23
|
-
pricingModel?: string;
|
|
22
|
+
pricePerRunUsd?: string;
|
|
24
23
|
stats?: {
|
|
25
24
|
completedJobs?: number;
|
|
26
25
|
avgRating?: number | null;
|
|
@@ -39,8 +38,9 @@ interface RunResultLike {
|
|
|
39
38
|
error_code?: string;
|
|
40
39
|
latency_ms?: number;
|
|
41
40
|
execution_latency_ms?: number;
|
|
42
|
-
cost?: number;
|
|
43
|
-
|
|
41
|
+
cost?: number | string;
|
|
42
|
+
settled_amount?: number | string;
|
|
43
|
+
estimated_cost?: number | string;
|
|
44
44
|
input_tokens?: number;
|
|
45
45
|
tags?: string[];
|
|
46
46
|
consumption_mode?: string;
|
package/dist/core/formatters.js
CHANGED
|
@@ -32,20 +32,18 @@ export function isFileOutput(output) {
|
|
|
32
32
|
return output != null && typeof output === "object" && output.type === "file" && !!output.url;
|
|
33
33
|
}
|
|
34
34
|
// ── Price formatting ─────────────────────────────────────────────
|
|
35
|
-
export function formatPrice(
|
|
36
|
-
if (!
|
|
35
|
+
export function formatPrice(pricePerRunUsd) {
|
|
36
|
+
if (!pricePerRunUsd)
|
|
37
37
|
return "free";
|
|
38
|
-
const p = parseFloat(
|
|
39
|
-
|
|
40
|
-
return `$${p.toFixed(2)}/req`;
|
|
41
|
-
return `$${p.toFixed(3)}/1k tokens`;
|
|
38
|
+
const p = parseFloat(pricePerRunUsd);
|
|
39
|
+
return `$${p.toFixed(2)}/req`;
|
|
42
40
|
}
|
|
43
41
|
export function agentLine(agent) {
|
|
44
42
|
const name = agent.name ?? "Unknown";
|
|
45
43
|
const slug = agent.slug ? ` (${agent.slug})` : "";
|
|
46
44
|
const rating = agent.avgRating ?? agent.stats?.avgRating ?? null;
|
|
47
45
|
const jobs = agent.stats?.completedJobs ?? agent.totalExecutions ?? 0;
|
|
48
|
-
const price = formatPrice(agent.
|
|
46
|
+
const price = formatPrice(agent.pricePerRunUsd);
|
|
49
47
|
const reliability = agent.successRate != null && Number(agent.successRate) < 1
|
|
50
48
|
? ` • ${(Number(agent.successRate) * 100).toFixed(0)}% reliable`
|
|
51
49
|
: "";
|
|
@@ -83,6 +81,12 @@ export function agentList(agents, query) {
|
|
|
83
81
|
const lines = agents.map((a) => ` ${agentLine(a)}`);
|
|
84
82
|
return [header, "", ...lines].join("\n");
|
|
85
83
|
}
|
|
84
|
+
function toDisplayCost(value) {
|
|
85
|
+
if (value == null)
|
|
86
|
+
return null;
|
|
87
|
+
const numeric = typeof value === "number" ? value : Number.parseFloat(value);
|
|
88
|
+
return Number.isFinite(numeric) ? numeric : null;
|
|
89
|
+
}
|
|
86
90
|
export function formatRunResult(result, opts) {
|
|
87
91
|
const lines = [];
|
|
88
92
|
// Output
|
|
@@ -109,7 +113,7 @@ export function formatRunResult(result, opts) {
|
|
|
109
113
|
lines.push("");
|
|
110
114
|
}
|
|
111
115
|
// Summary line
|
|
112
|
-
const cost = result.cost ?? result.estimated_cost;
|
|
116
|
+
const cost = toDisplayCost(result.cost ?? result.settled_amount ?? result.estimated_cost);
|
|
113
117
|
const usedCreditPack = result.consumption_mode === "credit_pack";
|
|
114
118
|
const status = result.status === "success" || result.status === "completed" ? "✓" : "✗";
|
|
115
119
|
const agent = result.agent_name ?? result.agent_id ?? "";
|
package/dist/core/passes.d.ts
CHANGED
|
@@ -34,7 +34,7 @@ export interface CreditPackInventory {
|
|
|
34
34
|
balances: CreditPackRecord[];
|
|
35
35
|
}
|
|
36
36
|
export declare function getCreditPackProgram(agent: AgentRecord): CreditPackProgramInfo | null;
|
|
37
|
-
export declare function getCreditPackInventory(agentId: string): Promise<CreditPackInventory | null>;
|
|
37
|
+
export declare function getCreditPackInventory(agentId: string, method?: string): Promise<CreditPackInventory | null>;
|
|
38
38
|
export declare function getActiveCreditPack(inventory: CreditPackInventory | null): CreditPackRecord | null;
|
|
39
39
|
export declare function formatCreditPackOffer(offer: CreditPackOffer): string;
|
|
40
40
|
export declare function formatCreditPack(pack: CreditPackRecord): string;
|
package/dist/core/passes.js
CHANGED
|
@@ -4,9 +4,12 @@ export function getCreditPackProgram(agent) {
|
|
|
4
4
|
const creditPacks = payment.credit_packs;
|
|
5
5
|
return creditPacks ?? null;
|
|
6
6
|
}
|
|
7
|
-
export async function getCreditPackInventory(agentId) {
|
|
7
|
+
export async function getCreditPackInventory(agentId, method) {
|
|
8
8
|
try {
|
|
9
|
-
return await apiGet(`/agents/${agentId}/credit-packs`, {
|
|
9
|
+
return await apiGet(`/agents/${agentId}/credit-packs`, {
|
|
10
|
+
ensureConsumerPrincipal: true,
|
|
11
|
+
principalMethod: method,
|
|
12
|
+
});
|
|
10
13
|
}
|
|
11
14
|
catch {
|
|
12
15
|
return null;
|
package/dist/core/payments.d.ts
CHANGED
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
* which to use per-request via `--pay-with <wallet-id|chain|card>`.
|
|
11
11
|
*/
|
|
12
12
|
import type { AgentRecord } from "./types.js";
|
|
13
|
+
export declare function isCardPaymentEnabled(): boolean;
|
|
13
14
|
/**
|
|
14
15
|
* Returns a payment-aware fetch for a specific method, or the best
|
|
15
16
|
* available method if none is specified.
|
package/dist/core/payments.js
CHANGED
|
@@ -10,6 +10,13 @@
|
|
|
10
10
|
* which to use per-request via `--pay-with <wallet-id|chain|card>`.
|
|
11
11
|
*/
|
|
12
12
|
import { getConfig, getWallets, getDefaultWallet, getCardConfig, resolveWalletAndChain, getApiUrl, } from "./config.js";
|
|
13
|
+
// Feature flag: disable card payment for launch. Flip to true once Stripe
|
|
14
|
+
// approves the live SPT issuer. Config/card state is still persisted so this
|
|
15
|
+
// can be re-enabled without reconfiguring wallets.
|
|
16
|
+
const ENABLE_CARD_PAYMENT = false;
|
|
17
|
+
export function isCardPaymentEnabled() {
|
|
18
|
+
return ENABLE_CARD_PAYMENT;
|
|
19
|
+
}
|
|
13
20
|
// Cache per wallet+chain combo to avoid re-initializing
|
|
14
21
|
const fetchCache = new Map();
|
|
15
22
|
const REGISTRY_METHOD_MAP = {
|
|
@@ -45,9 +52,9 @@ function clearStaleCardCache(activeKey) {
|
|
|
45
52
|
}
|
|
46
53
|
}
|
|
47
54
|
// ── Per-protocol initializers ───────────────────────────────────
|
|
48
|
-
async function
|
|
55
|
+
async function initEvmMppForChain(wallet, chain) {
|
|
49
56
|
try {
|
|
50
|
-
const { Mppx
|
|
57
|
+
const { Mppx } = await import("mppx/client");
|
|
51
58
|
let account;
|
|
52
59
|
if (wallet.keyType === "ows" && wallet.owsWalletId) {
|
|
53
60
|
const { owsAccountFromWalletId } = await import("./ows-adapter.js");
|
|
@@ -60,8 +67,16 @@ async function initMpp(wallet) {
|
|
|
60
67
|
else {
|
|
61
68
|
return null;
|
|
62
69
|
}
|
|
63
|
-
const
|
|
64
|
-
|
|
70
|
+
const methods = [];
|
|
71
|
+
if (chain === "tempo") {
|
|
72
|
+
const { tempoChargeClient } = await import("./tempo-charge.js");
|
|
73
|
+
methods.push(tempoChargeClient({ account }));
|
|
74
|
+
}
|
|
75
|
+
else {
|
|
76
|
+
const { baseChargeClient } = await import("./base-charge.js");
|
|
77
|
+
methods.push(baseChargeClient({ account }));
|
|
78
|
+
}
|
|
79
|
+
const mppx = Mppx.create({ methods: methods, polyfill: false });
|
|
65
80
|
return mppx.fetch.bind(mppx);
|
|
66
81
|
}
|
|
67
82
|
catch {
|
|
@@ -77,6 +92,7 @@ async function initSolanaMpp(wallet) {
|
|
|
77
92
|
const { solanaChargeClient } = await import("./solana-charge.js");
|
|
78
93
|
const mppx = Mppx.create({
|
|
79
94
|
methods: [solanaChargeClient({ wallet })],
|
|
95
|
+
polyfill: false,
|
|
80
96
|
});
|
|
81
97
|
return mppx.fetch.bind(mppx);
|
|
82
98
|
}
|
|
@@ -113,6 +129,7 @@ async function initCard() {
|
|
|
113
129
|
return spt;
|
|
114
130
|
},
|
|
115
131
|
})],
|
|
132
|
+
polyfill: false,
|
|
116
133
|
});
|
|
117
134
|
return mppx.fetch.bind(mppx);
|
|
118
135
|
}
|
|
@@ -127,7 +144,10 @@ async function initForChain(wallet, chain) {
|
|
|
127
144
|
if (chain === "solana") {
|
|
128
145
|
return initSolanaMpp(wallet);
|
|
129
146
|
}
|
|
130
|
-
|
|
147
|
+
if (chain === "tempo" || chain === "base") {
|
|
148
|
+
return initEvmMppForChain(wallet, chain);
|
|
149
|
+
}
|
|
150
|
+
return null;
|
|
131
151
|
}
|
|
132
152
|
// ── Public API ──────────────────────────────────────────────────
|
|
133
153
|
/**
|
|
@@ -139,6 +159,9 @@ async function initForChain(wallet, chain) {
|
|
|
139
159
|
export async function getPaymentFetch(method) {
|
|
140
160
|
// Card payment
|
|
141
161
|
if (method === "card") {
|
|
162
|
+
if (!ENABLE_CARD_PAYMENT) {
|
|
163
|
+
throw new Error('Card payments are temporarily unavailable. Use a crypto wallet (tempo, base, or solana).');
|
|
164
|
+
}
|
|
142
165
|
const ck = cardCacheKey();
|
|
143
166
|
clearStaleCardCache(ck ?? undefined);
|
|
144
167
|
if (!ck) {
|
|
@@ -247,13 +270,13 @@ export function getConfiguredMethods() {
|
|
|
247
270
|
methods.push(c);
|
|
248
271
|
}
|
|
249
272
|
}
|
|
250
|
-
// Add card if configured
|
|
251
|
-
if (getCardConfig()) {
|
|
273
|
+
// Add card if configured (gated for launch)
|
|
274
|
+
if (ENABLE_CARD_PAYMENT && getCardConfig()) {
|
|
252
275
|
methods.push("card");
|
|
253
276
|
}
|
|
254
|
-
// Respect defaultPaymentMethod — move it to front of list
|
|
277
|
+
// Respect defaultPaymentMethod — move it to front of list (ignore card when disabled)
|
|
255
278
|
const defaultMethod = getConfig().defaultPaymentMethod;
|
|
256
|
-
if (defaultMethod) {
|
|
279
|
+
if (defaultMethod && (defaultMethod !== "card" || ENABLE_CARD_PAYMENT)) {
|
|
257
280
|
const idx = methods.indexOf(defaultMethod);
|
|
258
281
|
if (idx > 0) {
|
|
259
282
|
methods.splice(idx, 1);
|
package/dist/core/principal.d.ts
CHANGED
|
@@ -1,2 +1,5 @@
|
|
|
1
1
|
export declare function getConsumerPrincipal(): Promise<string | null>;
|
|
2
|
+
export declare function getBaseRebatePrincipal(): Promise<string | null>;
|
|
3
|
+
export declare function getConsumerPrincipalForMethod(method?: string): Promise<string | null>;
|
|
2
4
|
export declare function ensureConsumerPrincipal(): Promise<string>;
|
|
5
|
+
export declare function ensureConsumerPrincipalForMethod(method?: string): Promise<string>;
|
package/dist/core/principal.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { addWallet, getDefaultWallet, getWallets } from "./config.js";
|
|
1
|
+
import { addWallet, getDefaultWallet, getWallets, resolveWalletAndChain, } from "./config.js";
|
|
2
2
|
import { getWalletAddress } from "./payments.js";
|
|
3
3
|
import { createOwsWallet, isOwsAvailable, listOwsWallets, } from "./ows-adapter.js";
|
|
4
4
|
const BASE_CHAIN_ID = "8453";
|
|
@@ -27,6 +27,12 @@ async function walletPrincipal(wallet) {
|
|
|
27
27
|
}
|
|
28
28
|
return null;
|
|
29
29
|
}
|
|
30
|
+
async function principalForChain(chain) {
|
|
31
|
+
const address = await getWalletAddress(chain);
|
|
32
|
+
if (!address)
|
|
33
|
+
return null;
|
|
34
|
+
return principalFromAddress(address, chain === "solana" ? "solana" : "evm");
|
|
35
|
+
}
|
|
30
36
|
function preferredWallets() {
|
|
31
37
|
const wallets = getWallets();
|
|
32
38
|
const defaultWallet = getDefaultWallet();
|
|
@@ -96,6 +102,18 @@ export async function getConsumerPrincipal() {
|
|
|
96
102
|
}
|
|
97
103
|
return null;
|
|
98
104
|
}
|
|
105
|
+
export async function getBaseRebatePrincipal() {
|
|
106
|
+
return (await principalForChain("base")) ?? principalForChain("tempo");
|
|
107
|
+
}
|
|
108
|
+
export async function getConsumerPrincipalForMethod(method) {
|
|
109
|
+
if (!method || method === "card") {
|
|
110
|
+
return getConsumerPrincipal();
|
|
111
|
+
}
|
|
112
|
+
const resolved = resolveWalletAndChain(method);
|
|
113
|
+
if (!resolved)
|
|
114
|
+
return null;
|
|
115
|
+
return principalForChain(resolved.chain);
|
|
116
|
+
}
|
|
99
117
|
export async function ensureConsumerPrincipal() {
|
|
100
118
|
const existing = await getConsumerPrincipal();
|
|
101
119
|
if (existing)
|
|
@@ -107,3 +125,13 @@ export async function ensureConsumerPrincipal() {
|
|
|
107
125
|
}
|
|
108
126
|
return principal;
|
|
109
127
|
}
|
|
128
|
+
export async function ensureConsumerPrincipalForMethod(method) {
|
|
129
|
+
const existing = await getConsumerPrincipalForMethod(method);
|
|
130
|
+
if (existing)
|
|
131
|
+
return existing;
|
|
132
|
+
if (method && method !== "card") {
|
|
133
|
+
throw new Error(`Could not derive a consumer principal for payment method "${method}". ` +
|
|
134
|
+
"Check wallet_status and confirm that chain is configured for the active wallet.");
|
|
135
|
+
}
|
|
136
|
+
return ensureConsumerPrincipal();
|
|
137
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export interface ChainSettings {
|
|
2
|
+
chainId: number | string;
|
|
3
|
+
usdc: string;
|
|
4
|
+
rpcUrl: string;
|
|
5
|
+
explorer?: string;
|
|
6
|
+
}
|
|
7
|
+
export interface PublicSettings {
|
|
8
|
+
network: "testnet" | "mainnet";
|
|
9
|
+
stripe: {
|
|
10
|
+
mode: "test" | "live";
|
|
11
|
+
};
|
|
12
|
+
chains: {
|
|
13
|
+
tempo: ChainSettings;
|
|
14
|
+
base: ChainSettings;
|
|
15
|
+
solana: ChainSettings & {
|
|
16
|
+
cluster: "mainnet-beta" | "devnet";
|
|
17
|
+
};
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
export declare function getSettings(): Promise<PublicSettings | null>;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { getApiUrl } from "./config.js";
|
|
2
|
+
let cache = null;
|
|
3
|
+
const TTL_MS = 5 * 60 * 1000;
|
|
4
|
+
export async function getSettings() {
|
|
5
|
+
const now = Date.now();
|
|
6
|
+
if (cache && cache.expiresAt > now)
|
|
7
|
+
return cache.data;
|
|
8
|
+
try {
|
|
9
|
+
const res = await fetch(`${getApiUrl()}/settings`);
|
|
10
|
+
if (!res.ok)
|
|
11
|
+
return cache?.data ?? null;
|
|
12
|
+
const data = await res.json();
|
|
13
|
+
cache = { data, expiresAt: now + TTL_MS };
|
|
14
|
+
return data;
|
|
15
|
+
}
|
|
16
|
+
catch {
|
|
17
|
+
return cache?.data ?? null;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { Connection, PublicKey } from "@solana/web3.js";
|
|
1
2
|
import type { WalletEntry } from "./config.js";
|
|
2
3
|
export declare const SOLANA_USDC_MINT: "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v";
|
|
3
4
|
export declare const SOLANA_CHAIN_ID: "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp";
|
|
@@ -5,5 +6,9 @@ interface SolanaChargeClientConfig {
|
|
|
5
6
|
wallet: WalletEntry;
|
|
6
7
|
rpcUrl?: string;
|
|
7
8
|
}
|
|
9
|
+
export declare function resolveRecipientTokenAccount(connection: Connection, mint: PublicKey, recipient: PublicKey): Promise<{
|
|
10
|
+
tokenAccount: PublicKey;
|
|
11
|
+
needsCreateAssociatedAccount: boolean;
|
|
12
|
+
}>;
|
|
8
13
|
export declare function solanaChargeClient(config: SolanaChargeClientConfig): any;
|
|
9
14
|
export {};
|
|
@@ -6,7 +6,8 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import { Credential, Method, z } from "mppx";
|
|
8
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";
|
|
9
|
+
import { ASSOCIATED_TOKEN_PROGRAM_ID, TOKEN_PROGRAM_ID, createAssociatedTokenAccountInstruction, createTransferCheckedInstruction, getAssociatedTokenAddressSync, } from "@solana/spl-token";
|
|
10
|
+
import { toAtomicAmount } from "./amount-utils.js";
|
|
10
11
|
export const SOLANA_USDC_MINT = "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v";
|
|
11
12
|
export const SOLANA_CHAIN_ID = "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp";
|
|
12
13
|
const SOLANA_RPC = "https://api.mainnet-beta.solana.com";
|
|
@@ -55,19 +56,34 @@ async function getKeypair(wallet) {
|
|
|
55
56
|
}
|
|
56
57
|
throw new Error(`Wallet "${wallet.id}" cannot sign Solana transactions.`);
|
|
57
58
|
}
|
|
58
|
-
async function resolveRecipientTokenAccount(connection, mint, recipient) {
|
|
59
|
+
export async function resolveRecipientTokenAccount(connection, mint, recipient) {
|
|
59
60
|
const accountInfo = await connection.getParsedAccountInfo(recipient, "confirmed");
|
|
60
61
|
const owner = accountInfo.value?.owner;
|
|
61
62
|
if (owner && owner.toBase58() === TOKEN_PROGRAM_ID.toBase58()) {
|
|
62
|
-
return
|
|
63
|
+
return {
|
|
64
|
+
tokenAccount: recipient,
|
|
65
|
+
needsCreateAssociatedAccount: false,
|
|
66
|
+
};
|
|
63
67
|
}
|
|
64
|
-
|
|
68
|
+
const associatedTokenAccount = getAssociatedTokenAddressSync(mint, recipient, false, TOKEN_PROGRAM_ID, ASSOCIATED_TOKEN_PROGRAM_ID);
|
|
69
|
+
const associatedAccountInfo = await connection.getParsedAccountInfo(associatedTokenAccount, "confirmed");
|
|
70
|
+
const associatedOwner = associatedAccountInfo.value?.owner;
|
|
71
|
+
if (associatedOwner && associatedOwner.toBase58() === TOKEN_PROGRAM_ID.toBase58()) {
|
|
72
|
+
return {
|
|
73
|
+
tokenAccount: associatedTokenAccount,
|
|
74
|
+
needsCreateAssociatedAccount: false,
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
return {
|
|
78
|
+
tokenAccount: associatedTokenAccount,
|
|
79
|
+
needsCreateAssociatedAccount: true,
|
|
80
|
+
};
|
|
65
81
|
}
|
|
66
82
|
export function solanaChargeClient(config) {
|
|
67
83
|
return Method.toClient(solanaChargeMethod, {
|
|
68
84
|
async createCredential({ challenge }) {
|
|
69
85
|
const { request } = challenge;
|
|
70
|
-
const amount =
|
|
86
|
+
const amount = toAtomicAmount(request.amount, request.decimals ?? 6);
|
|
71
87
|
const decimals = request.decimals ?? 6;
|
|
72
88
|
const mint = new PublicKey(request.currency ?? SOLANA_USDC_MINT);
|
|
73
89
|
const recipient = new PublicKey(request.recipient);
|
|
@@ -75,13 +91,20 @@ export function solanaChargeClient(config) {
|
|
|
75
91
|
const owner = keypair.publicKey;
|
|
76
92
|
const connection = new Connection(config.rpcUrl ?? SOLANA_RPC, "confirmed");
|
|
77
93
|
const sourceTokenAccount = getAssociatedTokenAddressSync(mint, owner, false, TOKEN_PROGRAM_ID, ASSOCIATED_TOKEN_PROGRAM_ID);
|
|
78
|
-
const
|
|
79
|
-
const
|
|
94
|
+
const destination = await resolveRecipientTokenAccount(connection, mint, recipient);
|
|
95
|
+
const instructions = [];
|
|
96
|
+
if (destination.needsCreateAssociatedAccount) {
|
|
97
|
+
instructions.push(createAssociatedTokenAccountInstruction(owner, destination.tokenAccount, recipient, mint, TOKEN_PROGRAM_ID, ASSOCIATED_TOKEN_PROGRAM_ID));
|
|
98
|
+
}
|
|
99
|
+
instructions.push(createTransferCheckedInstruction(sourceTokenAccount, mint, destination.tokenAccount, owner, amount, decimals));
|
|
80
100
|
const { blockhash } = await connection.getLatestBlockhash("confirmed");
|
|
81
101
|
const transaction = new Transaction({
|
|
82
102
|
feePayer: owner,
|
|
83
103
|
recentBlockhash: blockhash,
|
|
84
|
-
})
|
|
104
|
+
});
|
|
105
|
+
for (const instruction of instructions) {
|
|
106
|
+
transaction.add(instruction);
|
|
107
|
+
}
|
|
85
108
|
const signature = await sendAndConfirmTransaction(connection, transaction, [keypair], {
|
|
86
109
|
commitment: "confirmed",
|
|
87
110
|
});
|
|
@@ -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
|
+
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Custom MPP payment method: Tempo EVM USDC charge (client-side).
|
|
3
|
+
*
|
|
4
|
+
* Sends a standard ERC-20 transfer on Tempo and returns the tx hash as the
|
|
5
|
+
* payment credential.
|
|
6
|
+
*/
|
|
7
|
+
import { Method, Credential, z } from "mppx";
|
|
8
|
+
import { toAtomicAmount } from "./amount-utils.js";
|
|
9
|
+
const TEMPO_USDC = "0x20c000000000000000000000b9537d11c60e8b50";
|
|
10
|
+
const PATH_USD = "0x20c0000000000000000000000000000000000000";
|
|
11
|
+
const TEMPO_MAINNET_CHAIN_ID = 4217;
|
|
12
|
+
const TEMPO_TESTNET_CHAIN_ID = 42431;
|
|
13
|
+
const ERC20_ABI = [
|
|
14
|
+
{
|
|
15
|
+
type: "function",
|
|
16
|
+
name: "transfer",
|
|
17
|
+
inputs: [
|
|
18
|
+
{ name: "to", type: "address" },
|
|
19
|
+
{ name: "value", type: "uint256" },
|
|
20
|
+
],
|
|
21
|
+
outputs: [{ name: "", type: "bool" }],
|
|
22
|
+
stateMutability: "nonpayable",
|
|
23
|
+
},
|
|
24
|
+
];
|
|
25
|
+
const tempoChargeMethod = Method.from({
|
|
26
|
+
name: "tempo",
|
|
27
|
+
intent: "charge",
|
|
28
|
+
schema: {
|
|
29
|
+
credential: {
|
|
30
|
+
payload: z.object({
|
|
31
|
+
hash: z.string(),
|
|
32
|
+
type: z.literal("hash"),
|
|
33
|
+
}),
|
|
34
|
+
},
|
|
35
|
+
request: z.pipe(z.object({
|
|
36
|
+
amount: z.string(),
|
|
37
|
+
currency: z.string(),
|
|
38
|
+
recipient: z.string(),
|
|
39
|
+
chainId: z.optional(z.number()),
|
|
40
|
+
decimals: z.optional(z.number()),
|
|
41
|
+
}), z.transform((v) => ({
|
|
42
|
+
...v,
|
|
43
|
+
methodDetails: {
|
|
44
|
+
chainId: v.chainId ?? TEMPO_MAINNET_CHAIN_ID,
|
|
45
|
+
},
|
|
46
|
+
}))),
|
|
47
|
+
},
|
|
48
|
+
});
|
|
49
|
+
export function tempoChargeClient(config) {
|
|
50
|
+
return Method.toClient(tempoChargeMethod, {
|
|
51
|
+
async createCredential({ challenge }) {
|
|
52
|
+
const { request } = challenge;
|
|
53
|
+
const amount = toAtomicAmount(request.amount, request.decimals ?? 6);
|
|
54
|
+
const recipient = request.recipient;
|
|
55
|
+
const currency = (request.currency ?? TEMPO_USDC);
|
|
56
|
+
const chainId = request.methodDetails?.chainId ?? request.chainId ?? TEMPO_MAINNET_CHAIN_ID;
|
|
57
|
+
const { createWalletClient, createPublicClient, http } = await import("viem");
|
|
58
|
+
const { tempo, tempoModerato } = await import("viem/chains");
|
|
59
|
+
const chain = chainId === TEMPO_TESTNET_CHAIN_ID ? tempoModerato : tempo;
|
|
60
|
+
const rpcUrl = config.rpcUrl ?? chain.rpcUrls.default.http[0];
|
|
61
|
+
const walletClient = createWalletClient({
|
|
62
|
+
account: config.account,
|
|
63
|
+
chain,
|
|
64
|
+
transport: http(rpcUrl),
|
|
65
|
+
});
|
|
66
|
+
const publicClient = createPublicClient({
|
|
67
|
+
chain,
|
|
68
|
+
transport: http(rpcUrl),
|
|
69
|
+
});
|
|
70
|
+
const hash = await walletClient.writeContract({
|
|
71
|
+
address: currency,
|
|
72
|
+
abi: ERC20_ABI,
|
|
73
|
+
functionName: "transfer",
|
|
74
|
+
args: [recipient, amount],
|
|
75
|
+
});
|
|
76
|
+
await publicClient.waitForTransactionReceipt({ hash });
|
|
77
|
+
return Credential.serialize({
|
|
78
|
+
challenge,
|
|
79
|
+
payload: { hash, type: "hash" },
|
|
80
|
+
source: `did:pkh:eip155:${chainId}:${config.account.address}`,
|
|
81
|
+
});
|
|
82
|
+
},
|
|
83
|
+
});
|
|
84
|
+
}
|
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;
|
package/dist/index.js
CHANGED
|
@@ -12,6 +12,7 @@ import { registerWalletTools } from "./tools/wallet.js";
|
|
|
12
12
|
import { registerFavoriteTools } from "./tools/favorites.js";
|
|
13
13
|
import { registerTipTools } from "./tools/tip.js";
|
|
14
14
|
import { registerPassTools } from "./tools/passes.js";
|
|
15
|
+
import { registerObservabilityTools } from "./tools/observability.js";
|
|
15
16
|
// ── Resources ────────────────────────────────────────────────────
|
|
16
17
|
import { registerAgentResources } from "./resources/agents.js";
|
|
17
18
|
import { registerWalletResources } from "./resources/wallet.js";
|
|
@@ -39,12 +40,14 @@ export async function startMcpServer() {
|
|
|
39
40
|
"5. Ask user to rate or tip after a successful run",
|
|
40
41
|
"",
|
|
41
42
|
"PAYMENT:",
|
|
42
|
-
"-
|
|
43
|
-
"-
|
|
43
|
+
"- Crypto wallets (Tempo USDC, Base USDC, Solana USDC) are the core supported rails.",
|
|
44
|
+
"- Saved cards can also be used, but card-backed MPP depends on Stripe Shared Payment Token availability in the current environment.",
|
|
44
45
|
"- Card and crypto are SEPARATE payment methods. Card charges a credit card; crypto sends USDC on-chain.",
|
|
45
|
-
"-
|
|
46
|
-
"-
|
|
47
|
-
"-
|
|
46
|
+
"- When a card is configured, it becomes the default payment method. Use wallet_status to confirm whether `Card MPP: ready` before relying on it.",
|
|
47
|
+
"- run_agent() and solve() auto-detect the default compatible payment method when pay_with is omitted.",
|
|
48
|
+
"- Include pay_with explicitly when you need a specific rail or want deterministic control over the payment method used.",
|
|
49
|
+
"- Use wallet_status to see the exact payment methods the user has configured before calling a paid tool.",
|
|
50
|
+
"- Use open_observability_dashboard() to open a secure web usage dashboard for runs/spend/rebates.",
|
|
48
51
|
"- Payment is automatic once configured. Users are never charged for failed runs.",
|
|
49
52
|
"- Do NOT ask the user to set up payment before they try to run an agent. Let them explore freely.",
|
|
50
53
|
"- If a specific payment method fails, report the error clearly. Do NOT silently fall back to a different method.",
|
|
@@ -70,6 +73,7 @@ export async function startMcpServer() {
|
|
|
70
73
|
registerFavoriteTools(server);
|
|
71
74
|
registerTipTools(server);
|
|
72
75
|
registerPassTools(server);
|
|
76
|
+
registerObservabilityTools(server);
|
|
73
77
|
// Register resources
|
|
74
78
|
registerAgentResources(server);
|
|
75
79
|
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, buy_agent_credit_pack, 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 {
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|