@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
@@ -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
  });
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,46 @@
1
+ import { describe, expect, it, vi } from "vitest";
2
+ import { Credential, Method, Mppx, z } from "../mpp-client.js";
3
+ describe("local MPP client", () => {
4
+ it("handles a 402 challenge and retries with a payment credential", async () => {
5
+ const challengeRequest = Buffer.from(JSON.stringify({
6
+ amount: "1000000",
7
+ currency: "USDC",
8
+ recipient: "0x0000000000000000000000000000000000000001",
9
+ })).toString("base64url");
10
+ const fetchMock = vi
11
+ .fn()
12
+ .mockResolvedValueOnce(new Response("payment required", {
13
+ status: 402,
14
+ headers: {
15
+ "WWW-Authenticate": `Payment id="test-id", realm="api.test", method="base", intent="charge", request="${challengeRequest}"`,
16
+ },
17
+ }))
18
+ .mockResolvedValueOnce(new Response("ok", { status: 200 }));
19
+ const method = Method.toClient(Method.from({
20
+ name: "base",
21
+ intent: "charge",
22
+ schema: {
23
+ credential: { payload: z.object({ type: z.literal("hash"), hash: z.string() }) },
24
+ request: z.object({ amount: z.string() }),
25
+ },
26
+ }), {
27
+ createCredential({ challenge }) {
28
+ return Credential.serialize({
29
+ challenge,
30
+ payload: { type: "hash", hash: "0xabc" },
31
+ source: "did:pkh:eip155:8453:0x0000000000000000000000000000000000000002",
32
+ });
33
+ },
34
+ });
35
+ const client = Mppx.create({ fetch: fetchMock, methods: [method], polyfill: false });
36
+ const response = await client.fetch("https://api.test/agents/example/run", {
37
+ method: "POST",
38
+ headers: { "Content-Type": "application/json" },
39
+ body: "{}",
40
+ });
41
+ expect(response.status).toBe(200);
42
+ expect(fetchMock).toHaveBeenCalledTimes(2);
43
+ const retryInit = fetchMock.mock.calls[1]?.[1];
44
+ expect(new Headers(retryInit?.headers).get("Authorization")).toMatch(/^Payment\s+/);
45
+ });
46
+ });
@@ -22,7 +22,7 @@ vi.mock("../config.js", () => ({
22
22
  getWallets: () => currentWallets,
23
23
  resolveWalletAndChain: () => currentResolvedMethod,
24
24
  }));
25
- vi.mock("mppx/client", () => ({
25
+ vi.mock("../mpp-client.js", () => ({
26
26
  Mppx: {
27
27
  create: (config) => mockMppxCreate(config),
28
28
  },
@@ -53,20 +53,11 @@ describe("payment method initialization", () => {
53
53
  .mockReturnValueOnce({ fetch: createdFetches[2] })
54
54
  .mockReturnValueOnce({ fetch: createdFetches[3] });
55
55
  });
56
- it("rebuilds the cached card fetch when the card config changes", async () => {
56
+ it("rejects explicit card payment while card payments are launch-gated", async () => {
57
57
  const { getPaymentFetch } = await import("../payments.js");
58
- const firstFetch = await getPaymentFetch("card");
59
- currentCard = {
60
- consumerToken: "consumer_two",
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 = {
@@ -12,10 +12,32 @@ export class ApiError extends Error {
12
12
  this.name = "ApiError";
13
13
  }
14
14
  }
15
- async function buildHeaders(options) {
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);
@@ -4,7 +4,7 @@
4
4
  * Signs and sends a standard ERC-20 transfer on Base chain, then returns
5
5
  * the tx hash as a credential. Plugs into mppx's compose/dispatch system.
6
6
  */
7
- import { Method, Credential, z } from "mppx";
7
+ import { Method, Credential, z } from "./mpp-client.js";
8
8
  import { toAtomicAmount } from "./amount-utils.js";
9
9
  // Base USDC (Circle native)
10
10
  const BASE_USDC = "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913";
@@ -24,6 +24,10 @@ interface AgentLike {
24
24
  completedJobs?: number;
25
25
  avgRating?: number | null;
26
26
  };
27
+ provider?: {
28
+ name?: string;
29
+ slug?: string;
30
+ } | null;
27
31
  [key: string]: unknown;
28
32
  }
29
33
  export declare function agentLine(agent: AgentLike): string;
@@ -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)
@@ -0,0 +1,62 @@
1
+ import * as z from "zod/mini";
2
+ type Challenge = {
3
+ id: string;
4
+ realm: string;
5
+ method: string;
6
+ intent: string;
7
+ request: Record<string, unknown>;
8
+ description?: string;
9
+ digest?: string;
10
+ expires?: string;
11
+ opaque?: Record<string, string>;
12
+ };
13
+ type ClientMethod = {
14
+ name: string;
15
+ intent: string;
16
+ context?: {
17
+ parse(value: unknown): unknown;
18
+ };
19
+ createCredential(args: {
20
+ challenge: Challenge;
21
+ context?: unknown;
22
+ }): Promise<string> | string;
23
+ };
24
+ type MethodDefinition = {
25
+ name: string;
26
+ intent: string;
27
+ schema?: unknown;
28
+ };
29
+ type CreateConfig = {
30
+ methods: ClientMethod[];
31
+ fetch?: typeof fetch;
32
+ polyfill?: boolean;
33
+ };
34
+ type CredentialInput = {
35
+ challenge: Challenge;
36
+ payload: unknown;
37
+ source?: string;
38
+ };
39
+ export { z };
40
+ export declare const Method: {
41
+ from<T extends MethodDefinition>(method: T): T;
42
+ toClient<T extends MethodDefinition>(method: T, options: {
43
+ context?: {
44
+ parse(value: unknown): unknown;
45
+ };
46
+ createCredential(args: {
47
+ challenge: Challenge;
48
+ context?: unknown;
49
+ }): Promise<string> | string;
50
+ }): T & ClientMethod;
51
+ };
52
+ export declare const Credential: {
53
+ serialize(credential: CredentialInput): string;
54
+ };
55
+ export declare const Mppx: {
56
+ create(config: CreateConfig): {
57
+ fetch: typeof fetch;
58
+ methods: ClientMethod[];
59
+ rawFetch: typeof fetch;
60
+ };
61
+ };
62
+ export declare function stripe(_parameters?: unknown): ClientMethod;
@@ -0,0 +1,208 @@
1
+ import * as z from "zod/mini";
2
+ const PAYMENT_FETCH_WRAPPER = Symbol.for("agentwonderland.mpp.fetch.wrapper");
3
+ export { z };
4
+ export const Method = {
5
+ from(method) {
6
+ return method;
7
+ },
8
+ toClient(method, options) {
9
+ return {
10
+ ...method,
11
+ context: options.context,
12
+ createCredential: options.createCredential,
13
+ };
14
+ },
15
+ };
16
+ export const Credential = {
17
+ serialize(credential) {
18
+ const wire = {
19
+ challenge: {
20
+ ...credential.challenge,
21
+ request: serializePaymentRequest(credential.challenge.request),
22
+ },
23
+ payload: credential.payload,
24
+ ...(credential.source ? { source: credential.source } : {}),
25
+ };
26
+ return `Payment ${base64UrlEncode(JSON.stringify(wire))}`;
27
+ },
28
+ };
29
+ export const Mppx = {
30
+ create(config) {
31
+ const rawFetch = unwrapFetch(config.fetch ?? globalThis.fetch);
32
+ const methods = config.methods.flat();
33
+ const paymentFetch = createPaymentFetch(rawFetch, methods);
34
+ if (config.polyfill) {
35
+ globalThis.fetch = paymentFetch;
36
+ }
37
+ return {
38
+ fetch: paymentFetch,
39
+ methods,
40
+ rawFetch,
41
+ };
42
+ },
43
+ };
44
+ export function stripe(_parameters) {
45
+ throw new Error("Stripe card payments are temporarily unavailable.");
46
+ }
47
+ function createPaymentFetch(baseFetch, methods) {
48
+ const wrapped = (async (input, init) => {
49
+ const response = await baseFetch(input, init);
50
+ if (response.status !== 402) {
51
+ return response;
52
+ }
53
+ const challenges = challengesFromResponse(response);
54
+ const method = methods.find((candidate) => challenges.some((challenge) => challenge.method === candidate.name && challenge.intent === candidate.intent));
55
+ const challenge = method
56
+ ? challenges.find((candidate) => candidate.method === method.name && candidate.intent === method.intent)
57
+ : undefined;
58
+ if (!method || !challenge) {
59
+ throw new Error(`No method found for challenges: ${challenges.map((item) => `${item.method}.${item.intent}`).join(", ")}. ` +
60
+ `Available: ${methods.map((item) => `${item.name}.${item.intent}`).join(", ")}`);
61
+ }
62
+ const context = init?.context;
63
+ const parsedContext = method.context && context !== undefined ? method.context.parse(context) : undefined;
64
+ const credential = await method.createCredential(parsedContext !== undefined ? { challenge, context: parsedContext } : { challenge });
65
+ return baseFetch(input, {
66
+ ...init,
67
+ headers: withAuthorizationHeader(init?.headers, credential),
68
+ });
69
+ });
70
+ wrapped[PAYMENT_FETCH_WRAPPER] = baseFetch;
71
+ return wrapped;
72
+ }
73
+ function challengesFromResponse(response) {
74
+ const header = response.headers.get("WWW-Authenticate");
75
+ if (!header) {
76
+ throw new Error("Missing WWW-Authenticate header.");
77
+ }
78
+ const starts = Array.from(header.matchAll(/Payment\s+/gi), (match) => match.index).filter((index) => index !== undefined);
79
+ if (starts.length === 0) {
80
+ throw new Error("No Payment schemes found.");
81
+ }
82
+ return starts.map((start, index) => {
83
+ const end = index + 1 < starts.length ? starts[index + 1] : header.length;
84
+ return deserializeChallenge(header.slice(start, end).replace(/,\s*$/, ""));
85
+ });
86
+ }
87
+ function deserializeChallenge(value) {
88
+ const paramsStart = value.search(/\s/);
89
+ if (!/^Payment\s+/i.test(value) || paramsStart < 0) {
90
+ throw new Error("Missing Payment scheme.");
91
+ }
92
+ const params = parseAuthParams(value.slice(paramsStart + 1));
93
+ if (!params.request) {
94
+ throw new Error("Missing request parameter.");
95
+ }
96
+ if (!params.id || !params.realm || !params.method || !params.intent) {
97
+ throw new Error("Malformed payment challenge.");
98
+ }
99
+ return {
100
+ id: params.id,
101
+ realm: params.realm,
102
+ method: params.method,
103
+ intent: params.intent,
104
+ request: deserializePaymentRequest(params.request),
105
+ ...(params.description ? { description: params.description } : {}),
106
+ ...(params.digest ? { digest: params.digest } : {}),
107
+ ...(params.expires ? { expires: params.expires } : {}),
108
+ ...(params.opaque ? { opaque: deserializePaymentRequest(params.opaque) } : {}),
109
+ };
110
+ }
111
+ function parseAuthParams(input) {
112
+ const result = {};
113
+ let index = 0;
114
+ while (index < input.length) {
115
+ while (index < input.length && /[\s,]/.test(input[index] ?? ""))
116
+ index++;
117
+ if (index >= input.length)
118
+ break;
119
+ const keyStart = index;
120
+ while (index < input.length && /[A-Za-z0-9_-]/.test(input[index] ?? ""))
121
+ index++;
122
+ const key = input.slice(keyStart, index);
123
+ if (!key)
124
+ throw new Error("Malformed auth-param.");
125
+ while (index < input.length && /\s/.test(input[index] ?? ""))
126
+ index++;
127
+ if (input[index] !== "=")
128
+ break;
129
+ index++;
130
+ while (index < input.length && /\s/.test(input[index] ?? ""))
131
+ index++;
132
+ const [value, nextIndex] = readAuthParamValue(input, index);
133
+ result[key] = value;
134
+ index = nextIndex;
135
+ }
136
+ return result;
137
+ }
138
+ function readAuthParamValue(input, start) {
139
+ if (input[start] !== "\"") {
140
+ let index = start;
141
+ while (index < input.length && input[index] !== ",")
142
+ index++;
143
+ return [input.slice(start, index).trim(), index];
144
+ }
145
+ let index = start + 1;
146
+ let value = "";
147
+ let escaped = false;
148
+ while (index < input.length) {
149
+ const char = input[index];
150
+ index++;
151
+ if (escaped) {
152
+ value += char;
153
+ escaped = false;
154
+ continue;
155
+ }
156
+ if (char === "\\") {
157
+ escaped = true;
158
+ continue;
159
+ }
160
+ if (char === "\"") {
161
+ return [value, index];
162
+ }
163
+ value += char;
164
+ }
165
+ throw new Error("Unterminated quoted-string.");
166
+ }
167
+ function withAuthorizationHeader(headers, credential) {
168
+ const next = new Headers(headers);
169
+ next.set("Authorization", credential);
170
+ return next;
171
+ }
172
+ function unwrapFetch(candidate) {
173
+ let current = candidate;
174
+ while (current[PAYMENT_FETCH_WRAPPER]) {
175
+ current = current[PAYMENT_FETCH_WRAPPER];
176
+ }
177
+ return current;
178
+ }
179
+ function deserializePaymentRequest(encoded) {
180
+ return JSON.parse(base64UrlDecode(encoded));
181
+ }
182
+ function serializePaymentRequest(request) {
183
+ return base64UrlEncode(stableStringify(request));
184
+ }
185
+ function stableStringify(value) {
186
+ if (value === null || typeof value !== "object") {
187
+ return JSON.stringify(value);
188
+ }
189
+ if (Array.isArray(value)) {
190
+ return `[${value.map((item) => stableStringify(item)).join(",")}]`;
191
+ }
192
+ const record = value;
193
+ return `{${Object.keys(record)
194
+ .sort()
195
+ .map((key) => `${JSON.stringify(key)}:${stableStringify(record[key])}`)
196
+ .join(",")}}`;
197
+ }
198
+ function base64UrlEncode(value) {
199
+ return Buffer.from(value, "utf8")
200
+ .toString("base64")
201
+ .replace(/=/g, "")
202
+ .replace(/\+/g, "-")
203
+ .replace(/\//g, "_");
204
+ }
205
+ function base64UrlDecode(value) {
206
+ const padded = value.replace(/-/g, "+").replace(/_/g, "/").padEnd(Math.ceil(value.length / 4) * 4, "=");
207
+ return Buffer.from(padded, "base64").toString("utf8");
208
+ }
@@ -54,7 +54,7 @@ function clearStaleCardCache(activeKey) {
54
54
  // ── Per-protocol initializers ───────────────────────────────────
55
55
  async function initEvmMppForChain(wallet, chain) {
56
56
  try {
57
- const { Mppx } = await import("mppx/client");
57
+ const { Mppx } = await import("./mpp-client.js");
58
58
  let account;
59
59
  if (wallet.keyType === "ows" && wallet.owsWalletId) {
60
60
  const { owsAccountFromWalletId } = await import("./ows-adapter.js");
@@ -88,7 +88,7 @@ async function initSolanaMpp(wallet) {
88
88
  if (wallet.keyType !== "ows" && !wallet.key) {
89
89
  return null;
90
90
  }
91
- const { Mppx } = await import("mppx/client");
91
+ const { Mppx } = await import("./mpp-client.js");
92
92
  const { solanaChargeClient } = await import("./solana-charge.js");
93
93
  const mppx = Mppx.create({
94
94
  methods: [solanaChargeClient({ wallet })],
@@ -105,7 +105,7 @@ async function initCard() {
105
105
  if (!cardConfig)
106
106
  return null;
107
107
  try {
108
- const { Mppx, stripe } = await import("mppx/client");
108
+ const { Mppx, stripe } = await import("./mpp-client.js");
109
109
  const apiUrl = getApiUrl();
110
110
  const pmId = cardConfig.paymentMethodId ?? undefined;
111
111
  const mppx = Mppx.create({
@@ -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;
@@ -4,13 +4,15 @@
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 { ASSOCIATED_TOKEN_PROGRAM_ID, TOKEN_PROGRAM_ID, createAssociatedTokenAccountInstruction, createTransferCheckedInstruction, getAssociatedTokenAddressSync, } from "@solana/spl-token";
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(config.rpcUrl ?? SOLANA_RPC, "confirmed");
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:${SOLANA_CHAIN_ID}:${owner.toBase58()}`,
118
+ source: `did:pkh:${chainId}:${owner.toBase58()}`,
115
119
  });
116
120
  },
117
121
  });
@@ -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 { toAtomicAmount } from "./amount-utils.js";
9
9
  const TEMPO_USDC = "0x20c000000000000000000000b9537d11c60e8b50";
10
10
  const PATH_USD = "0x20c0000000000000000000000000000000000000";
@@ -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,
@@ -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
  }
@@ -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";
@@ -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,2 @@
1
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ export declare function registerProviderTools(server: McpServer): void;