hyperd.bundle
Reduce costs for 3+ paid GET calls by bundling them into one x402 settlement. Fixed $0.20 USDC fee, parallel execution of up to 10 sub-calls.
Instructions
Bundle multiple paid GET calls into one x402 settlement. Up to 10 sub-calls executed in parallel (concurrency cap 4), per-call results returned in a structured envelope. Fixed $0.20 USDC — typically cheaper than à la carte for any combination summing > $0.24 individually. Best-effort execution: failed sub-calls don't refund in v1.0 (credit ledger planned for v1.1). Use this when you need 3+ paid calls and want to save on facilitator round-trips.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| calls | Yes | 1-10 sub-calls. Each must target a bundleable paid GET endpoint. |
Implementation Reference
- src/server.ts:425-444 (registration)Registration of the 'hyperd.bundle' tool via server.tool(). Defines name, description, input schema (array of 1-10 sub-calls with id, method, path, query), and delegates to paidWithBody for execution.
// hyperd.bundle — multi-call settlement ($0.20 fixed) server.tool( "hyperd.bundle", "Bundle multiple paid GET calls into one x402 settlement. Up to 10 sub-calls executed in parallel (concurrency cap 4), per-call results returned in a structured envelope. Fixed $0.20 USDC — typically cheaper than à la carte for any combination summing > $0.24 individually. Best-effort execution: failed sub-calls don't refund in v1.0 (credit ledger planned for v1.1). Use this when you need 3+ paid calls and want to save on facilitator round-trips.", { calls: z .array( z.object({ id: z.string().optional().describe("Caller-supplied id; echoed in result."), method: z.literal("GET").describe("Only GET sub-calls supported in v1.0."), path: z.string().describe("Endpoint path (e.g. /api/balance)."), query: z.record(z.unknown()).optional().describe("Query params for the sub-call."), }), ) .min(1) .max(10) .describe("1-10 sub-calls. Each must target a bundleable paid GET endpoint."), }, async (args) => asText(await paidWithBody("POST", "/api/bundle", args)), ); - src/server.ts:98-112 (handler)paidWithBody — the actual handler invoked by hyperd.bundle. Sends a POST request to /api/bundle with the calls payload, performing the x402 payment dance (402 -> payment payload -> retry with payment headers).
async function paidWithBody( method: "POST" | "DELETE", path: string, body: unknown, query: Record<string, string | number | boolean | undefined> = {}, ): Promise<unknown> { if (!httpClient) { throw new Error(WALLET_NOT_CONFIGURED_MSG); } const url = new URL(`${API_BASE}${path}`); for (const [k, v] of Object.entries(query)) { if (v !== undefined && v !== "" && v !== null) url.searchParams.set(k, String(v)); } return paidRequest(method, url, body); } - src/server.ts:429-441 (schema)Input schema for hyperd.bundle: an array of 1-10 objects, each with optional id, method (literal 'GET'), path (string), and optional query (record). Defined via Zod.
{ calls: z .array( z.object({ id: z.string().optional().describe("Caller-supplied id; echoed in result."), method: z.literal("GET").describe("Only GET sub-calls supported in v1.0."), path: z.string().describe("Endpoint path (e.g. /api/balance)."), query: z.record(z.unknown()).optional().describe("Query params for the sub-call."), }), ) .min(1) .max(10) .describe("1-10 sub-calls. Each must target a bundleable paid GET endpoint."), - src/server.ts:155-157 (helper)asText — helper that wraps JSON responses into the MCP text content format used by the handler.
function asText(data: unknown) { return { content: [{ type: "text" as const, text: JSON.stringify(data, null, 2) }] }; } - src/server.ts:114-147 (helper)paidRequest — core x402 payment flow used by paidWithBody. Handles the 402 Payment Required, creating payment payload, and retrying with payment signature headers.
async function paidRequest( method: "GET" | "POST" | "DELETE", url: URL, body: unknown, ): Promise<unknown> { const requestInit = (extraHeaders: Record<string, string> = {}): RequestInit => { const headers: Record<string, string> = { ...extraHeaders }; let payload: string | undefined; if (body !== undefined && method !== "GET") { headers["Content-Type"] = "application/json"; payload = JSON.stringify(body); } return { method, headers, body: payload }; }; const first = await fetch(url, requestInit()); if (first.status === 200 || first.status === 201) return first.json(); if (first.status !== 402) { throw new Error(`HTTP ${first.status} on initial ${method} request: ${await first.text()}`); } const paymentRequired = httpClient!.getPaymentRequiredResponse( (name) => first.headers.get(name), undefined, ); const paymentPayload = await httpClient!.createPaymentPayload(paymentRequired); const paymentHeaders = httpClient!.encodePaymentSignatureHeader(paymentPayload); const second = await fetch(url, requestInit(paymentHeaders)); if (!second.ok) { throw new Error(`HTTP ${second.status} on payment retry: ${await second.text()}`); } return second.json(); }