agentfootprint
Version:
The explainable agent framework — backtrack a wrong answer to the exact context that caused it (evidence, not guesses). Built on footprintjs.
79 lines (78 loc) • 4.21 kB
TypeScript
/**
* clientPort — what the MCP client pool asks of a client it opens, and what the
* client hands back beside the ordinary `McpClient`.
*
* Pattern: an internal seam, never on a door.
* Role: The pool's clients are ordinary `mcpClient`s (`mcpClient.ts`): their
* discovery, their per-tool checks (`callChecks.ts`, counted per
* client object by `listingGenerations.ts`), their retry series and
* their request ledger are the library's, unchanged. A pool needs
* four things more, and only a pool does, so they cross here:
*
* - a CONNECT signal apart from the client's lifetime signal: one open runs
* under the pool's own signal and open deadline, never a caller's;
* - the request guard of whatever the pool is doing on the client
* (`requestGuard.ts`), so every request is checked and counted;
* - the server's tools-changed notice, heard through the SDK's
* `listChanged.tools` with its automatic refresh OFF — a notice can only
* make the pool list again, through `listing`, which `mcpClient` counts;
* - a port back: a listing under the pool's own signal, a callable tool for a
* definition this client did not list (a shared one), and an ending whose
* settlement is reported apart from its answer.
*/
import type { Tool } from '../tools/tools.js';
import type { RequestGuard } from './requestGuard.js';
import type { Ending } from './terminate.js';
import type { McpListedTool } from './types.js';
import type { ThrottleFetch } from './throttleRetry.js';
/** @internal What a pool passes when it opens a client. */
export interface PooledClientSeam {
/** Bounds the connect (`initialize`) only: the pool's signal and its open deadline. */
readonly connectSignal: AbortSignal;
/**
* Wrap the transport's outermost `fetch`. `claim` finds the guard of the
* pooled tool call a request body belongs to (its ledger), `undefined` for
* any other request.
*/
readonly wrapFetch: (inner: ThrottleFetch, claim: (body: unknown) => RequestGuard | undefined) => ThrottleFetch;
/** The guard for a message that is not a pooled tool call — asked at the watched `send`. */
readonly guardFor: (message: unknown) => RequestGuard | undefined;
/** The server said its tool list changed. Called by the SDK; must not throw. */
readonly onToolsChanged: () => void;
}
/** @internal One listing, through the client's own discovery. */
export interface ClientListing {
/** The tools as `mcpClient` wraps them: each carries the checks of this listing. */
readonly tools: readonly Tool[];
/** The definitions as the server listed them, in the same order. */
readonly listed: readonly McpListedTool[];
}
/** @internal What a pooled client hands the pool. */
export interface ClientPort {
/** List every page through the client's own discovery, under `signal`. Replaces its cache. */
listing(signal: AbortSignal | undefined): Promise<ClientListing>;
/**
* A callable tool for `listed` on this connection that carries its OWN
* checks — for a definition this client did not list itself, so the SDK on
* this connection holds none for it.
*/
bind(listed: McpListedTool): Tool;
/** End the remote session and close: the answer by its deadline, the settlement apart. */
end(): Ending;
}
/**
* @internal What a pooled open that FAILED leaves behind: the server may have
* opened a session all the same (its `initialize` answer can land after the
* open was aborted), so the pool can still end it — once the open's work has
* settled, it asks `hasSession` and, if so, ends it under the ending rule.
*/
export interface HalfOpen {
/** Does the transport hold a session id the server issued — read when asked. */
hasSession(): boolean;
/** End that session, then close locally. */
end(): Ending;
}
/** @internal Remember what `error` (a failed pooled open's) left behind. */
export declare function rememberHalfOpen(error: unknown, halfOpen: HalfOpen): void;
/** @internal What a failed pooled open left behind, if anything. */
export declare function halfOpenOf(error: unknown): HalfOpen | undefined;