/**
 * The MCP connection pool of ONE run tree: the legs of a tree — the root run and every delegated child it spawns, background
 * and kept ones included — that declare the same server lease ONE connection (one client, one stdio process, one authorization,
 * one lock holder) instead of dialling a copy each.
 *
 * WHAT IS SHARED AND WHAT IS NOT. Only the connection is: the SDK client over its transport. Everything a leg binds to
 * itself stays the leg's own — the tool objects it mints around the client (their reminder-disclosure counters, their
 * revocation probe, their runId), its liveness record, its close announcer. A shared tool object would write into the account of a
 * parent leg that ended; a shared connection cannot.
 *
 * LIFETIME. A lease is released when its leg's materialization is disposed (the leg ended, failed, was cancelled or parked); the
 * last holder's release closes the connection. The pool itself holds no connection between leases and dies with the run that
 * created it: after `close()` a tree member that asks for a connection dials one of its own (a kept child resumed hours later,
 * a revived one), so the DECLARATION stays the one source of truth — no handle is ever recorded anywhere.
 *
 * THE KEY is the server's declaration as the dial reads it (name and transport) and the principal the task runs under: a
 * different principal is a different identity at the server. A server that opted in to elicitation never enters the pool — the
 * answer to its questions belongs to the leg that is waiting on it.
 */
import type { Client } from "@modelcontextprotocol/sdk/client/index.js";
import type { McpServerSpec } from "./mcp-server-spec.js";
/** The slice of a listed tool the SDK client caches per connection (its output validator, its task-support flags). */
export interface McpToolMeta {
    readonly name: string;
    readonly outputSchema?: unknown;
    readonly execution?: unknown;
}
/** Records one `tools/list` result on a pooled connection (see {@link McpLease.openListing}): the tools the leg mounted from it, of which
 *  `retained` names the ones the listing did not return (an incomplete walk keeps them mounted) — those join the membership only. */
export type McpListingWriter = (tools: ReadonlyArray<McpToolMeta>, apply: (all: McpToolMeta[]) => void, retained?: ReadonlySet<string>) => void;
/** One leg's hold on a pooled connection. */
export interface McpLease {
    readonly client: Client;
    /**
     * Open a `tools/list` round trip on the connection — call it BEFORE the request. The returned writer records the result twice over:
     * as the connection's metadata for each listed tool, at this listing's version (its place in the order listings were opened on the
     * connection: a listing opened earlier never overwrites what a later one wrote, whichever answer arrives first; an entry the listing
     * did not return — `retained` — writes no record), and as THIS leg's membership (the names it mounted). It then rewrites the client's per-connection tool cache with the current metadata of every name
     * some holder mounted: the SDK clears and refills that cache on each write, so a leg that mounted a subset must not erase what its
     * siblings' tools need. A release changes the membership only and rewrites the cache from the same metadata.
     */
    openListing(): McpListingWriter;
    /** Run `fn` when the connection closes under the lease (the server died, the transport dropped). Never called for this lease's own release. */
    onClose(fn: () => void): void;
    /** Give the hold back; the last holder's release closes the connection. Idempotent. */
    release(): Promise<void>;
}
export declare class McpConnectionPool {
    private readonly entries;
    private readonly dialing;
    /** Bumped by each {@link detach}: a dial that finishes after its key was detached serves the legs already waiting on it and publishes nothing. */
    private readonly generation;
    private shut;
    /**
     * The key a declaration leases under. `kind` is the dial's own single reading of the transport kind (the dial reads it once and threads it, so a
     * transport object that answers differently to two readers cannot split the dial from its key); every other transport member is read here.
     * The declaration's mount identity (`fingerprint`) is part of the key when declared: the revocation probe is asked with it, so two mounts the
     * host tells apart never share one connection (an undeclared fingerprint leaves the key as it always was).
     * So is the startup bound the dial runs under (`startupMs`, the dial's one reading of {@link mcpStartupTimeoutMs}, threaded like `kind`):
     * a connection is dialled under the declaration of the mount that dials it, so a mount never waits on, or shares, a dial bounded by
     * another mount's clock. No bound in force (none declared, no environment seat) leaves the key as it always was.
     */
    static keyOf(spec: McpServerSpec, principal: string | undefined, kind: string | undefined, startupMs: number | undefined): string;
    /**
     * Lease the connection for `key`, dialling it with `dial` when none is live. `fresh` replaces the pool's current connection with a new
     * dial (a re-dial): the connection it replaces lives on until its other holders release it. Resolves `undefined` once the pool is
     * closed — the caller dials for itself. A dial that rejects rejects every waiter of that dial, and the pool keeps nothing of it.
     */
    acquire(key: string, dial: () => Promise<Client>, opts?: {
        fresh?: boolean;
    }): Promise<McpLease | undefined>;
    /** Make the next lease of `key` dial a new connection. */
    detach(key: string): void;
    /** No lease is granted after this; the connections still held stay open until their holders release them. */
    close(): void;
    /** How many connections the pool holds open right now (for the lifecycle pins). */
    get live(): number;
    private dialNew;
    private lease;
}
