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.
94 lines • 4.75 kB
TypeScript
/**
* The bounded result store for the LOSSY surface.
*
* MCP is the lossy surface (docs/agent-response-design.md): a tool result is
* text in an agent's context window, so a listing that would be fine as JSON
* over HTTP is a context bomb here. The rule that follows is "truncate the
* VIEW, never the DATA":
*
* 1. the FULL result is persisted under an opaque `ref`,
* 2. the tool returns a bounded window plus `shown` / `total` — so the agent
* is TOLD it is looking at a slice, never left to infer it, and
* 3. `expand_result` is the affordance that fetches the rest.
*
* A tool that renders `items.slice(0, 20)` and says nothing is the failure this
* module exists to prevent: the agent reads twenty rows, concludes there are
* twenty, and acts on a number that is wrong.
*
* BOUNDS. The store is in-process (it dies with the server, which is correct —
* a ref is a handle on THIS session's answer, not a cache) and doubly bounded:
* at most {@link RESULT_STORE_MAX_ENTRIES} results are retained, oldest evicted
* first, and every entry expires {@link RESULT_STORE_TTL_MS} after it was
* stored. A long-lived agent host cannot grow this without limit. TTL runs from
* the STORE time and is not refreshed by reads, so a ref cannot be kept alive
* forever by polling it.
*
* SECRET-BEARING RESULTS ARE NEVER STORED. `secret: true` returns the bounded
* view with `ref: null` and writes nothing — enforced here, in code, not by
* convention at the call sites. Per docs/agent-response-design.md a
* secret-bearing response is never cached, never persisted into an
* agent-surface result store, and never logged; a one-shot credential (a
* maintenance lease's `holder_token`, a freshly minted key) that landed in a
* retained buffer would be readable for the next half hour by anything that
* could name its ref.
*/
/** How many results are retained at once. Oldest is evicted first. */
export declare const RESULT_STORE_MAX_ENTRIES = 32;
/** How long a stored result stays addressable, measured from the store time. */
export declare const RESULT_STORE_TTL_MS: number;
/** The default window a tool shows inline before pointing at `expand_result`. */
export declare const RESULT_STORE_DEFAULT_SHOWN = 20;
/** `expand_result`'s default page size when the caller does not ask for one. */
export declare const RESULT_STORE_DEFAULT_EXPAND_LIMIT = 100;
/** The ceiling on one `expand_result` window — the surface stays bounded even when expanded. */
export declare const RESULT_STORE_MAX_EXPAND_LIMIT = 1000;
/** What a tool returns inline: a window, honestly labelled, plus the handle to the rest. */
export interface StoredResultView<T> {
/** The handle `expand_result` takes. `null` when nothing was stored (a secret-bearing result). */
ref: string | null;
/** How many items are in {@link items} — the size of the VIEW. */
shown: number;
/** How many items exist in total — the size of the DATA. */
total: number;
items: T[];
}
/** One window of a previously stored result. */
export interface ExpandedResult {
ref: string;
/** What produced it, e.g. `vault_heads`. Lets the caller render it sensibly. */
kind: string;
offset: number;
shown: number;
total: number;
items: unknown[];
}
/**
* Persist the full result and return a bounded view of it.
*
* @param kind What produced this, e.g. `vault_heads`. Echoed by `expand_result`.
* @param items The COMPLETE list. Never pre-truncate before calling this.
* @param opts.shown Window size. Defaults to {@link RESULT_STORE_DEFAULT_SHOWN}.
* @param opts.secret `true` ⇒ store NOTHING and return `ref: null`. See the module header.
*/
export declare function storeResult<T>(kind: string, items: T[], opts?: {
shown?: number;
secret?: boolean;
}): StoredResultView<T>;
/**
* Read a window of a stored result.
*
* Returns `null` when the ref is unknown, already evicted, or expired — the
* three are deliberately indistinguishable, because the caller's recovery is
* the same in all three cases: re-run the tool that produced it.
*/
export declare function expandResult(ref: string, opts?: {
offset?: number;
limit?: number;
}): ExpandedResult | null;
/** Drop every retained result. Test-only, and the server's own reset if it ever needs one. */
export declare function _resetResultStore(): void;
/** Override the clock so TTL behaviour is testable without sleeping. Test-only; `null` restores it. */
export declare function _setResultStoreClock(clock: (() => number) | null): void;
/** How many results are currently retained. Test-only introspection. */
export declare function _resultStoreSize(): number;
//# sourceMappingURL=result-store.d.ts.map