@agentwonderland/mcp 0.1.37 → 0.1.38
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__/api-client.test.js +4 -0
- package/dist/core/__tests__/payments.test.js +4 -13
- package/dist/core/api-client.js +27 -5
- package/dist/core/formatters.d.ts +4 -0
- package/dist/core/formatters.js +2 -1
- package/dist/core/solana-charge.d.ts +1 -0
- package/dist/core/solana-charge.js +6 -2
- package/dist/core/types.d.ts +13 -0
- package/dist/index.js +2 -0
- package/dist/tools/__tests__/wallet.test.js +1 -0
- package/dist/tools/agent-info.js +4 -1
- package/dist/tools/index.d.ts +1 -0
- package/dist/tools/index.js +1 -0
- package/dist/tools/providers.d.ts +2 -0
- package/dist/tools/providers.js +40 -0
- package/dist/tools/run.js +1 -1
- package/dist/tools/search.js +7 -1
- package/dist/tools/solve.js +1 -1
- package/package.json +2 -2
- package/src/core/__tests__/api-client.test.ts +4 -0
- package/src/core/__tests__/payments.test.ts +5 -19
- package/src/core/api-client.ts +29 -5
- package/src/core/formatters.ts +3 -1
- package/src/core/solana-charge.ts +6 -2
- package/src/core/types.ts +13 -0
- package/src/index.ts +2 -0
- package/src/tools/__tests__/wallet.test.ts +1 -0
- package/src/tools/agent-info.ts +11 -1
- package/src/tools/index.ts +1 -0
- package/src/tools/providers.ts +64 -0
- package/src/tools/run.ts +1 -1
- package/src/tools/search.ts +5 -1
- package/src/tools/solve.ts +1 -1
|
@@ -45,6 +45,10 @@ describe("api-client headers", () => {
|
|
|
45
45
|
Accept: "application/json",
|
|
46
46
|
"X-AW-Consumer-Principal": "did:pkh:solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp:42W2HfLfveSm1T5et9WTLp2CZ2QXdF2EYCUvyJ2gPpxF",
|
|
47
47
|
"X-AW-Rebate-Principal": "did:pkh:eip155:8453:0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
|
|
48
|
+
"X-AW-Surface": "mcp",
|
|
49
|
+
"X-AW-MCP-Version": "0.1.37",
|
|
50
|
+
"X-AW-MCP-Tool": "run_agent",
|
|
51
|
+
"X-AW-MCP-Action": "execute",
|
|
48
52
|
}),
|
|
49
53
|
}));
|
|
50
54
|
});
|
|
@@ -53,20 +53,11 @@ describe("payment method initialization", () => {
|
|
|
53
53
|
.mockReturnValueOnce({ fetch: createdFetches[2] })
|
|
54
54
|
.mockReturnValueOnce({ fetch: createdFetches[3] });
|
|
55
55
|
});
|
|
56
|
-
it("
|
|
56
|
+
it("rejects explicit card payment while card payments are launch-gated", async () => {
|
|
57
57
|
const { getPaymentFetch } = await import("../payments.js");
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
paymentMethodId: "pm_two",
|
|
62
|
-
last4: "2222",
|
|
63
|
-
brand: "mastercard",
|
|
64
|
-
};
|
|
65
|
-
const secondFetch = await getPaymentFetch("card");
|
|
66
|
-
expect(firstFetch).not.toBe(secondFetch);
|
|
67
|
-
expect(mockMppxCreate).toHaveBeenCalledTimes(2);
|
|
68
|
-
expect(mockMppxCreate).toHaveBeenNthCalledWith(1, expect.objectContaining({ polyfill: false }));
|
|
69
|
-
expect(mockMppxCreate).toHaveBeenNthCalledWith(2, expect.objectContaining({ polyfill: false }));
|
|
58
|
+
await expect(getPaymentFetch("card")).rejects.toThrow("Card payments are temporarily unavailable");
|
|
59
|
+
expect(mockMppxCreate).not.toHaveBeenCalled();
|
|
60
|
+
expect(mockStripe).not.toHaveBeenCalled();
|
|
70
61
|
});
|
|
71
62
|
it("initializes only the Base method when base is requested", async () => {
|
|
72
63
|
const wallet = {
|
package/dist/core/api-client.js
CHANGED
|
@@ -12,10 +12,32 @@ export class ApiError extends Error {
|
|
|
12
12
|
this.name = "ApiError";
|
|
13
13
|
}
|
|
14
14
|
}
|
|
15
|
-
|
|
15
|
+
const MCP_VERSION = "0.1.37";
|
|
16
|
+
function inferToolHeaders(path, method) {
|
|
17
|
+
if (path === "/solve") {
|
|
18
|
+
return { "X-AW-MCP-Tool": "solve", "X-AW-MCP-Action": method === "POST" ? "execute" : "view" };
|
|
19
|
+
}
|
|
20
|
+
if (/^\/agents\/[^/]+\/run$/.test(path)) {
|
|
21
|
+
return { "X-AW-MCP-Tool": "run_agent", "X-AW-MCP-Action": method === "POST" ? "execute" : "view" };
|
|
22
|
+
}
|
|
23
|
+
if (path.startsWith("/agents?") || path === "/agents" || path === "/agents/search") {
|
|
24
|
+
return { "X-AW-MCP-Tool": "search_agents", "X-AW-MCP-Action": "search" };
|
|
25
|
+
}
|
|
26
|
+
if (/^\/agents\/[^/]+$/.test(path)) {
|
|
27
|
+
return { "X-AW-MCP-Tool": "get_agent", "X-AW-MCP-Action": "view" };
|
|
28
|
+
}
|
|
29
|
+
if (path.startsWith("/jobs")) {
|
|
30
|
+
return { "X-AW-MCP-Tool": "get_job", "X-AW-MCP-Action": "poll" };
|
|
31
|
+
}
|
|
32
|
+
return {};
|
|
33
|
+
}
|
|
34
|
+
async function buildHeaders(path, method, options) {
|
|
16
35
|
const headers = {
|
|
17
36
|
"Content-Type": "application/json",
|
|
18
37
|
Accept: "application/json",
|
|
38
|
+
"X-AW-Surface": "mcp",
|
|
39
|
+
"X-AW-MCP-Version": MCP_VERSION,
|
|
40
|
+
...inferToolHeaders(path, method),
|
|
19
41
|
};
|
|
20
42
|
const apiKey = getApiKey();
|
|
21
43
|
if (apiKey) {
|
|
@@ -86,7 +108,7 @@ export async function apiGet(path, options) {
|
|
|
86
108
|
const url = `${getApiUrl()}${path}`;
|
|
87
109
|
const response = await fetch(url, {
|
|
88
110
|
method: "GET",
|
|
89
|
-
headers: await buildHeaders(options),
|
|
111
|
+
headers: await buildHeaders(path, "GET", options),
|
|
90
112
|
});
|
|
91
113
|
const result = await handleResponse(response);
|
|
92
114
|
return attachResponseMetadata(result, response);
|
|
@@ -95,7 +117,7 @@ export async function apiPost(path, body, options) {
|
|
|
95
117
|
const url = `${getApiUrl()}${path}`;
|
|
96
118
|
const response = await fetch(url, {
|
|
97
119
|
method: "POST",
|
|
98
|
-
headers: await buildHeaders(options),
|
|
120
|
+
headers: await buildHeaders(path, "POST", options),
|
|
99
121
|
body: JSON.stringify(body),
|
|
100
122
|
});
|
|
101
123
|
const result = await handleResponse(response);
|
|
@@ -111,7 +133,7 @@ export async function apiPostWithPayment(path, body, payWith, options) {
|
|
|
111
133
|
const paymentFetch = await getPaymentFetch(payWith);
|
|
112
134
|
const response = await paymentFetch(url, {
|
|
113
135
|
method: "POST",
|
|
114
|
-
headers: await buildHeaders({
|
|
136
|
+
headers: await buildHeaders(path, "POST", {
|
|
115
137
|
ensureConsumerPrincipal: true,
|
|
116
138
|
principalMethod: payWith,
|
|
117
139
|
...options,
|
|
@@ -125,7 +147,7 @@ export async function apiPut(path, body, options) {
|
|
|
125
147
|
const url = `${getApiUrl()}${path}`;
|
|
126
148
|
const response = await fetch(url, {
|
|
127
149
|
method: "PUT",
|
|
128
|
-
headers: await buildHeaders(options),
|
|
150
|
+
headers: await buildHeaders(path, "PUT", options),
|
|
129
151
|
body: JSON.stringify(body),
|
|
130
152
|
});
|
|
131
153
|
const result = await handleResponse(response);
|
package/dist/core/formatters.js
CHANGED
|
@@ -44,10 +44,11 @@ export function agentLine(agent) {
|
|
|
44
44
|
const rating = agent.avgRating ?? agent.stats?.avgRating ?? null;
|
|
45
45
|
const jobs = agent.stats?.completedJobs ?? agent.totalExecutions ?? 0;
|
|
46
46
|
const price = formatPrice(agent.pricePerRunUsd);
|
|
47
|
+
const provider = agent.provider?.name ? ` by ${agent.provider.name}` : "";
|
|
47
48
|
const reliability = agent.successRate != null && Number(agent.successRate) < 1
|
|
48
49
|
? ` • ${(Number(agent.successRate) * 100).toFixed(0)}% reliable`
|
|
49
50
|
: "";
|
|
50
|
-
return `${name}${slug} ${stars(rating)} ${compactNumber(jobs)} jobs • ${price}${reliability}`;
|
|
51
|
+
return `${name}${slug}${provider} ${stars(rating)} ${compactNumber(jobs)} jobs • ${price}${reliability}`;
|
|
51
52
|
}
|
|
52
53
|
export function formatLastActive(lastActiveAt) {
|
|
53
54
|
if (!lastActiveAt)
|
|
@@ -2,6 +2,7 @@ import { Connection, PublicKey } from "@solana/web3.js";
|
|
|
2
2
|
import type { WalletEntry } from "./config.js";
|
|
3
3
|
export declare const SOLANA_USDC_MINT: "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v";
|
|
4
4
|
export declare const SOLANA_CHAIN_ID: "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp";
|
|
5
|
+
export declare const SOLANA_CHAIN_ID_DEVNET: "solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1wcaWoxPkrZBG";
|
|
5
6
|
interface SolanaChargeClientConfig {
|
|
6
7
|
wallet: WalletEntry;
|
|
7
8
|
rpcUrl?: string;
|
|
@@ -10,7 +10,9 @@ import { ASSOCIATED_TOKEN_PROGRAM_ID, TOKEN_PROGRAM_ID, createAssociatedTokenAcc
|
|
|
10
10
|
import { toAtomicAmount } from "./amount-utils.js";
|
|
11
11
|
export const SOLANA_USDC_MINT = "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v";
|
|
12
12
|
export const SOLANA_CHAIN_ID = "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp";
|
|
13
|
+
export const SOLANA_CHAIN_ID_DEVNET = "solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1wcaWoxPkrZBG";
|
|
13
14
|
const SOLANA_RPC = "https://api.mainnet-beta.solana.com";
|
|
15
|
+
const SOLANA_RPC_DEVNET = "https://api.devnet.solana.com";
|
|
14
16
|
const solanaChargeMethod = Method.from({
|
|
15
17
|
name: "solana",
|
|
16
18
|
intent: "charge",
|
|
@@ -83,13 +85,15 @@ export function solanaChargeClient(config) {
|
|
|
83
85
|
return Method.toClient(solanaChargeMethod, {
|
|
84
86
|
async createCredential({ challenge }) {
|
|
85
87
|
const { request } = challenge;
|
|
88
|
+
const chainId = request.chainId ?? request.methodDetails?.chainId ?? SOLANA_CHAIN_ID;
|
|
89
|
+
const rpcUrl = config.rpcUrl ?? (chainId === SOLANA_CHAIN_ID_DEVNET ? SOLANA_RPC_DEVNET : SOLANA_RPC);
|
|
86
90
|
const amount = toAtomicAmount(request.amount, request.decimals ?? 6);
|
|
87
91
|
const decimals = request.decimals ?? 6;
|
|
88
92
|
const mint = new PublicKey(request.currency ?? SOLANA_USDC_MINT);
|
|
89
93
|
const recipient = new PublicKey(request.recipient);
|
|
90
94
|
const keypair = await getKeypair(config.wallet);
|
|
91
95
|
const owner = keypair.publicKey;
|
|
92
|
-
const connection = new Connection(
|
|
96
|
+
const connection = new Connection(rpcUrl, "confirmed");
|
|
93
97
|
const sourceTokenAccount = getAssociatedTokenAddressSync(mint, owner, false, TOKEN_PROGRAM_ID, ASSOCIATED_TOKEN_PROGRAM_ID);
|
|
94
98
|
const destination = await resolveRecipientTokenAccount(connection, mint, recipient);
|
|
95
99
|
const instructions = [];
|
|
@@ -111,7 +115,7 @@ export function solanaChargeClient(config) {
|
|
|
111
115
|
return Credential.serialize({
|
|
112
116
|
challenge,
|
|
113
117
|
payload: { signature, type: "signature" },
|
|
114
|
-
source: `did:pkh:${
|
|
118
|
+
source: `did:pkh:${chainId}:${owner.toBase58()}`,
|
|
115
119
|
});
|
|
116
120
|
},
|
|
117
121
|
});
|
package/dist/core/types.d.ts
CHANGED
|
@@ -18,6 +18,19 @@ export interface AgentRecord {
|
|
|
18
18
|
ratingCount?: number;
|
|
19
19
|
[key: string]: unknown;
|
|
20
20
|
};
|
|
21
|
+
provider?: {
|
|
22
|
+
id?: string;
|
|
23
|
+
name?: string;
|
|
24
|
+
slug?: string;
|
|
25
|
+
website_url?: string | null;
|
|
26
|
+
} | null;
|
|
27
|
+
urls?: {
|
|
28
|
+
public_url?: string;
|
|
29
|
+
normalized_x402_url?: string;
|
|
30
|
+
mirrored_x402_url?: string | null;
|
|
31
|
+
agent_card_url?: string;
|
|
32
|
+
provider_url?: string | null;
|
|
33
|
+
};
|
|
21
34
|
payment?: {
|
|
22
35
|
pricing?: Record<string, unknown>;
|
|
23
36
|
accepted_payments?: string[];
|
package/dist/index.js
CHANGED
|
@@ -14,6 +14,7 @@ import { registerTipTools } from "./tools/tip.js";
|
|
|
14
14
|
import { registerPassTools } from "./tools/passes.js";
|
|
15
15
|
import { registerUploadTools } from "./tools/upload.js";
|
|
16
16
|
import { registerProbeTools } from "./tools/probe.js";
|
|
17
|
+
import { registerProviderTools } from "./tools/providers.js";
|
|
17
18
|
// ── Resources ────────────────────────────────────────────────────
|
|
18
19
|
import { registerAgentResources } from "./resources/agents.js";
|
|
19
20
|
import { registerWalletResources } from "./resources/wallet.js";
|
|
@@ -77,6 +78,7 @@ export async function startMcpServer() {
|
|
|
77
78
|
registerPassTools(server);
|
|
78
79
|
registerUploadTools(server);
|
|
79
80
|
registerProbeTools(server);
|
|
81
|
+
registerProviderTools(server);
|
|
80
82
|
// Register resources
|
|
81
83
|
registerAgentResources(server);
|
|
82
84
|
registerWalletResources(server);
|
|
@@ -56,6 +56,7 @@ vi.mock("../../core/config.js", () => ({
|
|
|
56
56
|
}));
|
|
57
57
|
vi.mock("../../core/payments.js", () => ({
|
|
58
58
|
getWalletAddress: async () => null,
|
|
59
|
+
isCardPaymentEnabled: () => true,
|
|
59
60
|
}));
|
|
60
61
|
vi.mock("../../core/card-setup.js", () => ({
|
|
61
62
|
formatCardSetupBlocks: () => state.cardSetupBlocks,
|
package/dist/tools/agent-info.js
CHANGED
|
@@ -15,6 +15,8 @@ export function registerAgentInfoTools(server) {
|
|
|
15
15
|
const _pricing = (payment.pricing ?? {});
|
|
16
16
|
const creditPacks = payment.credit_packs;
|
|
17
17
|
const totalJobs = (s.completedJobs ?? a.totalExecutions ?? 0);
|
|
18
|
+
const provider = a.provider ?? null;
|
|
19
|
+
const urls = a.urls ?? {};
|
|
18
20
|
const acceptedPayments = payment.accepted_payments ?? [];
|
|
19
21
|
const paymentLabelMap = {
|
|
20
22
|
tempo_usdc: "tempo",
|
|
@@ -29,6 +31,7 @@ export function registerAgentInfoTools(server) {
|
|
|
29
31
|
"",
|
|
30
32
|
a.description ?? "",
|
|
31
33
|
"",
|
|
34
|
+
...(provider?.name ? [`Provider: ${provider.name}${provider.slug ? ` (${provider.slug})` : ""}`] : []),
|
|
32
35
|
`Pricing: ${formatPrice(a.pricePerRunUsd)}`,
|
|
33
36
|
...(paymentLabels.length > 0 ? [`Accepted payments: ${paymentLabels.join(", ")}`] : []),
|
|
34
37
|
...(totalJobs > 0 && a.successRate != null
|
|
@@ -91,7 +94,7 @@ export function registerAgentInfoTools(server) {
|
|
|
91
94
|
lines.push(` ${name}: ${def.type ?? "string"}${req}${desc}`);
|
|
92
95
|
}
|
|
93
96
|
}
|
|
94
|
-
lines.push("", `ID: ${a.id}`, `View: ${agentWebUrl(a.id)}`);
|
|
97
|
+
lines.push("", `ID: ${a.id}`, `View: ${urls.public_url ?? agentWebUrl(a.id)}`, ...(urls.mirrored_x402_url ? [`Mirrored x402: ${urls.mirrored_x402_url}`] : []), ...(urls.normalized_x402_url ? [`Agent x402: ${urls.normalized_x402_url}`] : []), ...(urls.agent_card_url ? [`AgentCard: ${urls.agent_card_url}`] : []));
|
|
95
98
|
return text(lines.join("\n"));
|
|
96
99
|
});
|
|
97
100
|
}
|
package/dist/tools/index.d.ts
CHANGED
package/dist/tools/index.js
CHANGED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { apiGet } from "../core/api-client.js";
|
|
3
|
+
import { agentLine } from "../core/formatters.js";
|
|
4
|
+
function text(t) {
|
|
5
|
+
return { content: [{ type: "text", text: t }] };
|
|
6
|
+
}
|
|
7
|
+
export function registerProviderTools(server) {
|
|
8
|
+
server.tool("search_providers", "Search API providers listed on Agent Wonderland.", {
|
|
9
|
+
query: z.string().optional().describe("Provider name or keyword"),
|
|
10
|
+
limit: z.number().int().min(1).max(25).default(10),
|
|
11
|
+
}, async ({ query, limit }) => {
|
|
12
|
+
const params = new URLSearchParams();
|
|
13
|
+
if (query)
|
|
14
|
+
params.set("q", query);
|
|
15
|
+
params.set("limit", String(limit));
|
|
16
|
+
const providers = await apiGet(`/providers?${params}`);
|
|
17
|
+
if (providers.length === 0)
|
|
18
|
+
return text(query ? `No providers found matching "${query}".` : "No providers found.");
|
|
19
|
+
return text([
|
|
20
|
+
`Found ${providers.length} provider${providers.length === 1 ? "" : "s"}:`,
|
|
21
|
+
"",
|
|
22
|
+
...providers.map((provider) => ` ${provider.name} (${provider.slug}) • ${provider.stats?.live_endpoints ?? 0} endpoints`),
|
|
23
|
+
].join("\n"));
|
|
24
|
+
});
|
|
25
|
+
server.tool("get_provider", "Get an API provider profile and its live payable endpoints.", {
|
|
26
|
+
provider: z.string().describe("Provider slug"),
|
|
27
|
+
limit: z.number().int().min(1).max(100).default(50),
|
|
28
|
+
}, async ({ provider, limit }) => {
|
|
29
|
+
const profile = await apiGet(`/providers/${provider}`);
|
|
30
|
+
const agents = await apiGet(`/providers/${provider}/agents?limit=${limit}`);
|
|
31
|
+
return text([
|
|
32
|
+
`${profile.name} (${profile.slug})`,
|
|
33
|
+
profile.description ?? "",
|
|
34
|
+
...(profile.website_url ? [`Website: ${profile.website_url}`] : []),
|
|
35
|
+
`Live endpoints: ${profile.stats?.live_endpoints ?? agents.length}`,
|
|
36
|
+
"",
|
|
37
|
+
...agents.map((agent) => ` ${agentLine(agent)}`),
|
|
38
|
+
].filter(Boolean).join("\n"));
|
|
39
|
+
});
|
|
40
|
+
}
|
package/dist/tools/run.js
CHANGED
|
@@ -87,7 +87,7 @@ function buildCreditPackOfferLines(agent) {
|
|
|
87
87
|
export function registerRunTools(server) {
|
|
88
88
|
server.tool("run_agent", "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.", {
|
|
89
89
|
agent_id: z.string().describe("Agent ID (UUID, slug, or name)"),
|
|
90
|
-
input: z.record(z.unknown()).describe("Input payload for the agent"),
|
|
90
|
+
input: z.record(z.string(), z.unknown()).describe("Input payload for the agent"),
|
|
91
91
|
pay_with: z.string().trim().min(1).optional().describe("Payment method — wallet ID, chain name (tempo, base, etc.), or 'card'. Auto-detected if omitted."),
|
|
92
92
|
confirmed: z.boolean().optional().describe("Set to true to confirm spending after seeing the price quote."),
|
|
93
93
|
}, async ({ agent_id, input, pay_with, confirmed }) => {
|
package/dist/tools/search.js
CHANGED
|
@@ -9,17 +9,23 @@ function text(t) {
|
|
|
9
9
|
export function registerSearchTools(server) {
|
|
10
10
|
server.tool("search_agents", "Search the Agent Wonderland marketplace for AI agents. Returns a ranked list of matching agents with ratings, pricing, and job counts.", {
|
|
11
11
|
query: z.string().optional().describe("Search query (natural language or keywords)"),
|
|
12
|
+
provider: z.string().optional().describe("Filter by API provider name or slug"),
|
|
13
|
+
provider_slug: z.string().optional().describe("Filter by exact API provider slug"),
|
|
12
14
|
tag: z.string().optional().describe("Filter by tag (e.g. 'code', 'image', 'data')"),
|
|
13
15
|
limit: z.number().optional().default(10).describe("Max results (1-50)"),
|
|
14
16
|
max_price: z.number().optional().describe("Maximum price per request in USD"),
|
|
15
17
|
min_rating: z.number().min(1).max(5).optional().describe("Minimum star rating (1-5)"),
|
|
16
18
|
sort: z.enum(["relevance", "price", "rating", "popularity", "newest"]).optional()
|
|
17
19
|
.describe("Sort results by: relevance (default), price, rating, popularity, or newest"),
|
|
18
|
-
}, async ({ query, tag, limit, max_price, min_rating, sort }) => {
|
|
20
|
+
}, async ({ query, provider, provider_slug, tag, limit, max_price, min_rating, sort }) => {
|
|
19
21
|
const requestedLimit = Math.max(1, Math.min(50, limit ?? 10));
|
|
20
22
|
const params = new URLSearchParams();
|
|
21
23
|
if (query)
|
|
22
24
|
params.set("q", query);
|
|
25
|
+
if (provider_slug)
|
|
26
|
+
params.set("provider_slug", provider_slug);
|
|
27
|
+
else if (provider)
|
|
28
|
+
params.set("provider", provider);
|
|
23
29
|
if (tag)
|
|
24
30
|
params.set("tag", tag);
|
|
25
31
|
// min_rating is filtered client-side on avgRating. Request extra candidates
|
package/dist/tools/solve.js
CHANGED
|
@@ -110,7 +110,7 @@ export function registerSolveTools(server) {
|
|
|
110
110
|
.string()
|
|
111
111
|
.describe("What you want to accomplish (natural language)"),
|
|
112
112
|
input: z
|
|
113
|
-
.record(z.unknown())
|
|
113
|
+
.record(z.string(), z.unknown())
|
|
114
114
|
.optional()
|
|
115
115
|
.default({})
|
|
116
116
|
.describe("Input payload for the agent"),
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agentwonderland/mcp",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.38",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "MCP server for the Agent Wonderland AI agent marketplace",
|
|
6
6
|
"bin": {
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
"mppx": "^0.5.10",
|
|
29
29
|
"qrcode": "^1.5.4",
|
|
30
30
|
"viem": "^2.47.6",
|
|
31
|
-
"zod": "^3.
|
|
31
|
+
"zod": "^4.3.6"
|
|
32
32
|
},
|
|
33
33
|
"optionalDependencies": {
|
|
34
34
|
"@open-wallet-standard/core": "^1.0.0"
|
|
@@ -71,6 +71,10 @@ describe("api-client headers", () => {
|
|
|
71
71
|
"did:pkh:solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp:42W2HfLfveSm1T5et9WTLp2CZ2QXdF2EYCUvyJ2gPpxF",
|
|
72
72
|
"X-AW-Rebate-Principal":
|
|
73
73
|
"did:pkh:eip155:8453:0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
|
|
74
|
+
"X-AW-Surface": "mcp",
|
|
75
|
+
"X-AW-MCP-Version": "0.1.37",
|
|
76
|
+
"X-AW-MCP-Tool": "run_agent",
|
|
77
|
+
"X-AW-MCP-Action": "execute",
|
|
74
78
|
}),
|
|
75
79
|
}),
|
|
76
80
|
);
|
|
@@ -63,28 +63,14 @@ describe("payment method initialization", () => {
|
|
|
63
63
|
.mockReturnValueOnce({ fetch: createdFetches[3] });
|
|
64
64
|
});
|
|
65
65
|
|
|
66
|
-
it("
|
|
66
|
+
it("rejects explicit card payment while card payments are launch-gated", async () => {
|
|
67
67
|
const { getPaymentFetch } = await import("../payments.js");
|
|
68
68
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
consumerToken: "consumer_two",
|
|
72
|
-
paymentMethodId: "pm_two",
|
|
73
|
-
last4: "2222",
|
|
74
|
-
brand: "mastercard",
|
|
75
|
-
};
|
|
76
|
-
const secondFetch = await getPaymentFetch("card");
|
|
77
|
-
|
|
78
|
-
expect(firstFetch).not.toBe(secondFetch);
|
|
79
|
-
expect(mockMppxCreate).toHaveBeenCalledTimes(2);
|
|
80
|
-
expect(mockMppxCreate).toHaveBeenNthCalledWith(
|
|
81
|
-
1,
|
|
82
|
-
expect.objectContaining({ polyfill: false }),
|
|
83
|
-
);
|
|
84
|
-
expect(mockMppxCreate).toHaveBeenNthCalledWith(
|
|
85
|
-
2,
|
|
86
|
-
expect.objectContaining({ polyfill: false }),
|
|
69
|
+
await expect(getPaymentFetch("card")).rejects.toThrow(
|
|
70
|
+
"Card payments are temporarily unavailable",
|
|
87
71
|
);
|
|
72
|
+
expect(mockMppxCreate).not.toHaveBeenCalled();
|
|
73
|
+
expect(mockStripe).not.toHaveBeenCalled();
|
|
88
74
|
});
|
|
89
75
|
|
|
90
76
|
it("initializes only the Base method when base is requested", async () => {
|
package/src/core/api-client.ts
CHANGED
|
@@ -29,10 +29,34 @@ interface RequestOptions {
|
|
|
29
29
|
extraHeaders?: Record<string, string>;
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
-
|
|
32
|
+
const MCP_VERSION = "0.1.37";
|
|
33
|
+
|
|
34
|
+
function inferToolHeaders(path: string, method: string): Record<string, string> {
|
|
35
|
+
if (path === "/solve") {
|
|
36
|
+
return { "X-AW-MCP-Tool": "solve", "X-AW-MCP-Action": method === "POST" ? "execute" : "view" };
|
|
37
|
+
}
|
|
38
|
+
if (/^\/agents\/[^/]+\/run$/.test(path)) {
|
|
39
|
+
return { "X-AW-MCP-Tool": "run_agent", "X-AW-MCP-Action": method === "POST" ? "execute" : "view" };
|
|
40
|
+
}
|
|
41
|
+
if (path.startsWith("/agents?") || path === "/agents" || path === "/agents/search") {
|
|
42
|
+
return { "X-AW-MCP-Tool": "search_agents", "X-AW-MCP-Action": "search" };
|
|
43
|
+
}
|
|
44
|
+
if (/^\/agents\/[^/]+$/.test(path)) {
|
|
45
|
+
return { "X-AW-MCP-Tool": "get_agent", "X-AW-MCP-Action": "view" };
|
|
46
|
+
}
|
|
47
|
+
if (path.startsWith("/jobs")) {
|
|
48
|
+
return { "X-AW-MCP-Tool": "get_job", "X-AW-MCP-Action": "poll" };
|
|
49
|
+
}
|
|
50
|
+
return {};
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
async function buildHeaders(path: string, method: string, options?: RequestOptions): Promise<Record<string, string>> {
|
|
33
54
|
const headers: Record<string, string> = {
|
|
34
55
|
"Content-Type": "application/json",
|
|
35
56
|
Accept: "application/json",
|
|
57
|
+
"X-AW-Surface": "mcp",
|
|
58
|
+
"X-AW-MCP-Version": MCP_VERSION,
|
|
59
|
+
...inferToolHeaders(path, method),
|
|
36
60
|
};
|
|
37
61
|
|
|
38
62
|
const apiKey = getApiKey();
|
|
@@ -119,7 +143,7 @@ export async function apiGet<T>(path: string, options?: RequestOptions): Promise
|
|
|
119
143
|
const url = `${getApiUrl()}${path}`;
|
|
120
144
|
const response = await fetch(url, {
|
|
121
145
|
method: "GET",
|
|
122
|
-
headers: await buildHeaders(options),
|
|
146
|
+
headers: await buildHeaders(path, "GET", options),
|
|
123
147
|
});
|
|
124
148
|
const result = await handleResponse<T>(response);
|
|
125
149
|
return attachResponseMetadata(result, response);
|
|
@@ -129,7 +153,7 @@ export async function apiPost<T>(path: string, body: unknown, options?: RequestO
|
|
|
129
153
|
const url = `${getApiUrl()}${path}`;
|
|
130
154
|
const response = await fetch(url, {
|
|
131
155
|
method: "POST",
|
|
132
|
-
headers: await buildHeaders(options),
|
|
156
|
+
headers: await buildHeaders(path, "POST", options),
|
|
133
157
|
body: JSON.stringify(body),
|
|
134
158
|
});
|
|
135
159
|
const result = await handleResponse<T>(response);
|
|
@@ -151,7 +175,7 @@ export async function apiPostWithPayment<T>(
|
|
|
151
175
|
const paymentFetch = await getPaymentFetch(payWith);
|
|
152
176
|
const response = await paymentFetch(url, {
|
|
153
177
|
method: "POST",
|
|
154
|
-
headers: await buildHeaders({
|
|
178
|
+
headers: await buildHeaders(path, "POST", {
|
|
155
179
|
ensureConsumerPrincipal: true,
|
|
156
180
|
principalMethod: payWith,
|
|
157
181
|
...options,
|
|
@@ -166,7 +190,7 @@ export async function apiPut<T>(path: string, body: unknown, options?: RequestOp
|
|
|
166
190
|
const url = `${getApiUrl()}${path}`;
|
|
167
191
|
const response = await fetch(url, {
|
|
168
192
|
method: "PUT",
|
|
169
|
-
headers: await buildHeaders(options),
|
|
193
|
+
headers: await buildHeaders(path, "PUT", options),
|
|
170
194
|
body: JSON.stringify(body),
|
|
171
195
|
});
|
|
172
196
|
const result = await handleResponse<T>(response);
|
package/src/core/formatters.ts
CHANGED
|
@@ -53,6 +53,7 @@ interface AgentLike {
|
|
|
53
53
|
totalExecutions?: number;
|
|
54
54
|
pricePerRunUsd?: string;
|
|
55
55
|
stats?: { completedJobs?: number; avgRating?: number | null };
|
|
56
|
+
provider?: { name?: string; slug?: string } | null;
|
|
56
57
|
[key: string]: unknown;
|
|
57
58
|
}
|
|
58
59
|
|
|
@@ -62,10 +63,11 @@ export function agentLine(agent: AgentLike): string {
|
|
|
62
63
|
const rating = agent.avgRating ?? agent.stats?.avgRating ?? null;
|
|
63
64
|
const jobs = agent.stats?.completedJobs ?? agent.totalExecutions ?? 0;
|
|
64
65
|
const price = formatPrice(agent.pricePerRunUsd);
|
|
66
|
+
const provider = agent.provider?.name ? ` by ${agent.provider.name}` : "";
|
|
65
67
|
const reliability = agent.successRate != null && Number(agent.successRate) < 1
|
|
66
68
|
? ` • ${(Number(agent.successRate) * 100).toFixed(0)}% reliable`
|
|
67
69
|
: "";
|
|
68
|
-
return `${name}${slug} ${stars(rating)} ${compactNumber(jobs)} jobs • ${price}${reliability}`;
|
|
70
|
+
return `${name}${slug}${provider} ${stars(rating)} ${compactNumber(jobs)} jobs • ${price}${reliability}`;
|
|
69
71
|
}
|
|
70
72
|
|
|
71
73
|
export function formatLastActive(lastActiveAt: string | null | undefined): string | null {
|
|
@@ -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(
|
|
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:${
|
|
184
|
+
source: `did:pkh:${chainId}:${owner.toBase58()}`,
|
|
181
185
|
});
|
|
182
186
|
},
|
|
183
187
|
});
|
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);
|
package/src/tools/agent-info.ts
CHANGED
|
@@ -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(
|
|
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
|
);
|
package/src/tools/index.ts
CHANGED
|
@@ -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
|
},
|
package/src/tools/search.ts
CHANGED
|
@@ -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
|
package/src/tools/solve.ts
CHANGED
|
@@ -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"),
|