7-day forensics report
forensicsAnalyze an x402 endpoint with 7-day hourly uptime, latency percentiles, status-code distribution, namespace concentration, and decoy probability score. Fee: $0.001 USDC.
Instructions
Deep history for one x402 endpoint: hourly uptime over 7 days, latency p50/p90/p99, status-code distribution, concentration-group stats (how crowded this provider's namespace is), and a decoy probability score [0, 1]. Costs $0.001 USDC. Superset of preflight — if you're running forensics you don't need preflight too.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| url | Yes | The full URL of the x402 endpoint to analyse. |
Implementation Reference
- src/index.ts:366-387 (registration)MCP server registration of the 'forensics' tool. Registered via server.registerTool with zod schema for url input, calls callPaid('/api/v1/forensics', {url}) and returns text content or error.
server.registerTool( "forensics", { title: "7-day forensics report", description: "Deep history for one x402 endpoint: hourly uptime over 7 days, latency p50/p90/p99, status-code distribution, concentration-group stats (how crowded this provider's namespace is), and a decoy probability score [0, 1]. Costs $0.001 USDC. Superset of preflight — if you're running forensics you don't need preflight too.", inputSchema: { url: z.string().url().describe("The full URL of the x402 endpoint to analyse."), }, }, async ({ url }) => { try { const text = await callPaid("/api/v1/forensics", { url }); return { content: [{ type: "text" as const, text }] }; } catch (err) { return { isError: true, content: [{ type: "text" as const, text: (err as Error).message }], }; } }, ); - AgentKit ActionProvider implementation of the 'forensics' tool (handler). Decorated with @CreateAction, takes a URL via ForensicsSchema, delegates to callPaid(walletProvider, '/api/v1/forensics', {url}).
@CreateAction({ name: "forensics", description: "Deep history report for one x402 endpoint: hourly uptime over 7 days, latency p50/p90/p99, status-code distribution, concentration-group stats (how crowded this provider's namespace is), and a decoy probability score [0, 1]. Costs $0.001 USDC. Superset of preflight — if you're running forensics you don't need preflight too.", schema: ForensicsSchema, }) async forensics( walletProvider: EvmWalletProvider, args: z.infer<typeof ForensicsSchema>, ): Promise<string> { return this.callPaid(walletProvider, "/api/v1/forensics", { url: args.url }); } - Input schema for the forensics tool. ForensicsSchema is defined as an alias for PreflightSchema — requires a single 'url' field validated as a URL string.
/** * Input schema for the `preflight` and `forensics` actions. */ export const PreflightSchema = z.object({ url: z .string() .url() .describe( "Full URL of the x402 endpoint the agent is about to pay (must be http(s)://, max 2048 chars).", ), }); export const ForensicsSchema = PreflightSchema; - src/index.ts:372-374 (schema)Inline input schema for the forensics tool in the MCP server registration. Requires a single 'url' field validated as a URL string.
inputSchema: { url: z.string().url().describe("The full URL of the x402 endpoint to analyse."), }, - src/index.ts:209-247 (helper)Helper function callPaid used by the forensics tool handler. Sends a POST request with x402 payment to the given path, parses JSON response, and provides clear error messages (including for nginx 502 case referenced in comment).
async function callPaid(path: string, body: unknown): Promise<string> { const f = payingFetch(); let res: Response; try { res = await f(`${BASE_URL}${path}`, { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify(body ?? {}), signal: AbortSignal.timeout(DEFAULT_TIMEOUT_MS), }); } catch (err) { const e = err as { name?: string }; if (e.name === "AbortError" || e.name === "TimeoutError") { throw new Error( `x402station ${path} timed out after ${DEFAULT_TIMEOUT_MS}ms`, ); } throw err; } const receipt = res.headers.get("x-payment-response"); // Read body as text first, then parse JSON ONLY when the response is // actually OK. Otherwise nginx 502/504 (Next.js down) would arrive as // an HTML body, res.json() would throw a SyntaxError, and the agent // would see "Unexpected token '<', '<!DOCTYPE'..." instead of a clear // "x402station /api/v1/forensics returned 502" message. Audit M-1. const raw = await res.text(); if (!res.ok) { const snippet = raw.length > 200 ? raw.slice(0, 200) + "…" : raw; // If the receipt header is set, settlement happened upstream — surface // that so the agent doesn't retry quickly and double-charge. The body // (if JSON-parseable) likely already carries `payment_settled: true` // for routes that adopted the audit-2026-04-28 pattern; we mention // the receipt presence on the error message itself for the older 503 // shapes too. Audit 2026-04-28 (Sonnet HIGH-2 on mcp-adapter). const settled = receipt ? " (PAYMENT SETTLED — do NOT retry quickly)" : ""; throw new Error(`x402station ${path} returned ${res.status}${settled}: ${snippet}`); } let data: unknown;