@cryptoapis-io/mcp-x402-pay 0.1.2 → 0.2.0

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/README.md CHANGED
@@ -3,9 +3,12 @@
3
3
  > **This is the BUYER side of x402** — the tool an agent uses to *spend* (pay for a resource). Merchants
4
4
  > who want to *charge* for an API use the middleware SDK (`@cryptoapis-io/x402-merchant-sdk`), not an MCP tool.
5
5
 
6
- An MCP server that lets an AI agent **pay x402-gated HTTP endpoints**. One tool, `x402_pay`: it fetches a
7
- URL and, if the server returns `402 Payment Required`, it authorizes the payment via the CryptoAPIs buyer
8
- service, **signs locally**, retries, and returns the paid response.
6
+ An MCP server that lets an AI agent **find and pay** x402-gated HTTP endpoints.
7
+
8
+ - **`x402_pay`** — fetches a URL and, if the server returns `402 Payment Required`, authorizes the payment
9
+ via the CryptoAPIs buyer service, **signs locally**, retries, and returns the paid response.
10
+ - **`x402_discover`** — browses the facilitator's catalogue of x402 resources and their prices, so an agent
11
+ can *find* a paid API instead of only calling one it was handed.
9
12
 
10
13
  **Non-custodial:** the private key is passed per request and never leaves the process (no HTTP server —
11
14
  stdio only).
@@ -53,6 +56,24 @@ bare name — and exactly one of `address` (any chain; **required for Solana/Kas
53
56
  Returns `{ status, paid, body, settlement? }`. On a 402 with no acceptable option (or over `maxAmount`),
54
57
  `paid:false` with a `reason` — nothing is signed or paid.
55
58
 
59
+ ## Tool: `x402_discover`
60
+
61
+ Browse the x402 "Bazaar" — the registered x402 resources and what each charges
62
+ ([spec §8](https://github.com/coinbase/x402/blob/main/specs/x402-specification-v2.md)).
63
+
64
+ | Input | Required | Description |
65
+ |---|---|---|
66
+ | `type` | | filter by resource type, e.g. `"http"` |
67
+ | `limit` | | page size, 1–100 (default 20) |
68
+ | `offset` | | rows to skip, for paging (default 0) |
69
+ | `facilitatorBaseUrl` | | override the facilitator (QA/local) |
70
+
71
+ Returns `{ resources: [{ resource, type, x402Version, accepts, lastUpdated, metadata? }], pagination }`.
72
+
73
+ **Public — no API key, no wallet, spends nothing**, so it is always safe to call. Note `accepts[].amount`
74
+ is in **atomic units** (USDC 6-decimals: `"10000"` = $0.01) — convert before quoting a price to a user.
75
+ Pass a chosen `resource` to `x402_pay` to actually buy it.
76
+
56
77
  ## Flow
57
78
 
58
79
  1. `fetch(url)`. Not 402 → return it.
@@ -46,5 +46,20 @@ export declare const tools: readonly [import("./types.js").McpX402ToolDef<import
46
46
  allowedNetworks?: string[] | undefined;
47
47
  buyerBaseUrl?: string | undefined;
48
48
  maxAmount?: string | undefined;
49
+ }>>, import("./types.js").McpX402ToolDef<import("zod").ZodObject<{
50
+ type: import("zod").ZodOptional<import("zod").ZodString>;
51
+ limit: import("zod").ZodOptional<import("zod").ZodNumber>;
52
+ offset: import("zod").ZodOptional<import("zod").ZodNumber>;
53
+ facilitatorBaseUrl: import("zod").ZodOptional<import("zod").ZodString>;
54
+ }, "strip", import("zod").ZodTypeAny, {
55
+ type?: string | undefined;
56
+ limit?: number | undefined;
57
+ offset?: number | undefined;
58
+ facilitatorBaseUrl?: string | undefined;
59
+ }, {
60
+ type?: string | undefined;
61
+ limit?: number | undefined;
62
+ offset?: number | undefined;
63
+ facilitatorBaseUrl?: string | undefined;
49
64
  }>>];
50
65
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/tools/index.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,KAAK;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IAAyB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/tools/index.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,KAAK;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IAA2C,CAAC"}
@@ -1,3 +1,4 @@
1
1
  import { x402PayTool } from "./x402-pay/index.js";
2
- export const tools = [x402PayTool];
2
+ import { x402DiscoverTool } from "./x402-discover/index.js";
3
+ export const tools = [x402PayTool, x402DiscoverTool];
3
4
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/tools/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAElD,MAAM,CAAC,MAAM,KAAK,GAAG,CAAC,WAAW,CAAU,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/tools/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAClD,OAAO,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AAE5D,MAAM,CAAC,MAAM,KAAK,GAAG,CAAC,WAAW,EAAE,gBAAgB,CAAU,CAAC"}
@@ -0,0 +1,4 @@
1
+ import { X402DiscoverSchema } from "./schema.js";
2
+ import type { McpX402ToolDef } from "../types.js";
3
+ export declare const x402DiscoverTool: McpX402ToolDef<typeof X402DiscoverSchema>;
4
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/tools/x402-discover/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AACjD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAuDlD,eAAO,MAAM,gBAAgB,EAAE,cAAc,CAAC,OAAO,kBAAkB,CAStE,CAAC"}
@@ -0,0 +1,40 @@
1
+ import { X402DiscoverSchema } from "./schema.js";
2
+ const DEFAULT_FACILITATOR_BASE_URL = "https://ai.cryptoapis.io/x402/merchant";
3
+ /**
4
+ * List x402-enabled resources from the facilitator's Bazaar catalogue.
5
+ *
6
+ * No credential is sent: `/discovery/resources` is public by design, so an agent can
7
+ * survey what is for sale — and at what price — before deciding whether to spend.
8
+ */
9
+ async function x402Discover(input) {
10
+ const root = (input.facilitatorBaseUrl ?? DEFAULT_FACILITATOR_BASE_URL).replace(/\/$/, "");
11
+ const params = new URLSearchParams();
12
+ if (input.type !== undefined)
13
+ params.set("type", input.type);
14
+ if (input.limit !== undefined)
15
+ params.set("limit", String(input.limit));
16
+ if (input.offset !== undefined)
17
+ params.set("offset", String(input.offset));
18
+ const qs = params.toString();
19
+ const res = await fetch(`${root}/discovery/resources${qs ? `?${qs}` : ""}`, { method: "GET" });
20
+ if (!res.ok) {
21
+ // 503 here means the catalogue is unreadable, NOT that there is nothing for sale —
22
+ // surface the difference so the agent retries instead of concluding "no resources".
23
+ throw new Error(`x402 discovery failed: ${res.status} ${await res.text().catch(() => "")}`.trim());
24
+ }
25
+ const body = (await res.json());
26
+ return {
27
+ resources: body.items ?? [],
28
+ pagination: body.pagination ?? { limit: 0, offset: 0, total: 0 },
29
+ };
30
+ }
31
+ export const x402DiscoverTool = {
32
+ name: "x402_discover",
33
+ description: "Discover x402-gated APIs you can pay for — browse the facilitator's Bazaar catalogue of registered x402 resources, each with the price and payment terms it accepts. Use this BEFORE x402_pay whenever you need to FIND a paid endpoint rather than call one you were already given: the pay tool only fetches a URL you hand it, so this is the only way to locate a monetized API on your own. Returns { resources: [{ resource, type, x402Version, accepts: [{ scheme, network, amount, asset, payTo }], lastUpdated, metadata? }], pagination: { limit, offset, total } } — `amount` is in ATOMIC units (USDC 6-decimals: \"10000\" = $0.01), so convert before quoting a price to the user. PUBLIC: needs no API key, no wallet and spends nothing, so it is always safe to call. Filter with `type` (e.g. \"http\") and page with `limit`/`offset`. Then pass a chosen `resource` URL to x402_pay to actually buy it.",
34
+ inputSchema: X402DiscoverSchema,
35
+ handler: async (input) => {
36
+ const result = await x402Discover(input);
37
+ return { content: [{ type: "text", text: JSON.stringify(result) }] };
38
+ },
39
+ };
40
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/tools/x402-discover/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAGjD,MAAM,4BAA4B,GAAG,wCAAwC,CAAC;AAoB9E;;;;;GAKG;AACH,KAAK,UAAU,YAAY,CAAC,KAAwB;IAIhD,MAAM,IAAI,GAAG,CAAC,KAAK,CAAC,kBAAkB,IAAI,4BAA4B,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;IAC3F,MAAM,MAAM,GAAG,IAAI,eAAe,EAAE,CAAC;IACrC,IAAI,KAAK,CAAC,IAAI,KAAK,SAAS;QAAE,MAAM,CAAC,GAAG,CAAC,MAAM,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;IAC7D,IAAI,KAAK,CAAC,KAAK,KAAK,SAAS;QAAE,MAAM,CAAC,GAAG,CAAC,OAAO,EAAE,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC;IACxE,IAAI,KAAK,CAAC,MAAM,KAAK,SAAS;QAAE,MAAM,CAAC,GAAG,CAAC,QAAQ,EAAE,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC;IAC3E,MAAM,EAAE,GAAG,MAAM,CAAC,QAAQ,EAAE,CAAC;IAE7B,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,IAAI,uBAAuB,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,CAAC;IAC/F,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;QACV,mFAAmF;QACnF,oFAAoF;QACpF,MAAM,IAAI,KAAK,CAAC,0BAA0B,GAAG,CAAC,MAAM,IAAI,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC,CAAC;IACvG,CAAC;IACD,MAAM,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,CAG7B,CAAC;IACF,OAAO;QACH,SAAS,EAAE,IAAI,CAAC,KAAK,IAAI,EAAE;QAC3B,UAAU,EAAE,IAAI,CAAC,UAAU,IAAI,EAAE,KAAK,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE;KACnE,CAAC;AACN,CAAC;AAED,MAAM,CAAC,MAAM,gBAAgB,GAA8C;IACvE,IAAI,EAAE,eAAe;IACrB,WAAW,EACP,63BAA63B;IACj4B,WAAW,EAAE,kBAAkB;IAC/B,OAAO,EAAE,KAAK,EAAE,KAAwB,EAAE,EAAE;QACxC,MAAM,MAAM,GAAG,MAAM,YAAY,CAAC,KAAK,CAAC,CAAC;QACzC,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,EAAE,CAAC,EAAE,CAAC;IACzE,CAAC;CACJ,CAAC"}
@@ -0,0 +1,27 @@
1
+ import * as z from "zod";
2
+ /**
3
+ * Browse the x402 "Bazaar" — the catalogue of x402-enabled resources registered with a
4
+ * facilitator, each with the payment requirements it accepts (x402 v2 §8).
5
+ *
6
+ * This is the FIND half of x402: `x402_pay` can only pay a URL it is handed, so without
7
+ * discovery an agent can never locate a paid API on its own. The endpoint is PUBLIC — no
8
+ * API key and no wallet — so browsing costs nothing and commits nothing.
9
+ */
10
+ export declare const X402DiscoverSchema: z.ZodObject<{
11
+ type: z.ZodOptional<z.ZodString>;
12
+ limit: z.ZodOptional<z.ZodNumber>;
13
+ offset: z.ZodOptional<z.ZodNumber>;
14
+ facilitatorBaseUrl: z.ZodOptional<z.ZodString>;
15
+ }, "strip", z.ZodTypeAny, {
16
+ type?: string | undefined;
17
+ limit?: number | undefined;
18
+ offset?: number | undefined;
19
+ facilitatorBaseUrl?: string | undefined;
20
+ }, {
21
+ type?: string | undefined;
22
+ limit?: number | undefined;
23
+ offset?: number | undefined;
24
+ facilitatorBaseUrl?: string | undefined;
25
+ }>;
26
+ export type X402DiscoverInput = z.infer<typeof X402DiscoverSchema>;
27
+ //# sourceMappingURL=schema.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../../src/tools/x402-discover/schema.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,CAAC,MAAM,KAAK,CAAC;AAEzB;;;;;;;GAOG;AACH,eAAO,MAAM,kBAAkB;;;;;;;;;;;;;;;EAuB7B,CAAC;AAEH,MAAM,MAAM,iBAAiB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAC"}
@@ -0,0 +1,34 @@
1
+ import * as z from "zod";
2
+ /**
3
+ * Browse the x402 "Bazaar" — the catalogue of x402-enabled resources registered with a
4
+ * facilitator, each with the payment requirements it accepts (x402 v2 §8).
5
+ *
6
+ * This is the FIND half of x402: `x402_pay` can only pay a URL it is handed, so without
7
+ * discovery an agent can never locate a paid API on its own. The endpoint is PUBLIC — no
8
+ * API key and no wallet — so browsing costs nothing and commits nothing.
9
+ */
10
+ export const X402DiscoverSchema = z.object({
11
+ type: z
12
+ .string()
13
+ .optional()
14
+ .describe('Filter by resource type, e.g. "http". Omit to list every type.'),
15
+ limit: z
16
+ .number()
17
+ .int()
18
+ .min(1)
19
+ .max(100)
20
+ .optional()
21
+ .describe("Max results to return, 1-100 (default 20). The facilitator clamps out-of-range values rather than erroring."),
22
+ offset: z
23
+ .number()
24
+ .int()
25
+ .min(0)
26
+ .optional()
27
+ .describe("How many results to skip, for paging (default 0). Use with `pagination.total` from a previous call."),
28
+ facilitatorBaseUrl: z
29
+ .string()
30
+ .url()
31
+ .optional()
32
+ .describe("Override the facilitator base URL (default https://ai.cryptoapis.io/x402/merchant). Useful against QA/local."),
33
+ });
34
+ //# sourceMappingURL=schema.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema.js","sourceRoot":"","sources":["../../../src/tools/x402-discover/schema.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,CAAC,MAAM,KAAK,CAAC;AAEzB;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAC,CAAC,MAAM,CAAC;IACvC,IAAI,EAAE,CAAC;SACF,MAAM,EAAE;SACR,QAAQ,EAAE;SACV,QAAQ,CAAC,gEAAgE,CAAC;IAC/E,KAAK,EAAE,CAAC;SACH,MAAM,EAAE;SACR,GAAG,EAAE;SACL,GAAG,CAAC,CAAC,CAAC;SACN,GAAG,CAAC,GAAG,CAAC;SACR,QAAQ,EAAE;SACV,QAAQ,CAAC,6GAA6G,CAAC;IAC5H,MAAM,EAAE,CAAC;SACJ,MAAM,EAAE;SACR,GAAG,EAAE;SACL,GAAG,CAAC,CAAC,CAAC;SACN,QAAQ,EAAE;SACV,QAAQ,CAAC,qGAAqG,CAAC;IACpH,kBAAkB,EAAE,CAAC;SAChB,MAAM,EAAE;SACR,GAAG,EAAE;SACL,QAAQ,EAAE;SACV,QAAQ,CAAC,8GAA8G,CAAC;CAChI,CAAC,CAAC"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@cryptoapis-io/mcp-x402-pay",
3
3
  "mcpName": "io.github.CryptoAPIs-io/mcp-x402-pay",
4
- "version": "0.1.2",
4
+ "version": "0.2.0",
5
5
  "description": "MCP server that pays x402-gated HTTP endpoints — one x402_pay tool that fetches, and on a 402 authorizes via the CryptoAPIs buyer service, signs LOCALLY, and retries.",
6
6
  "license": "MIT",
7
7
  "type": "module",
package/server.json CHANGED
@@ -6,12 +6,12 @@
6
6
  "url": "https://github.com/CryptoAPIs-io/cryptoapis-mcp-x402-pay",
7
7
  "source": "github"
8
8
  },
9
- "version": "0.1.2",
9
+ "version": "0.2.0",
10
10
  "packages": [
11
11
  {
12
12
  "registryType": "npm",
13
13
  "identifier": "@cryptoapis-io/mcp-x402-pay",
14
- "version": "0.1.2",
14
+ "version": "0.2.0",
15
15
  "transport": {
16
16
  "type": "stdio"
17
17
  }