agentfootprint
Version:
The explainable agent framework — backtrack a wrong answer to the exact context that caused it (evidence, not guesses). Built on footprintjs.
91 lines (90 loc) • 4.67 kB
TypeScript
/**
* toolList — every page of a server's `tools/list` answer, or a typed refusal
* that says the list is incomplete.
*
* Pattern: one bounded loop + one typed refusal.
* Role: MCP pages a tool list: a page carries `nextCursor` while the server
* has more, and the next page is asked for with `{ cursor }`. Reading
* only the first page cut a paginating server's catalogue short in
* silence — the agent saw part of it and nothing said so. This reads
* until the server sends no cursor, within a bound, and when it cannot
* finish the list it says why:
*
* - `'page-limit'` — the list did not end within {@link MAX_TOOL_LIST_PAGES}
* pages;
* - `'cursor-repeated'` — a page named a cursor this listing already sent, so
* following it would loop.
*
* The first page is asked exactly as before pagination was followed: no params
* (and, with no signal, no arguments at all). The client's signal rides every
* page in the SDK's trailing options, and it is checked immediately before each
* page is asked for and again when each answer arrives (`lib/abort.ts`): once
* it has fired, nothing more is asked for and no page is accepted, the last one
* included. An abort is the caller's word, so it wins over an incomplete list.
* What it cannot do is settle a page a connection that ignores the signal is
* still holding: that promise settles when the connection says.
*
* Every page request is counted from the moment it is sent until it settles
* (`listingGenerations.ts`): the MCP SDK's `Client` replaces its per-tool call
* checks with each answer, so a tool's checks depend on which answer is the
* latest (`callChecks.ts`).
*/
import type { Tool } from '../tools/tools.js';
import type { ListingGenerations } from './listingGenerations.js';
import type { McpConnection, McpListedTool } from './types.js';
/**
* The most `tools/list` requests one listing sends. A bound on REQUESTS, not on
* the catalogue's size: a page may hold any number of tools. A server that
* keeps paging past it is reported as incomplete rather than read without end.
*/
export declare const MAX_TOOL_LIST_PAGES = 100;
/** Why a server's tool list could not be read to its end. */
export type McpToolListIncompleteReason = 'page-limit' | 'cursor-repeated';
/** What a listing read: every tool its pages held, how many pages, and whether it ended. */
export interface ToolListing {
readonly tools: readonly McpListedTool[];
readonly pages: number;
/**
* For a list read in ONE answer, the generation that answer settled at: the
* MCP SDK's `Client` holds its tools' checks while it stays the latest
* (`listingGenerations.ts`). Absent for a paged list: no one answer holds all
* of its tools.
*/
readonly generation?: number;
/** Absent when the server's list ended: the last page carried no cursor. */
readonly incomplete?: McpToolListIncompleteReason;
}
/**
* Read the server's tool list, page by page, until a page carries no cursor —
* or until it cannot be finished, which the result says (`incomplete`).
*
* @param listings the connection's count of list requests, which every page
* request joins (`listingGenerations.ts`)
* @throws the abort, when `signal` has fired before a page is asked for or by
* the time its answer arrives; and whatever the connection rejects a page
* with, as it always did
*/
export declare function listEveryPage(connection: McpConnection, signal: AbortSignal | undefined, listings: ListingGenerations): Promise<ToolListing>;
/**
* A server's tool list that could not be read to its end. `tools()` and
* `refresh()` reject with it rather than return a list cut short: an agent
* handed part of a catalogue cannot know what it was not offered.
*
* `tools` holds what WAS listed — wrapped and callable, exactly as `tools()`
* would have returned them — for a caller that decides to use an incomplete
* list knowingly. Names the server, the reason and the count, never a cursor.
*/
export declare class McpToolListIncompleteError extends Error {
readonly reason: McpToolListIncompleteReason;
readonly server: string;
/** The pages read before the listing stopped. */
readonly pages: number;
/** The tools those pages listed, in page order. */
readonly tools: readonly Tool[];
readonly code: "ERR_MCP_TOOL_LIST_INCOMPLETE";
constructor(reason: McpToolListIncompleteReason, server: string,
/** The pages read before the listing stopped. */
pages: number,
/** The tools those pages listed, in page order. */
tools: readonly Tool[]);
}