UNPKG

run402-mcp

Version:

MCP server for Run402 — full-stack backend infrastructure for AI agents: Postgres, auth, storage, serverless functions and atomic deploys. Paid with x402/MPP. Includes $0.03 image generation.

77 lines • 3.64 kB
/** * `expand_result` — the other half of the lossy surface's contract. * * A tool that shows a window MUST hand back the affordance for the rest, or * "truncate the view, never the data" is just truncation with a nicer message. * This is that affordance: give it the `ref` a tool printed and it returns any * window of the FULL result the tool actually received. * * A secret-bearing result never has a ref (see `../result-store.js`), so it can * never be reached from here — that is enforced at store time, not by this tool * refusing to look. */ import { z } from "zod"; import { RESULT_STORE_DEFAULT_EXPAND_LIMIT, RESULT_STORE_MAX_ENTRIES, RESULT_STORE_MAX_EXPAND_LIMIT, RESULT_STORE_TTL_MS, expandResult, } from "../result-store.js"; import { errorResult } from "../structured.js"; const TTL_MINUTES = Math.round(RESULT_STORE_TTL_MS / 60_000); export const expandResultSchema = { ref: z .string() .describe("The opaque handle a tool printed alongside its bounded view (res_ followed by 16 hex). Refs are per-process and short-lived — do not store one across sessions."), offset: z .number() .int() .min(0) .optional() .describe("Index of the first item to return. Defaults to 0. Page by adding the previous window's shown count."), limit: z .number() .int() .min(1) .max(RESULT_STORE_MAX_EXPAND_LIMIT) .optional() .describe(`How many items to return, ${RESULT_STORE_DEFAULT_EXPAND_LIMIT} by default and at most ${RESULT_STORE_MAX_EXPAND_LIMIT}. The window stays bounded even when expanded — page with offset rather than asking for everything at once.`), }; export async function handleExpandResult(args) { const page = expandResult(args.ref, { ...(args.offset !== undefined ? { offset: args.offset } : {}), ...(args.limit !== undefined ? { limit: args.limit } : {}), }); if (!page) { return errorResult([ `No result is held under ref ${args.ref}.`, "", `Refs live in this server process only: they expire ${TTL_MINUTES} minutes after the tool ran, and only the ${RESULT_STORE_MAX_ENTRIES} most recent are kept.`, "Unknown, evicted and expired are deliberately the same answer, because the recovery is the same one: re-run the tool that produced the ref.", "A result that carried a secret never has a ref at all — it is not retained, so there is nothing here to expand.", ].join("\n"), { code: "RESULT_REF_NOT_FOUND", message: `No result is held under ref ${args.ref}.`, next_actions: [{ type: "rerun_tool", why: "Refs are per-process and expire; re-run the tool that produced the ref." }], }); } const end = page.offset + page.shown; const lines = [ `${page.ref} (${page.kind}) — items ${page.offset}..${Math.max(page.offset, end - 1)} of ${page.total}.`, ]; if (end < page.total) { lines.push(`${page.total - end} more: expand_result with ref ${page.ref} and offset ${end}.`); } else if (page.total > 0) { lines.push("This window reaches the end of the result."); } lines.push("", JSON.stringify(page.items, null, 2)); return { content: [{ type: "text", text: lines.join("\n") }], structuredContent: { status: "ok", ref: page.ref, kind: page.kind, offset: page.offset, shown: page.shown, total: page.total, items: page.items, }, }; } //# sourceMappingURL=expand-result.js.map