@agentwonderland/mcp 0.1.26 → 0.1.27

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -12,7 +12,6 @@ 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";
16
15
  // ── Resources ────────────────────────────────────────────────────
17
16
  import { registerAgentResources } from "./resources/agents.js";
18
17
  import { registerWalletResources } from "./resources/wallet.js";
@@ -47,7 +46,6 @@ export async function startMcpServer() {
47
46
  "- run_agent() and solve() auto-detect the default compatible payment method when pay_with is omitted.",
48
47
  "- Include pay_with explicitly when you need a specific rail or want deterministic control over the payment method used.",
49
48
  "- 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.",
51
49
  "- Payment is automatic once configured. Users are never charged for failed runs.",
52
50
  "- Do NOT ask the user to set up payment before they try to run an agent. Let them explore freely.",
53
51
  "- If a specific payment method fails, report the error clearly. Do NOT silently fall back to a different method.",
@@ -73,7 +71,6 @@ export async function startMcpServer() {
73
71
  registerFavoriteTools(server);
74
72
  registerTipTools(server);
75
73
  registerPassTools(server);
76
- registerObservabilityTools(server);
77
74
  // Register resources
78
75
  registerAgentResources(server);
79
76
  registerWalletResources(server);
@@ -44,7 +44,7 @@ export function registerPrompts(server) {
44
44
  "",
45
45
  "Steps:",
46
46
  "1. Use search_agents to find relevant agents",
47
- "2. Use compare_agents on the top 2-3 candidates",
47
+ "2. Use get_agent to inspect the top candidate (schema, pricing, rating)",
48
48
  "3. Recommend the best one based on price, rating, and success rate",
49
49
  "4. Ask if I want to run it",
50
50
  ].join("\n"),
@@ -14,14 +14,26 @@ export function registerAgentInfoTools(server) {
14
14
  const payment = (a.payment ?? {});
15
15
  const _pricing = (payment.pricing ?? {});
16
16
  const creditPacks = payment.credit_packs;
17
+ const totalJobs = (s.completedJobs ?? a.totalExecutions ?? 0);
18
+ const acceptedPayments = payment.accepted_payments ?? [];
19
+ const paymentLabelMap = {
20
+ tempo_usdc: "tempo",
21
+ base_usdc: "base",
22
+ solana_usdc: "solana",
23
+ stripe_card: "card",
24
+ };
25
+ const paymentLabels = acceptedPayments.map((m) => paymentLabelMap[m] ?? m);
17
26
  const lines = [
18
27
  `${a.name}`,
19
- `${stars(a.avgRating ?? s.avgRating)} (${s.ratingCount ?? 0} reviews) • ${compactNumber((s.completedJobs ?? a.totalExecutions ?? 0))} jobs`,
28
+ `${stars(a.avgRating ?? s.avgRating)} (${s.ratingCount ?? 0} reviews) • ${compactNumber(totalJobs)} jobs`,
20
29
  "",
21
30
  a.description ?? "",
22
31
  "",
23
32
  `Pricing: ${formatPrice(a.pricePerRunUsd)}`,
24
- `Reliability: ${a.successRate != null ? (Number(a.successRate) * 100).toFixed(0) + "%" : "N/A"}`,
33
+ ...(paymentLabels.length > 0 ? [`Accepted payments: ${paymentLabels.join(", ")}`] : []),
34
+ ...(totalJobs > 0 && a.successRate != null
35
+ ? [`Reliability: ${(Number(a.successRate) * 100).toFixed(0)}% (${compactNumber(totalJobs)} runs)`]
36
+ : []),
25
37
  `Avg latency: ${a.avgResponseTimeMs != null ? a.avgResponseTimeMs + "ms" : "N/A"}`,
26
38
  ...(() => {
27
39
  const lastActive = formatLastActive(a.lastActiveAt);
@@ -82,30 +94,4 @@ export function registerAgentInfoTools(server) {
82
94
  lines.push("", `ID: ${a.id}`, `View: ${agentWebUrl(a.id)}`);
83
95
  return text(lines.join("\n"));
84
96
  });
85
- // ── compare_agents ──────────────────────────────────────────────
86
- server.tool("compare_agents", "Compare multiple agents side-by-side on rating, price, success rate, and job count.", {
87
- agent_ids: z
88
- .array(z.string())
89
- .min(2)
90
- .max(5)
91
- .describe("Agent IDs to compare (2-5)"),
92
- }, async ({ agent_ids }) => {
93
- const agents = await Promise.all(agent_ids.map((id) => apiGet(`/agents/${id}`)));
94
- const header = "Agent Comparison:\n";
95
- const lines = agents.map((a) => {
96
- const s = (a.stats ?? {});
97
- const rating = a.avgRating ?? s.avgRating;
98
- const jobs = (s.completedJobs ?? a.totalExecutions ?? 0);
99
- const tipCount = (s.tipCount ?? 0);
100
- return [
101
- ` ${a.name}`,
102
- ` ${stars(rating)} (${s.ratingCount ?? 0} reviews)${tipCount > 0 ? ` • ${tipCount} tips` : ""}`,
103
- ` ${compactNumber(jobs)} jobs • ${formatPrice(a.pricePerRunUsd)}`,
104
- ` Success: ${a.successRate != null ? (Number(a.successRate) * 100).toFixed(0) + "%" : "N/A"}`,
105
- ` ${agentWebUrl(a.id)}`,
106
- "",
107
- ].join("\n");
108
- });
109
- return text(header + lines.join("\n"));
110
- });
111
97
  }
@@ -6,39 +6,93 @@ import { formatRunResult } from "../core/formatters.js";
6
6
  function text(t) {
7
7
  return { content: [{ type: "text", text: t }] };
8
8
  }
9
+ async function getConsumerWalletAddresses() {
10
+ const addresses = new Set();
11
+ for (const chain of ["tempo", "base", "solana"]) {
12
+ const addr = await getWalletAddress(chain);
13
+ if (addr)
14
+ addresses.add(addr);
15
+ }
16
+ return [...addresses];
17
+ }
18
+ function formatRelativeTime(iso) {
19
+ if (!iso)
20
+ return "";
21
+ const t = new Date(iso).getTime();
22
+ if (!Number.isFinite(t))
23
+ return "";
24
+ const diffSec = Math.max(0, Math.floor((Date.now() - t) / 1000));
25
+ if (diffSec < 60)
26
+ return `${diffSec}s ago`;
27
+ if (diffSec < 3600)
28
+ return `${Math.floor(diffSec / 60)}m ago`;
29
+ if (diffSec < 86400)
30
+ return `${Math.floor(diffSec / 3600)}h ago`;
31
+ return `${Math.floor(diffSec / 86400)}d ago`;
32
+ }
9
33
  export function registerJobTools(server) {
10
34
  // ── get_job ─────────────────────────────────────────────────────
11
35
  server.tool("get_job", "Get the status and output of a job by ID. Use to poll async jobs until they complete.", {
12
36
  job_id: z.string().describe("Job ID (UUID)"),
13
37
  }, async ({ job_id }) => {
14
- let url = `/jobs/${job_id}`;
15
- if (!isAuthenticated() && hasWalletConfigured()) {
16
- const address = await getWalletAddress();
17
- if (address) {
18
- url += `?wallet=${encodeURIComponent(address)}`;
38
+ const walletLookup = !isAuthenticated() && hasWalletConfigured();
39
+ const addresses = walletLookup ? await getConsumerWalletAddresses() : [""];
40
+ let result = null;
41
+ for (const addr of addresses) {
42
+ const url = addr ? `/jobs/${job_id}?wallet=${encodeURIComponent(addr)}` : `/jobs/${job_id}`;
43
+ try {
44
+ result = await apiGet(url);
45
+ break;
46
+ }
47
+ catch (err) {
48
+ const status = err.status;
49
+ if (status !== 404)
50
+ throw err;
19
51
  }
20
52
  }
21
- const result = await apiGet(url);
53
+ if (!result) {
54
+ return text(`Job ${job_id} not found. Either the job ID is wrong or it was paid with a wallet not configured here.`);
55
+ }
22
56
  if (result.status === "processing") {
23
57
  return text(`Job ${job_id} is still processing...`);
24
58
  }
25
59
  return text(formatRunResult(result));
26
60
  });
27
- // ── list_jobs (renamed from list_my_jobs) ───────────────────────
28
- server.tool("list_jobs", "List your recent jobs with status, cost, and agent info.", {
29
- limit: z.coerce.number().optional().default(10).describe("Max results (1-50)"),
61
+ // ── list_jobs ───────────────────────────────────────────────────
62
+ server.tool("list_jobs", "List your recent jobs across all configured payment wallets, with status, cost, agent, method, and age.", {
63
+ limit: z.coerce.number().int().min(1).max(50).optional().default(10).describe("Max results (1-50)"),
30
64
  }, async ({ limit }) => {
31
- let url = `/jobs?limit=${limit ?? 10}`;
32
- // If not authenticated via API key, use wallet address for lookup
33
- if (!isAuthenticated() && hasWalletConfigured()) {
34
- const address = await getWalletAddress();
35
- if (address) {
36
- url += `&wallet=${encodeURIComponent(address)}`;
65
+ const requestedLimit = limit ?? 10;
66
+ const walletLookup = !isAuthenticated() && hasWalletConfigured();
67
+ const addresses = walletLookup ? await getConsumerWalletAddresses() : [""];
68
+ const seen = new Set();
69
+ const collected = [];
70
+ for (const addr of addresses) {
71
+ const url = addr
72
+ ? `/jobs?wallet=${encodeURIComponent(addr)}&limit=${requestedLimit}`
73
+ : `/jobs?limit=${requestedLimit}`;
74
+ try {
75
+ const jobs = await apiGet(url);
76
+ for (const j of jobs) {
77
+ const id = j.job_id;
78
+ if (!seen.has(id)) {
79
+ seen.add(id);
80
+ collected.push(j);
81
+ }
82
+ }
83
+ }
84
+ catch {
85
+ // Skip address that errored; continue with others.
37
86
  }
38
87
  }
39
- const jobs = await apiGet(url);
40
- if (jobs.length === 0)
88
+ if (collected.length === 0)
41
89
  return text("No jobs found.");
90
+ collected.sort((a, b) => {
91
+ const aTime = new Date(a.created_at ?? 0).getTime();
92
+ const bTime = new Date(b.created_at ?? 0).getTime();
93
+ return bTime - aTime;
94
+ });
95
+ const jobs = collected.slice(0, requestedLimit);
42
96
  const lines = [`Recent jobs (${jobs.length}):`];
43
97
  for (const j of jobs) {
44
98
  const status = j.status === "completed"
@@ -46,10 +100,15 @@ export function registerJobTools(server) {
46
100
  : j.status === "processing"
47
101
  ? "\u2026"
48
102
  : "\u2717";
49
- const cost = j.estimated_cost != null
50
- ? `$${Number(j.estimated_cost).toFixed(4)}`
51
- : "";
52
- lines.push(` ${status} ${j.job_id?.slice(0, 8)}\u2026 ${j.agent_id ? String(j.agent_id).slice(0, 8) + "\u2026" : ""} ${cost}`);
103
+ const amount = (j.settled_amount ?? j.estimated_cost);
104
+ const cost = amount != null ? `$${Number(amount).toFixed(4)}` : "";
105
+ const method = j.settlement_trace
106
+ ?.payment_attempt?.payment_method;
107
+ const methodLabel = method ? `[${method.replace(/_usdc$/, "").replace("stripe_", "")}]` : "";
108
+ const shortId = j.job_id?.slice(0, 8);
109
+ const shortAgent = j.agent_id ? String(j.agent_id).slice(0, 8) : "?";
110
+ const when = formatRelativeTime(j.created_at);
111
+ lines.push(` ${status} ${shortId} agent=${shortAgent} ${cost} ${methodLabel} ${when}`.trimEnd());
53
112
  }
54
113
  return text(lines.join("\n"));
55
114
  });
@@ -1,7 +1,7 @@
1
1
  import { z } from "zod";
2
2
  import { apiGet, apiPostWithPayment } from "../core/api-client.js";
3
3
  import { formatCreditPack, formatCreditPackOffer, getCreditPackInventory, getCreditPackProgram, } from "../core/passes.js";
4
- import { getCompatiblePaymentMethods, getConfiguredMethods, hasWalletConfigured, normalizePaymentMethod, } from "../core/payments.js";
4
+ import { getCompatiblePaymentMethods, getConfiguredMethods, hasWalletConfigured, normalizePaymentMethod, isCardPaymentEnabled, } from "../core/payments.js";
5
5
  import { requiresSpendConfirmation } from "../core/config.js";
6
6
  import { ensureConsumerPrincipalForMethod } from "../core/principal.js";
7
7
  import { getOrCreatePendingCardSetup, formatCardSetupBlocks } from "../core/card-setup.js";
@@ -39,15 +39,26 @@ export function registerPassTools(server) {
39
39
  confirmed: z.boolean().optional().describe("Set to true to confirm the purchase after seeing the quote."),
40
40
  }, async ({ agent_id, pack_id, pay_with, confirmed }) => {
41
41
  if (!hasWalletConfigured()) {
42
- try {
43
- const { url } = await getOrCreatePendingCardSetup();
44
- return multiText(...formatCardSetupBlocks(url));
42
+ if (isCardPaymentEnabled()) {
43
+ try {
44
+ const { url } = await getOrCreatePendingCardSetup();
45
+ return multiText(...formatCardSetupBlocks(url));
46
+ }
47
+ catch {
48
+ // Fall through to the setup message below.
49
+ }
45
50
  }
46
- catch {
47
- return text("No payment method configured.\n\n" +
48
- "To add a credit card: wallet_setup({ action: \"add-card\" })\n" +
49
- "To use crypto: wallet_setup({ action: \"create\" })");
51
+ const setupLines = [
52
+ "No payment method configured.",
53
+ "",
54
+ "Supported rails: Tempo USDC, Base USDC, Solana USDC.",
55
+ "Run wallet_setup({ action: \"create\" }) to create a crypto wallet,",
56
+ "or wallet_setup({ action: \"import\" }) with an existing key.",
57
+ ];
58
+ if (isCardPaymentEnabled()) {
59
+ setupLines.push("", "Or wallet_setup({ action: \"add-card\" }) to connect a credit card.");
50
60
  }
61
+ return text(setupLines.join("\n"));
51
62
  }
52
63
  const agent = await getAgent(agent_id);
53
64
  const agentName = agent.name ?? agent_id;
package/dist/tools/run.js CHANGED
@@ -2,7 +2,7 @@ import { z } from "zod";
2
2
  import { apiGet, apiPost, apiPostWithPayment } from "../core/api-client.js";
3
3
  import { uploadLocalFiles } from "../core/file-upload.js";
4
4
  import { formatCreditPackOffer, getActiveCreditPack, getCreditPackInventory, getCreditPackProgram, } from "../core/passes.js";
5
- import { getCompatiblePaymentMethods, getConfiguredMethods, hasWalletConfigured, getWalletAddress, normalizePaymentMethod, } from "../core/payments.js";
5
+ import { getCompatiblePaymentMethods, getConfiguredMethods, hasWalletConfigured, getWalletAddress, normalizePaymentMethod, isCardPaymentEnabled, } from "../core/payments.js";
6
6
  import { requiresSpendConfirmation, getDefaultTipAmount } from "../core/config.js";
7
7
  import { formatRunResult } from "../core/formatters.js";
8
8
  import { canSpend, recordSpend, requiresPolicyConfirmation } from "../core/spend-policy.js";
@@ -65,15 +65,26 @@ export function registerRunTools(server) {
65
65
  confirmed: z.boolean().optional().describe("Set to true to confirm spending after seeing the price quote."),
66
66
  }, async ({ agent_id, input, pay_with, confirmed }) => {
67
67
  if (!hasWalletConfigured()) {
68
- try {
69
- const { url } = await getOrCreatePendingCardSetup();
70
- return multiText(...formatCardSetupBlocks(url));
68
+ if (isCardPaymentEnabled()) {
69
+ try {
70
+ const { url } = await getOrCreatePendingCardSetup();
71
+ return multiText(...formatCardSetupBlocks(url));
72
+ }
73
+ catch {
74
+ // Fall through to the setup message below.
75
+ }
71
76
  }
72
- catch {
73
- return text("No payment method configured.\n\n" +
74
- "To add a credit card: wallet_setup({ action: \"add-card\" })\n" +
75
- "To use crypto: wallet_setup({ action: \"create\" })");
77
+ const setupLines = [
78
+ "No payment method configured.",
79
+ "",
80
+ "Supported rails: Tempo USDC, Base USDC, Solana USDC.",
81
+ "Run wallet_setup({ action: \"create\" }) to create a crypto wallet,",
82
+ "or wallet_setup({ action: \"import\" }) with an existing key.",
83
+ ];
84
+ if (isCardPaymentEnabled()) {
85
+ setupLines.push("", "Or wallet_setup({ action: \"add-card\" }) to connect a credit card.");
76
86
  }
87
+ return text(setupLines.join("\n"));
77
88
  }
78
89
  let agent;
79
90
  try {
@@ -16,14 +16,15 @@ export function registerSearchTools(server) {
16
16
  sort: z.enum(["relevance", "price", "rating", "popularity", "newest"]).optional()
17
17
  .describe("Sort results by: relevance (default), price, rating, popularity, or newest"),
18
18
  }, async ({ query, tag, limit, max_price, min_rating, sort }) => {
19
- const requestedLimit = limit ?? 10;
19
+ const requestedLimit = Math.max(1, Math.min(50, limit ?? 10));
20
20
  const params = new URLSearchParams();
21
21
  if (query)
22
22
  params.set("q", query);
23
23
  if (tag)
24
24
  params.set("tag", tag);
25
- // When filtering by min_rating client-side, request more results
26
- const apiLimit = min_rating ? requestedLimit * 3 : requestedLimit;
25
+ // min_rating is filtered client-side on avgRating. Request extra candidates
26
+ // so the post-filter result still has a chance of meeting requestedLimit.
27
+ const apiLimit = min_rating ? Math.min(100, requestedLimit * 3) : requestedLimit;
27
28
  params.set("limit", String(apiLimit));
28
29
  if (max_price)
29
30
  params.set("price_max", String(max_price));
@@ -45,13 +46,13 @@ export function registerSearchTools(server) {
45
46
  params.set("accepted_payment_methods", acceptedMethods.join(","));
46
47
  }
47
48
  let agents = await apiGet(`/agents?${params}`);
48
- // Client-side min_rating filter
49
+ // Client-side min_rating filter — avgRating is the 1-5 user-facing score.
50
+ // Agents with no ratings yet (avgRating == null) are excluded when a
51
+ // minimum is requested, matching user expectation of "at least N stars".
49
52
  if (min_rating) {
50
53
  agents = agents.filter((a) => {
51
- const rating = a.reputationScore ?? a.avgRating ?? 0;
52
- // Convert 0-1 reputation to 1-5 scale if needed
53
- const stars = rating <= 1 ? rating * 5 : rating;
54
- return stars >= min_rating;
54
+ const avg = a.avgRating ?? a.stats?.avgRating;
55
+ return typeof avg === "number" && avg >= min_rating;
55
56
  });
56
57
  }
57
58
  // Trim to requested limit after filtering
@@ -1,7 +1,7 @@
1
1
  import { z } from "zod";
2
2
  import { apiGet, apiPost, apiPostWithPayment } from "../core/api-client.js";
3
3
  import { getOrCreatePendingCardSetup, formatCardSetupBlocks } from "../core/card-setup.js";
4
- import { getCompatiblePaymentMethods, hasWalletConfigured, getConfiguredMethods, getAcceptedPaymentMethods, getWalletAddress, normalizePaymentMethod, toRegistryPaymentMethod, } from "../core/payments.js";
4
+ import { getCompatiblePaymentMethods, hasWalletConfigured, getConfiguredMethods, getAcceptedPaymentMethods, getWalletAddress, normalizePaymentMethod, toRegistryPaymentMethod, isCardPaymentEnabled, } from "../core/payments.js";
5
5
  import { requiresSpendConfirmation, getDefaultTipAmount } from "../core/config.js";
6
6
  import { agentList, formatRunResult } from "../core/formatters.js";
7
7
  import { canSpend, recordSpend, requiresPolicyConfirmation } from "../core/spend-policy.js";
@@ -114,15 +114,26 @@ export function registerSolveTools(server) {
114
114
  .describe("Set to true to confirm spending after seeing the price quote."),
115
115
  }, async ({ intent, input, budget, pay_with, confirmed }) => {
116
116
  if (!hasWalletConfigured()) {
117
- try {
118
- const { url } = await getOrCreatePendingCardSetup();
119
- return multiText(...formatCardSetupBlocks(url));
117
+ if (isCardPaymentEnabled()) {
118
+ try {
119
+ const { url } = await getOrCreatePendingCardSetup();
120
+ return multiText(...formatCardSetupBlocks(url));
121
+ }
122
+ catch {
123
+ // Fall through to the setup message below.
124
+ }
120
125
  }
121
- catch {
122
- return text("No payment method configured.\n\n" +
123
- "To add a credit card: wallet_setup({ action: \"add-card\" })\n" +
124
- "To use crypto: wallet_setup({ action: \"create\" })");
126
+ const setupLines = [
127
+ "No payment method configured.",
128
+ "",
129
+ "Supported rails: Tempo USDC, Base USDC, Solana USDC.",
130
+ "Run wallet_setup({ action: \"create\" }) to create a crypto wallet,",
131
+ "or wallet_setup({ action: \"import\" }) with an existing key.",
132
+ ];
133
+ if (isCardPaymentEnabled()) {
134
+ setupLines.push("", "Or wallet_setup({ action: \"add-card\" }) to connect a credit card.");
125
135
  }
136
+ return text(setupLines.join("\n"));
126
137
  }
127
138
  const pendingKey = makeSolvePendingKey(intent, input, budget);
128
139
  const pending = pendingSolves.get(pendingKey);
@@ -157,7 +168,8 @@ export function registerSolveTools(server) {
157
168
  const cost = result.cost;
158
169
  const usedCreditPack = result.consumption_mode === "credit_pack";
159
170
  const tipMsg = result.feedback_token ? await autoTip(jobId, agentId, agentName, result.feedback_token) : "";
160
- return multiText(formatRunResult(result), feedbackAsk(jobId, agentId, usedCreditPack ? undefined : cost, tipMsg));
171
+ const header = agentName ? [`Matched agent: ${agentName}`, ""].join("\n") : "";
172
+ return multiText(header + formatRunResult(result), feedbackAsk(jobId, agentId, usedCreditPack ? undefined : cost, tipMsg));
161
173
  }
162
174
  catch (err) {
163
175
  const status = err instanceof Error &&
@@ -254,8 +266,9 @@ export function registerSolveTools(server) {
254
266
  catch (err) {
255
267
  const apiErr = err;
256
268
  if (apiErr?.status === 402) {
269
+ const reason = apiErr.message ? `Payment failed: ${apiErr.message}` : "Payment failed — wallet may not have enough funds or the selected method was rejected.";
257
270
  return text([
258
- "Payment failed — your wallet may not have enough funds or the selected method was rejected.",
271
+ reason,
259
272
  "",
260
273
  "Check your balance and try again.",
261
274
  "Use wallet_status to check your current payment methods.",
@@ -65,11 +65,11 @@ export function registerWalletTools(server) {
65
65
  }
66
66
  return text(lines.join("\n"));
67
67
  });
68
- // ── wallet_setup (NEW) ──────────────────────────────────────────
69
- server.tool("wallet_setup", "Set up or manage payment methods. Options: 'add-card' to connect a credit/debit card, 'remove-card' to disconnect a card, 'create' a crypto wallet, or 'import' an existing key. After card setup, use wallet_status to confirm whether card-backed MPP is ready. For crypto wallet changes (removal, key rotation), direct users to edit their config files manually never handle private keys programmatically.", {
68
+ // ── wallet_setup ────────────────────────────────────────────────
69
+ server.tool("wallet_setup", "Set up or manage a payment wallet. 'create' makes a new encrypted crypto wallet (OWS); 'import' takes an existing private key. Tempo/Base share one EVM key a single wallet covers both. Solana uses a separate ed25519 key. For crypto wallet deletion or key rotation, direct users to edit ~/.agentwonderland/config.json or ~/.ows/ manually; never handle key material programmatically.", {
70
70
  action: z
71
71
  .enum(["create", "import", "add-card", "remove-card"])
72
- .describe("'add-card' to connect a card, 'remove-card' to disconnect it, 'create' a crypto wallet, or 'import' an existing key"),
72
+ .describe("'create' a crypto wallet (recommended), 'import' an existing key, 'add-card'/'remove-card' for credit card (card-backed MPP availability depends on Stripe SPT)"),
73
73
  name: z
74
74
  .string()
75
75
  .optional()
@@ -79,7 +79,7 @@ export function registerWalletTools(server) {
79
79
  .optional()
80
80
  .describe("Private key hex string (required for 'import', ignored for 'create')"),
81
81
  chain: z.enum(["tempo", "base", "solana"]).optional()
82
- .describe("Primary chain (default: tempo). Solana uses an OWS wallet plus Stripe deposit-mode USDC."),
82
+ .describe("Primary chain (default: tempo). Tempo/Base use a shared EVM wallet; Solana uses a separate OWS wallet."),
83
83
  }, async ({ action, name, key, chain }) => {
84
84
  // ── Card setup flow ──────────────────────────────────────
85
85
  if (action === "add-card") {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agentwonderland/mcp",
3
- "version": "0.1.26",
3
+ "version": "0.1.27",
4
4
  "type": "module",
5
5
  "description": "MCP server for the Agent Wonderland AI agent marketplace",
6
6
  "bin": {
package/src/index.ts CHANGED
@@ -14,7 +14,6 @@ import { registerWalletTools } from "./tools/wallet.js";
14
14
  import { registerFavoriteTools } from "./tools/favorites.js";
15
15
  import { registerTipTools } from "./tools/tip.js";
16
16
  import { registerPassTools } from "./tools/passes.js";
17
- import { registerObservabilityTools } from "./tools/observability.js";
18
17
 
19
18
  // ── Resources ────────────────────────────────────────────────────
20
19
  import { registerAgentResources } from "./resources/agents.js";
@@ -54,7 +53,6 @@ export async function startMcpServer(): Promise<void> {
54
53
  "- run_agent() and solve() auto-detect the default compatible payment method when pay_with is omitted.",
55
54
  "- Include pay_with explicitly when you need a specific rail or want deterministic control over the payment method used.",
56
55
  "- Use wallet_status to see the exact payment methods the user has configured before calling a paid tool.",
57
- "- Use open_observability_dashboard() to open a secure web usage dashboard for runs/spend/rebates.",
58
56
  "- Payment is automatic once configured. Users are never charged for failed runs.",
59
57
  "- Do NOT ask the user to set up payment before they try to run an agent. Let them explore freely.",
60
58
  "- If a specific payment method fails, report the error clearly. Do NOT silently fall back to a different method.",
@@ -82,7 +80,6 @@ export async function startMcpServer(): Promise<void> {
82
80
  registerFavoriteTools(server);
83
81
  registerTipTools(server);
84
82
  registerPassTools(server);
85
- registerObservabilityTools(server);
86
83
 
87
84
  // Register resources
88
85
  registerAgentResources(server);
@@ -62,7 +62,7 @@ export function registerPrompts(server: McpServer) {
62
62
  "",
63
63
  "Steps:",
64
64
  "1. Use search_agents to find relevant agents",
65
- "2. Use compare_agents on the top 2-3 candidates",
65
+ "2. Use get_agent to inspect the top candidate (schema, pricing, rating)",
66
66
  "3. Recommend the best one based on price, rating, and success rate",
67
67
  "4. Ask if I want to run it",
68
68
  ].join("\n"),
@@ -31,14 +31,27 @@ export function registerAgentInfoTools(server: McpServer): void {
31
31
  }>;
32
32
  } | null | undefined;
33
33
 
34
+ const totalJobs = (s.completedJobs ?? a.totalExecutions ?? 0) as number;
35
+ const acceptedPayments = (payment.accepted_payments as string[] | undefined) ?? [];
36
+ const paymentLabelMap: Record<string, string> = {
37
+ tempo_usdc: "tempo",
38
+ base_usdc: "base",
39
+ solana_usdc: "solana",
40
+ stripe_card: "card",
41
+ };
42
+ const paymentLabels = acceptedPayments.map((m) => paymentLabelMap[m] ?? m);
43
+
34
44
  const lines = [
35
45
  `${a.name}`,
36
- `${stars(a.avgRating ?? (s.avgRating as number))} (${s.ratingCount ?? 0} reviews) • ${compactNumber((s.completedJobs ?? a.totalExecutions ?? 0) as number)} jobs`,
46
+ `${stars(a.avgRating ?? (s.avgRating as number))} (${s.ratingCount ?? 0} reviews) • ${compactNumber(totalJobs)} jobs`,
37
47
  "",
38
48
  (a.description as string) ?? "",
39
49
  "",
40
50
  `Pricing: ${formatPrice(a.pricePerRunUsd)}`,
41
- `Reliability: ${a.successRate != null ? (Number(a.successRate) * 100).toFixed(0) + "%" : "N/A"}`,
51
+ ...(paymentLabels.length > 0 ? [`Accepted payments: ${paymentLabels.join(", ")}`] : []),
52
+ ...(totalJobs > 0 && a.successRate != null
53
+ ? [`Reliability: ${(Number(a.successRate) * 100).toFixed(0)}% (${compactNumber(totalJobs)} runs)`]
54
+ : []),
42
55
  `Avg latency: ${(a.avgResponseTimeMs as number) != null ? a.avgResponseTimeMs + "ms" : "N/A"}`,
43
56
  ...(() => {
44
57
  const lastActive = formatLastActive(a.lastActiveAt as string | null);
@@ -104,40 +117,4 @@ export function registerAgentInfoTools(server: McpServer): void {
104
117
  return text(lines.join("\n"));
105
118
  },
106
119
  );
107
-
108
- // ── compare_agents ──────────────────────────────────────────────
109
- server.tool(
110
- "compare_agents",
111
- "Compare multiple agents side-by-side on rating, price, success rate, and job count.",
112
- {
113
- agent_ids: z
114
- .array(z.string())
115
- .min(2)
116
- .max(5)
117
- .describe("Agent IDs to compare (2-5)"),
118
- },
119
- async ({ agent_ids }) => {
120
- const agents = await Promise.all(
121
- agent_ids.map((id) => apiGet<AgentRecord>(`/agents/${id}`)),
122
- );
123
-
124
- const header = "Agent Comparison:\n";
125
- const lines = agents.map((a) => {
126
- const s = (a.stats ?? {}) as Record<string, unknown>;
127
- const rating = a.avgRating ?? (s.avgRating as number);
128
- const jobs = (s.completedJobs ?? a.totalExecutions ?? 0) as number;
129
- const tipCount = (s.tipCount ?? 0) as number;
130
- return [
131
- ` ${a.name}`,
132
- ` ${stars(rating)} (${s.ratingCount ?? 0} reviews)${tipCount > 0 ? ` • ${tipCount} tips` : ""}`,
133
- ` ${compactNumber(jobs)} jobs • ${formatPrice(a.pricePerRunUsd)}`,
134
- ` Success: ${a.successRate != null ? (Number(a.successRate) * 100).toFixed(0) + "%" : "N/A"}`,
135
- ` ${agentWebUrl(a.id)}`,
136
- "",
137
- ].join("\n");
138
- });
139
-
140
- return text(header + lines.join("\n"));
141
- },
142
- );
143
120
  }
package/src/tools/jobs.ts CHANGED
@@ -9,6 +9,26 @@ function text(t: string) {
9
9
  return { content: [{ type: "text" as const, text: t }] };
10
10
  }
11
11
 
12
+ async function getConsumerWalletAddresses(): Promise<string[]> {
13
+ const addresses = new Set<string>();
14
+ for (const chain of ["tempo", "base", "solana"]) {
15
+ const addr = await getWalletAddress(chain);
16
+ if (addr) addresses.add(addr);
17
+ }
18
+ return [...addresses];
19
+ }
20
+
21
+ function formatRelativeTime(iso: string | null | undefined): string {
22
+ if (!iso) return "";
23
+ const t = new Date(iso).getTime();
24
+ if (!Number.isFinite(t)) return "";
25
+ const diffSec = Math.max(0, Math.floor((Date.now() - t) / 1000));
26
+ if (diffSec < 60) return `${diffSec}s ago`;
27
+ if (diffSec < 3600) return `${Math.floor(diffSec / 60)}m ago`;
28
+ if (diffSec < 86400) return `${Math.floor(diffSec / 3600)}h ago`;
29
+ return `${Math.floor(diffSec / 86400)}d ago`;
30
+ }
31
+
12
32
  export function registerJobTools(server: McpServer): void {
13
33
  // ── get_job ─────────────────────────────────────────────────────
14
34
  server.tool(
@@ -18,16 +38,25 @@ export function registerJobTools(server: McpServer): void {
18
38
  job_id: z.string().describe("Job ID (UUID)"),
19
39
  },
20
40
  async ({ job_id }) => {
21
- let url = `/jobs/${job_id}`;
41
+ const walletLookup = !isAuthenticated() && hasWalletConfigured();
42
+ const addresses = walletLookup ? await getConsumerWalletAddresses() : [""];
22
43
 
23
- if (!isAuthenticated() && hasWalletConfigured()) {
24
- const address = await getWalletAddress();
25
- if (address) {
26
- url += `?wallet=${encodeURIComponent(address)}`;
44
+ let result: Record<string, unknown> | null = null;
45
+ for (const addr of addresses) {
46
+ const url = addr ? `/jobs/${job_id}?wallet=${encodeURIComponent(addr)}` : `/jobs/${job_id}`;
47
+ try {
48
+ result = await apiGet<Record<string, unknown>>(url);
49
+ break;
50
+ } catch (err) {
51
+ const status = (err as { status?: number }).status;
52
+ if (status !== 404) throw err;
27
53
  }
28
54
  }
29
55
 
30
- const result = await apiGet<Record<string, unknown>>(url);
56
+ if (!result) {
57
+ return text(`Job ${job_id} not found. Either the job ID is wrong or it was paid with a wallet not configured here.`);
58
+ }
59
+
31
60
  if (result.status === "processing") {
32
61
  return text(`Job ${job_id} is still processing...`);
33
62
  }
@@ -35,26 +64,46 @@ export function registerJobTools(server: McpServer): void {
35
64
  },
36
65
  );
37
66
 
38
- // ── list_jobs (renamed from list_my_jobs) ───────────────────────
67
+ // ── list_jobs ───────────────────────────────────────────────────
39
68
  server.tool(
40
69
  "list_jobs",
41
- "List your recent jobs with status, cost, and agent info.",
70
+ "List your recent jobs across all configured payment wallets, with status, cost, agent, method, and age.",
42
71
  {
43
- limit: z.coerce.number().optional().default(10).describe("Max results (1-50)"),
72
+ limit: z.coerce.number().int().min(1).max(50).optional().default(10).describe("Max results (1-50)"),
44
73
  },
45
74
  async ({ limit }) => {
46
- let url = `/jobs?limit=${limit ?? 10}`;
75
+ const requestedLimit = limit ?? 10;
76
+ const walletLookup = !isAuthenticated() && hasWalletConfigured();
77
+ const addresses = walletLookup ? await getConsumerWalletAddresses() : [""];
47
78
 
48
- // If not authenticated via API key, use wallet address for lookup
49
- if (!isAuthenticated() && hasWalletConfigured()) {
50
- const address = await getWalletAddress();
51
- if (address) {
52
- url += `&wallet=${encodeURIComponent(address)}`;
79
+ const seen = new Set<string>();
80
+ const collected: Array<Record<string, unknown>> = [];
81
+ for (const addr of addresses) {
82
+ const url = addr
83
+ ? `/jobs?wallet=${encodeURIComponent(addr)}&limit=${requestedLimit}`
84
+ : `/jobs?limit=${requestedLimit}`;
85
+ try {
86
+ const jobs = await apiGet<Array<Record<string, unknown>>>(url);
87
+ for (const j of jobs) {
88
+ const id = j.job_id as string;
89
+ if (!seen.has(id)) {
90
+ seen.add(id);
91
+ collected.push(j);
92
+ }
93
+ }
94
+ } catch {
95
+ // Skip address that errored; continue with others.
53
96
  }
54
97
  }
55
98
 
56
- const jobs = await apiGet<Array<Record<string, unknown>>>(url);
57
- if (jobs.length === 0) return text("No jobs found.");
99
+ if (collected.length === 0) return text("No jobs found.");
100
+
101
+ collected.sort((a, b) => {
102
+ const aTime = new Date((a.created_at as string) ?? 0).getTime();
103
+ const bTime = new Date((b.created_at as string) ?? 0).getTime();
104
+ return bTime - aTime;
105
+ });
106
+ const jobs = collected.slice(0, requestedLimit);
58
107
 
59
108
  const lines = [`Recent jobs (${jobs.length}):`];
60
109
  for (const j of jobs) {
@@ -64,12 +113,16 @@ export function registerJobTools(server: McpServer): void {
64
113
  : j.status === "processing"
65
114
  ? "\u2026"
66
115
  : "\u2717";
67
- const cost =
68
- j.estimated_cost != null
69
- ? `$${Number(j.estimated_cost).toFixed(4)}`
70
- : "";
116
+ const amount = (j.settled_amount ?? j.estimated_cost) as string | number | undefined;
117
+ const cost = amount != null ? `$${Number(amount).toFixed(4)}` : "";
118
+ const method = ((j.settlement_trace as Record<string, unknown> | undefined)
119
+ ?.payment_attempt as { payment_method?: string } | undefined)?.payment_method;
120
+ const methodLabel = method ? `[${method.replace(/_usdc$/, "").replace("stripe_", "")}]` : "";
121
+ const shortId = (j.job_id as string)?.slice(0, 8);
122
+ const shortAgent = j.agent_id ? String(j.agent_id).slice(0, 8) : "?";
123
+ const when = formatRelativeTime(j.created_at as string | null);
71
124
  lines.push(
72
- ` ${status} ${(j.job_id as string)?.slice(0, 8)}\u2026 ${j.agent_id ? String(j.agent_id).slice(0, 8) + "\u2026" : ""} ${cost}`,
125
+ ` ${status} ${shortId} agent=${shortAgent} ${cost} ${methodLabel} ${when}`.trimEnd(),
73
126
  );
74
127
  }
75
128
  return text(lines.join("\n"));
@@ -12,6 +12,7 @@ import {
12
12
  getConfiguredMethods,
13
13
  hasWalletConfigured,
14
14
  normalizePaymentMethod,
15
+ isCardPaymentEnabled,
15
16
  } from "../core/payments.js";
16
17
  import { requiresSpendConfirmation } from "../core/config.js";
17
18
  import { ensureConsumerPrincipalForMethod } from "../core/principal.js";
@@ -71,16 +72,25 @@ export function registerPassTools(server: McpServer): void {
71
72
  },
72
73
  async ({ agent_id, pack_id, pay_with, confirmed }) => {
73
74
  if (!hasWalletConfigured()) {
74
- try {
75
- const { url } = await getOrCreatePendingCardSetup();
76
- return multiText(...formatCardSetupBlocks(url));
77
- } catch {
78
- return text(
79
- "No payment method configured.\n\n" +
80
- "To add a credit card: wallet_setup({ action: \"add-card\" })\n" +
81
- "To use crypto: wallet_setup({ action: \"create\" })",
82
- );
75
+ if (isCardPaymentEnabled()) {
76
+ try {
77
+ const { url } = await getOrCreatePendingCardSetup();
78
+ return multiText(...formatCardSetupBlocks(url));
79
+ } catch {
80
+ // Fall through to the setup message below.
81
+ }
82
+ }
83
+ const setupLines = [
84
+ "No payment method configured.",
85
+ "",
86
+ "Supported rails: Tempo USDC, Base USDC, Solana USDC.",
87
+ "Run wallet_setup({ action: \"create\" }) to create a crypto wallet,",
88
+ "or wallet_setup({ action: \"import\" }) with an existing key.",
89
+ ];
90
+ if (isCardPaymentEnabled()) {
91
+ setupLines.push("", "Or wallet_setup({ action: \"add-card\" }) to connect a credit card.");
83
92
  }
93
+ return text(setupLines.join("\n"));
84
94
  }
85
95
 
86
96
  const agent = await getAgent(agent_id);
package/src/tools/run.ts CHANGED
@@ -14,6 +14,7 @@ import {
14
14
  hasWalletConfigured,
15
15
  getWalletAddress,
16
16
  normalizePaymentMethod,
17
+ isCardPaymentEnabled,
17
18
  } from "../core/payments.js";
18
19
  import { requiresSpendConfirmation, getDefaultTipAmount } from "../core/config.js";
19
20
  import { formatRunResult } from "../core/formatters.js";
@@ -106,16 +107,25 @@ export function registerRunTools(server: McpServer): void {
106
107
  },
107
108
  async ({ agent_id, input, pay_with, confirmed }) => {
108
109
  if (!hasWalletConfigured()) {
109
- try {
110
- const { url } = await getOrCreatePendingCardSetup();
111
- return multiText(...formatCardSetupBlocks(url));
112
- } catch {
113
- return text(
114
- "No payment method configured.\n\n" +
115
- "To add a credit card: wallet_setup({ action: \"add-card\" })\n" +
116
- "To use crypto: wallet_setup({ action: \"create\" })",
117
- );
110
+ if (isCardPaymentEnabled()) {
111
+ try {
112
+ const { url } = await getOrCreatePendingCardSetup();
113
+ return multiText(...formatCardSetupBlocks(url));
114
+ } catch {
115
+ // Fall through to the setup message below.
116
+ }
117
+ }
118
+ const setupLines = [
119
+ "No payment method configured.",
120
+ "",
121
+ "Supported rails: Tempo USDC, Base USDC, Solana USDC.",
122
+ "Run wallet_setup({ action: \"create\" }) to create a crypto wallet,",
123
+ "or wallet_setup({ action: \"import\" }) with an existing key.",
124
+ ];
125
+ if (isCardPaymentEnabled()) {
126
+ setupLines.push("", "Or wallet_setup({ action: \"add-card\" }) to connect a credit card.");
118
127
  }
128
+ return text(setupLines.join("\n"));
119
129
  }
120
130
 
121
131
  let agent: AgentRecord;
@@ -24,13 +24,14 @@ export function registerSearchTools(server: McpServer): void {
24
24
  .describe("Sort results by: relevance (default), price, rating, popularity, or newest"),
25
25
  },
26
26
  async ({ query, tag, limit, max_price, min_rating, sort }) => {
27
- const requestedLimit = limit ?? 10;
27
+ const requestedLimit = Math.max(1, Math.min(50, limit ?? 10));
28
28
  const params = new URLSearchParams();
29
29
  if (query) params.set("q", query);
30
30
  if (tag) params.set("tag", tag);
31
31
 
32
- // When filtering by min_rating client-side, request more results
33
- const apiLimit = min_rating ? requestedLimit * 3 : requestedLimit;
32
+ // min_rating is filtered client-side on avgRating. Request extra candidates
33
+ // so the post-filter result still has a chance of meeting requestedLimit.
34
+ const apiLimit = min_rating ? Math.min(100, requestedLimit * 3) : requestedLimit;
34
35
  params.set("limit", String(apiLimit));
35
36
 
36
37
  if (max_price) params.set("price_max", String(max_price));
@@ -56,13 +57,13 @@ export function registerSearchTools(server: McpServer): void {
56
57
 
57
58
  let agents = await apiGet<AgentRecord[]>(`/agents?${params}`);
58
59
 
59
- // Client-side min_rating filter
60
+ // Client-side min_rating filter — avgRating is the 1-5 user-facing score.
61
+ // Agents with no ratings yet (avgRating == null) are excluded when a
62
+ // minimum is requested, matching user expectation of "at least N stars".
60
63
  if (min_rating) {
61
- agents = agents.filter((a: any) => {
62
- const rating = a.reputationScore ?? a.avgRating ?? 0;
63
- // Convert 0-1 reputation to 1-5 scale if needed
64
- const stars = rating <= 1 ? rating * 5 : rating;
65
- return stars >= min_rating;
64
+ agents = agents.filter((a) => {
65
+ const avg = a.avgRating ?? (a.stats as { avgRating?: number | null } | undefined)?.avgRating;
66
+ return typeof avg === "number" && avg >= min_rating;
66
67
  });
67
68
  }
68
69
 
@@ -10,6 +10,7 @@ import {
10
10
  getWalletAddress,
11
11
  normalizePaymentMethod,
12
12
  toRegistryPaymentMethod,
13
+ isCardPaymentEnabled,
13
14
  } from "../core/payments.js";
14
15
  import { requiresSpendConfirmation, getDefaultTipAmount } from "../core/config.js";
15
16
  import { agentList, formatRunResult } from "../core/formatters.js";
@@ -149,16 +150,25 @@ export function registerSolveTools(server: McpServer): void {
149
150
  },
150
151
  async ({ intent, input, budget, pay_with, confirmed }) => {
151
152
  if (!hasWalletConfigured()) {
152
- try {
153
- const { url } = await getOrCreatePendingCardSetup();
154
- return multiText(...formatCardSetupBlocks(url));
155
- } catch {
156
- return text(
157
- "No payment method configured.\n\n" +
158
- "To add a credit card: wallet_setup({ action: \"add-card\" })\n" +
159
- "To use crypto: wallet_setup({ action: \"create\" })",
160
- );
153
+ if (isCardPaymentEnabled()) {
154
+ try {
155
+ const { url } = await getOrCreatePendingCardSetup();
156
+ return multiText(...formatCardSetupBlocks(url));
157
+ } catch {
158
+ // Fall through to the setup message below.
159
+ }
160
+ }
161
+ const setupLines = [
162
+ "No payment method configured.",
163
+ "",
164
+ "Supported rails: Tempo USDC, Base USDC, Solana USDC.",
165
+ "Run wallet_setup({ action: \"create\" }) to create a crypto wallet,",
166
+ "or wallet_setup({ action: \"import\" }) with an existing key.",
167
+ ];
168
+ if (isCardPaymentEnabled()) {
169
+ setupLines.push("", "Or wallet_setup({ action: \"add-card\" }) to connect a credit card.");
161
170
  }
171
+ return text(setupLines.join("\n"));
162
172
  }
163
173
 
164
174
  const pendingKey = makeSolvePendingKey(intent, input, budget);
@@ -200,7 +210,8 @@ export function registerSolveTools(server: McpServer): void {
200
210
  const cost = (result as Record<string, unknown>).cost as number | undefined;
201
211
  const usedCreditPack = (result as Record<string, unknown>).consumption_mode === "credit_pack";
202
212
  const tipMsg = result.feedback_token ? await autoTip(jobId, agentId, agentName, result.feedback_token as string) : "";
203
- return multiText(formatRunResult(result), feedbackAsk(jobId, agentId, usedCreditPack ? undefined : cost, tipMsg));
213
+ const header = agentName ? [`Matched agent: ${agentName}`, ""].join("\n") : "";
214
+ return multiText(header + formatRunResult(result), feedbackAsk(jobId, agentId, usedCreditPack ? undefined : cost, tipMsg));
204
215
  } catch (err: unknown) {
205
216
  const status =
206
217
  err instanceof Error &&
@@ -318,9 +329,10 @@ export function registerSolveTools(server: McpServer): void {
318
329
  } catch (err: unknown) {
319
330
  const apiErr = err as { status?: number; message?: string };
320
331
  if (apiErr?.status === 402) {
332
+ const reason = apiErr.message ? `Payment failed: ${apiErr.message}` : "Payment failed — wallet may not have enough funds or the selected method was rejected.";
321
333
  return text(
322
334
  [
323
- "Payment failed — your wallet may not have enough funds or the selected method was rejected.",
335
+ reason,
324
336
  "",
325
337
  "Check your balance and try again.",
326
338
  "Use wallet_status to check your current payment methods.",
@@ -101,14 +101,14 @@ export function registerWalletTools(server: McpServer): void {
101
101
  },
102
102
  );
103
103
 
104
- // ── wallet_setup (NEW) ──────────────────────────────────────────
104
+ // ── wallet_setup ────────────────────────────────────────────────
105
105
  server.tool(
106
106
  "wallet_setup",
107
- "Set up or manage payment methods. Options: 'add-card' to connect a credit/debit card, 'remove-card' to disconnect a card, 'create' a crypto wallet, or 'import' an existing key. After card setup, use wallet_status to confirm whether card-backed MPP is ready. For crypto wallet changes (removal, key rotation), direct users to edit their config files manually never handle private keys programmatically.",
107
+ "Set up or manage a payment wallet. 'create' makes a new encrypted crypto wallet (OWS); 'import' takes an existing private key. Tempo/Base share one EVM key a single wallet covers both. Solana uses a separate ed25519 key. For crypto wallet deletion or key rotation, direct users to edit ~/.agentwonderland/config.json or ~/.ows/ manually; never handle key material programmatically.",
108
108
  {
109
109
  action: z
110
110
  .enum(["create", "import", "add-card", "remove-card"])
111
- .describe("'add-card' to connect a card, 'remove-card' to disconnect it, 'create' a crypto wallet, or 'import' an existing key"),
111
+ .describe("'create' a crypto wallet (recommended), 'import' an existing key, 'add-card'/'remove-card' for credit card (card-backed MPP availability depends on Stripe SPT)"),
112
112
  name: z
113
113
  .string()
114
114
  .optional()
@@ -120,7 +120,7 @@ export function registerWalletTools(server: McpServer): void {
120
120
  "Private key hex string (required for 'import', ignored for 'create')",
121
121
  ),
122
122
  chain: z.enum(["tempo", "base", "solana"]).optional()
123
- .describe("Primary chain (default: tempo). Solana uses an OWS wallet plus Stripe deposit-mode USDC."),
123
+ .describe("Primary chain (default: tempo). Tempo/Base use a shared EVM wallet; Solana uses a separate OWS wallet."),
124
124
  },
125
125
  async ({ action, name, key, chain }) => {
126
126
  // ── Card setup flow ──────────────────────────────────────
@@ -1,43 +0,0 @@
1
- import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
- import { apiPost } from "../core/api-client.js";
3
-
4
- function text(t: string) {
5
- return { content: [{ type: "text" as const, text: t }] };
6
- }
7
-
8
- export function registerObservabilityTools(server: McpServer): void {
9
- server.tool(
10
- "open_observability_dashboard",
11
- "Generate a secure one-click sign-in URL for the Agent Wonderland web observability dashboard. The dashboard shows your agent runs, spend, rebates, and recent activity.",
12
- {},
13
- async () => {
14
- const result = await apiPost<{
15
- url: string;
16
- expires_at: string;
17
- consumer_principal?: string;
18
- }>(
19
- "/observability/link",
20
- {},
21
- { ensureConsumerPrincipal: true },
22
- );
23
-
24
- const lines = [
25
- "Your secure observability link is ready:",
26
- result.url,
27
- "",
28
- `Expires: ${result.expires_at}`,
29
- ];
30
-
31
- if (result.consumer_principal) {
32
- lines.push(`Consumer principal: ${result.consumer_principal}`);
33
- }
34
-
35
- lines.push(
36
- "",
37
- "Open the link in your browser to view usage metrics, spend, rebates, and recent runs.",
38
- );
39
-
40
- return text(lines.join("\n"));
41
- },
42
- );
43
- }