@agentwonderland/mcp 0.1.37 → 0.1.39

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 (45) hide show
  1. package/dist/core/__tests__/api-client.test.js +4 -0
  2. package/dist/core/__tests__/mpp-client.test.d.ts +1 -0
  3. package/dist/core/__tests__/mpp-client.test.js +46 -0
  4. package/dist/core/__tests__/payments.test.js +5 -14
  5. package/dist/core/api-client.js +27 -5
  6. package/dist/core/base-charge.js +1 -1
  7. package/dist/core/formatters.d.ts +4 -0
  8. package/dist/core/formatters.js +2 -1
  9. package/dist/core/mpp-client.d.ts +62 -0
  10. package/dist/core/mpp-client.js +208 -0
  11. package/dist/core/payments.js +3 -3
  12. package/dist/core/solana-charge.d.ts +1 -0
  13. package/dist/core/solana-charge.js +7 -3
  14. package/dist/core/tempo-charge.js +1 -1
  15. package/dist/core/types.d.ts +13 -0
  16. package/dist/index.js +2 -0
  17. package/dist/tools/__tests__/wallet.test.js +1 -0
  18. package/dist/tools/agent-info.js +4 -1
  19. package/dist/tools/index.d.ts +1 -0
  20. package/dist/tools/index.js +1 -0
  21. package/dist/tools/providers.d.ts +2 -0
  22. package/dist/tools/providers.js +40 -0
  23. package/dist/tools/run.js +1 -1
  24. package/dist/tools/search.js +7 -1
  25. package/dist/tools/solve.js +1 -1
  26. package/package.json +3 -4
  27. package/src/core/__tests__/api-client.test.ts +4 -0
  28. package/src/core/__tests__/mpp-client.test.ts +53 -0
  29. package/src/core/__tests__/payments.test.ts +6 -20
  30. package/src/core/api-client.ts +29 -5
  31. package/src/core/base-charge.ts +1 -1
  32. package/src/core/formatters.ts +3 -1
  33. package/src/core/mpp-client.ts +286 -0
  34. package/src/core/payments.ts +3 -3
  35. package/src/core/solana-charge.ts +7 -3
  36. package/src/core/tempo-charge.ts +1 -1
  37. package/src/core/types.ts +13 -0
  38. package/src/index.ts +2 -0
  39. package/src/tools/__tests__/wallet.test.ts +1 -0
  40. package/src/tools/agent-info.ts +11 -1
  41. package/src/tools/index.ts +1 -0
  42. package/src/tools/providers.ts +64 -0
  43. package/src/tools/run.ts +1 -1
  44. package/src/tools/search.ts +5 -1
  45. package/src/tools/solve.ts +1 -1
@@ -4,7 +4,7 @@
4
4
  * Sends an SPL Token transfer on Solana mainnet and returns the transaction
5
5
  * signature as the payment credential.
6
6
  */
7
- import { Credential, Method, z } from "mppx";
7
+ import { Credential, Method, z } from "./mpp-client.js";
8
8
  import { Connection, Keypair, PublicKey, Transaction, sendAndConfirmTransaction } from "@solana/web3.js";
9
9
  import {
10
10
  ASSOCIATED_TOKEN_PROGRAM_ID,
@@ -18,7 +18,9 @@ import { toAtomicAmount } from "./amount-utils.js";
18
18
 
19
19
  export const SOLANA_USDC_MINT = "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v" as const;
20
20
  export const SOLANA_CHAIN_ID = "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp" as const;
21
+ export const SOLANA_CHAIN_ID_DEVNET = "solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1wcaWoxPkrZBG" as const;
21
22
  const SOLANA_RPC = "https://api.mainnet-beta.solana.com";
23
+ const SOLANA_RPC_DEVNET = "https://api.devnet.solana.com";
22
24
 
23
25
  const solanaChargeMethod = Method.from({
24
26
  name: "solana" as const,
@@ -116,13 +118,15 @@ export function solanaChargeClient(config: SolanaChargeClientConfig) {
116
118
  return Method.toClient(solanaChargeMethod as any, {
117
119
  async createCredential({ challenge }: any) {
118
120
  const { request } = challenge;
121
+ const chainId = request.chainId ?? request.methodDetails?.chainId ?? SOLANA_CHAIN_ID;
122
+ const rpcUrl = config.rpcUrl ?? (chainId === SOLANA_CHAIN_ID_DEVNET ? SOLANA_RPC_DEVNET : SOLANA_RPC);
119
123
  const amount = toAtomicAmount(request.amount, request.decimals ?? 6);
120
124
  const decimals = request.decimals ?? 6;
121
125
  const mint = new PublicKey(request.currency ?? SOLANA_USDC_MINT);
122
126
  const recipient = new PublicKey(request.recipient);
123
127
  const keypair = await getKeypair(config.wallet);
124
128
  const owner = keypair.publicKey;
125
- const connection = new Connection(config.rpcUrl ?? SOLANA_RPC, "confirmed");
129
+ const connection = new Connection(rpcUrl, "confirmed");
126
130
 
127
131
  const sourceTokenAccount = getAssociatedTokenAddressSync(
128
132
  mint,
@@ -177,7 +181,7 @@ export function solanaChargeClient(config: SolanaChargeClientConfig) {
177
181
  return Credential.serialize({
178
182
  challenge,
179
183
  payload: { signature, type: "signature" as const },
180
- source: `did:pkh:${SOLANA_CHAIN_ID}:${owner.toBase58()}`,
184
+ source: `did:pkh:${chainId}:${owner.toBase58()}`,
181
185
  });
182
186
  },
183
187
  });
@@ -4,7 +4,7 @@
4
4
  * Sends a standard ERC-20 transfer on Tempo and returns the tx hash as the
5
5
  * payment credential.
6
6
  */
7
- import { Method, Credential, z } from "mppx";
7
+ import { Method, Credential, z } from "./mpp-client.js";
8
8
  import type { LocalAccount } from "viem/accounts";
9
9
  import { toAtomicAmount } from "./amount-utils.js";
10
10
 
package/src/core/types.ts CHANGED
@@ -19,6 +19,19 @@ export interface AgentRecord {
19
19
  ratingCount?: number;
20
20
  [key: string]: unknown;
21
21
  };
22
+ provider?: {
23
+ id?: string;
24
+ name?: string;
25
+ slug?: string;
26
+ website_url?: string | null;
27
+ } | null;
28
+ urls?: {
29
+ public_url?: string;
30
+ normalized_x402_url?: string;
31
+ mirrored_x402_url?: string | null;
32
+ agent_card_url?: string;
33
+ provider_url?: string | null;
34
+ };
22
35
  payment?: {
23
36
  pricing?: Record<string, unknown>;
24
37
  accepted_payments?: string[];
package/src/index.ts CHANGED
@@ -16,6 +16,7 @@ import { registerTipTools } from "./tools/tip.js";
16
16
  import { registerPassTools } from "./tools/passes.js";
17
17
  import { registerUploadTools } from "./tools/upload.js";
18
18
  import { registerProbeTools } from "./tools/probe.js";
19
+ import { registerProviderTools } from "./tools/providers.js";
19
20
 
20
21
  // ── Resources ────────────────────────────────────────────────────
21
22
  import { registerAgentResources } from "./resources/agents.js";
@@ -86,6 +87,7 @@ export async function startMcpServer(): Promise<void> {
86
87
  registerPassTools(server);
87
88
  registerUploadTools(server);
88
89
  registerProbeTools(server);
90
+ registerProviderTools(server);
89
91
 
90
92
  // Register resources
91
93
  registerAgentResources(server);
@@ -86,6 +86,7 @@ vi.mock("../../core/config.js", () => ({
86
86
 
87
87
  vi.mock("../../core/payments.js", () => ({
88
88
  getWalletAddress: async () => null,
89
+ isCardPaymentEnabled: () => true,
89
90
  }));
90
91
 
91
92
  vi.mock("../../core/card-setup.js", () => ({
@@ -32,6 +32,8 @@ export function registerAgentInfoTools(server: McpServer): void {
32
32
  } | null | undefined;
33
33
 
34
34
  const totalJobs = (s.completedJobs ?? a.totalExecutions ?? 0) as number;
35
+ const provider = a.provider ?? null;
36
+ const urls = a.urls ?? {};
35
37
  const acceptedPayments = (payment.accepted_payments as string[] | undefined) ?? [];
36
38
  const paymentLabelMap: Record<string, string> = {
37
39
  tempo_usdc: "tempo",
@@ -47,6 +49,7 @@ export function registerAgentInfoTools(server: McpServer): void {
47
49
  "",
48
50
  (a.description as string) ?? "",
49
51
  "",
52
+ ...(provider?.name ? [`Provider: ${provider.name}${provider.slug ? ` (${provider.slug})` : ""}`] : []),
50
53
  `Pricing: ${formatPrice(a.pricePerRunUsd)}`,
51
54
  ...(paymentLabels.length > 0 ? [`Accepted payments: ${paymentLabels.join(", ")}`] : []),
52
55
  ...(totalJobs > 0 && a.successRate != null
@@ -113,7 +116,14 @@ export function registerAgentInfoTools(server: McpServer): void {
113
116
  }
114
117
  }
115
118
 
116
- lines.push("", `ID: ${a.id}`, `View: ${agentWebUrl(a.id)}`);
119
+ lines.push(
120
+ "",
121
+ `ID: ${a.id}`,
122
+ `View: ${urls.public_url ?? agentWebUrl(a.id)}`,
123
+ ...(urls.mirrored_x402_url ? [`Mirrored x402: ${urls.mirrored_x402_url}`] : []),
124
+ ...(urls.normalized_x402_url ? [`Agent x402: ${urls.normalized_x402_url}`] : []),
125
+ ...(urls.agent_card_url ? [`AgentCard: ${urls.agent_card_url}`] : []),
126
+ );
117
127
  return text(lines.join("\n"));
118
128
  },
119
129
  );
@@ -10,3 +10,4 @@ export { registerTipTools } from "./tip.js";
10
10
  export { registerPassTools } from "./passes.js";
11
11
  export { registerUploadTools } from "./upload.js";
12
12
  export { registerProbeTools } from "./probe.js";
13
+ export { registerProviderTools } from "./providers.js";
@@ -0,0 +1,64 @@
1
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import { z } from "zod";
3
+ import { apiGet } from "../core/api-client.js";
4
+ import { agentLine } from "../core/formatters.js";
5
+ import type { AgentRecord } from "../core/types.js";
6
+
7
+ function text(t: string) {
8
+ return { content: [{ type: "text" as const, text: t }] };
9
+ }
10
+
11
+ interface ProviderRecord {
12
+ id: string;
13
+ name: string;
14
+ slug: string;
15
+ description?: string | null;
16
+ website_url?: string | null;
17
+ logo_url?: string | null;
18
+ support_email?: string | null;
19
+ stats?: { live_endpoints?: number; total_executions?: number };
20
+ }
21
+
22
+ export function registerProviderTools(server: McpServer): void {
23
+ server.tool(
24
+ "search_providers",
25
+ "Search API providers listed on Agent Wonderland.",
26
+ {
27
+ query: z.string().optional().describe("Provider name or keyword"),
28
+ limit: z.number().int().min(1).max(25).default(10),
29
+ },
30
+ async ({ query, limit }) => {
31
+ const params = new URLSearchParams();
32
+ if (query) params.set("q", query);
33
+ params.set("limit", String(limit));
34
+ const providers = await apiGet<ProviderRecord[]>(`/providers?${params}`);
35
+ if (providers.length === 0) return text(query ? `No providers found matching "${query}".` : "No providers found.");
36
+ return text([
37
+ `Found ${providers.length} provider${providers.length === 1 ? "" : "s"}:`,
38
+ "",
39
+ ...providers.map((provider) => ` ${provider.name} (${provider.slug}) • ${provider.stats?.live_endpoints ?? 0} endpoints`),
40
+ ].join("\n"));
41
+ },
42
+ );
43
+
44
+ server.tool(
45
+ "get_provider",
46
+ "Get an API provider profile and its live payable endpoints.",
47
+ {
48
+ provider: z.string().describe("Provider slug"),
49
+ limit: z.number().int().min(1).max(100).default(50),
50
+ },
51
+ async ({ provider, limit }) => {
52
+ const profile = await apiGet<ProviderRecord>(`/providers/${provider}`);
53
+ const agents = await apiGet<AgentRecord[]>(`/providers/${provider}/agents?limit=${limit}`);
54
+ return text([
55
+ `${profile.name} (${profile.slug})`,
56
+ profile.description ?? "",
57
+ ...(profile.website_url ? [`Website: ${profile.website_url}`] : []),
58
+ `Live endpoints: ${profile.stats?.live_endpoints ?? agents.length}`,
59
+ "",
60
+ ...agents.map((agent) => ` ${agentLine(agent)}`),
61
+ ].filter(Boolean).join("\n"));
62
+ },
63
+ );
64
+ }
package/src/tools/run.ts CHANGED
@@ -126,7 +126,7 @@ export function registerRunTools(server: McpServer): void {
126
126
  "Run an AI agent from the marketplace. Pays automatically via configured wallet. Returns the agent's output, cost, and job ID for tracking. If spending confirmation is enabled, first call returns a price quote — call again with confirmed: true to execute. Local file paths in the input (e.g. /Users/.../photo.jpg) are automatically uploaded to temporary storage and replaced with download URLs before execution. If a file you need isn't on this MCP server's filesystem (e.g. a sandboxed /mnt/... attachment), call upload_file first to get a presigned upload URL, PUT the bytes to it, then pass the returned GET URL as input instead — that keeps the bytes out of the conversation context.",
127
127
  {
128
128
  agent_id: z.string().describe("Agent ID (UUID, slug, or name)"),
129
- input: z.record(z.unknown()).describe("Input payload for the agent"),
129
+ input: z.record(z.string(), z.unknown()).describe("Input payload for the agent"),
130
130
  pay_with: z.string().trim().min(1).optional().describe("Payment method — wallet ID, chain name (tempo, base, etc.), or 'card'. Auto-detected if omitted."),
131
131
  confirmed: z.boolean().optional().describe("Set to true to confirm spending after seeing the price quote."),
132
132
  },
@@ -16,6 +16,8 @@ export function registerSearchTools(server: McpServer): void {
16
16
  "Search the Agent Wonderland marketplace for AI agents. Returns a ranked list of matching agents with ratings, pricing, and job counts.",
17
17
  {
18
18
  query: z.string().optional().describe("Search query (natural language or keywords)"),
19
+ provider: z.string().optional().describe("Filter by API provider name or slug"),
20
+ provider_slug: z.string().optional().describe("Filter by exact API provider slug"),
19
21
  tag: z.string().optional().describe("Filter by tag (e.g. 'code', 'image', 'data')"),
20
22
  limit: z.number().optional().default(10).describe("Max results (1-50)"),
21
23
  max_price: z.number().optional().describe("Maximum price per request in USD"),
@@ -23,10 +25,12 @@ export function registerSearchTools(server: McpServer): void {
23
25
  sort: z.enum(["relevance", "price", "rating", "popularity", "newest"]).optional()
24
26
  .describe("Sort results by: relevance (default), price, rating, popularity, or newest"),
25
27
  },
26
- async ({ query, tag, limit, max_price, min_rating, sort }) => {
28
+ async ({ query, provider, provider_slug, tag, limit, max_price, min_rating, sort }) => {
27
29
  const requestedLimit = Math.max(1, Math.min(50, limit ?? 10));
28
30
  const params = new URLSearchParams();
29
31
  if (query) params.set("q", query);
32
+ if (provider_slug) params.set("provider_slug", provider_slug);
33
+ else if (provider) params.set("provider", provider);
30
34
  if (tag) params.set("tag", tag);
31
35
 
32
36
  // min_rating is filtered client-side on avgRating. Request extra candidates
@@ -143,7 +143,7 @@ export function registerSolveTools(server: McpServer): void {
143
143
  .string()
144
144
  .describe("What you want to accomplish (natural language)"),
145
145
  input: z
146
- .record(z.unknown())
146
+ .record(z.string(), z.unknown())
147
147
  .optional()
148
148
  .default({})
149
149
  .describe("Input payload for the agent"),