@agentwonderland/mcp 0.1.1

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.
Files changed (71) hide show
  1. package/dist/core/api-client.d.ts +14 -0
  2. package/dist/core/api-client.js +98 -0
  3. package/dist/core/config.d.ts +77 -0
  4. package/dist/core/config.js +297 -0
  5. package/dist/core/formatters.d.ts +70 -0
  6. package/dist/core/formatters.js +193 -0
  7. package/dist/core/index.d.ts +6 -0
  8. package/dist/core/index.js +6 -0
  9. package/dist/core/ows-adapter.d.ts +43 -0
  10. package/dist/core/ows-adapter.js +100 -0
  11. package/dist/core/payments.d.ts +41 -0
  12. package/dist/core/payments.js +254 -0
  13. package/dist/core/types.d.ts +27 -0
  14. package/dist/core/types.js +4 -0
  15. package/dist/index.d.ts +2 -0
  16. package/dist/index.js +53 -0
  17. package/dist/prompts/index.d.ts +2 -0
  18. package/dist/prompts/index.js +89 -0
  19. package/dist/resources/agents.d.ts +2 -0
  20. package/dist/resources/agents.js +34 -0
  21. package/dist/resources/jobs.d.ts +2 -0
  22. package/dist/resources/jobs.js +15 -0
  23. package/dist/resources/wallet.d.ts +2 -0
  24. package/dist/resources/wallet.js +26 -0
  25. package/dist/tools/_token-cache.d.ts +5 -0
  26. package/dist/tools/_token-cache.js +9 -0
  27. package/dist/tools/agent-info.d.ts +2 -0
  28. package/dist/tools/agent-info.js +97 -0
  29. package/dist/tools/favorites.d.ts +2 -0
  30. package/dist/tools/favorites.js +51 -0
  31. package/dist/tools/index.d.ts +9 -0
  32. package/dist/tools/index.js +9 -0
  33. package/dist/tools/jobs.d.ts +2 -0
  34. package/dist/tools/jobs.js +49 -0
  35. package/dist/tools/rate.d.ts +2 -0
  36. package/dist/tools/rate.js +44 -0
  37. package/dist/tools/run.d.ts +2 -0
  38. package/dist/tools/run.js +80 -0
  39. package/dist/tools/search.d.ts +2 -0
  40. package/dist/tools/search.js +81 -0
  41. package/dist/tools/solve.d.ts +2 -0
  42. package/dist/tools/solve.js +124 -0
  43. package/dist/tools/tip.d.ts +2 -0
  44. package/dist/tools/tip.js +40 -0
  45. package/dist/tools/wallet.d.ts +2 -0
  46. package/dist/tools/wallet.js +197 -0
  47. package/package.json +49 -0
  48. package/src/core/api-client.ts +114 -0
  49. package/src/core/config.ts +384 -0
  50. package/src/core/formatters.ts +256 -0
  51. package/src/core/index.ts +6 -0
  52. package/src/core/ows-adapter.ts +214 -0
  53. package/src/core/payments.ts +278 -0
  54. package/src/core/types.ts +28 -0
  55. package/src/index.ts +65 -0
  56. package/src/prompts/index.ts +120 -0
  57. package/src/resources/agents.ts +37 -0
  58. package/src/resources/jobs.ts +17 -0
  59. package/src/resources/wallet.ts +30 -0
  60. package/src/tools/_token-cache.ts +18 -0
  61. package/src/tools/agent-info.ts +120 -0
  62. package/src/tools/favorites.ts +74 -0
  63. package/src/tools/index.ts +9 -0
  64. package/src/tools/jobs.ts +69 -0
  65. package/src/tools/rate.ts +62 -0
  66. package/src/tools/run.ts +97 -0
  67. package/src/tools/search.ts +96 -0
  68. package/src/tools/solve.ts +162 -0
  69. package/src/tools/tip.ts +59 -0
  70. package/src/tools/wallet.ts +268 -0
  71. package/tsconfig.json +15 -0
@@ -0,0 +1,162 @@
1
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import { z } from "zod";
3
+ import { apiGet, apiPost, apiPostWithPayment } from "../core/api-client.js";
4
+ import {
5
+ hasWalletConfigured,
6
+ getConfiguredMethods,
7
+ getAcceptedPaymentMethods,
8
+ } from "../core/payments.js";
9
+ import { agentList, formatRunResult } from "../core/formatters.js";
10
+ import type { AgentRecord } from "../core/types.js";
11
+ import { storeFeedbackToken } from "./_token-cache.js";
12
+
13
+ function text(t: string) {
14
+ return { content: [{ type: "text" as const, text: t }] };
15
+ }
16
+
17
+ function ratingPrompt(jobId: string): string {
18
+ return [
19
+ "",
20
+ "---",
21
+ "How was this result? You can:",
22
+ ` • rate_agent with job_id "${jobId}" and a score (1-5) — within 1 hour`,
23
+ " • tip_agent to show appreciation — within 1 hour",
24
+ " • favorite_agent to save this agent for later",
25
+ ].join("\n");
26
+ }
27
+
28
+ export function registerSolveTools(server: McpServer): void {
29
+ server.tool(
30
+ "solve",
31
+ "Solve a task by finding the best agent, paying, and executing. The primary way to use the marketplace — describe what you need and the system handles discovery, selection, payment, and execution.",
32
+ {
33
+ intent: z
34
+ .string()
35
+ .describe("What you want to accomplish (natural language)"),
36
+ input: z
37
+ .record(z.unknown())
38
+ .optional()
39
+ .default({})
40
+ .describe("Input payload for the agent"),
41
+ budget: z
42
+ .number()
43
+ .positive()
44
+ .max(100)
45
+ .optional()
46
+ .default(1.0)
47
+ .describe("Maximum budget in USD"),
48
+ pay_with: z
49
+ .string()
50
+ .optional()
51
+ .describe(
52
+ "Payment method — wallet ID, chain name (tempo, base, etc.), or 'card'. Auto-detected if omitted.",
53
+ ),
54
+ },
55
+ async ({ intent, input, budget, pay_with }) => {
56
+ if (!hasWalletConfigured()) {
57
+ return text(
58
+ "No wallet configured. Set one up first:\n\n" +
59
+ ' wallet_setup({ action: "create", name: "my-wallet" })\n\n' +
60
+ "Then fund it with USDC on Tempo and try again."
61
+ );
62
+ }
63
+
64
+ const method = pay_with ?? getConfiguredMethods()[0];
65
+
66
+ // Path 1: If authenticated, use the platform /solve route
67
+ try {
68
+ const result = await apiPost<Record<string, unknown>>("/solve", {
69
+ intent,
70
+ input,
71
+ budget,
72
+ });
73
+ const jobId = (result as Record<string, unknown>).job_id as string ?? "";
74
+ const agentId = (result as Record<string, unknown>).agent_id as string ?? "";
75
+
76
+ if (result.feedback_token) {
77
+ storeFeedbackToken(jobId, result.feedback_token as string, agentId);
78
+ }
79
+
80
+ return text(formatRunResult(result) + ratingPrompt(jobId));
81
+ } catch (err: unknown) {
82
+ const isAuthError =
83
+ err instanceof Error &&
84
+ "status" in err &&
85
+ (err as { status: number }).status === 401;
86
+ if (!isAuthError || !hasWalletConfigured()) throw err;
87
+ }
88
+
89
+ // Path 2: Wallet-only discovery — find agents, pick best, pay via MPP
90
+ const params = new URLSearchParams({ q: intent, limit: "5" });
91
+ const acceptedMethods = getAcceptedPaymentMethods();
92
+ if (acceptedMethods.length > 0) {
93
+ params.set("accepted_payment_methods", acceptedMethods.join(","));
94
+ }
95
+ const agents = await apiGet<AgentRecord[]>(`/agents?${params}`);
96
+
97
+ if (!agents || agents.length === 0) {
98
+ return text(`No agents found matching "${intent}".`);
99
+ }
100
+
101
+ // Show discovery results
102
+ const discovery = agentList(agents, intent);
103
+
104
+ // Pick cheapest agent within budget
105
+ const inputTokens = Math.ceil(JSON.stringify(input).length / 4);
106
+ const affordable = agents.filter((a) => {
107
+ const price = parseFloat(a.pricePer1kTokens ?? "0.01");
108
+ const cost =
109
+ a.pricingModel === "fixed" ? price : (inputTokens / 1000) * price;
110
+ return cost <= budget;
111
+ });
112
+ const selected = affordable[0] ?? agents[0];
113
+
114
+ // Estimate cost for the selected agent
115
+ const selectedPrice = parseFloat(selected.pricePer1kTokens ?? "0.01");
116
+ const estimatedCost =
117
+ selected.pricingModel === "fixed"
118
+ ? selectedPrice
119
+ : (inputTokens / 1000) * selectedPrice;
120
+
121
+ let result: Record<string, unknown>;
122
+ try {
123
+ result = await apiPostWithPayment<Record<string, unknown>>(
124
+ `/agents/${selected.id}/run`,
125
+ { input },
126
+ method,
127
+ );
128
+ } catch (err: unknown) {
129
+ const apiErr = err as { status?: number; message?: string };
130
+ if (apiErr?.status === 402) {
131
+ return text(
132
+ "Payment failed — your wallet may not have enough USDC.\n\n" +
133
+ "Check your balance and fund your wallet, then try again.\n" +
134
+ "Use wallet_status to check your current payment methods."
135
+ );
136
+ }
137
+ return text(`Error: ${apiErr?.message ?? "Failed to run agent"}`);
138
+ }
139
+
140
+ // Cache feedback token if present
141
+ const jobId = (result as Record<string, unknown>).job_id as string ?? "";
142
+ const agentId2 = (result as Record<string, unknown>).agent_id as string ?? selected.id;
143
+
144
+ if (result.feedback_token) {
145
+ storeFeedbackToken(jobId, result.feedback_token as string, agentId2);
146
+ }
147
+
148
+ // Combine discovery + result
149
+ const output = [
150
+ discovery,
151
+ "",
152
+ `Running ${selected.name} — best match`,
153
+ `Estimated cost: $${estimatedCost.toFixed(4)}`,
154
+ "",
155
+ formatRunResult(result, { paymentMethod: method }),
156
+ ratingPrompt(jobId),
157
+ ].join("\n");
158
+
159
+ return text(output);
160
+ },
161
+ );
162
+ }
@@ -0,0 +1,59 @@
1
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import { z } from "zod";
3
+ import { apiPost } from "../core/api-client.js";
4
+ import { getFeedbackToken } from "./_token-cache.js";
5
+
6
+ function text(t: string) {
7
+ return { content: [{ type: "text" as const, text: t }] };
8
+ }
9
+
10
+ export function registerTipTools(server: McpServer) {
11
+ server.tool(
12
+ "tip_agent",
13
+ "Send a tip to an agent you've used. Tips help surface the best agents. Must be used within 1 hour of running the agent.",
14
+ {
15
+ job_id: z.string().describe("Job ID from a completed execution"),
16
+ agent_id: z.string().describe("Agent ID to tip"),
17
+ amount: z.number().min(0.01).max(50).describe("Tip amount in USD"),
18
+ },
19
+ async ({ job_id, agent_id, amount }) => {
20
+ const tokenData = getFeedbackToken(job_id);
21
+ if (!tokenData) {
22
+ return text(
23
+ "No feedback token found for this job. You can only tip agents immediately after running them.",
24
+ );
25
+ }
26
+
27
+ // Use the resolved UUID from the run result (handles slugs)
28
+ const resolvedAgentId = tokenData.agent_id || agent_id;
29
+
30
+ try {
31
+ await apiPost("/tips", {
32
+ job_id,
33
+ agent_id: resolvedAgentId,
34
+ feedback_token: tokenData.token,
35
+ amount,
36
+ });
37
+ return text(
38
+ `Tipped $${amount.toFixed(2)} for job ${job_id}. Thanks — tips help the best agents get discovered!`,
39
+ );
40
+ } catch (err: unknown) {
41
+ const apiErr = err as {
42
+ status?: number;
43
+ body?: { error?: string };
44
+ message?: string;
45
+ };
46
+ if (apiErr?.status === 401) {
47
+ return text(
48
+ "Feedback token expired. Tips must be submitted within 1 hour of running the agent.",
49
+ );
50
+ }
51
+ if (apiErr?.status === 429) {
52
+ return text("You can only tip this agent once every 30 days.");
53
+ }
54
+ const msg = apiErr?.body?.error || apiErr?.message || "Tip failed";
55
+ return text(`Could not send tip: ${msg}`);
56
+ }
57
+ },
58
+ );
59
+ }
@@ -0,0 +1,268 @@
1
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import { z } from "zod";
3
+ import { getWallets, getCardConfig, addWallet } from "../core/config.js";
4
+ import { getWalletAddress } from "../core/payments.js";
5
+ import {
6
+ isOwsAvailable,
7
+ createOwsWallet,
8
+ importKeyToOws,
9
+ listOwsWallets,
10
+ } from "../core/ows-adapter.js";
11
+
12
+ function text(t: string) {
13
+ return { content: [{ type: "text" as const, text: t }] };
14
+ }
15
+
16
+ export function registerWalletTools(server: McpServer): void {
17
+ // ── wallet_status (extracted from check_wallet) ─────────────────
18
+ server.tool(
19
+ "wallet_status",
20
+ "Check payment readiness. Shows all configured wallets, their chains, addresses, and card status.",
21
+ {},
22
+ async () => {
23
+ const wallets = getWallets();
24
+ const card = getCardConfig();
25
+
26
+ if (wallets.length === 0 && !card) {
27
+ return text(
28
+ "No payment methods configured.\nUse wallet_setup to create or import a wallet.",
29
+ );
30
+ }
31
+
32
+ const lines = ["Payment methods:"];
33
+
34
+ for (const w of wallets) {
35
+ const addr = await getWalletAddress(w.id);
36
+ const label = w.label ? ` (${w.label})` : "";
37
+ const storage =
38
+ w.keyType === "ows" ? " [encrypted]" : " [plaintext]";
39
+ lines.push(
40
+ ` ${w.id}${label}${storage}: ${w.chains.join(", ")} \u2014 ${addr ?? "unknown"}`,
41
+ );
42
+ }
43
+
44
+ if (card) {
45
+ lines.push(` Card: ${card.brand} ****${card.last4}`);
46
+ }
47
+
48
+ return text(lines.join("\n"));
49
+ },
50
+ );
51
+
52
+ // ── wallet_setup (NEW) ──────────────────────────────────────────
53
+ server.tool(
54
+ "wallet_setup",
55
+ "Create or import a wallet for paying agents. Uses OWS (Open Wallet Standard) for encrypted key storage when available, falling back to plaintext.",
56
+ {
57
+ action: z
58
+ .enum(["create", "import"])
59
+ .describe("'create' a new wallet or 'import' an existing private key"),
60
+ name: z
61
+ .string()
62
+ .optional()
63
+ .describe("Wallet name/label (default: auto-generated)"),
64
+ key: z
65
+ .string()
66
+ .optional()
67
+ .describe(
68
+ "Private key hex string (required for 'import', ignored for 'create')",
69
+ ),
70
+ chain: z.enum(["tempo", "base", "solana"]).optional()
71
+ .describe("Primary chain (default: tempo). Solana support is via Stripe deposit mode."),
72
+ },
73
+ async ({ action, name, key, chain }) => {
74
+ if (action === "import" && !key) {
75
+ return text(
76
+ "Error: 'key' parameter is required when action is 'import'.",
77
+ );
78
+ }
79
+
80
+ const selectedChains = chain === "solana" ? ["solana"] : ["tempo", "base"];
81
+ const defaultCh = chain ?? "tempo";
82
+
83
+ const owsReady = await isOwsAvailable();
84
+
85
+ if (action === "create") {
86
+ // Check for existing OWS wallets first
87
+ if (owsReady) {
88
+ const existing = await listOwsWallets();
89
+ if (existing.length > 0) {
90
+ const w = existing[0];
91
+ // Check if already in config
92
+ const wallets = getWallets();
93
+ const alreadyLinked = wallets.find(wl => wl.owsWalletId === w.id);
94
+ if (!alreadyLinked) {
95
+ addWallet({
96
+ id: w.name,
97
+ keyType: "ows",
98
+ owsWalletId: w.id,
99
+ chains: selectedChains,
100
+ defaultChain: defaultCh,
101
+ label: w.name,
102
+ });
103
+ }
104
+ return text([
105
+ "Found existing OWS wallet:",
106
+ ` Name: ${w.name}`,
107
+ ` Address: ${w.address}`,
108
+ ` Chains: ${selectedChains.join(", ")}`,
109
+ ` Storage: ~/.ows/ (encrypted)`,
110
+ "",
111
+ alreadyLinked ? "Already linked to Agent Wonderland." : "Linked to Agent Wonderland.",
112
+ "",
113
+ `Fund this address with USDC on ${defaultCh === "solana" ? "Solana" : "Tempo"} to start using agents.`,
114
+ ].join("\n"));
115
+ }
116
+ }
117
+
118
+ if (!owsReady) {
119
+ return text(
120
+ "Cannot create wallet: OWS (Open Wallet Standard) is not installed.\n" +
121
+ "Install with: npm install -g @open-wallet-standard/core\n\n" +
122
+ "Alternatively, use action 'import' with an existing private key.",
123
+ );
124
+ }
125
+
126
+ const walletName = name ?? `aw-${Date.now()}`;
127
+ const result = await createOwsWallet(walletName);
128
+
129
+ // Persist to config so payment flows can find it
130
+ addWallet({
131
+ id: walletName,
132
+ keyType: "ows",
133
+ owsWalletId: result.walletId,
134
+ chains: selectedChains,
135
+ defaultChain: defaultCh,
136
+ label: walletName,
137
+ });
138
+
139
+ return text(
140
+ [
141
+ `Wallet created [encrypted]:`,
142
+ ` ID: ${result.walletId}`,
143
+ ` Address: ${result.address}`,
144
+ ` Name: ${walletName}`,
145
+ ` Chains: ${selectedChains.join(", ")}`,
146
+ ` Storage: ~/.ows/ (AES-256-GCM encrypted)`,
147
+ "",
148
+ `Fund this address with USDC on ${defaultCh === "solana" ? "Solana" : "Tempo"} to start using agents.`,
149
+ "For testnet: npx mppx account fund",
150
+ ].join("\n"),
151
+ );
152
+ }
153
+
154
+ // action === "import"
155
+ if (owsReady) {
156
+ const walletName = name ?? `imported-${Date.now()}`;
157
+ const result = await importKeyToOws(key!, walletName);
158
+
159
+ addWallet({
160
+ id: walletName,
161
+ keyType: "ows",
162
+ owsWalletId: result.walletId,
163
+ chains: selectedChains,
164
+ defaultChain: defaultCh,
165
+ label: walletName,
166
+ });
167
+
168
+ return text(
169
+ [
170
+ `Key imported to OWS [encrypted]:`,
171
+ ` ID: ${result.walletId}`,
172
+ ` Address: ${result.address}`,
173
+ ` Name: ${walletName}`,
174
+ ` Chains: ${selectedChains.join(", ")}`,
175
+ ].join("\n"),
176
+ );
177
+ }
178
+
179
+ // No OWS — store plaintext
180
+ try {
181
+ const { privateKeyToAccount } = await import("viem/accounts");
182
+ const normalizedKey = (
183
+ key!.startsWith("0x") ? key! : `0x${key!}`
184
+ ) as `0x${string}`;
185
+ const account = privateKeyToAccount(normalizedKey);
186
+ const walletName = name ?? `wallet-${Date.now()}`;
187
+
188
+ addWallet({
189
+ id: walletName,
190
+ keyType: "evm",
191
+ key: normalizedKey,
192
+ chains: selectedChains,
193
+ defaultChain: defaultCh,
194
+ label: walletName,
195
+ });
196
+
197
+ return text(
198
+ [
199
+ `Key imported [plaintext] \u2014 OWS not available for encrypted storage.`,
200
+ ` Address: ${account.address}`,
201
+ ` Name: ${walletName}`,
202
+ ` Chains: ${selectedChains.join(", ")}`,
203
+ "",
204
+ "Install OWS for encrypted storage: npm install -g @open-wallet-standard/core",
205
+ ].join("\n"),
206
+ );
207
+ } catch {
208
+ return text("Error: Invalid private key format.");
209
+ }
210
+ },
211
+ );
212
+
213
+ // ── wallet_set_policy (NEW) ─────────────────────────────────────
214
+ server.tool(
215
+ "wallet_set_policy",
216
+ "Set spending limits on a wallet to control agent costs. Limits reset daily.",
217
+ {
218
+ wallet_id: z.string().describe("Wallet ID to set policy on"),
219
+ max_per_tx: z
220
+ .number()
221
+ .positive()
222
+ .optional()
223
+ .describe("Maximum USD per transaction"),
224
+ max_per_day: z
225
+ .number()
226
+ .positive()
227
+ .optional()
228
+ .describe("Maximum USD per day across all transactions"),
229
+ },
230
+ async ({ wallet_id, max_per_tx, max_per_day }) => {
231
+ const wallets = getWallets();
232
+ const wallet = wallets.find((w) => w.id === wallet_id);
233
+
234
+ if (!wallet) {
235
+ const available = wallets.map((w) => w.id).join(", ") || "none";
236
+ return text(
237
+ `Wallet "${wallet_id}" not found. Available wallets: ${available}`,
238
+ );
239
+ }
240
+
241
+ if (max_per_tx == null && max_per_day == null) {
242
+ return text(
243
+ "No policy changes specified. Provide max_per_tx and/or max_per_day.",
244
+ );
245
+ }
246
+
247
+ // Build policy summary
248
+ const policies: string[] = [];
249
+ if (max_per_tx != null) {
250
+ policies.push(`Max per transaction: $${max_per_tx.toFixed(2)}`);
251
+ }
252
+ if (max_per_day != null) {
253
+ policies.push(`Max per day: $${max_per_day.toFixed(2)}`);
254
+ }
255
+
256
+ // Note: actual persistence depends on the config module supporting policy fields.
257
+ // For now, we report what would be set. The core/config module will handle storage.
258
+ return text(
259
+ [
260
+ `Spending policy set for wallet "${wallet_id}":`,
261
+ ...policies.map((p) => ` ${p}`),
262
+ "",
263
+ "Policy will be enforced on all future transactions from this wallet.",
264
+ ].join("\n"),
265
+ );
266
+ },
267
+ );
268
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,15 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "module": "NodeNext",
5
+ "moduleResolution": "NodeNext",
6
+ "outDir": "dist",
7
+ "rootDir": "src",
8
+ "strict": true,
9
+ "esModuleInterop": true,
10
+ "skipLibCheck": true,
11
+ "declaration": true
12
+ },
13
+ "include": ["src/**/*"],
14
+ "exclude": ["src/__tests__"]
15
+ }