agentfootprint
Version:
The explainable agent framework — backtrack a wrong answer to the exact context that caused it (evidence, not guesses). Built on footprintjs.
184 lines • 9.39 kB
TypeScript
/**
* options — what `mcpClientPool` is built from, read and checked once.
*
* Pattern: one options type + one reader that refuses by name.
* Role: The pool's budgets and clocks (`docs/design/2026-10-mcp-client-pool.md`
* § 3.1). Every number is checked when the pool is built, never at the
* first call. The pool takes the `mcpClient` options that shape one
* connection and hands them to every client it opens; it never takes
* `connection`, because it must build the transport itself — to end
* the session, to hear the tools-changed notice, and to place the send
* gate.
*/
import type { MemoryIdentity } from '../../memory/identity/types.js';
import { type McpReplayOptions } from '../replay.js';
import type { RetryOnThrottle } from '../throttleRetry.js';
import type { McpJsonSchemaValidator, McpSdk, McpTransport } from '../types.js';
import type { McpPoolReport } from './record.js';
import type { PoolClientOpener } from './plan.js';
/** The identity a pool keys a client by: `{ tenant?, principal?, conversationId }`. */
export type McpPoolIdentity = MemoryIdentity;
/** What a conversation's tool list may be shared under: two names the app vouches for. */
export interface McpPoolShareContext {
/**
* What the transport hides in its code — a signer's role, the credentials of
* an AWS client, a custom `fetch`. Hashed into the context, never recorded.
*/
readonly configuration: string;
/**
* What the server's tool visibility depends on — a role and a policy version
* (`'analyst@policy-7'`). A version in the name reaches a new entry without
* `permissionsChanged`, for conversations that already have a client too:
* their tools are listed again under the new name, and a tool bound under the
* old one is refused before it is sent. Hashed into the context, never
* recorded.
*/
readonly authorization: string;
}
/**
* Share tool definitions between conversations — declared by the app, checked by
* the pool. `share` is asked for every conversation before anything opens, and
* again at every listing, every call and every send gate after that, so a
* changed answer takes effect at the next one; it must be synchronous and
* cheap. A listing's answer is kept only if the names still give the context it
* was listed for when it arrives — otherwise it is dropped and listed again. Return both names, or `undefined` to keep that conversation's list to
* itself. Conversations share a list only when the transport's comparable
* data, both names and the tenant are all equal. A `share` that throws refuses
* the open (`init-failed`) or, for a conversation that already has a client,
* that listing or call (`binding-changed`) — its error as `cause`. Never share
* a server whose list follows the session (one that unlocks tools as a
* conversation goes on).
*/
export interface McpPoolDefinitionSharing {
readonly share: (identity: McpPoolIdentity) => McpPoolShareContext | undefined;
}
/** Options for {@link mcpClientPool}. */
export interface McpClientPoolOptions {
/** The server's name: every tool's `source`, the provider's id, and the record. */
readonly name: string;
/**
* The transport each client is opened over. A FUNCTION of the identity is
* preferred: it is called before each client opens (and again for every
* replacement) with the identity the client is for, so each client gets a
* transport — and a session map — of its own, and a provider can vend THAT
* user's credential. One shared object serves every client; a shared
* `'reuse'` runtime transport then needs `maxClients + maxEnding` at or below
* its 256 remembered connections.
*/
readonly transport: McpTransport | ((identity: McpPoolIdentity) => McpTransport);
/** Open clients — opening, busy or idle. Default 64. */
readonly maxClients?: number;
/** Open clients for one principal (one tenant's principal). Default 8. */
readonly maxClientsPerPrincipal?: number;
/** Clients being ended at once, until each ending's work has settled. Default 8. */
readonly maxEnding?: number;
/**
* Opens, listings, calls and endings outstanding on any client, each from
* its admission until its work has settled — not merely until its caller was
* answered. Default 256. Size it above `maxEnding`.
*/
readonly maxOperations?: number;
/** One open per conversation, under this ceiling. Default 30 000 ms. */
readonly initTimeoutMs?: number;
/**
* End a client idle this long, at the next pool interaction (no timer is
* installed). Default 600 000 ms — below AgentCore Runtime's 900 s idle
* timeout; set it lower for a runtime configured lower.
*/
readonly idleMs?: number;
/** An app lease (`acquire`) is revoked past this. Default 900 000 ms (15 minutes). */
readonly maxLeaseMs?: number;
/** A tool list is listed again after this, at the next list. Default 600 000 ms. */
readonly definitionTtlMs?: number;
/** `'per-client'` (the default), or the app's declared sharing contract. */
readonly definitions?: 'per-client' | McpPoolDefinitionSharing;
/** Refuse an identity without a `principal`. Default `true`. */
readonly requirePrincipal?: boolean;
/** Refuse an identity without a `tenant` — for multitenant hosts. Default `false`. */
readonly requireTenant?: boolean;
/**
* What happens outside a run — endings, deferred endings, revoked leases,
* work that settled after its deadline. Ids and outcomes only. A throwing
* handler is swallowed and changes nothing.
*/
readonly onEvent?: (report: McpPoolReport) => void;
/** Passed to every client: how a tool's result is decoded (`McpClientOptions.resultMode`). */
readonly resultMode?: 'text' | 'structured' | 'structured-or-json';
/** Passed to every client: the `initialize` identity. */
readonly clientInfo?: {
readonly name: string;
readonly version: string;
};
/** Passed to every client: the 429 retry (`McpClientOptions.retryOnThrottle`). */
readonly retryOnThrottle?: RetryOnThrottle;
/**
* Passed to every client: who may send a tool call again
* (`McpClientOptions.replay`). Read and checked when the pool is built. The
* default stays `'compatibility'`. Whatever the policy, the pool's own send
* gate refuses a pooled request it cannot check (`request-unmatched`).
*/
readonly replay?: McpReplayOptions;
/** Passed to every client: the two SDK modules, imported by you (`McpClientOptions.sdk`). */
readonly sdk?: McpSdk;
/**
* Passed to every client: the JSON Schema validator a tool's own output checks
* use (`McpClientOptions.jsonSchemaValidator`). With `sdk`, give it: a shared
* definition always runs its own checks, and without a validator a tool that
* declares an `outputSchema` is refused unsent.
*/
readonly jsonSchemaValidator?: McpJsonSchemaValidator;
/** Clock seam — tests drive idle, lease and definition ages without waiting. */
readonly now?: () => number;
/** @internal Test seam: how a client is opened (the real path is `openPooledClient`). */
readonly _open?: PoolClientOpener;
}
/** The defaults, named so a test and a docstring cannot drift from the code. */
export declare const MCP_POOL_DEFAULTS: Readonly<{
maxClients: 64;
maxClientsPerPrincipal: 8;
maxEnding: 8;
maxOperations: 256;
initTimeoutMs: 30000;
idleMs: 600000;
maxLeaseMs: 900000;
definitionTtlMs: 600000;
}>;
/** At most this many shared definition entries are kept; the least recently used goes first. */
export declare const MAX_DEFINITION_ENTRIES = 256;
/** A list longer than this is used by its own client and never shared. */
export declare const MAX_SHARED_TOOLS = 512;
/** The pool's settings, read once. */
export interface PoolSettings {
readonly name: string;
readonly transport: (identity: McpPoolIdentity) => McpTransport;
readonly maxClients: number;
readonly maxClientsPerPrincipal: number;
readonly maxEnding: number;
readonly maxOperations: number;
readonly initTimeoutMs: number;
readonly idleMs: number;
readonly maxLeaseMs: number;
readonly definitionTtlMs: number;
readonly share: McpPoolDefinitionSharing['share'] | undefined;
readonly requirePrincipal: boolean;
readonly requireTenant: boolean;
readonly onEvent: ((report: McpPoolReport) => void) | undefined;
readonly client: {
readonly resultMode?: McpClientPoolOptions['resultMode'];
readonly clientInfo?: McpClientPoolOptions['clientInfo'];
readonly retryOnThrottle?: RetryOnThrottle;
readonly replay?: McpReplayOptions;
readonly sdk?: McpSdk;
readonly jsonSchemaValidator?: McpJsonSchemaValidator;
};
readonly now: () => number;
readonly open: PoolClientOpener | undefined;
}
/**
* Read `options` once.
*
* @throws TypeError naming the option, when one is missing, mistyped or out of
* range, or when `connection` is given — here, when the pool is built.
*/
export declare function readPoolOptions(options: McpClientPoolOptions): PoolSettings;
//# sourceMappingURL=options.d.ts.map