import { ModelMessage, UserModelMessage, AssistantModelMessage, ToolModelMessage, StopCondition, ToolSet, TimeoutConfiguration, dynamicTool } from 'ai'; import { H as HostJson, f as HostComputerInput, g as HostSkillSelection, a as HostConnectionDefaults, c as HostMcp, d as HostServerOverride, b as HostInit } from './public-types-CX8stXC3.js'; import { av as ManagedMcpClientNotificationMethod, aw as ManagedMcpClientNotificationHandler, C as ClientCapabilityOptions, ax as ManagedMcpClient, g as MCPClientManagerConfig, ah as MCPClientManagerOptions, S as ServerSummary, h as MCPConnectionStatus, M as MCPServerConfig, ay as ClientRequestOptions, L as ListToolsResult, a6 as Tool, a7 as AiSdkTool, E as ExecuteToolArguments, G as TaskOptions, ae as ExecuteToolRequest, az as ListResourcesParams, aA as ReadResourceParams, aB as SubscribeResourceParams, aC as UnsubscribeResourceParams, aD as ListResourceTemplatesParams, aE as ListPromptsParams, aF as GetPromptParams, ad as ElicitationHandler, ab as ElicitationCallback } from './types-CI0Xyszt.js'; import { ServerCapabilities, InputRequests, CallToolResult, InputResponses, InputRequiredResult, StandardSchemaV1, RequestOptions, Client, LoggingLevel, ElicitResult } from '@modelcontextprotocol/client'; import { d as ModelVisibleMcpToolResults, c as HostStyleId, e as McpToolResultImageRendering, f as Harness, S as ServerId } from './types-HXAijHji.js'; import { z } from 'zod'; /** * Core types for SDK evals functionality */ type CoreMessage = ModelMessage; type CoreUserMessage = UserModelMessage; type CoreAssistantMessage = AssistantModelMessage; type CoreToolMessage = ToolModelMessage; /** * Built-in LLM providers with native SDK support */ type LLMProvider = "anthropic" | "openai" | "azure" | "bedrock" | "deepseek" | "google" | "ollama" | "mistral" | "openrouter" | "xai"; /** * Compatible API protocols for custom providers */ type CompatibleProtocol = "openai-compatible" | "anthropic-compatible"; /** * Configuration for a custom provider (user-defined) */ interface CustomProvider { /** Unique name for this provider (used in model strings, e.g., "groq/llama-3") */ name: string; /** API protocol this provider is compatible with */ protocol: CompatibleProtocol; /** Base URL for the API endpoint */ baseUrl: string; /** List of available model IDs */ modelIds: string[]; /** Optional API key (can also be provided at runtime) */ apiKey?: string; /** Environment variable name to read API key from (fallback) */ apiKeyEnvVar?: string; /** * Use Chat Completions API (.chat()) instead of default. * Required for some OpenAI-compatible providers like LiteLLM. * Only applies to openai-compatible protocol. */ useChatCompletions?: boolean; } /** * Configuration for an LLM */ interface LLMConfig { provider: LLMProvider; model: string; apiKey: string; } /** * Represents a tool call made by the LLM */ interface ToolCall { toolName: string; arguments: Record; } /** * Token usage statistics */ interface TokenUsage { inputTokens: number; outputTokens: number; totalTokens: number; } /** * Latency breakdown for prompt execution */ interface LatencyBreakdown { /** Total wall-clock time in milliseconds */ e2eMs: number; /** LLM API time in milliseconds */ llmMs: number; /** MCP tool execution time in milliseconds */ mcpMs: number; } /** * Raw prompt result data (used internally) */ interface PromptResultData { /** The original prompt/query that was sent */ prompt: string; /** The full conversation history (user, assistant, tool messages) */ messages: ModelMessage[]; text: string; toolCalls: ToolCall[]; usage: TokenUsage; latency: LatencyBreakdown; error?: string; /** LLM provider name (e.g., "openai", "anthropic") */ provider?: string; /** LLM model name (e.g., "gpt-4o", "claude-3-5-sonnet-20241022") */ model?: string; /** Persisted widget snapshots captured during MCP App tool execution */ widgetSnapshots?: EvalWidgetSnapshotInput[]; /** Timeline spans for eval trace visualization (relative to prompt start) */ spans?: EvalTraceSpanInput[]; } /** * PromptResult class - wraps the result of a HostRunner prompt */ /** * Represents the result of a HostRunner prompt. * Provides convenient methods to inspect tool calls, token usage, and errors. */ declare class PromptResult { /** The original prompt/query that was sent */ readonly prompt: string; /** The text response from the LLM */ readonly text: string; /** The full conversation history */ private readonly _messages; /** Latency breakdown (e2e, llm, mcp) */ private readonly _latency; /** Tool calls made during the prompt */ private readonly _toolCalls; /** Token usage statistics */ private readonly _usage; /** Widget snapshots captured during tool execution */ private readonly _widgetSnapshots; /** Error message if the prompt failed */ private readonly _error?; /** LLM provider name (e.g., "openai", "anthropic") */ private readonly _provider?; /** LLM model name (e.g., "gpt-4o", "claude-3-5-sonnet-20241022") */ private readonly _model?; /** Timeline spans (times relative to prompt run start) */ private readonly _spans; /** * Create a new PromptResult * @param data - The raw prompt result data */ constructor(data: PromptResultData); /** * Get the original query/prompt that was sent. * * @returns The original prompt string */ getPrompt(): string; /** * Get the full conversation history (user, assistant, tool messages). * Returns a copy to prevent external modification. * * @returns Array of CoreMessage objects */ getMessages(): CoreMessage[]; /** * Get only user messages from the conversation. * * @returns Array of CoreUserMessage objects */ getUserMessages(): CoreUserMessage[]; /** * Get only assistant messages from the conversation. * * @returns Array of CoreAssistantMessage objects */ getAssistantMessages(): CoreAssistantMessage[]; /** * Get only tool result messages from the conversation. * * @returns Array of CoreToolMessage objects */ getToolMessages(): CoreToolMessage[]; /** * Get the end-to-end latency in milliseconds. * This is the total wall-clock time for the prompt. * * @returns End-to-end latency in milliseconds */ e2eLatencyMs(): number; /** * Get the LLM API latency in milliseconds. * This is the time spent waiting for LLM responses (excluding tool execution). * * @returns LLM latency in milliseconds */ llmLatencyMs(): number; /** * Get the MCP tool execution latency in milliseconds. * This is the time spent executing MCP tools. * * @returns MCP tool latency in milliseconds */ mcpLatencyMs(): number; /** * Get the full latency breakdown. * * @returns LatencyBreakdown object with e2eMs, llmMs, and mcpMs */ getLatency(): LatencyBreakdown; /** * Get the names of all tools that were called during this prompt. * Returns a standard string[] that can be used with .includes(). * * @returns Array of tool names */ toolsCalled(): string[]; /** * Check if a specific tool was called during this prompt. * Case-sensitive exact match. * * @param toolName - The name of the tool to check for * @returns true if the tool was called */ hasToolCall(toolName: string): boolean; /** * Get all tool calls with their arguments. * * @returns Array of ToolCall objects */ getToolCalls(): ToolCall[]; /** * Get the arguments passed to a specific tool call. * Returns undefined if the tool was not called. * If the tool was called multiple times, returns the first call's arguments. * * @param toolName - The name of the tool * @returns The arguments object or undefined */ getToolArguments(toolName: string): Record | undefined; /** * Get the total number of tokens used. * * @returns Total tokens (input + output) */ totalTokens(): number; /** * Get the number of input tokens used. * * @returns Input token count */ inputTokens(): number; /** * Get the number of output tokens used. * * @returns Output token count */ outputTokens(): number; /** * Get the full token usage statistics. * * @returns TokenUsage object */ getUsage(): TokenUsage; /** * Get widget snapshots captured during tool execution. */ getWidgetSnapshots(): EvalWidgetSnapshotInput[]; /** * Timeline spans for this prompt (e.g. step / llm / tool / error), if captured. */ getSpans(): EvalTraceSpanInput[]; /** * Check if this prompt resulted in an error. * * @returns true if there was an error */ hasError(): boolean; /** * Get the error message if the prompt failed. * * @returns The error message or undefined */ getError(): string | undefined; /** * Get the LLM provider name. * * @returns The provider name or undefined */ getProvider(): string | undefined; /** * Get the LLM model name. * * @returns The model name or undefined */ getModel(): string | undefined; /** * Create a PromptResult from raw data. * Factory method for convenience. * * @param data - The raw prompt result data * @returns A new PromptResult instance */ static from(data: PromptResultData): PromptResult; /** * Create an error PromptResult. * Factory method for error cases. * * @param error - The error message * @param latency - The latency breakdown or e2e time in milliseconds * @returns A new PromptResult instance with error state */ static error(error: string, latency?: LatencyBreakdown | number, prompt?: string, metadata?: { provider?: string; model?: string; spans?: EvalTraceSpanInput[]; }): PromptResult; /** * Format the conversation trace as a JSON string. * Useful for debugging failed evaluations. * * @returns A JSON string of the conversation messages */ formatTrace(): string; /** * Convert this prompt result into an EvalResultInput for report ingestion. */ toEvalResult(options?: Partial> & { failOnToolError?: boolean; }): EvalResultInput; } /** * Options for the run() method */ interface PromptOptions { /** Previous PromptResult(s) to include as conversation context for multi-turn conversations */ context?: PromptResult | PromptResult[]; /** Optional abort signal for cancelling the prompt runtime. */ abortSignal?: AbortSignal; /** * Additional stop conditions for the agentic loop. * Evaluated after each step completes (tools execute normally). * `stepCountIs(maxSteps)` is always applied as a safety guard * in addition to any conditions provided here. * * Import helpers like `hasToolCall` and `stepCountIs` from `"@mcpjam/sdk"`. * * @example * ```typescript * import { hasToolCall } from "@mcpjam/sdk"; * * // Stop the loop after the step where "search_tasks" is called * const result = await executor.run("Find my tasks", { * stopWhen: hasToolCall("search_tasks"), * }); * expect(result.hasToolCall("search_tasks")).toBe(true); * * // Multiple conditions (any one being true stops the loop) * const result = await executor.run("Do something", { * stopWhen: [hasToolCall("tool_a"), hasToolCall("tool_b")], * }); * ``` */ stopWhen?: StopCondition | Array>; /** * Timeout for the prompt runtime. * * - `number`: total timeout for the entire prompt call in milliseconds * - `{ totalMs }`: total timeout across all steps * - `{ stepMs }`: timeout for each generation step * - `{ chunkMs }`: accepted for parity and primarily relevant to streaming APIs * * The runtime creates an internal abort signal. Tools can stop early if they * respect the `abortSignal` passed to `execute()`. */ timeout?: TimeoutConfiguration; /** Shortcut for a total prompt timeout in milliseconds. */ timeoutMs?: number; /** * Stop the prompt loop after the step where one of these tools is called and * short-circuit that tool execution with a stub result. */ stopAfterToolCall?: string | string[]; } /** * Minimal executor interface that eval tests run against. Implemented by * `HostRunner` (sync, tools pre-resolved) and `HostRuntime` (live binding * to a manager). Use `HostRunner.mock()` for deterministic tests without * an unsafe `as unknown as HostRunner` cast. */ interface HostExecutor { run(message: string, options?: PromptOptions): Promise; withOptions(options: Record): HostExecutor; getPromptHistory(): PromptResult[]; resetPromptHistory(): void; /** * Returns the immutable `HostJson` snapshot driving this executor, if it * was constructed from a `Host`. Optional because callers can also build * an executor without supplying a `Host` (legacy explicit-config path). */ getHostSnapshot?(): HostJson | undefined; /** * Returns replay configs for the executor's attached MCP servers. * Optional — `HostRunner` and `HostRuntime` expose it when their * underlying manager supports it, so SDK eval uploads can infer * replay configs without callers manually copying them into * `mcpjam.serverReplayConfigs`. */ getServerReplayConfigs?(): MCPServerReplayConfig[] | undefined; } /** * `HostRuntime` — live binding of a `Host` to an `MCPClientManager`. * * `Host` is a pure, serializable spec; `HostRunner` is a sync executor with * tools pre-resolved. `HostRuntime` sits between them: it holds a live * `Host` reference plus a structural manager and, on every `.run(...)`, * snapshots the host, validates server ids against the manager, resolves * the active tool set, and delegates to a fresh `HostRunner` constructed * with that snapshot. * * Bundle safety: this module deliberately avoids static imports of * `HostRunner`, `ai`, or `MCPClientManager`. The runner is dynamically * imported inside `.run()` so the `host-config` browser bundle remains * free of the AI SDK and Node-only runtime dependencies. * * Stateless across turns: `.run()` calls do NOT auto-replay prior turns * into the next runner. Conversation continuity stays explicit through * `PromptOptions.context`. The runtime accumulates `PromptResult` history * for inspection/reporting only. */ /** * Structural shape of the AI SDK tool record returned by * `MCPClientManager.getToolsForAiSdk()`. Kept narrow so this module does * not import `ai` types. */ type AiSdkToolRecord = Record; /** * Structural shape of an MCP client manager from `HostRuntime`'s point of * view: server registry + tool resolution. Both `MCPClientManager` and * lightweight test fakes satisfy this without dragging the concrete class * into this bundle-safe module. */ type HostRuntimeManager = HostServerRegistry & { getToolsForAiSdk(serverIds?: string[] | string, options?: { includeAppOnly?: boolean; needsApproval?: boolean; modelVisibleMcpToolResults?: ModelVisibleMcpToolResults; }): Promise; /** * Optional. When the runtime is bound to a manager that exposes * replayable server configs (the concrete `MCPClientManager` does), * `HostRuntime.getServerReplayConfigs()` delegates here so SDK eval * uploads (`EvalTest`/`EvalSuite` -> `resolveServerReplayConfigs`) can * stamp them without callers manually copying configs into * `mcpjam.serverReplayConfigs`. */ getServerReplayConfigs?(): MCPServerReplayConfig[] | undefined; }; /** * Defaults bound at `HostRuntime` construction and applied to every `.run()`. * * `apiKey` is required because every `.run()` constructs a fresh `HostRunner` * and the runner requires it. The remaining fields override the corresponding * host-snapshot-derived values when set. */ interface HostRuntimeDefaults { apiKey: string; /** Overrides the host snapshot's model. */ model?: string; /** Overrides the host snapshot's systemPrompt. */ systemPrompt?: string; /** Overrides the host snapshot's temperature. */ temperature?: number; maxSteps?: number; customProviders?: Map | Record; /** Overrides the host-config-resolved OpenAI compat decision. */ injectOpenAiCompat?: boolean; } /** * Live binding of a `Host` to an `MCPClientManager`. Construct via * `host.withManager(manager, { apiKey })` or the explicit constructor. */ declare class HostRuntime implements HostExecutor { private readonly host; private readonly manager; private readonly defaults; private promptHistory; constructor(host: Host, manager: HostRuntimeManager, defaults: HostRuntimeDefaults); /** * Execute the host against `input`. Snapshots the live `Host` on every * call so mutations between `.run()` invocations are reflected. * * Stateless across turns: prior `PromptResult`s are recorded in * {@link getPromptHistory} for inspection but NOT auto-replayed. * Conversation continuity stays explicit — caller passes `context: r1` * via `options` for follow-up turns. */ run(input: string, options?: PromptOptions): Promise; /** * Return a new `HostRuntime` bound to the same `host` and `manager` with * `defaults` shallow-merged. The new runtime has its own (empty) prompt * history, matching `HostRunner.withOptions(...)` semantics. */ withOptions(options: Partial | Record): HostRuntime; getPromptHistory(): PromptResult[]; resetPromptHistory(): void; /** * Snapshot of the bound `Host` taken at call time. Useful for reporters * that want to stamp the current host config into per-iteration metadata. */ getHostSnapshot(): HostJson; /** * Delegate to the bound manager so SDK eval uploads * (`EvalTest`/`EvalSuite` -> `resolveServerReplayConfigs`) can infer * replay configs from the runtime, matching the `HostRunner` shape. * Returns `undefined` when the manager doesn't expose * `getServerReplayConfigs` (custom structural managers). */ getServerReplayConfigs(): MCPServerReplayConfig[] | undefined; } declare class Host { /** * Host style id (e.g. "mcpjam", "claude", "chatgpt"). **Required** at * `toJSON()` time — there is no SDK default, so an external agent author * who forgets to set it gets a loud error rather than silently inheriting * MCPJam product chrome on a Claude-style flow. * * Note: `style` is a **product knob**, not part of SEP-1865 / the MCP base * spec. It selects which host-style preset (chrome, default capabilities, * compat-runtime shims) the inspector applies. */ style: HostStyleId; /** * LLM model id, e.g. `"anthropic/claude-sonnet-4-6"`. **Required** at * `toJSON()` time — no SDK default. */ model: string; systemPrompt: string; temperature: number; requireToolApproval: boolean; /** Opt into progressive MCP tool discovery (search/load meta-tools). */ progressiveToolDiscovery?: boolean; /** * SEP-1865 `_meta.ui.visibility` filtering. `undefined` → spec default * (filter); explicit `false` → show every tool (for hosts that don't * implement visibility). */ respectToolVisibility?: boolean; /** Host policy for model visibility of MCP tool-result content/resources. */ modelVisibleMcpToolResults?: ModelVisibleMcpToolResults; /** Human-facing rendering policy for MCP tool-returned images. */ mcpToolResultImageRendering?: McpToolResultImageRendering; /** * Personal cloud workstation attached to this host (chat `bash` tool + * web terminal; one machine per project+user). `undefined` or `null` ⇒ * no computer — both serialize identically, so a cleared field hashes * the same as one never set. */ computer?: HostComputerInput | null; /** * Which harness runs the turn. `undefined` ⇒ emulated (MCPJam's own loop); * `"claude-code"` runs the turn in a real Claude Code runtime via the AI SDK * harness, which executes inside the host's attached `computer`. */ harness?: Harness; /** Required servers. Mutable — `requireServer`/`removeRequiredServer` are sugar. */ servers: ServerId[]; /** Optional (auto-connect-if-available) servers. */ optionalServers: ServerId[]; /** * Skill selection. `undefined` (or `{ mode: "all-visible" }`, * which normalizes to absent at `toJSON()`) ⇒ legacy all-visible behavior; * `{ mode: "explicit", skillIds }` restricts advertised skills * (`[]` = explicitly none — preserved, distinct from absent). */ skillSelection?: HostSkillSelection; connectionDefaults: HostConnectionDefaults; /** * MCP `ClientCapabilities` the inspector advertises in `initialize`. * Untyped (`Record`) so future spec additions and host * extensions (SEP-1724) can be added without an SDK release. The MCP Apps * extension lives at `clientCapabilities.extensions["io.modelcontextprotocol/ui"]`. */ clientCapabilities: Record; /** SEP-1865 `HostContext` (theme, displayMode, …) advertised via `ui/initialize`. */ hostContext: Record; /** * Override the SEP-1865 MCP-Apps `hostCapabilities` blob the inspector * advertises in `ui/initialize`. * * Semantics: `undefined` = "use the host-style preset" (the inspector * picks a sensible default per style). `{}` = "advertise nothing" * (distinct from `undefined`; hashes distinctly). Use this to *cap* what * a host advertises — e.g. force `serverTools: undefined` to block widget * → server tool proxying for a hardened host. */ hostCapabilitiesOverride?: Record; /** Override the chat-UI surface (logo, fonts, …). `undefined` vs `{}` semantics match `hostCapabilitiesOverride`. */ chatUiOverride?: Record; /** * The host's MCP settings. * * Spec-aligned shape (`protocolVersion`, `initialize`, `apps`, `extensions`) * is **mutable in place** — assign or mutate any leaf directly: * * ```ts * host.mcp.protocolVersion = "2025-11-25"; * host.mcp.initialize = { clientInfo: { name: "my-app", version: "1.0" } }; * host.mcp.apps = { sandbox: { csp: { mode: "declared" } } }; * ``` * * The SEP-1865 sandbox knobs at `mcp.apps.sandbox.csp` / `.permissions` are * **host enforcement caps**, not capability grants to the widget. The * widget *declares* what it needs via its resource metadata; the host MAY * further restrict but MUST NOT loosen (`restrictTo` intersects the * declared set; `mode: "declared"` honors the declared set as-is). * * A "freshly constructed, untouched" `host.mcp` (all fields undefined) * collapses to no `mcpProfile` in canonical, so it hashes identically to * `host.mcp = undefined`. */ mcp: HostMcp; /** Per-server connection overrides keyed by server id. */ serverOverrides: Record; constructor(init?: HostInit); setStyle(style: HostStyleId): this; setModel(model: string): this; setSystemPrompt(systemPrompt: string): this; setTemperature(temperature: number): this; setRequireToolApproval(require?: boolean): this; setProgressiveToolDiscovery(enabled: boolean): this; setRespectToolVisibility(respect: boolean): this; setModelVisibleMcpToolResults(policy: ModelVisibleMcpToolResults | undefined): this; setMcpToolResultImageRendering(rendering: McpToolResultImageRendering): this; /** * Attach a personal computer (the resource; grant capabilities on it via * `builtInToolIds`, e.g. `"bash"`), or pass `null` to detach. */ setComputer(computer?: HostComputerInput | null): this; /** * Mark a server id as required for this host; no-op if `id` is already in * the list. Required ids must resolve to a known server at execution * time; see {@link assertHostServersKnown}. */ requireServer(id: ServerId): this; /** Drop a required server id; no-op if absent. */ removeRequiredServer(id: ServerId): this; /** Append an optional (auto-connect-if-available) server; deduped. */ addOptionalServer(id: ServerId): this; removeOptionalServer(id: ServerId): this; /** * Replace the per-server override for `id`. Passing an empty `{}` is * preserved here but stripped from the canonical output (an override with * no fields adds no information). */ setServerOverride(id: ServerId, override: HostServerOverride): this; removeServerOverride(id: ServerId): this; /** * Reset `mcp` to an empty object — equivalent to "use SDK defaults / no * host-level MCP profile." Collapses to no `mcpProfile` in canonical. */ clearMcp(): this; /** * Throw a clear error if required fields (`style`, `model`) are still empty. * Called from `toJSON()` so the failure lands at the moment of use, not * deep inside the canonicalizer with a less obvious message. */ private requireConfigured; /** * Build the internal canonicalizer input from the current public-shape * properties. Snapshotted (`structuredClone`) so the canonicalizer sees a * stable copy even if the caller is mid-mutation. */ private toInternalInput; /** * Serialize to the normalized public `HostJson` shape (clean MCP * vocabulary — `mcp`, `servers`, `style`; no `mcpProfile`/`schemaVersion`). * Normalized and round-trippable: `new Host(host.toJSON())` reproduces an * equivalent host. Throws if the configuration is invalid (e.g. * `style`/`model` not set, a non-finite temperature, or a malformed MCP * profile). */ toJSON(): HostJson; /** * Bind this `Host` to a live MCP client manager and return a `HostRuntime` * — the ergonomic execution surface for hosts. * * `defaults.apiKey` is required because every `.run()` constructs a fresh * runner internally. The remaining fields override host-snapshot-derived * values (model / systemPrompt / temperature / injectOpenAiCompat) per call. * * The runtime holds a live reference to this `Host`, so mutations made * between `.run()` invocations (e.g. `host.requireServer(...)`) are * reflected on the next run. `.run()` snapshots the host each time. */ withManager(manager: HostRuntimeManager, defaults: HostRuntimeDefaults): HostRuntime; /** * One-shot convenience: bind a manager, run once, discard the runtime. * * Equivalent to `host.withManager(mcpClientManager, rest).run(input)`. * The throwaway runtime carries no prompt history across calls — each * `host.run(...)` starts fresh. For multi-turn or accumulating * inspection state, prefer `host.withManager(...)` and reuse the runtime. */ run(input: string, runtime: HostRuntimeDefaults & { mcpClientManager: HostRuntimeManager; }): Promise; } /** * Accepted shapes anywhere `HostRunner` / `HostRuntime` take a host: * * - `Host` — a live, mutable builder (snapshotted via `.toJSON()`). * - `HostInit` — the constructor-init shape (instantiated then snapshotted). * - `HostJson` — an already-snapshotted, immutable value (passed through). */ type HostSource = Host | HostInit | HostJson; /** * Structural predicate for "is this already a `HostJson` snapshot?" * * Used by {@link snapshotHostSource} so callers that already snapshotted * (e.g. `HostRuntime.run()` calling `this.host.toJSON()` once per turn) * can pass the result straight through without double-snapshotting. Avoids * `instanceof Host` for the positive branch so a snapshot can safely cross * bundle / package boundaries. * * Explicitly rejects `Host` instances so a configured `Host` (whose * `style`/`model`/`servers` properties also satisfy the shape) takes the * `.toJSON()` path. */ declare function isHostJson(value: unknown): value is HostJson; /** * Normalize any `HostSource` to an immutable `HostJson` snapshot. Idempotent * for current snapshots. */ declare function snapshotHostSource(host: HostSource): HostJson; /** * Minimal structural shape for "something that knows which server ids exist * at runtime." Both `MCPClientManager` and lightweight test fakes satisfy * this without dragging the concrete class into the bundle-safe * `host-config` module. * * `listServers` is optional but enables a better error message when the * registry can enumerate its ids cheaply. */ type HostServerRegistry = { hasServer(id: string): boolean; listServers?(): string[]; }; /** * Validate that every required server id in `host.servers` exists in the * registry. Unknown required ids throw before tool resolution; unknown * `optionalServers` are silently skipped (they are "auto-connect-if-available" * by contract). A known server that returns zero tools is NOT a validation * failure — that's a legitimate tool-less server. */ declare function assertHostServersKnown(host: HostJson, registry: HostServerRegistry): void; /** * Return the subset of `host.servers` + `host.optionalServers` that the * registry actually knows about. Caller is responsible for asserting * required ids first; this only filters. */ declare function resolveKnownServerIds(host: HostJson, registry: HostServerRegistry): string[]; /** * Eval tool-call matchers. * * This module is browser-safe and intentionally has no node-only deps so * it can be imported into client bundles via `@mcpjam/sdk/matchers`. * * `evaluateToolCalls` is a richer, configurable replacement for the * existing `matchToolCalls(expected: string[], actual: string[]): boolean` * in `./validators`. The simple boolean API in `./validators` stays * unchanged for SDK consumers that already depend on it. */ type EvalToolCall = ToolCall; type EvalArgumentMismatch = { toolName: string; expectedArgs: Record; actualArgs: Record; }; type EvalOutOfOrderToolCall = { toolName: string; expectedIndex: number; actualIndex: number; }; type EvalMatchOptions = { /** * Trajectory pairing mode for actual vs. expected tool calls. * * `"ignore"` (default) — order-agnostic greedy pairing: for each expected * call in iteration order, the matcher consumes the earliest still-unmatched * actual call whose tool name + arguments are compatible. Out-of-order * pairings are never flagged. * * `"strict"` — index-aligned positional match. expected[i] is paired with * actual[i] if compatible; otherwise expected[i] is missing. Any extras * (j ≥ |expected| or mismatched at index j) are unconsumed actuals. * * `"superset"` — greedy left-to-right consume. The cursor walks forward * through actual once; for each expected call in iteration order, the * matcher advances the cursor until it finds a compatible actual call. * Useful for "the agent must perform these steps in order, but extra * unrelated steps interleaved are fine." */ toolCallOrder?: "ignore" | "strict" | "superset"; /** * Bound on extra actual tool calls beyond what was paired with expected. * * `null` (default) — extras allowed without bound (previous * `allowExtraToolCalls: true` behavior). * * `0` — strict, no extras allowed (previous `allowExtraToolCalls: false`). * * `N > 0` (must be a non-negative integer) — up to N extras allowed. * * Evaluated **independently** of `toolCallOrder`: extras = |actual| − |matched|. */ maxExtraToolCalls?: number | null; /** * LEGACY: prefer `maxExtraToolCalls`. When present without * `maxExtraToolCalls`, the matcher entry shims `true → null`, `false → 0`. * Remove after v. */ allowExtraToolCalls?: boolean; /** * `"partial"` (default) — only expected keys are checked; actual may * carry extra keys; empty expected args match anything; placeholder * strings like `"string"`, `"number"`, `"any"` are interpreted as type * checks (matches current inspector behavior). * * `"exact"` — deep equality on the args object; no extras allowed; no * placeholders. * * `"ignore"` — args are not compared. */ argumentMatching?: "exact" | "partial" | "ignore"; }; type EvalToolCallMatchResult = { missing: EvalToolCall[]; extra: EvalToolCall[]; outOfOrder: EvalOutOfOrderToolCall[]; argumentMismatches: EvalArgumentMismatch[]; passed: boolean; }; /** * Canonical defaults for {@link EvalMatchOptions}. Exported so the * inspector server, client, and tests share a single source of truth * with `evaluateToolCalls` instead of redefining the same literals. * * `maxExtraToolCalls: null` preserves the previous `allowExtraToolCalls: true` * behavior: extras are reported in `extra[]` but never fail the test by * themselves. */ declare const MATCH_OPTIONS_DEFAULTS: Required>; /** * Merge match options from suite → case → run-override layers on top of * defaults. `undefined` fields inherit from the next layer; explicit * values win at their layer. Returns a fully-populated options object * suitable to snapshot or pass directly to `evaluateToolCalls`. * * Legacy `allowExtraToolCalls` on any layer is shimmed to * `maxExtraToolCalls` (`true → null`, `false → 0`). An explicit * `maxExtraToolCalls` on the same layer wins. */ declare function resolveMatchOptions(suite?: EvalMatchOptions, testCase?: EvalMatchOptions, runOverride?: EvalMatchOptions): Required>; /** Validate the complete resolved matcher contract at an authoring boundary. */ declare function assertValidMatchOptions(options: EvalMatchOptions): void; /** * Validate `maxExtraToolCalls`. Throws on values v8/JSON would accept as * a number but the matcher cannot honor (negative, fractional, NaN, * Infinity). Called from the matcher entry point; UI / mutation layers * should reject earlier with their own error type. */ declare function assertValidMaxExtra(value: number | null | undefined): void; /** * Evaluate actual tool calls against expected. * * Defaults preserve today's inspector behavior precisely: * - `toolCallOrder: "ignore"` (order does not matter) * - `maxExtraToolCalls: null` (extras reported but non-fatal) * - `argumentMatching: "partial"` (placeholders, empty-expected matches * anything, extras allowed on actual args) * * Pass `isNegativeTest: true` to flip the test: it then passes iff *no* * tool calls were made. */ declare function evaluateToolCalls(expected: EvalToolCall[], actual: EvalToolCall[], options?: EvalMatchOptions & { isNegativeTest?: boolean; }): EvalToolCallMatchResult; /** * The Wave-0 vocabulary for the user-value chain. * * This module is browser-safe and intentionally has no node-only deps. * * These are ENUMS ONLY — the words every downstream surface agrees to use. * Deriving a stage's state from a run, or the row shape that derivation * produces, is deliberately NOT here: pinning the vocabulary is Wave 0's job, * pinning the derivation output belongs to whoever writes the derivation. What * matters now is that the mirroring Convex validators, the reporting surfaces * and the importers all spell these the same way. * * Each list is a `const` array plus a derived type plus a zod enum, the same * pattern `TEST_STEP_KINDS` uses, so a new member cannot be added in one place * and forgotten in another. */ /** * The user-value chain, in CHAIN ORDER. * * **The array order is normative and must never be reordered or sorted.** A run * walks these stages in sequence, and "not reached" is derived from POSITION: * every stage after the first failed one was never reached. Sorting this array * alphabetically — or inserting a stage in the wrong slot — silently changes * which stages a failure is reported to have blocked. * * - `connection` — the server was reachable and the session initialized. * - `discovery` — its tools/resources were listed and readable. * - `selection` — the model chose the right tool for the request. * - `call` — the call was made with usable arguments. * - `response` — the server returned data the model could use. * - `userValue` — the user's actual request was satisfied. */ declare const USER_VALUE_STAGES: readonly ["connection", "discovery", "selection", "call", "response", "userValue"]; type UserValueStage = (typeof USER_VALUE_STAGES)[number]; declare const userValueStageSchema: z.ZodEnum<{ response: "response"; call: "call"; discovery: "discovery"; connection: "connection"; selection: "selection"; userValue: "userValue"; }>; /** * What a stage did, for one run. * * These are STRINGS on the wire and in storage. No numeric encoding is exported * from here, and none should be introduced: an ordinal encoding invites * comparison (`state > 0`), and `notMeasured` vs `notApplicable` vs `notReached` * are not points on a scale — they are three different reasons there is no * verdict, and collapsing them is how "we never checked" gets rendered as * "it passed". * * - `passed` — measured, and it worked. * - `failed` — measured, and it did not. * - `notReached` — an earlier stage failed, so this one never ran. * - `notMeasured` — this run captured nothing that could decide it. * - `notApplicable` — the stage does not apply to this case at all. */ declare const STAGE_STATES: readonly ["passed", "failed", "notReached", "notMeasured", "notApplicable"]; type StageState = (typeof STAGE_STATES)[number]; declare const stageStateSchema: z.ZodEnum<{ failed: "failed"; passed: "passed"; notReached: "notReached"; notMeasured: "notMeasured"; notApplicable: "notApplicable"; }>; /** * Where the blame for a failure sits — the coarse bucket a failing run is * grouped under when someone asks "what is actually broken?". * * - `setup` — the harness/environment never got to the test. * - `metadata` — tool names, descriptions or schemas misled the model. * - `selection` — the model picked the wrong tool (or none). * - `arguments` — the right tool, called wrongly. * - `serverData` — the server answered, but with unusable data. * - `userValue` — everything mechanical worked; the user still wasn't served. * - `evaluator` — the grader itself failed, so the run says nothing about * the server. Never folded into the others: a broken judge is not a * server defect, and counting it as one poisons every rate derived from it. */ declare const FAILURE_CATEGORIES: readonly ["setup", "metadata", "selection", "arguments", "serverData", "userValue", "evaluator"]; type FailureCategory = (typeof FAILURE_CATEGORIES)[number]; declare const failureCategorySchema: z.ZodEnum<{ arguments: "arguments"; metadata: "metadata"; selection: "selection"; userValue: "userValue"; setup: "setup"; serverData: "serverData"; evaluator: "evaluator"; }>; /** * The lifecycle of one iteration. * * The first six mirror the persisted union in `mcpjam-backend` * (`convex/schema.ts`, `evalIterations.status`) EXACTLY, in that order. * `setup_failed` and `skipped` are the additions this contract pins: * * - `setup_failed` — the iteration never began because its environment could * not be prepared. A `failed` iteration says something about the server; * this one says something about us, and merging the two inflates every * failure rate with harness noise. * - `skipped` — deliberately not run (a disabled case, a filtered selection). * Distinct from `cancelled`, which is a run that was stopped mid-flight. * * Mirroring this list into the backend union is a separate change: extending * the stored union is the backend's to make, and this file is the source it * mirrors from. */ declare const ITERATION_STATUSES: readonly ["pending", "running", "completed", "failed", "cancelled", "timed_out", "setup_failed", "skipped"]; type IterationStatus = (typeof ITERATION_STATUSES)[number]; declare const iterationStatusSchema: z.ZodEnum<{ failed: "failed"; completed: "completed"; cancelled: "cancelled"; pending: "pending"; skipped: "skipped"; running: "running"; timed_out: "timed_out"; setup_failed: "setup_failed"; }>; /** * How faithfully an imported case reproduces its source. * * Recorded per case so an imported suite can be audited rather than trusted: * * - `exact` — the source construct maps onto ours by a cited * structural rule, with nothing dropped or invented. * - `approximated` — it runs, but the mapping lost or guessed something. * **This is the pessimistic default at every write boundary**: `exact` is a * claim that must be earned by a rule, and "I could not find a rule" is * `approximated`, never `exact`. * - `unsupported` — the source construct has no counterpart here. Preserved * in the mapping report, never smuggled into the executable suite as a * weakened assertion. * - `unresolved` — something the case references (a tool name, a server, a * fixture) did not resolve against live discovery. **This one is decided by * CODE, not by the caller**: the validator re-resolves every reference * itself, so a converter can neither claim it nor claim its way out of it. * * `unsupported` and `unresolved` are also the two that cannot be accepted into * eligibility. An `approximated` case can run once a human accepts the * documented semantic difference; these two must be repaired and revalidated * first, because there is nothing coherent to accept. */ declare const IMPORT_MAPPING_STATUSES: readonly ["exact", "approximated", "unsupported", "unresolved"]; type ImportMappingStatus = (typeof IMPORT_MAPPING_STATUSES)[number]; declare const importMappingStatusSchema: z.ZodEnum<{ unsupported: "unsupported"; exact: "exact"; approximated: "approximated"; unresolved: "unresolved"; }>; /** * The versioned eval **suite file** — one declarative document describing a * suite, its defaults, and its cases. * * This module is browser-safe and intentionally has no node-only deps. * * This file is the CONTRACT, not the loader. It says what a valid suite file * is; reading YAML, resolving defaults onto cases, and `eval validate` are a * separate concern that consumes these schemas. Two rules follow from that * split and are load-bearing: * * 1. **No `.default()` anywhere.** An omitted field stays omitted, so * `parse(x)` is byte-stable through `canonicalJson` and back. Default * *semantics* are documented in the JSDoc beside each field and applied by * the loader; materializing them here would mean a file grows fields it * never declared every time it round-trips, and the diff of an unchanged * suite would be full of values nobody wrote. * 2. **Every object this file DECLARES is `.strict()`, and so is every object * the step union declares.** A mis-mapped import field must fail loudly * rather than be silently dropped — the failure mode this schema exists to * prevent is an importer that "succeeds" while quietly discarding half of * what it read. Strictness also matches Convex `v.object`, which rejects * unknown fields, so a permissive schema here would accept payloads the * backend refuses. * * `stepsSchema` closed with this file (see `./steps.ts`), because step * level is where a mis-mapped import field actually lands and leaving it * open reproduced the whole failure one level down. Two reused things stay * open on purpose: a tool call's own `arguments` object, whose keys belong * to the server's input schema rather than to this contract, and * `predicateSchema`, which is a separate contract module with its own * mirror, its own fixtures and many more authoring surfaces — closing it is * a change made THERE, with its own consumer audit, not a side effect of * adding a file format. What IS guaranteed is that the generated JSON * Schema describes the same behaviour (see the generator's `io: "input"` * note): the two validators never disagree about which files they accept, * even where they are both permissive. * * ── Reserved values are ERRORS, not ignored ───────────────────────────────── * * `mode`, `reportingMode` and `captureLevel` each have values reserved for * capabilities that do not exist yet. Every one of them is a VALIDATION ERROR * in `schemaVersion` 1. Accepting-and-ignoring a reserved value is the worst * available outcome: a file that says `captureLevel: "none"` and captures * everything is a privacy incident with a paper trail claiming otherwise. * Because they are expressed as `z.literal`, the generated JSON Schema emits * `const` and rejects them structurally too. * * ── Versioning policy ──────────────────────────────────────────────────────── * * `schemaVersion` is `const "1"`. Additive OPTIONAL fields stay within `"1"`; a * breaking revision becomes `"2"`. A v1 validator handed a `"2"` file says so in * words that name the fix ("upgrade the CLI/SDK"), because the alternative — a * generic "invalid enum value" — sends people to edit a file that is correct. */ /** The only `schemaVersion` this validator accepts. */ declare const EVAL_SUITE_SCHEMA_VERSION = "1"; /** The `$id` of the published JSON Schema for this contract. */ declare const EVAL_SUITE_SCHEMA_ID = "https://mcpjam.com/schemas/eval-suite/v1.json"; /** * Max cases in ONE suite file. * * Deliberately LARGER than the hosted batch-create cap (100 per call): a file * is an authored artifact and a repo of 400 cases is one suite, while a batch * call is a request bound by request size. A max-size file therefore uploads in * several batch calls. The two numbers are not meant to match — do not "align" * them. */ declare const MAX_SUITE_FILE_CASES = 500; /** * Max cases the hosted batch-create surface accepts in ONE call. * * The other half of the note above, kept beside it so the two numbers are read * together and neither drifts toward the other. Mirrored by * `MAX_TEST_CASES_PER_BATCH` in the platform's own batch mutation, which is * where the limit is enforced; this copy is what lets a client CHUNK to the * limit instead of discovering it from a rejected call. */ declare const MAX_BATCH_CREATE_CASES = 100; /** Max characters in a suite name or a case title. */ declare const MAX_SUITE_FILE_TITLE_CHARS = 200; /** Max transcript predicates attached to one case. */ declare const MAX_CASE_ASSERTIONS = 50; /** Max repetitions per case (suite default or per-case override). */ declare const MAX_REPETITIONS = 100; /** Reserved for a future server-contract mode (no agent in the loop). */ declare const RESERVED_MODES: readonly ["serverContract"]; /** Reserved for future redacted/summarized reporting. */ declare const RESERVED_REPORTING_MODES: readonly ["restricted", "summary"]; /** Reserved for future reduced-capture policies. */ declare const RESERVED_CAPTURE_LEVELS: readonly ["metadataOnly", "none"]; /** * One server this suite runs against. * * `id` is the stable project-server reference and WINS when both are present; * `name` is the display fallback for environments with no bindings. Same * precedence as `toolCallStep`'s `serverId`/`serverName`, deliberately — a * suite file and a step must not disagree about how a server is addressed. */ declare const evalSuiteFileServerSchema: z.ZodObject<{ name: z.ZodString; id: z.ZodOptional; }, z.core.$strict>; type EvalSuiteFileServer = z.infer; /** * One hosted client attachment. * * `id` wins over `name`, matching server references. `servers` is the closed * server set attached to this host; an empty set is meaningful and preserved. */ declare const evalSuiteFileHostSchema: z.ZodObject<{ name: z.ZodString; id: z.ZodOptional; servers: z.ZodOptional; }, z.core.$strict>>>; }, z.core.$strict>; type EvalSuiteFileHost = z.infer; /** * A target always names at least one legacy server or one project environment. * Host attachments augment that target; hosts alone are not a runnable target. */ declare const evalSuiteFileTargetSchema: z.ZodUnion; servers: z.ZodOptional; }, z.core.$strict>>>; }, z.core.$strict>>>; servers: z.ZodArray; }, z.core.$strict>>; environment: z.ZodOptional; }, z.core.$strict>, z.ZodObject<{ hosts: z.ZodOptional; servers: z.ZodOptional; }, z.core.$strict>>>; }, z.core.$strict>>>; servers: z.ZodOptional; }, z.core.$strict>>>; environment: z.ZodString; }, z.core.$strict>]>; type EvalSuiteFileTarget = z.infer; /** * When a run's verdict is allowed to MEAN anything. * * All members are optional here and the file materializes none of them, but the * semantics are pinned so a loader is accountable to this file rather than to * memory: * * - `minEligibleTrials` — no numeric default, but ABSENT IS NOT "no * minimum". Omission selects the default coverage floor: every configured * trial must have been attempted, and the suite must have at least one * gradeable trial. An explicit `N` REPLACES that floor with * `eligibleTrials >= N`, which deliberately tolerates unattempted trials. * - `minCompletionRate` — defaults to **0.8**. * - `maxEvaluatorErrorRate` — defaults to **0.1**. * * The three are INDEPENDENT checks; the loader resolves the coverage rule into * `ResolvedEvalSuiteFileValidity.coverage`, and * `contract/verdict-policy.ts` is where a verdict is decided against it. * * A run that misses any of these is INVALID, which is not the same as failed: a * suite whose judge errored on half its iterations has not measured the server, * and reporting that as a failure blames the server for the grader. Reading * omission as "no minimum" is the same bug in a quieter form: it lets a suite * that ran one trial out of thirty report a confident pass. */ declare const evalSuiteFileValiditySchema: z.ZodObject<{ minEligibleTrials: z.ZodOptional; minCompletionRate: z.ZodOptional; maxEvaluatorErrorRate: z.ZodOptional; }, z.core.$strict>; type EvalSuiteFileValidity = z.infer; /** * Which tools the agent is RESTRAINED from calling. * * `mode` does the restraining: `readOnly` permits only tools an annotation * classifies read-only, `default` permits everything except tools annotated * `destructiveHint`. `deny` removes a tool by name under either mode. * * `allow` is an OVERRIDE, not a whitelist — it exempts a named tool from the * mode-derived rules (including `default` mode's destructive deny-by-default), * and it never restricts anything on its own. `{ mode: "default", allow: * ["read_file"] }` therefore restrains NOTHING; the tools an author wants * stopped belong in `deny`, or the suite belongs in `readOnly` mode. Stated * here because reading `allow` as "the only tools the agent may call" is the * one misreading that silently produces an unrestricted run. * * `allow`/`deny` entries are non-empty tool names; the empty string is rejected * structurally (rather than in a refinement) so the generated JSON Schema * rejects it too — an empty entry in a deny list reads as "deny nothing" to a * naive matcher and as "deny everything" to a prefix matcher. */ declare const evalSuiteFileToolPolicySchema: z.ZodObject<{ mode: z.ZodEnum<{ default: "default"; readOnly: "readOnly"; }>; allow: z.ZodOptional>; deny: z.ZodOptional>; }, z.core.$strict>; type EvalSuiteFileToolPolicy = z.infer; declare const evalSuiteFileDefaultsSchema: z.ZodObject<{ model: z.ZodString; provider: z.ZodOptional; systemPrompt: z.ZodOptional; temperature: z.ZodOptional; repetitions: z.ZodNumber; passThreshold: z.ZodNumber; validity: z.ZodObject<{ minEligibleTrials: z.ZodOptional; minCompletionRate: z.ZodOptional; maxEvaluatorErrorRate: z.ZodOptional; }, z.core.$strict>; toolPolicy: z.ZodOptional; allow: z.ZodOptional>; deny: z.ZodOptional>; }, z.core.$strict>>; captureLevel: z.ZodOptional>; }, z.core.$strict>; type EvalSuiteFileDefaults = z.infer; /** * Where an imported suite came from — the POINTER, not the report. * * The detailed mapping report is a separate artifact linked by `reportHash`, so * a suite file stays readable and diffable no matter how verbose the converter * was. `sourceHash` is what makes a re-import identifiable: the same source * bytes converted twice are the same import, and a changed `sourceHash` with an * unchanged suite is a re-import that needs auditing. */ declare const evalSuiteFileProvenanceSchema: z.ZodObject<{ sourceHash: z.ZodString; sourceFormat: z.ZodString; sourceFormatVersion: z.ZodOptional; converter: z.ZodOptional; converterVersion: z.ZodOptional; model: z.ZodOptional; discoverySnapshotHash: z.ZodOptional; reportHash: z.ZodString; importedAt: z.ZodOptional; }, z.core.$strict>; type EvalSuiteFileProvenance = z.infer; /** * Per-case import record. * * `status` is CALLER-SUPPLIED and pessimistic: `exact` is a claim that must be * earned by a cited structural rule, and a converter that cannot cite one * records `approximated`. Absence of this whole block means the case was * authored natively — the file schema deliberately does NOT default a status, * because "no import block" and "imported, faithfulness unknown" are different * facts and a default would erase the difference. */ declare const evalSuiteFileCaseImportSchema: z.ZodObject<{ status: z.ZodEnum<{ unsupported: "unsupported"; exact: "exact"; approximated: "approximated"; unresolved: "unresolved"; }>; sourceCaseKey: z.ZodOptional; note: z.ZodOptional; }, z.core.$strict>; type EvalSuiteFileCaseImport = z.infer; /** * One case. * * **`id` is the identity; `title` is display text.** They are never derived * from each other and never swapped: history joins on `id`, so a case renamed * from "Refund flow" to "Refunds" is the SAME case with the same history. That * is the entire reason `id` exists as a separate required field rather than * being hashed out of the title. */ declare const evalSuiteFileCaseSchema: z.ZodObject<{ id: z.ZodString; title: z.ZodString; steps: z.ZodArray; prompt: z.ZodString; }, z.core.$strict>, z.ZodObject<{ id: z.ZodString; kind: z.ZodLiteral<"toolCall">; serverId: z.ZodOptional; serverName: z.ZodString; toolName: z.ZodString; arguments: z.ZodRecord; renderTimeoutMs: z.ZodOptional; }, z.core.$strict>, z.ZodObject<{ id: z.ZodString; kind: z.ZodLiteral<"interact">; toolName: z.ZodString; action: z.ZodDiscriminatedUnion<[z.ZodObject<{ kind: z.ZodLiteral<"click">; target: z.ZodObject<{ role: z.ZodOptional; exact: z.ZodOptional; }, z.core.$strict>>; text: z.ZodOptional; css: z.ZodOptional; testId: z.ZodOptional; nth: z.ZodOptional; }, z.core.$strict>; clickType: z.ZodOptional>; }, z.core.$strict>, z.ZodObject<{ kind: z.ZodLiteral<"type">; target: z.ZodObject<{ role: z.ZodOptional; exact: z.ZodOptional; }, z.core.$strict>>; text: z.ZodOptional; css: z.ZodOptional; testId: z.ZodOptional; nth: z.ZodOptional; }, z.core.$strict>; text: z.ZodString; }, z.core.$strict>, z.ZodObject<{ kind: z.ZodLiteral<"key">; key: z.ZodString; }, z.core.$strict>, z.ZodObject<{ kind: z.ZodLiteral<"scroll">; direction: z.ZodEnum<{ up: "up"; down: "down"; }>; amount: z.ZodOptional; }, z.core.$strict>, z.ZodObject<{ kind: z.ZodLiteral<"wait">; ms: z.ZodNumber; }, z.core.$strict>], "kind">; }, z.core.$strict>, z.ZodObject<{ id: z.ZodString; kind: z.ZodLiteral<"assert">; assertion: z.ZodUnion; toolName: z.ZodString; text: z.ZodString; }, z.core.$strict>, z.ZodObject<{ kind: z.ZodLiteral<"elementVisible">; toolName: z.ZodString; target: z.ZodObject<{ role: z.ZodOptional; exact: z.ZodOptional; }, z.core.$strict>>; text: z.ZodOptional; css: z.ZodOptional; testId: z.ZodOptional; nth: z.ZodOptional; }, z.core.$strict>; }, z.core.$strict>, z.ZodObject<{ kind: z.ZodLiteral<"elementHidden">; toolName: z.ZodString; target: z.ZodObject<{ role: z.ZodOptional; exact: z.ZodOptional; }, z.core.$strict>>; text: z.ZodOptional; css: z.ZodOptional; testId: z.ZodOptional; nth: z.ZodOptional; }, z.core.$strict>; }, z.core.$strict>, z.ZodObject<{ kind: z.ZodLiteral<"inputValue">; toolName: z.ZodString; target: z.ZodObject<{ role: z.ZodOptional; exact: z.ZodOptional; }, z.core.$strict>>; text: z.ZodOptional; css: z.ZodOptional; testId: z.ZodOptional; nth: z.ZodOptional; }, z.core.$strict>; equals: z.ZodString; }, z.core.$strict>, z.ZodObject<{ kind: z.ZodLiteral<"widgetToolCalled">; toolName: z.ZodString; calledToolName: z.ZodString; }, z.core.$strict>], "kind">, z.ZodIntersection; toolName: z.ZodString; args: z.ZodObject<{ args: z.ZodRecord; argumentMatching: z.ZodOptional>; }, z.core.$strip>; minCount: z.ZodOptional; }, z.core.$strip>, z.ZodObject<{ type: z.ZodLiteral<"toolCalledAtLeastOnce">; toolName: z.ZodString; }, z.core.$strip>, z.ZodObject<{ type: z.ZodLiteral<"toolNeverCalled">; toolName: z.ZodString; }, z.core.$strip>, z.ZodObject<{ type: z.ZodLiteral<"firstToolWas">; toolName: z.ZodString; }, z.core.$strip>, z.ZodObject<{ type: z.ZodLiteral<"responseContains">; needle: z.ZodString; caseSensitive: z.ZodOptional; }, z.core.$strip>, z.ZodObject<{ type: z.ZodLiteral<"responseMatches">; pattern: z.ZodString; }, z.core.$strip>, z.ZodObject<{ type: z.ZodLiteral<"noToolErrors">; }, z.core.$strip>, z.ZodObject<{ type: z.ZodLiteral<"finalAssistantMessageNonEmpty">; }, z.core.$strip>, z.ZodObject<{ type: z.ZodLiteral<"tokenBudgetUnder">; tokens: z.ZodNumber; }, z.core.$strip>, z.ZodObject<{ type: z.ZodLiteral<"widgetRendered">; toolName: z.ZodOptional; }, z.core.$strip>, z.ZodObject<{ type: z.ZodLiteral<"widgetRenderLatencyUnder">; ms: z.ZodNumber; toolName: z.ZodOptional; }, z.core.$strip>, z.ZodObject<{ type: z.ZodLiteral<"widgetNoConsoleErrors">; toolName: z.ZodOptional; }, z.core.$strip>, z.ZodObject<{ type: z.ZodLiteral<"turnCountUnder">; turns: z.ZodNumber; }, z.core.$strip>], "type">, z.ZodObject<{ kind: z.ZodOptional; }, z.core.$strip>>]>; }, z.core.$strict>], "kind">>; assertions: z.ZodOptional; toolName: z.ZodString; args: z.ZodObject<{ args: z.ZodRecord; argumentMatching: z.ZodOptional>; }, z.core.$strip>; minCount: z.ZodOptional; }, z.core.$strip>, z.ZodObject<{ type: z.ZodLiteral<"toolCalledAtLeastOnce">; toolName: z.ZodString; }, z.core.$strip>, z.ZodObject<{ type: z.ZodLiteral<"toolNeverCalled">; toolName: z.ZodString; }, z.core.$strip>, z.ZodObject<{ type: z.ZodLiteral<"firstToolWas">; toolName: z.ZodString; }, z.core.$strip>, z.ZodObject<{ type: z.ZodLiteral<"responseContains">; needle: z.ZodString; caseSensitive: z.ZodOptional; }, z.core.$strip>, z.ZodObject<{ type: z.ZodLiteral<"responseMatches">; pattern: z.ZodString; }, z.core.$strip>, z.ZodObject<{ type: z.ZodLiteral<"noToolErrors">; }, z.core.$strip>, z.ZodObject<{ type: z.ZodLiteral<"finalAssistantMessageNonEmpty">; }, z.core.$strip>, z.ZodObject<{ type: z.ZodLiteral<"tokenBudgetUnder">; tokens: z.ZodNumber; }, z.core.$strip>, z.ZodObject<{ type: z.ZodLiteral<"widgetRendered">; toolName: z.ZodOptional; }, z.core.$strip>, z.ZodObject<{ type: z.ZodLiteral<"widgetRenderLatencyUnder">; ms: z.ZodNumber; toolName: z.ZodOptional; }, z.core.$strip>, z.ZodObject<{ type: z.ZodLiteral<"widgetNoConsoleErrors">; toolName: z.ZodOptional; }, z.core.$strip>, z.ZodObject<{ type: z.ZodLiteral<"turnCountUnder">; turns: z.ZodNumber; }, z.core.$strip>], "type">>>; expectedOutput: z.ZodOptional; isNegativeTest: z.ZodOptional; model: z.ZodOptional; repetitions: z.ZodOptional; passThreshold: z.ZodOptional; disabled: z.ZodOptional; import: z.ZodOptional; sourceCaseKey: z.ZodOptional; note: z.ZodOptional; }, z.core.$strict>>; }, z.core.$strict>; type EvalSuiteFileCase = z.infer; /** * The suite-file validator. * * The cross-field rules below are expressed as refinements, which means they do * NOT project into the generated JSON Schema. That is stated rather than * hidden: the JSON Schema is the STRUCTURAL contract for third-party tooling, * and this zod schema is the authoritative superset. Anything that must be * enforced by both lives in the object shape above. * * What is deliberately NOT enforced here: * * - **"a non-`exact` import must be `disabled`".** Import eligibility is * runtime policy — an audited case can legitimately be enabled while still * recorded as `approximated`. Encoding it structurally would make an audited * acceptance unrepresentable, so the file could not express the outcome the * audit exists to produce. * - **negative-case ⇄ `toolCalledWith` contradictions.** That is semantic * validation over predicate content, and it already exists in the corpus * guard; a second implementation here would be a second thing to keep in * sync. */ declare const evalSuiteFileSchema: z.ZodObject<{ schemaVersion: z.ZodLiteral<"1">; mode: z.ZodLiteral<"agentWorkflow">; reportingMode: z.ZodLiteral<"standard">; suite: z.ZodObject<{ id: z.ZodString; name: z.ZodString; description: z.ZodOptional; }, z.core.$strict>; target: z.ZodUnion; servers: z.ZodOptional; }, z.core.$strict>>>; }, z.core.$strict>>>; servers: z.ZodArray; }, z.core.$strict>>; environment: z.ZodOptional; }, z.core.$strict>, z.ZodObject<{ hosts: z.ZodOptional; servers: z.ZodOptional; }, z.core.$strict>>>; }, z.core.$strict>>>; servers: z.ZodOptional; }, z.core.$strict>>>; environment: z.ZodString; }, z.core.$strict>]>; defaults: z.ZodObject<{ model: z.ZodString; provider: z.ZodOptional; systemPrompt: z.ZodOptional; temperature: z.ZodOptional; repetitions: z.ZodNumber; passThreshold: z.ZodNumber; validity: z.ZodObject<{ minEligibleTrials: z.ZodOptional; minCompletionRate: z.ZodOptional; maxEvaluatorErrorRate: z.ZodOptional; }, z.core.$strict>; toolPolicy: z.ZodOptional; allow: z.ZodOptional>; deny: z.ZodOptional>; }, z.core.$strict>>; captureLevel: z.ZodOptional>; }, z.core.$strict>; provenance: z.ZodOptional; converter: z.ZodOptional; converterVersion: z.ZodOptional; model: z.ZodOptional; discoverySnapshotHash: z.ZodOptional; reportHash: z.ZodString; importedAt: z.ZodOptional; }, z.core.$strict>>; cases: z.ZodArray; prompt: z.ZodString; }, z.core.$strict>, z.ZodObject<{ id: z.ZodString; kind: z.ZodLiteral<"toolCall">; serverId: z.ZodOptional; serverName: z.ZodString; toolName: z.ZodString; arguments: z.ZodRecord; renderTimeoutMs: z.ZodOptional; }, z.core.$strict>, z.ZodObject<{ id: z.ZodString; kind: z.ZodLiteral<"interact">; toolName: z.ZodString; action: z.ZodDiscriminatedUnion<[z.ZodObject<{ kind: z.ZodLiteral<"click">; target: z.ZodObject<{ role: z.ZodOptional; exact: z.ZodOptional; }, z.core.$strict>>; text: z.ZodOptional; css: z.ZodOptional; testId: z.ZodOptional; nth: z.ZodOptional; }, z.core.$strict>; clickType: z.ZodOptional>; }, z.core.$strict>, z.ZodObject<{ kind: z.ZodLiteral<"type">; target: z.ZodObject<{ role: z.ZodOptional; exact: z.ZodOptional; }, z.core.$strict>>; text: z.ZodOptional; css: z.ZodOptional; testId: z.ZodOptional; nth: z.ZodOptional; }, z.core.$strict>; text: z.ZodString; }, z.core.$strict>, z.ZodObject<{ kind: z.ZodLiteral<"key">; key: z.ZodString; }, z.core.$strict>, z.ZodObject<{ kind: z.ZodLiteral<"scroll">; direction: z.ZodEnum<{ up: "up"; down: "down"; }>; amount: z.ZodOptional; }, z.core.$strict>, z.ZodObject<{ kind: z.ZodLiteral<"wait">; ms: z.ZodNumber; }, z.core.$strict>], "kind">; }, z.core.$strict>, z.ZodObject<{ id: z.ZodString; kind: z.ZodLiteral<"assert">; assertion: z.ZodUnion; toolName: z.ZodString; text: z.ZodString; }, z.core.$strict>, z.ZodObject<{ kind: z.ZodLiteral<"elementVisible">; toolName: z.ZodString; target: z.ZodObject<{ role: z.ZodOptional; exact: z.ZodOptional; }, z.core.$strict>>; text: z.ZodOptional; css: z.ZodOptional; testId: z.ZodOptional; nth: z.ZodOptional; }, z.core.$strict>; }, z.core.$strict>, z.ZodObject<{ kind: z.ZodLiteral<"elementHidden">; toolName: z.ZodString; target: z.ZodObject<{ role: z.ZodOptional; exact: z.ZodOptional; }, z.core.$strict>>; text: z.ZodOptional; css: z.ZodOptional; testId: z.ZodOptional; nth: z.ZodOptional; }, z.core.$strict>; }, z.core.$strict>, z.ZodObject<{ kind: z.ZodLiteral<"inputValue">; toolName: z.ZodString; target: z.ZodObject<{ role: z.ZodOptional; exact: z.ZodOptional; }, z.core.$strict>>; text: z.ZodOptional; css: z.ZodOptional; testId: z.ZodOptional; nth: z.ZodOptional; }, z.core.$strict>; equals: z.ZodString; }, z.core.$strict>, z.ZodObject<{ kind: z.ZodLiteral<"widgetToolCalled">; toolName: z.ZodString; calledToolName: z.ZodString; }, z.core.$strict>], "kind">, z.ZodIntersection; toolName: z.ZodString; args: z.ZodObject<{ args: z.ZodRecord; argumentMatching: z.ZodOptional>; }, z.core.$strip>; minCount: z.ZodOptional; }, z.core.$strip>, z.ZodObject<{ type: z.ZodLiteral<"toolCalledAtLeastOnce">; toolName: z.ZodString; }, z.core.$strip>, z.ZodObject<{ type: z.ZodLiteral<"toolNeverCalled">; toolName: z.ZodString; }, z.core.$strip>, z.ZodObject<{ type: z.ZodLiteral<"firstToolWas">; toolName: z.ZodString; }, z.core.$strip>, z.ZodObject<{ type: z.ZodLiteral<"responseContains">; needle: z.ZodString; caseSensitive: z.ZodOptional; }, z.core.$strip>, z.ZodObject<{ type: z.ZodLiteral<"responseMatches">; pattern: z.ZodString; }, z.core.$strip>, z.ZodObject<{ type: z.ZodLiteral<"noToolErrors">; }, z.core.$strip>, z.ZodObject<{ type: z.ZodLiteral<"finalAssistantMessageNonEmpty">; }, z.core.$strip>, z.ZodObject<{ type: z.ZodLiteral<"tokenBudgetUnder">; tokens: z.ZodNumber; }, z.core.$strip>, z.ZodObject<{ type: z.ZodLiteral<"widgetRendered">; toolName: z.ZodOptional; }, z.core.$strip>, z.ZodObject<{ type: z.ZodLiteral<"widgetRenderLatencyUnder">; ms: z.ZodNumber; toolName: z.ZodOptional; }, z.core.$strip>, z.ZodObject<{ type: z.ZodLiteral<"widgetNoConsoleErrors">; toolName: z.ZodOptional; }, z.core.$strip>, z.ZodObject<{ type: z.ZodLiteral<"turnCountUnder">; turns: z.ZodNumber; }, z.core.$strip>], "type">, z.ZodObject<{ kind: z.ZodOptional; }, z.core.$strip>>]>; }, z.core.$strict>], "kind">>; assertions: z.ZodOptional; toolName: z.ZodString; args: z.ZodObject<{ args: z.ZodRecord; argumentMatching: z.ZodOptional>; }, z.core.$strip>; minCount: z.ZodOptional; }, z.core.$strip>, z.ZodObject<{ type: z.ZodLiteral<"toolCalledAtLeastOnce">; toolName: z.ZodString; }, z.core.$strip>, z.ZodObject<{ type: z.ZodLiteral<"toolNeverCalled">; toolName: z.ZodString; }, z.core.$strip>, z.ZodObject<{ type: z.ZodLiteral<"firstToolWas">; toolName: z.ZodString; }, z.core.$strip>, z.ZodObject<{ type: z.ZodLiteral<"responseContains">; needle: z.ZodString; caseSensitive: z.ZodOptional; }, z.core.$strip>, z.ZodObject<{ type: z.ZodLiteral<"responseMatches">; pattern: z.ZodString; }, z.core.$strip>, z.ZodObject<{ type: z.ZodLiteral<"noToolErrors">; }, z.core.$strip>, z.ZodObject<{ type: z.ZodLiteral<"finalAssistantMessageNonEmpty">; }, z.core.$strip>, z.ZodObject<{ type: z.ZodLiteral<"tokenBudgetUnder">; tokens: z.ZodNumber; }, z.core.$strip>, z.ZodObject<{ type: z.ZodLiteral<"widgetRendered">; toolName: z.ZodOptional; }, z.core.$strip>, z.ZodObject<{ type: z.ZodLiteral<"widgetRenderLatencyUnder">; ms: z.ZodNumber; toolName: z.ZodOptional; }, z.core.$strip>, z.ZodObject<{ type: z.ZodLiteral<"widgetNoConsoleErrors">; toolName: z.ZodOptional; }, z.core.$strip>, z.ZodObject<{ type: z.ZodLiteral<"turnCountUnder">; turns: z.ZodNumber; }, z.core.$strip>], "type">>>; expectedOutput: z.ZodOptional; isNegativeTest: z.ZodOptional; model: z.ZodOptional; repetitions: z.ZodOptional; passThreshold: z.ZodOptional; disabled: z.ZodOptional; import: z.ZodOptional; sourceCaseKey: z.ZodOptional; note: z.ZodOptional; }, z.core.$strict>>; }, z.core.$strict>>; }, z.core.$strict>; type EvalSuiteFile = z.infer; /** * The strictly-structural half of the contract, without the cross-field * refinements. * * Exported for ONE purpose: generating the JSON Schema, and proving in a test * that the generated schema and the zod validator agree on everything that is * structural. Validate real files with {@link evalSuiteFileSchema}. */ declare const evalSuiteFileStructuralSchema: z.ZodObject<{ schemaVersion: z.ZodLiteral<"1">; mode: z.ZodLiteral<"agentWorkflow">; reportingMode: z.ZodLiteral<"standard">; suite: z.ZodObject<{ id: z.ZodString; name: z.ZodString; description: z.ZodOptional; }, z.core.$strict>; target: z.ZodUnion; servers: z.ZodOptional; }, z.core.$strict>>>; }, z.core.$strict>>>; servers: z.ZodArray; }, z.core.$strict>>; environment: z.ZodOptional; }, z.core.$strict>, z.ZodObject<{ hosts: z.ZodOptional; servers: z.ZodOptional; }, z.core.$strict>>>; }, z.core.$strict>>>; servers: z.ZodOptional; }, z.core.$strict>>>; environment: z.ZodString; }, z.core.$strict>]>; defaults: z.ZodObject<{ model: z.ZodString; provider: z.ZodOptional; systemPrompt: z.ZodOptional; temperature: z.ZodOptional; repetitions: z.ZodNumber; passThreshold: z.ZodNumber; validity: z.ZodObject<{ minEligibleTrials: z.ZodOptional; minCompletionRate: z.ZodOptional; maxEvaluatorErrorRate: z.ZodOptional; }, z.core.$strict>; toolPolicy: z.ZodOptional; allow: z.ZodOptional>; deny: z.ZodOptional>; }, z.core.$strict>>; captureLevel: z.ZodOptional>; }, z.core.$strict>; provenance: z.ZodOptional; converter: z.ZodOptional; converterVersion: z.ZodOptional; model: z.ZodOptional; discoverySnapshotHash: z.ZodOptional; reportHash: z.ZodString; importedAt: z.ZodOptional; }, z.core.$strict>>; cases: z.ZodArray; prompt: z.ZodString; }, z.core.$strict>, z.ZodObject<{ id: z.ZodString; kind: z.ZodLiteral<"toolCall">; serverId: z.ZodOptional; serverName: z.ZodString; toolName: z.ZodString; arguments: z.ZodRecord; renderTimeoutMs: z.ZodOptional; }, z.core.$strict>, z.ZodObject<{ id: z.ZodString; kind: z.ZodLiteral<"interact">; toolName: z.ZodString; action: z.ZodDiscriminatedUnion<[z.ZodObject<{ kind: z.ZodLiteral<"click">; target: z.ZodObject<{ role: z.ZodOptional; exact: z.ZodOptional; }, z.core.$strict>>; text: z.ZodOptional; css: z.ZodOptional; testId: z.ZodOptional; nth: z.ZodOptional; }, z.core.$strict>; clickType: z.ZodOptional>; }, z.core.$strict>, z.ZodObject<{ kind: z.ZodLiteral<"type">; target: z.ZodObject<{ role: z.ZodOptional; exact: z.ZodOptional; }, z.core.$strict>>; text: z.ZodOptional; css: z.ZodOptional; testId: z.ZodOptional; nth: z.ZodOptional; }, z.core.$strict>; text: z.ZodString; }, z.core.$strict>, z.ZodObject<{ kind: z.ZodLiteral<"key">; key: z.ZodString; }, z.core.$strict>, z.ZodObject<{ kind: z.ZodLiteral<"scroll">; direction: z.ZodEnum<{ up: "up"; down: "down"; }>; amount: z.ZodOptional; }, z.core.$strict>, z.ZodObject<{ kind: z.ZodLiteral<"wait">; ms: z.ZodNumber; }, z.core.$strict>], "kind">; }, z.core.$strict>, z.ZodObject<{ id: z.ZodString; kind: z.ZodLiteral<"assert">; assertion: z.ZodUnion; toolName: z.ZodString; text: z.ZodString; }, z.core.$strict>, z.ZodObject<{ kind: z.ZodLiteral<"elementVisible">; toolName: z.ZodString; target: z.ZodObject<{ role: z.ZodOptional; exact: z.ZodOptional; }, z.core.$strict>>; text: z.ZodOptional; css: z.ZodOptional; testId: z.ZodOptional; nth: z.ZodOptional; }, z.core.$strict>; }, z.core.$strict>, z.ZodObject<{ kind: z.ZodLiteral<"elementHidden">; toolName: z.ZodString; target: z.ZodObject<{ role: z.ZodOptional; exact: z.ZodOptional; }, z.core.$strict>>; text: z.ZodOptional; css: z.ZodOptional; testId: z.ZodOptional; nth: z.ZodOptional; }, z.core.$strict>; }, z.core.$strict>, z.ZodObject<{ kind: z.ZodLiteral<"inputValue">; toolName: z.ZodString; target: z.ZodObject<{ role: z.ZodOptional; exact: z.ZodOptional; }, z.core.$strict>>; text: z.ZodOptional; css: z.ZodOptional; testId: z.ZodOptional; nth: z.ZodOptional; }, z.core.$strict>; equals: z.ZodString; }, z.core.$strict>, z.ZodObject<{ kind: z.ZodLiteral<"widgetToolCalled">; toolName: z.ZodString; calledToolName: z.ZodString; }, z.core.$strict>], "kind">, z.ZodIntersection; toolName: z.ZodString; args: z.ZodObject<{ args: z.ZodRecord; argumentMatching: z.ZodOptional>; }, z.core.$strip>; minCount: z.ZodOptional; }, z.core.$strip>, z.ZodObject<{ type: z.ZodLiteral<"toolCalledAtLeastOnce">; toolName: z.ZodString; }, z.core.$strip>, z.ZodObject<{ type: z.ZodLiteral<"toolNeverCalled">; toolName: z.ZodString; }, z.core.$strip>, z.ZodObject<{ type: z.ZodLiteral<"firstToolWas">; toolName: z.ZodString; }, z.core.$strip>, z.ZodObject<{ type: z.ZodLiteral<"responseContains">; needle: z.ZodString; caseSensitive: z.ZodOptional; }, z.core.$strip>, z.ZodObject<{ type: z.ZodLiteral<"responseMatches">; pattern: z.ZodString; }, z.core.$strip>, z.ZodObject<{ type: z.ZodLiteral<"noToolErrors">; }, z.core.$strip>, z.ZodObject<{ type: z.ZodLiteral<"finalAssistantMessageNonEmpty">; }, z.core.$strip>, z.ZodObject<{ type: z.ZodLiteral<"tokenBudgetUnder">; tokens: z.ZodNumber; }, z.core.$strip>, z.ZodObject<{ type: z.ZodLiteral<"widgetRendered">; toolName: z.ZodOptional; }, z.core.$strip>, z.ZodObject<{ type: z.ZodLiteral<"widgetRenderLatencyUnder">; ms: z.ZodNumber; toolName: z.ZodOptional; }, z.core.$strip>, z.ZodObject<{ type: z.ZodLiteral<"widgetNoConsoleErrors">; toolName: z.ZodOptional; }, z.core.$strip>, z.ZodObject<{ type: z.ZodLiteral<"turnCountUnder">; turns: z.ZodNumber; }, z.core.$strip>], "type">, z.ZodObject<{ kind: z.ZodOptional; }, z.core.$strip>>]>; }, z.core.$strict>], "kind">>; assertions: z.ZodOptional; toolName: z.ZodString; args: z.ZodObject<{ args: z.ZodRecord; argumentMatching: z.ZodOptional>; }, z.core.$strip>; minCount: z.ZodOptional; }, z.core.$strip>, z.ZodObject<{ type: z.ZodLiteral<"toolCalledAtLeastOnce">; toolName: z.ZodString; }, z.core.$strip>, z.ZodObject<{ type: z.ZodLiteral<"toolNeverCalled">; toolName: z.ZodString; }, z.core.$strip>, z.ZodObject<{ type: z.ZodLiteral<"firstToolWas">; toolName: z.ZodString; }, z.core.$strip>, z.ZodObject<{ type: z.ZodLiteral<"responseContains">; needle: z.ZodString; caseSensitive: z.ZodOptional; }, z.core.$strip>, z.ZodObject<{ type: z.ZodLiteral<"responseMatches">; pattern: z.ZodString; }, z.core.$strip>, z.ZodObject<{ type: z.ZodLiteral<"noToolErrors">; }, z.core.$strip>, z.ZodObject<{ type: z.ZodLiteral<"finalAssistantMessageNonEmpty">; }, z.core.$strip>, z.ZodObject<{ type: z.ZodLiteral<"tokenBudgetUnder">; tokens: z.ZodNumber; }, z.core.$strip>, z.ZodObject<{ type: z.ZodLiteral<"widgetRendered">; toolName: z.ZodOptional; }, z.core.$strip>, z.ZodObject<{ type: z.ZodLiteral<"widgetRenderLatencyUnder">; ms: z.ZodNumber; toolName: z.ZodOptional; }, z.core.$strip>, z.ZodObject<{ type: z.ZodLiteral<"widgetNoConsoleErrors">; toolName: z.ZodOptional; }, z.core.$strip>, z.ZodObject<{ type: z.ZodLiteral<"turnCountUnder">; turns: z.ZodNumber; }, z.core.$strip>], "type">>>; expectedOutput: z.ZodOptional; isNegativeTest: z.ZodOptional; model: z.ZodOptional; repetitions: z.ZodOptional; passThreshold: z.ZodOptional; disabled: z.ZodOptional; import: z.ZodOptional; sourceCaseKey: z.ZodOptional; note: z.ZodOptional; }, z.core.$strict>>; }, z.core.$strict>>; }, z.core.$strict>; /** * The versioned **run verdict policy** — what a suite-level eval verdict means, * and what must be measured before it is allowed to mean anything. * * This module is browser-safe and intentionally has no node-only deps. * * It is the CONTRACT and nothing else: the shapes a verdict travels in, the * closed vocabularies it is spelled with, and the validators that refuse a * self-inconsistent one. There is deliberately NO aggregator here — nothing in * this file reads trials and produces a verdict. The producer (hosted runner or * SDK run) is a later wave, and the fixtures in * `tests/fixtures/eval-verdict-policy-parity-fixtures.json` are what pin the * expected output it will have to reproduce. * * Two rules follow the same discipline as `./suite-file.ts` and are load- * bearing: * * 1. **No `.default()` anywhere.** An omitted field stays omitted, so a * payload is byte-stable through `canonicalJson` and back. Above all, * {@link EVAL_VERDICT_POLICY_VERSION} is never defaulted: a legacy row that * carries no `verdictPolicyVersion` was produced under the old percent- * threshold rules and MUST NOT be read as v2. Defaulting it would silently * restate old numbers under new semantics. * 2. **Every object declared here is `.strict()`.** Unknown fields are errors, * matching Convex `v.object`, which the backend mirror uses. * * ── Why a verdict can be `inconclusive` ────────────────────────────────────── * * A run that did not measure the server has not produced evidence about it, and * reporting that as `failed` blames the server for the harness or the grader. * So the policy is evaluated in TWO ORDERED phases and the order is normative: * * 1. **Validity.** Was enough of the run actually measured? Failing this is * `inconclusive`, with a reason drawn from * {@link EVAL_VALIDITY_DECISION_REASONS}. A case that produced ZERO * eligible trials makes the suite `inconclusive` too — even when its * threshold is `0`, because "nothing was graded" is not "everything that * was graded passed". * 2. **Task verdict.** Only once validity holds: every MEASURED case must * meet its own effective `passThreshold` for the suite to pass. * * Lifecycle and task verdict are ORTHOGONAL. `ITERATION_STATUSES` (see * `./chain.ts`) describes what happened to a trial mechanically; the verdict * describes whether the case's goal was met. A trial that ran to `completed` * with a failing task verdict is the normal way a case fails, and no mapper * from one vocabulary to the other exists — or should be added. The bridge * between them is {@link EvalTrialExclusionReason}, which records why a trial * was removed from a denominator, and it is supplied by the producer rather * than derived here. * * ── Every rate is an envelope, never a bare number ─────────────────────────── * * {@link EvalRateMeasurement} carries `numerator`, `denominator`, `exclusions` * and a `state`. A zero denominator is `notMeasured` with a `null` value, which * is unrepresentable as a pass: the one failure mode this shape exists to * prevent is `0/0` rendering as `0`, or as `1`, and either way as a verdict. * * ── Naming ────────────────────────────────────────────────────────────────── * * These are NOT the `EvalDecisionSummary` / `EvalDecisionVerdict` types in the * SDK main entry (`src/eval-decision-summary.ts`). Those are the existing * per-case stage-chain summary, whose verdict vocabulary is * `passed | failed | incomplete` and whose rate is a percent over CASES. This * contract is a separate, versioned surface over TRIALS with an `inconclusive` * verdict, so it takes distinct names rather than widening a shipped one. */ /** * The policy version, as a literal. * * `2` because v1 is the shipped percent-threshold behaviour that has no version * field at all. Absence therefore means v1, and {@link isEvalVerdictPolicyV2} * is the only sanctioned way to ask. */ declare const EVAL_VERDICT_POLICY_VERSION = 2; type EvalVerdictPolicyVersion = typeof EVAL_VERDICT_POLICY_VERSION; declare const evalVerdictPolicyVersionSchema: z.ZodLiteral<2>; /** The `$id` of the published JSON Schema for this contract. */ declare const EVAL_VERDICT_POLICY_SCHEMA_ID = "https://mcpjam.com/schemas/eval-verdict-policy/v2.json"; /** * True only when `value` is LITERALLY the v2 version. * * Exists so no consumer has to write `version ?? 2`. A row read out of storage * with no version is v1 and this returns `false` for it. */ declare function isEvalVerdictPolicyV2(value: unknown): boolean; /** * What a run (or one case within it) is allowed to conclude. * * - `passed` — measured, and every measured case met its threshold. * - `failed` — measured, and at least one measured case did not. * - `inconclusive` — not measured well enough to say either. Never merged * into `failed`: the distinction between "the server is broken" and "we did * not measure the server" is the entire point of this policy. */ declare const EVAL_RUN_VERDICTS: readonly ["passed", "failed", "inconclusive"]; type EvalRunVerdict = (typeof EVAL_RUN_VERDICTS)[number]; declare const evalRunVerdictSchema: z.ZodEnum<{ failed: "failed"; passed: "passed"; inconclusive: "inconclusive"; }>; declare function isEvalRunVerdict(value: unknown): value is EvalRunVerdict; /** * A rate or a threshold: a finite real number in [0,1]. NEVER a percent. * * `z.number()` already rejects `NaN` and both infinities, and the bounds reject * a percent-like `50` structurally — so the JSON Schema twin rejects all of * them too, rather than leaning on a refinement that would not project. */ declare const evalFractionSchema: z.ZodNumber; /** * The closed vocabulary for a trial that was NOT graded. * * Supplied by the producer, never derived here: mapping a lifecycle status onto * one of these is the producer's job, and a mapper in this file would quietly * become the thing that decides verdicts. * * - `notTerminal` — still `pending`/`running` when the roll-up was taken. * A `running` trial HAS begun, so it is attempted and its unfinished state * lowers the completion rate; a `pending` one has not. * - `skipped` — deliberately not run (disabled case, filtered * selection). Never attempted, so it is not a completion failure either. * - `setupFailed` — the environment was never prepared. Says something * about us, not about the server. * - `cancelled` — a run stopped mid-flight by a human or a shutdown. * Withdrawn from EVERY denominator, including the completion rate's: the * harness took the trial's outcome away, so counting it as an incomplete * attempt would report a suite as unfinished for a decision nobody made * about the server. See {@link EvalCaseVerdictAggregation.attemptedTrials}. * - `timedOut` — no terminal outcome inside the budget. * - `executionFailed` — the trial ran and errored out mechanically, so it has * no task verdict to grade. * - `evaluatorError` — the GRADER failed. Kept separate from every other * reason because it is the numerator of its own validity ceiling: folding a * broken judge into server failures poisons every rate derived from it. */ declare const EVAL_TRIAL_EXCLUSION_REASONS: readonly ["notTerminal", "skipped", "setupFailed", "cancelled", "timedOut", "executionFailed", "evaluatorError"]; type EvalTrialExclusionReason = (typeof EVAL_TRIAL_EXCLUSION_REASONS)[number]; declare const evalTrialExclusionReasonSchema: z.ZodEnum<{ cancelled: "cancelled"; skipped: "skipped"; notTerminal: "notTerminal"; setupFailed: "setupFailed"; timedOut: "timedOut"; executionFailed: "executionFailed"; evaluatorError: "evaluatorError"; }>; declare function isEvalTrialExclusionReason(value: unknown): value is EvalTrialExclusionReason; /** * How many trials each reason removed from ONE rate's denominator. * * A closed object of optional counts rather than an open map: the keys are the * vocabulary above, and a typo'd reason must fail loudly instead of being * counted under a key nobody reads. A reason that excluded nothing is OMITTED * (no `.default()`, so the payload round-trips byte-stable) — `0` and absent * mean the same thing and only one of them is written. */ declare const evalTrialExclusionsSchema: z.ZodObject<{ notTerminal: z.ZodOptional; skipped: z.ZodOptional; setupFailed: z.ZodOptional; cancelled: z.ZodOptional; timedOut: z.ZodOptional; executionFailed: z.ZodOptional; evaluatorError: z.ZodOptional; }, z.core.$strict>; type EvalTrialExclusions = z.infer; /** * Whether a rate says anything at all. * * `notMeasured` is not a low score. It is the absence of a score, and no floor * or ceiling can be satisfied by it — see * {@link EVAL_VALIDITY_DECISION_REASONS}. */ declare const EVAL_RATE_MEASUREMENT_STATES: readonly ["measured", "notMeasured"]; type EvalRateMeasurementState = (typeof EVAL_RATE_MEASUREMENT_STATES)[number]; declare const evalRateMeasurementStateSchema: z.ZodEnum<{ notMeasured: "notMeasured"; measured: "measured"; }>; /** * One rate, with the arithmetic that produced it. * * A discriminated union rather than one object with nullable fields, so the * `0/0` case is unrepresentable as a pass: `notMeasured` pins `value: null` and * `denominator: 0`, and `measured` requires `denominator >= 1`. A consumer that * forgets to branch gets a type error, not a `0` it can compare against a * threshold. * * `exclusions` travels with EVERY rate rather than once per case, because the * trials excluded from an eligibility denominator are not the ones excluded * from a completion denominator, and one shared tally could not say which was * which. */ declare const evalRateMeasurementStructuralSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{ state: z.ZodLiteral<"measured">; value: z.ZodNumber; numerator: z.ZodNumber; denominator: z.ZodNumber; exclusions: z.ZodObject<{ notTerminal: z.ZodOptional; skipped: z.ZodOptional; setupFailed: z.ZodOptional; cancelled: z.ZodOptional; timedOut: z.ZodOptional; executionFailed: z.ZodOptional; evaluatorError: z.ZodOptional; }, z.core.$strict>; }, z.core.$strict>, z.ZodObject<{ state: z.ZodLiteral<"notMeasured">; value: z.ZodNull; numerator: z.ZodLiteral<0>; denominator: z.ZodLiteral<0>; exclusions: z.ZodObject<{ notTerminal: z.ZodOptional; skipped: z.ZodOptional; setupFailed: z.ZodOptional; cancelled: z.ZodOptional; timedOut: z.ZodOptional; executionFailed: z.ZodOptional; evaluatorError: z.ZodOptional; }, z.core.$strict>; }, z.core.$strict>], "state">; /** * The rate validator, with the cross-field arithmetic the structural half * cannot express: the numerator never exceeds the denominator, and `value` IS * the quotient rather than a rounded-off restatement of it. * * The quotient is checked exactly. IEEE-754 division is deterministic across * the runtimes that mirror this contract, and a producer that rounds `value` * has produced a number that no longer matches the counts it ships beside — * which is precisely the drift that makes a hosted rate and a re-derived one * disagree. */ declare const evalRateMeasurementSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{ state: z.ZodLiteral<"measured">; value: z.ZodNumber; numerator: z.ZodNumber; denominator: z.ZodNumber; exclusions: z.ZodObject<{ notTerminal: z.ZodOptional; skipped: z.ZodOptional; setupFailed: z.ZodOptional; cancelled: z.ZodOptional; timedOut: z.ZodOptional; executionFailed: z.ZodOptional; evaluatorError: z.ZodOptional; }, z.core.$strict>; }, z.core.$strict>, z.ZodObject<{ state: z.ZodLiteral<"notMeasured">; value: z.ZodNull; numerator: z.ZodLiteral<0>; denominator: z.ZodLiteral<0>; exclusions: z.ZodObject<{ notTerminal: z.ZodOptional; skipped: z.ZodOptional; setupFailed: z.ZodOptional; cancelled: z.ZodOptional; timedOut: z.ZodOptional; executionFailed: z.ZodOptional; evaluatorError: z.ZodOptional; }, z.core.$strict>; }, z.core.$strict>], "state">; type EvalRateMeasurement = z.infer; /** * The suite-file `validity` block once its documented defaults are resolved — * the form a verdict is actually decided against. * * `coverage` is a DISCRIMINATED UNION rather than an optional * `minEligibleTrials` beside a boolean, because the two coverage rules are * mutually exclusive and a shape that can express both (or neither) would let * an incidental defaulting expression — `minEligibleTrials ?? 1` somewhere in a * runner — decide the policy: * * - `allConfiguredTrialsAttempted` — what an OMITTED `minEligibleTrials` * means: every configured trial must have been attempted, AND the suite * must have at least one gradeable trial. `minGradeableTrials` is pinned to * `1` so the second half of that rule is present in the payload instead of * living in prose. * - `minEligibleTrials` — an explicit floor N REPLACES the coverage rule * above: `eligibleTrials >= N`, and an unattempted configured trial is no * longer disqualifying on its own. * * `minCompletionRate` (suite-file default **0.8**) and `maxEvaluatorErrorRate` * (default **0.1**) are INDEPENDENT checks under either coverage rule. */ declare const evalValidityCoverageSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{ kind: z.ZodLiteral<"allConfiguredTrialsAttempted">; minGradeableTrials: z.ZodLiteral<1>; }, z.core.$strict>, z.ZodObject<{ kind: z.ZodLiteral<"minEligibleTrials">; minEligibleTrials: z.ZodNumber; }, z.core.$strict>], "kind">; type EvalValidityCoverage = z.infer; declare const resolvedEvalValidityPolicySchema: z.ZodObject<{ coverage: z.ZodDiscriminatedUnion<[z.ZodObject<{ kind: z.ZodLiteral<"allConfiguredTrialsAttempted">; minGradeableTrials: z.ZodLiteral<1>; }, z.core.$strict>, z.ZodObject<{ kind: z.ZodLiteral<"minEligibleTrials">; minEligibleTrials: z.ZodNumber; }, z.core.$strict>], "kind">; minCompletionRate: z.ZodNumber; maxEvaluatorErrorRate: z.ZodNumber; }, z.core.$strict>; type ResolvedEvalValidityPolicy = z.infer; /** * Reasons that make a run `inconclusive`. Evaluated BEFORE any task verdict. * * - `configuredTrialsNotAttempted` — coverage rule * `allConfiguredTrialsAttempted`: some configured trial never ran. * - `noGradeableTrials` — same rule: nothing in the suite was * gradeable. * - `eligibleTrialsBelowMinimum` — explicit `minEligibleTrials` N not * reached. * - `completionRateBelowMinimum` — measured, and under the floor. * - `completionRateNotMeasured` — nothing was attempted, so the floor is * unsatisfiable. A `notMeasured` rate never passes a floor. * - `evaluatorErrorRateAboveMaximum` — the grader failed too often for the * run to describe the server. * - `evaluatorErrorRateNotMeasured` — the same unsatisfiable case for the * ceiling. * - `caseHasNoEligibleTrials` — a case graded nothing. Inconclusive * even at `passThreshold: 0`. */ declare const EVAL_VALIDITY_DECISION_REASONS: readonly ["configuredTrialsNotAttempted", "noGradeableTrials", "eligibleTrialsBelowMinimum", "completionRateBelowMinimum", "completionRateNotMeasured", "evaluatorErrorRateAboveMaximum", "evaluatorErrorRateNotMeasured", "caseHasNoEligibleTrials"]; type EvalValidityDecisionReason = (typeof EVAL_VALIDITY_DECISION_REASONS)[number]; /** * Reasons that decide a `passed` / `failed` task verdict, once validity holds. * * - `casePassRateMetThreshold` — a case's own passing reason. * - `casePassRateBelowThreshold` — a case failed its threshold, and so * therefore did the suite. * - `allMeasuredCasesMetThreshold` — the suite's only passing reason. */ declare const EVAL_TASK_DECISION_REASONS: readonly ["casePassRateMetThreshold", "casePassRateBelowThreshold", "allMeasuredCasesMetThreshold"]; type EvalTaskDecisionReason = (typeof EVAL_TASK_DECISION_REASONS)[number]; /** The full closed reason vocabulary, validity reasons first. */ declare const EVAL_VERDICT_DECISION_REASONS: readonly ["configuredTrialsNotAttempted", "noGradeableTrials", "eligibleTrialsBelowMinimum", "completionRateBelowMinimum", "completionRateNotMeasured", "evaluatorErrorRateAboveMaximum", "evaluatorErrorRateNotMeasured", "caseHasNoEligibleTrials", "casePassRateMetThreshold", "casePassRateBelowThreshold", "allMeasuredCasesMetThreshold"]; type EvalVerdictDecisionReason = (typeof EVAL_VERDICT_DECISION_REASONS)[number]; declare const evalVerdictDecisionReasonSchema: z.ZodEnum<{ configuredTrialsNotAttempted: "configuredTrialsNotAttempted"; noGradeableTrials: "noGradeableTrials"; eligibleTrialsBelowMinimum: "eligibleTrialsBelowMinimum"; completionRateBelowMinimum: "completionRateBelowMinimum"; completionRateNotMeasured: "completionRateNotMeasured"; evaluatorErrorRateAboveMaximum: "evaluatorErrorRateAboveMaximum"; evaluatorErrorRateNotMeasured: "evaluatorErrorRateNotMeasured"; caseHasNoEligibleTrials: "caseHasNoEligibleTrials"; casePassRateMetThreshold: "casePassRateMetThreshold"; casePassRateBelowThreshold: "casePassRateBelowThreshold"; allMeasuredCasesMetThreshold: "allMeasuredCasesMetThreshold"; }>; declare function isEvalVerdictDecisionReason(value: unknown): value is EvalVerdictDecisionReason; declare function isEvalValidityDecisionReason(value: unknown): value is EvalValidityDecisionReason; /** * The provider/model pair one aggregate's trials executed under. * * A hosted run FANS ONE CASE across several provider/model pairs, and each pair * numbers its own iterations from 1. A case-only key therefore cannot say which * aggregate is which, and merging the pairs into one row would both destroy the * per-variant verdict and push `configuredTrials` past the portable * {@link MAX_REPETITIONS} range. So identity is `caseId` PLUS this, one row per * variant, and {@link evalVerdictDecisionSchema} refuses a duplicate pair or a * case that mixes variant-keyed and unkeyed rows. * * OMITTED means the run did not fan out — the single execution the suite file * itself describes (`defaults.model` / `case.model`). Absence is not a wildcard * and never matches a variant-keyed row. * * `model` and `provider` are spelled exactly as `evalSuiteFileDefaultsSchema` * spells them, provider included as the same optional disambiguating hint, * because these are the identifiers the suite file authored and a second * spelling would make two names for one pair. */ declare const evalExecutionVariantSchema: z.ZodObject<{ model: z.ZodString; provider: z.ZodOptional; }, z.core.$strict>; type EvalExecutionVariant = z.infer; /** * One case's trials, rolled up — for ONE execution variant when the run fanned * out across provider/model pairs. * * The arithmetic is pinned here because four runtimes must agree on it: * * - `passRate` — `passedTrials / eligibleTrials`. * - `completionRate` — completed attempted trials / `attemptedTrials`. * - `observedStability` — `max(passedTrials, failedTrials) / eligibleTrials`. * A run that failed EVERY eligible trial is perfectly stable: stability * `1`, pass rate `0`. Reading stability as a quality score is the misread * this note exists to prevent. * - `mixedVerdict` — both a pass and a fail are present. Not "unstable": * it is the flag that says the case did not agree with itself. * * `verdict` is `inconclusive` exactly when nothing was eligible, `passed` when * `passRate.value >= effectivePassThreshold` (EQUALITY passes, at every * threshold including `0` and `1`), and `failed` otherwise. */ declare const evalCaseVerdictAggregationStructuralSchema: z.ZodObject<{ caseId: z.ZodString; executionVariant: z.ZodOptional; }, z.core.$strict>>; configuredTrials: z.ZodNumber; attemptedTrials: z.ZodNumber; eligibleTrials: z.ZodNumber; passedTrials: z.ZodNumber; failedTrials: z.ZodNumber; effectivePassThreshold: z.ZodNumber; passRate: z.ZodDiscriminatedUnion<[z.ZodObject<{ state: z.ZodLiteral<"measured">; value: z.ZodNumber; numerator: z.ZodNumber; denominator: z.ZodNumber; exclusions: z.ZodObject<{ notTerminal: z.ZodOptional; skipped: z.ZodOptional; setupFailed: z.ZodOptional; cancelled: z.ZodOptional; timedOut: z.ZodOptional; executionFailed: z.ZodOptional; evaluatorError: z.ZodOptional; }, z.core.$strict>; }, z.core.$strict>, z.ZodObject<{ state: z.ZodLiteral<"notMeasured">; value: z.ZodNull; numerator: z.ZodLiteral<0>; denominator: z.ZodLiteral<0>; exclusions: z.ZodObject<{ notTerminal: z.ZodOptional; skipped: z.ZodOptional; setupFailed: z.ZodOptional; cancelled: z.ZodOptional; timedOut: z.ZodOptional; executionFailed: z.ZodOptional; evaluatorError: z.ZodOptional; }, z.core.$strict>; }, z.core.$strict>], "state">; completionRate: z.ZodDiscriminatedUnion<[z.ZodObject<{ state: z.ZodLiteral<"measured">; value: z.ZodNumber; numerator: z.ZodNumber; denominator: z.ZodNumber; exclusions: z.ZodObject<{ notTerminal: z.ZodOptional; skipped: z.ZodOptional; setupFailed: z.ZodOptional; cancelled: z.ZodOptional; timedOut: z.ZodOptional; executionFailed: z.ZodOptional; evaluatorError: z.ZodOptional; }, z.core.$strict>; }, z.core.$strict>, z.ZodObject<{ state: z.ZodLiteral<"notMeasured">; value: z.ZodNull; numerator: z.ZodLiteral<0>; denominator: z.ZodLiteral<0>; exclusions: z.ZodObject<{ notTerminal: z.ZodOptional; skipped: z.ZodOptional; setupFailed: z.ZodOptional; cancelled: z.ZodOptional; timedOut: z.ZodOptional; executionFailed: z.ZodOptional; evaluatorError: z.ZodOptional; }, z.core.$strict>; }, z.core.$strict>], "state">; observedStability: z.ZodDiscriminatedUnion<[z.ZodObject<{ state: z.ZodLiteral<"measured">; value: z.ZodNumber; numerator: z.ZodNumber; denominator: z.ZodNumber; exclusions: z.ZodObject<{ notTerminal: z.ZodOptional; skipped: z.ZodOptional; setupFailed: z.ZodOptional; cancelled: z.ZodOptional; timedOut: z.ZodOptional; executionFailed: z.ZodOptional; evaluatorError: z.ZodOptional; }, z.core.$strict>; }, z.core.$strict>, z.ZodObject<{ state: z.ZodLiteral<"notMeasured">; value: z.ZodNull; numerator: z.ZodLiteral<0>; denominator: z.ZodLiteral<0>; exclusions: z.ZodObject<{ notTerminal: z.ZodOptional; skipped: z.ZodOptional; setupFailed: z.ZodOptional; cancelled: z.ZodOptional; timedOut: z.ZodOptional; executionFailed: z.ZodOptional; evaluatorError: z.ZodOptional; }, z.core.$strict>; }, z.core.$strict>], "state">; mixedVerdict: z.ZodBoolean; verdict: z.ZodEnum<{ failed: "failed"; passed: "passed"; inconclusive: "inconclusive"; }>; reason: z.ZodEnum<{ configuredTrialsNotAttempted: "configuredTrialsNotAttempted"; noGradeableTrials: "noGradeableTrials"; eligibleTrialsBelowMinimum: "eligibleTrialsBelowMinimum"; completionRateBelowMinimum: "completionRateBelowMinimum"; completionRateNotMeasured: "completionRateNotMeasured"; evaluatorErrorRateAboveMaximum: "evaluatorErrorRateAboveMaximum"; evaluatorErrorRateNotMeasured: "evaluatorErrorRateNotMeasured"; caseHasNoEligibleTrials: "caseHasNoEligibleTrials"; casePassRateMetThreshold: "casePassRateMetThreshold"; casePassRateBelowThreshold: "casePassRateBelowThreshold"; allMeasuredCasesMetThreshold: "allMeasuredCasesMetThreshold"; }>; }, z.core.$strict>; /** * The case validator, with every cross-field rule the structural half cannot * express. * * These are CONSISTENCY checks on a supplied roll-up, not an aggregator: they * refuse a payload whose verdict does not follow from the counts it ships with. * That is what lets this wave pin the semantics before any producer exists — * the expected-output fixtures are checked by the same validator a real * producer will be checked by. */ declare const evalCaseVerdictAggregationSchema: z.ZodObject<{ caseId: z.ZodString; executionVariant: z.ZodOptional; }, z.core.$strict>>; configuredTrials: z.ZodNumber; attemptedTrials: z.ZodNumber; eligibleTrials: z.ZodNumber; passedTrials: z.ZodNumber; failedTrials: z.ZodNumber; effectivePassThreshold: z.ZodNumber; passRate: z.ZodDiscriminatedUnion<[z.ZodObject<{ state: z.ZodLiteral<"measured">; value: z.ZodNumber; numerator: z.ZodNumber; denominator: z.ZodNumber; exclusions: z.ZodObject<{ notTerminal: z.ZodOptional; skipped: z.ZodOptional; setupFailed: z.ZodOptional; cancelled: z.ZodOptional; timedOut: z.ZodOptional; executionFailed: z.ZodOptional; evaluatorError: z.ZodOptional; }, z.core.$strict>; }, z.core.$strict>, z.ZodObject<{ state: z.ZodLiteral<"notMeasured">; value: z.ZodNull; numerator: z.ZodLiteral<0>; denominator: z.ZodLiteral<0>; exclusions: z.ZodObject<{ notTerminal: z.ZodOptional; skipped: z.ZodOptional; setupFailed: z.ZodOptional; cancelled: z.ZodOptional; timedOut: z.ZodOptional; executionFailed: z.ZodOptional; evaluatorError: z.ZodOptional; }, z.core.$strict>; }, z.core.$strict>], "state">; completionRate: z.ZodDiscriminatedUnion<[z.ZodObject<{ state: z.ZodLiteral<"measured">; value: z.ZodNumber; numerator: z.ZodNumber; denominator: z.ZodNumber; exclusions: z.ZodObject<{ notTerminal: z.ZodOptional; skipped: z.ZodOptional; setupFailed: z.ZodOptional; cancelled: z.ZodOptional; timedOut: z.ZodOptional; executionFailed: z.ZodOptional; evaluatorError: z.ZodOptional; }, z.core.$strict>; }, z.core.$strict>, z.ZodObject<{ state: z.ZodLiteral<"notMeasured">; value: z.ZodNull; numerator: z.ZodLiteral<0>; denominator: z.ZodLiteral<0>; exclusions: z.ZodObject<{ notTerminal: z.ZodOptional; skipped: z.ZodOptional; setupFailed: z.ZodOptional; cancelled: z.ZodOptional; timedOut: z.ZodOptional; executionFailed: z.ZodOptional; evaluatorError: z.ZodOptional; }, z.core.$strict>; }, z.core.$strict>], "state">; observedStability: z.ZodDiscriminatedUnion<[z.ZodObject<{ state: z.ZodLiteral<"measured">; value: z.ZodNumber; numerator: z.ZodNumber; denominator: z.ZodNumber; exclusions: z.ZodObject<{ notTerminal: z.ZodOptional; skipped: z.ZodOptional; setupFailed: z.ZodOptional; cancelled: z.ZodOptional; timedOut: z.ZodOptional; executionFailed: z.ZodOptional; evaluatorError: z.ZodOptional; }, z.core.$strict>; }, z.core.$strict>, z.ZodObject<{ state: z.ZodLiteral<"notMeasured">; value: z.ZodNull; numerator: z.ZodLiteral<0>; denominator: z.ZodLiteral<0>; exclusions: z.ZodObject<{ notTerminal: z.ZodOptional; skipped: z.ZodOptional; setupFailed: z.ZodOptional; cancelled: z.ZodOptional; timedOut: z.ZodOptional; executionFailed: z.ZodOptional; evaluatorError: z.ZodOptional; }, z.core.$strict>; }, z.core.$strict>], "state">; mixedVerdict: z.ZodBoolean; verdict: z.ZodEnum<{ failed: "failed"; passed: "passed"; inconclusive: "inconclusive"; }>; reason: z.ZodEnum<{ configuredTrialsNotAttempted: "configuredTrialsNotAttempted"; noGradeableTrials: "noGradeableTrials"; eligibleTrialsBelowMinimum: "eligibleTrialsBelowMinimum"; completionRateBelowMinimum: "completionRateBelowMinimum"; completionRateNotMeasured: "completionRateNotMeasured"; evaluatorErrorRateAboveMaximum: "evaluatorErrorRateAboveMaximum"; evaluatorErrorRateNotMeasured: "evaluatorErrorRateNotMeasured"; caseHasNoEligibleTrials: "caseHasNoEligibleTrials"; casePassRateMetThreshold: "casePassRateMetThreshold"; casePassRateBelowThreshold: "casePassRateBelowThreshold"; allMeasuredCasesMetThreshold: "allMeasuredCasesMetThreshold"; }>; }, z.core.$strict>; type EvalCaseVerdictAggregation = z.infer; /** * The decision summary: one run's verdict, the validity phase that gated it, * and every case that fed it. * * `reasons` is ORDERED and non-empty, and it is the audit trail rather than * decoration — an `inconclusive` verdict whose reasons are all task reasons is * refused, because it claims the validity phase concluded something it cannot. */ declare const evalVerdictDecisionStructuralSchema: z.ZodObject<{ verdictPolicyVersion: z.ZodLiteral<2>; verdict: z.ZodEnum<{ failed: "failed"; passed: "passed"; inconclusive: "inconclusive"; }>; reasons: z.ZodArray>; validity: z.ZodObject<{ policy: z.ZodObject<{ coverage: z.ZodDiscriminatedUnion<[z.ZodObject<{ kind: z.ZodLiteral<"allConfiguredTrialsAttempted">; minGradeableTrials: z.ZodLiteral<1>; }, z.core.$strict>, z.ZodObject<{ kind: z.ZodLiteral<"minEligibleTrials">; minEligibleTrials: z.ZodNumber; }, z.core.$strict>], "kind">; minCompletionRate: z.ZodNumber; maxEvaluatorErrorRate: z.ZodNumber; }, z.core.$strict>; holds: z.ZodBoolean; configuredTrials: z.ZodNumber; attemptedTrials: z.ZodNumber; eligibleTrials: z.ZodNumber; completionRate: z.ZodDiscriminatedUnion<[z.ZodObject<{ state: z.ZodLiteral<"measured">; value: z.ZodNumber; numerator: z.ZodNumber; denominator: z.ZodNumber; exclusions: z.ZodObject<{ notTerminal: z.ZodOptional; skipped: z.ZodOptional; setupFailed: z.ZodOptional; cancelled: z.ZodOptional; timedOut: z.ZodOptional; executionFailed: z.ZodOptional; evaluatorError: z.ZodOptional; }, z.core.$strict>; }, z.core.$strict>, z.ZodObject<{ state: z.ZodLiteral<"notMeasured">; value: z.ZodNull; numerator: z.ZodLiteral<0>; denominator: z.ZodLiteral<0>; exclusions: z.ZodObject<{ notTerminal: z.ZodOptional; skipped: z.ZodOptional; setupFailed: z.ZodOptional; cancelled: z.ZodOptional; timedOut: z.ZodOptional; executionFailed: z.ZodOptional; evaluatorError: z.ZodOptional; }, z.core.$strict>; }, z.core.$strict>], "state">; evaluatorErrorRate: z.ZodDiscriminatedUnion<[z.ZodObject<{ state: z.ZodLiteral<"measured">; value: z.ZodNumber; numerator: z.ZodNumber; denominator: z.ZodNumber; exclusions: z.ZodObject<{ notTerminal: z.ZodOptional; skipped: z.ZodOptional; setupFailed: z.ZodOptional; cancelled: z.ZodOptional; timedOut: z.ZodOptional; executionFailed: z.ZodOptional; evaluatorError: z.ZodOptional; }, z.core.$strict>; }, z.core.$strict>, z.ZodObject<{ state: z.ZodLiteral<"notMeasured">; value: z.ZodNull; numerator: z.ZodLiteral<0>; denominator: z.ZodLiteral<0>; exclusions: z.ZodObject<{ notTerminal: z.ZodOptional; skipped: z.ZodOptional; setupFailed: z.ZodOptional; cancelled: z.ZodOptional; timedOut: z.ZodOptional; executionFailed: z.ZodOptional; evaluatorError: z.ZodOptional; }, z.core.$strict>; }, z.core.$strict>], "state">; }, z.core.$strict>; cases: z.ZodArray; }, z.core.$strict>>; configuredTrials: z.ZodNumber; attemptedTrials: z.ZodNumber; eligibleTrials: z.ZodNumber; passedTrials: z.ZodNumber; failedTrials: z.ZodNumber; effectivePassThreshold: z.ZodNumber; passRate: z.ZodDiscriminatedUnion<[z.ZodObject<{ state: z.ZodLiteral<"measured">; value: z.ZodNumber; numerator: z.ZodNumber; denominator: z.ZodNumber; exclusions: z.ZodObject<{ notTerminal: z.ZodOptional; skipped: z.ZodOptional; setupFailed: z.ZodOptional; cancelled: z.ZodOptional; timedOut: z.ZodOptional; executionFailed: z.ZodOptional; evaluatorError: z.ZodOptional; }, z.core.$strict>; }, z.core.$strict>, z.ZodObject<{ state: z.ZodLiteral<"notMeasured">; value: z.ZodNull; numerator: z.ZodLiteral<0>; denominator: z.ZodLiteral<0>; exclusions: z.ZodObject<{ notTerminal: z.ZodOptional; skipped: z.ZodOptional; setupFailed: z.ZodOptional; cancelled: z.ZodOptional; timedOut: z.ZodOptional; executionFailed: z.ZodOptional; evaluatorError: z.ZodOptional; }, z.core.$strict>; }, z.core.$strict>], "state">; completionRate: z.ZodDiscriminatedUnion<[z.ZodObject<{ state: z.ZodLiteral<"measured">; value: z.ZodNumber; numerator: z.ZodNumber; denominator: z.ZodNumber; exclusions: z.ZodObject<{ notTerminal: z.ZodOptional; skipped: z.ZodOptional; setupFailed: z.ZodOptional; cancelled: z.ZodOptional; timedOut: z.ZodOptional; executionFailed: z.ZodOptional; evaluatorError: z.ZodOptional; }, z.core.$strict>; }, z.core.$strict>, z.ZodObject<{ state: z.ZodLiteral<"notMeasured">; value: z.ZodNull; numerator: z.ZodLiteral<0>; denominator: z.ZodLiteral<0>; exclusions: z.ZodObject<{ notTerminal: z.ZodOptional; skipped: z.ZodOptional; setupFailed: z.ZodOptional; cancelled: z.ZodOptional; timedOut: z.ZodOptional; executionFailed: z.ZodOptional; evaluatorError: z.ZodOptional; }, z.core.$strict>; }, z.core.$strict>], "state">; observedStability: z.ZodDiscriminatedUnion<[z.ZodObject<{ state: z.ZodLiteral<"measured">; value: z.ZodNumber; numerator: z.ZodNumber; denominator: z.ZodNumber; exclusions: z.ZodObject<{ notTerminal: z.ZodOptional; skipped: z.ZodOptional; setupFailed: z.ZodOptional; cancelled: z.ZodOptional; timedOut: z.ZodOptional; executionFailed: z.ZodOptional; evaluatorError: z.ZodOptional; }, z.core.$strict>; }, z.core.$strict>, z.ZodObject<{ state: z.ZodLiteral<"notMeasured">; value: z.ZodNull; numerator: z.ZodLiteral<0>; denominator: z.ZodLiteral<0>; exclusions: z.ZodObject<{ notTerminal: z.ZodOptional; skipped: z.ZodOptional; setupFailed: z.ZodOptional; cancelled: z.ZodOptional; timedOut: z.ZodOptional; executionFailed: z.ZodOptional; evaluatorError: z.ZodOptional; }, z.core.$strict>; }, z.core.$strict>], "state">; mixedVerdict: z.ZodBoolean; verdict: z.ZodEnum<{ failed: "failed"; passed: "passed"; inconclusive: "inconclusive"; }>; reason: z.ZodEnum<{ configuredTrialsNotAttempted: "configuredTrialsNotAttempted"; noGradeableTrials: "noGradeableTrials"; eligibleTrialsBelowMinimum: "eligibleTrialsBelowMinimum"; completionRateBelowMinimum: "completionRateBelowMinimum"; completionRateNotMeasured: "completionRateNotMeasured"; evaluatorErrorRateAboveMaximum: "evaluatorErrorRateAboveMaximum"; evaluatorErrorRateNotMeasured: "evaluatorErrorRateNotMeasured"; caseHasNoEligibleTrials: "caseHasNoEligibleTrials"; casePassRateMetThreshold: "casePassRateMetThreshold"; casePassRateBelowThreshold: "casePassRateBelowThreshold"; allMeasuredCasesMetThreshold: "allMeasuredCasesMetThreshold"; }>; }, z.core.$strict>>; }, z.core.$strict>; /** * The decision validator. * * Beyond re-running every case's own consistency rules, it pins the two * things the phase ORDER means: * * 1. `holds: false` ⇒ `inconclusive`, and every reason is a validity reason. * 2. `holds: true` ⇒ the verdict follows from the cases alone: any * unmeasured case ⇒ `inconclusive`; any measured case under its own * threshold ⇒ `failed`; otherwise `passed`. * * `holds` itself is checked against the measurements it claims to summarize, so * a producer cannot assert validity it did not have, and `reasons` must be * EXACTLY the reasons those checks produce — in the vocabulary's own order, * with nothing missing and nothing invented. A reason list that is merely * plausible is an audit trail that cannot be audited: two producers would * disagree byte-for-byte on the same run, and a reader could not tell which * check actually failed. This is validation of a supplied summary — no verdict * is produced from trials anywhere in this file. */ declare const evalVerdictDecisionSchema: z.ZodObject<{ verdictPolicyVersion: z.ZodLiteral<2>; verdict: z.ZodEnum<{ failed: "failed"; passed: "passed"; inconclusive: "inconclusive"; }>; reasons: z.ZodArray>; validity: z.ZodObject<{ policy: z.ZodObject<{ coverage: z.ZodDiscriminatedUnion<[z.ZodObject<{ kind: z.ZodLiteral<"allConfiguredTrialsAttempted">; minGradeableTrials: z.ZodLiteral<1>; }, z.core.$strict>, z.ZodObject<{ kind: z.ZodLiteral<"minEligibleTrials">; minEligibleTrials: z.ZodNumber; }, z.core.$strict>], "kind">; minCompletionRate: z.ZodNumber; maxEvaluatorErrorRate: z.ZodNumber; }, z.core.$strict>; holds: z.ZodBoolean; configuredTrials: z.ZodNumber; attemptedTrials: z.ZodNumber; eligibleTrials: z.ZodNumber; completionRate: z.ZodDiscriminatedUnion<[z.ZodObject<{ state: z.ZodLiteral<"measured">; value: z.ZodNumber; numerator: z.ZodNumber; denominator: z.ZodNumber; exclusions: z.ZodObject<{ notTerminal: z.ZodOptional; skipped: z.ZodOptional; setupFailed: z.ZodOptional; cancelled: z.ZodOptional; timedOut: z.ZodOptional; executionFailed: z.ZodOptional; evaluatorError: z.ZodOptional; }, z.core.$strict>; }, z.core.$strict>, z.ZodObject<{ state: z.ZodLiteral<"notMeasured">; value: z.ZodNull; numerator: z.ZodLiteral<0>; denominator: z.ZodLiteral<0>; exclusions: z.ZodObject<{ notTerminal: z.ZodOptional; skipped: z.ZodOptional; setupFailed: z.ZodOptional; cancelled: z.ZodOptional; timedOut: z.ZodOptional; executionFailed: z.ZodOptional; evaluatorError: z.ZodOptional; }, z.core.$strict>; }, z.core.$strict>], "state">; evaluatorErrorRate: z.ZodDiscriminatedUnion<[z.ZodObject<{ state: z.ZodLiteral<"measured">; value: z.ZodNumber; numerator: z.ZodNumber; denominator: z.ZodNumber; exclusions: z.ZodObject<{ notTerminal: z.ZodOptional; skipped: z.ZodOptional; setupFailed: z.ZodOptional; cancelled: z.ZodOptional; timedOut: z.ZodOptional; executionFailed: z.ZodOptional; evaluatorError: z.ZodOptional; }, z.core.$strict>; }, z.core.$strict>, z.ZodObject<{ state: z.ZodLiteral<"notMeasured">; value: z.ZodNull; numerator: z.ZodLiteral<0>; denominator: z.ZodLiteral<0>; exclusions: z.ZodObject<{ notTerminal: z.ZodOptional; skipped: z.ZodOptional; setupFailed: z.ZodOptional; cancelled: z.ZodOptional; timedOut: z.ZodOptional; executionFailed: z.ZodOptional; evaluatorError: z.ZodOptional; }, z.core.$strict>; }, z.core.$strict>], "state">; }, z.core.$strict>; cases: z.ZodArray; }, z.core.$strict>>; configuredTrials: z.ZodNumber; attemptedTrials: z.ZodNumber; eligibleTrials: z.ZodNumber; passedTrials: z.ZodNumber; failedTrials: z.ZodNumber; effectivePassThreshold: z.ZodNumber; passRate: z.ZodDiscriminatedUnion<[z.ZodObject<{ state: z.ZodLiteral<"measured">; value: z.ZodNumber; numerator: z.ZodNumber; denominator: z.ZodNumber; exclusions: z.ZodObject<{ notTerminal: z.ZodOptional; skipped: z.ZodOptional; setupFailed: z.ZodOptional; cancelled: z.ZodOptional; timedOut: z.ZodOptional; executionFailed: z.ZodOptional; evaluatorError: z.ZodOptional; }, z.core.$strict>; }, z.core.$strict>, z.ZodObject<{ state: z.ZodLiteral<"notMeasured">; value: z.ZodNull; numerator: z.ZodLiteral<0>; denominator: z.ZodLiteral<0>; exclusions: z.ZodObject<{ notTerminal: z.ZodOptional; skipped: z.ZodOptional; setupFailed: z.ZodOptional; cancelled: z.ZodOptional; timedOut: z.ZodOptional; executionFailed: z.ZodOptional; evaluatorError: z.ZodOptional; }, z.core.$strict>; }, z.core.$strict>], "state">; completionRate: z.ZodDiscriminatedUnion<[z.ZodObject<{ state: z.ZodLiteral<"measured">; value: z.ZodNumber; numerator: z.ZodNumber; denominator: z.ZodNumber; exclusions: z.ZodObject<{ notTerminal: z.ZodOptional; skipped: z.ZodOptional; setupFailed: z.ZodOptional; cancelled: z.ZodOptional; timedOut: z.ZodOptional; executionFailed: z.ZodOptional; evaluatorError: z.ZodOptional; }, z.core.$strict>; }, z.core.$strict>, z.ZodObject<{ state: z.ZodLiteral<"notMeasured">; value: z.ZodNull; numerator: z.ZodLiteral<0>; denominator: z.ZodLiteral<0>; exclusions: z.ZodObject<{ notTerminal: z.ZodOptional; skipped: z.ZodOptional; setupFailed: z.ZodOptional; cancelled: z.ZodOptional; timedOut: z.ZodOptional; executionFailed: z.ZodOptional; evaluatorError: z.ZodOptional; }, z.core.$strict>; }, z.core.$strict>], "state">; observedStability: z.ZodDiscriminatedUnion<[z.ZodObject<{ state: z.ZodLiteral<"measured">; value: z.ZodNumber; numerator: z.ZodNumber; denominator: z.ZodNumber; exclusions: z.ZodObject<{ notTerminal: z.ZodOptional; skipped: z.ZodOptional; setupFailed: z.ZodOptional; cancelled: z.ZodOptional; timedOut: z.ZodOptional; executionFailed: z.ZodOptional; evaluatorError: z.ZodOptional; }, z.core.$strict>; }, z.core.$strict>, z.ZodObject<{ state: z.ZodLiteral<"notMeasured">; value: z.ZodNull; numerator: z.ZodLiteral<0>; denominator: z.ZodLiteral<0>; exclusions: z.ZodObject<{ notTerminal: z.ZodOptional; skipped: z.ZodOptional; setupFailed: z.ZodOptional; cancelled: z.ZodOptional; timedOut: z.ZodOptional; executionFailed: z.ZodOptional; evaluatorError: z.ZodOptional; }, z.core.$strict>; }, z.core.$strict>], "state">; mixedVerdict: z.ZodBoolean; verdict: z.ZodEnum<{ failed: "failed"; passed: "passed"; inconclusive: "inconclusive"; }>; reason: z.ZodEnum<{ configuredTrialsNotAttempted: "configuredTrialsNotAttempted"; noGradeableTrials: "noGradeableTrials"; eligibleTrialsBelowMinimum: "eligibleTrialsBelowMinimum"; completionRateBelowMinimum: "completionRateBelowMinimum"; completionRateNotMeasured: "completionRateNotMeasured"; evaluatorErrorRateAboveMaximum: "evaluatorErrorRateAboveMaximum"; evaluatorErrorRateNotMeasured: "evaluatorErrorRateNotMeasured"; caseHasNoEligibleTrials: "caseHasNoEligibleTrials"; casePassRateMetThreshold: "casePassRateMetThreshold"; casePassRateBelowThreshold: "casePassRateBelowThreshold"; allMeasuredCasesMetThreshold: "allMeasuredCasesMetThreshold"; }>; }, z.core.$strict>>; }, z.core.$strict>; type EvalVerdictDecision = z.infer; type EvalVerdictValidity = EvalVerdictDecision["validity"]; type EvalExpectedToolCall = { toolName: string; arguments?: Record; }; type EvalCiMetadata = { provider?: string; pipelineId?: string; jobId?: string; runUrl?: string; branch?: string; commitSha?: string; }; type EvalTraceSpanCategory = "step" | "llm" | "tool" | "error" | "connection" | "discovery"; type EvalTraceSpanStatus = "ok" | "error"; type EvalTraceSpanInput = { id: string; parentId?: string; name: string; category: EvalTraceSpanCategory; startMs: number; endMs: number; promptIndex?: number; stepIndex?: number; status?: EvalTraceSpanStatus; toolCallId?: string; toolName?: string; serverId?: string; modelId?: string; inputTokens?: number; outputTokens?: number; totalTokens?: number; messageStartIndex?: number; messageEndIndex?: number; finishReason?: string; provider?: string; responseId?: string; responseTimestamp?: string; ttfcMs?: number; mcpErrorCode?: number; }; type EvalTraceInput = string | Array<{ role: string; content: unknown; }> | { messages?: Array<{ role: string; content: unknown; }>; spans?: EvalTraceSpanInput[]; prompts?: unknown[]; raw?: unknown; }; type EvalWidgetCsp = { connectDomains?: string[]; resourceDomains?: string[]; frameDomains?: string[]; baseUriDomains?: string[]; }; type EvalWidgetPermissions = { camera?: Record; microphone?: Record; geolocation?: Record; clipboardWrite?: Record; }; type EvalWidgetSnapshotInput = { toolCallId: string; toolName: string; protocol: "mcp-apps"; serverId: string; resourceUri: string; toolMetadata: Record; widgetCsp: EvalWidgetCsp | null; widgetPermissions: EvalWidgetPermissions | null; widgetPermissive: boolean; prefersBorder: boolean; widgetHtml?: string; widgetHtmlBlobId?: string; injectedOpenAiCompat?: boolean; }; type EvalResultInput = { caseTitle: string; query?: string; passed: boolean; durationMs?: number; provider?: string; model?: string; expectedToolCalls?: EvalExpectedToolCall[]; actualToolCalls?: EvalExpectedToolCall[]; tokens?: { input?: number; output?: number; total?: number; }; error?: string; errorDetails?: string; trace?: EvalTraceInput; externalIterationId?: string; externalCaseId?: string; /** * The case's DECLARED identity (`EvalTestConfig.id`) — the id an author * committed beside the test. * * The backend resolves by this first (`by_testSuite_declaredCaseId`), falling * back to the content-hash key, and ADOPTS: an id-bearing upload that resolves * by hash to a case with no declared id patches the id on without touching the * immutable `caseKey`. That is what lets a renamed test keep its history. * * Must equal `externalCaseId` when both are present — the SDK enforces that at * construction and the backend rejects a mismatch at ingest. Never a silent * precedence between two identity claims. */ caseId?: string; /** * This trial's LIFECYCLE status — what happened to the execution, which is a * different question from `passed` (the task verdict). * * A graded failure is `completed` + `passed: false`: the trial ran, and the * server under test failed it. `failed` means the EXECUTION failed, * `setup_failed` that the environment never came up, `skipped` that the trial * was deliberately not run, `timed_out`/`cancelled` that it was stopped. * `pending`/`running` are non-terminal and rejected at ingest — a finished * trial cannot describe itself as still going. * * Optional ONLY for the legacy wire: a reporter that predates the verdict * policy omits it and the backend's named compatibility adapter derives a * status from the presence of an execution error (never from `passed`). Every * v2 report sends it. */ status?: IterationStatus; /** Extensible per-iteration metadata; predicate verdicts are nested here. */ metadata?: Record; isNegativeTest?: boolean; /** Reference output for judge scorers; emitted by the result mappers. */ expectedOutput?: string; advancedConfig?: Record; widgetSnapshots?: EvalWidgetSnapshotInput[]; /** * Per-result match options. When present, the inspector snapshots * these onto the appended iteration's `testCaseSnapshot.matchOptions` * so historical pass/fail computation honors them. */ matchOptions?: EvalMatchOptions; }; type MCPServerReplayConfig = { serverId: string; url: string; preferSSE?: boolean; accessToken?: string; refreshToken?: string; clientId?: string; clientSecret?: string; }; type MCPJamReportingConfig = { enabled?: boolean; apiKey?: string; baseUrl?: string; /** * MCPJam project id results are filed under (`MCPJAM_PROJECT_ID` env var * works too). Defaults to the API key org's Default project. */ project?: string; serverNames?: string[]; serverReplayConfigs?: MCPServerReplayConfig[]; suiteName?: string; suiteDescription?: string; notes?: string; passCriteria?: { minimumPassRate: number; }; strict?: boolean; /** * When not `false`, auto-reported results fail if the trace shows tool * execution errors. Default: strict tool outcomes (equivalent to `true`). */ failOnToolError?: boolean; externalRunId?: string; framework?: string; ci?: EvalCiMetadata; expectedIterations?: number; tags?: string[]; /** * Host configuration that drove this eval run. Sent unconditionally: the * `GET /sdk/v1/info` capability probe this once negotiated through was * deleted, and no probe replaced it — an older backend ignores the extra * field rather than rejecting it. When `iteration.hostSnapshot` is present * (Stage 4 per-iteration capture), it takes precedence; this field is the * fallback for executors that don't expose `getHostSnapshot` and runs * without per-iteration capture. The reporter computes the content * hash internally — callers never set `hostConfigHash`. */ host?: Host; /** * `evaluationConfigHash` for this run — the digest of the scorer definitions * every iteration graded with. * * Sent on the run-start body so the backend can persist it on * `testSuiteRun` and fold it into the run fingerprint: reusing an * `externalRunId` with a different evaluation config is a conflict, not a * duplicate. Same no-probe rule as `host` above — an un-upgraded backend * ignores it. */ evaluationConfigHash?: string; /** * Verdict-policy v2 run configuration, frozen by the backend at run start. * * PRESENT ⇒ this run is decided under `EVAL_VERDICT_POLICY_VERSION`: * per-case `repetitions`, FRACTIONAL `passThreshold`s in [0,1], and an * explicit validity policy. ABSENT ⇒ the run is decided the legacy way, by * suite-wide {@link MCPJamReportingConfig.passCriteria.minimumPassRate} * PERCENT. * * The two are never mixed: a fraction reinterpreted as a percent (or the * reverse) reports a verdict for a question nobody asked, so the backend * REFUSES a v2 request it cannot honor rather than falling back. */ verdictPolicy?: EvalRunVerdictPolicyRequest; }; /** One case's v2 policy, as declared on the run-start body. */ type EvalRunVerdictPolicyCaseRequest = { /** The case's DECLARED identity — must match the results' `caseId`. */ caseId: string; /** * The (model, provider) combinations this case runs under. Each variant owns * its own trials and its own aggregate, so a case that passes on one model * and fails on another cannot average into a single misleading verdict. */ executionVariants?: EvalExecutionVariant[]; /** Overrides the suite default; a COUNT of trials, not a rate. */ repetitions?: number; /** Overrides the suite default. A FRACTION in [0,1], never a percent. */ passThreshold?: number; }; type EvalRunVerdictPolicyRequest = { verdictPolicyVersion: EvalVerdictPolicyVersion; defaults: { repetitions: number; /** FRACTION in [0,1]. */ passThreshold: number; /** * DECLARED, not resolved: an omitted `minEligibleTrials` is the * "every configured trial attempted" coverage rule, which is a different * claim from any number. The backend resolves and FREEZES it on the run. */ validity?: EvalSuiteFileValidity; }; cases: EvalRunVerdictPolicyCaseRequest[]; }; type ReportEvalResultsInput = MCPJamReportingConfig & { suiteName: string; results: EvalResultInput[]; agent?: { getServerReplayConfigs?: () => MCPServerReplayConfig[] | undefined; }; /** * Optional executor surface used by Stage 5 host-config wire pickup as * a fallback when no per-iteration `hostSnapshot` and no * {@link MCPJamReportingConfig.host} were supplied. Structurally typed * so any object exposing `getHostSnapshot()` (e.g. `HostRunner`, * `HostRuntime`) qualifies — the reporter never holds a reference * beyond reading the snapshot. */ executor?: { getHostSnapshot?: () => HostJson | undefined; }; mcpClientManager?: MCPClientManager; }; type ReportEvalResultsOutput = { suiteId: string; runId: string; /** * The project the run landed in, echoed by the ingest response. Present * only against a backend that sends it — deliberately optional so an older * deployment still parses, and so the zero-config `project: "default"` case * (where the client never knew the id) resolves to a real one. * * Its job is the deep link: without it a printed run URL cannot carry * `?project=`, and the app has to guess which project to open. */ projectId?: string; status: "completed" | "failed"; /** * The run's verdict. * * `inconclusive` is a v2 outcome and NOT a synonym for `failed`: it means * nobody could measure the run (no eligible trials, evaluator errors over * the policy's ceiling, evidence outside the run's frozen snapshot). A gate * that collapses it into `failed` reports the server under test as broken * when the harness was. */ result: "passed" | "failed" | "inconclusive"; summary: { total: number; passed: number; failed: number; passRate: number; }; /** * The policy version the backend actually decided under. ABSENT ⇒ legacy * suite-wide percentage aggregation, which is also what an un-upgraded * deployment reports. */ verdictPolicyVersion?: EvalVerdictPolicyVersion; /** * The v2 decision, verbatim from the backend: per-case aggregates with their * eligible denominators, the validity outcome, and the exact reasons. * * Consumers RENDER this; they never recompute a verdict from it. The backend * decided against the run's frozen snapshot, and a client re-deriving one * from iteration rows can only disagree with the gate that already ran. */ verdictSummary?: EvalVerdictDecision; /** * Why a v2 run could not be decided, set alongside `result: "inconclusive"`. */ verdictPolicyIntegrityError?: string; }; /** * Notification handler management for MCPClientManager */ type NotificationMethodName = ManagedMcpClientNotificationMethod; type NotificationHandler = ManagedMcpClientNotificationHandler; /** * Tasks wire dispatch — the single place that decides *which* tasks wire (if * any) a given connection speaks. * * Two mutually exclusive wires exist: * * - `"legacy"` — the in-core 2025-11-25 experimental tasks utility * (`params.task = {ttl?}`, `tasks/list|get|result|cancel`). * - `"extension"` — `io.modelcontextprotocol/tasks` (SEP-2663), the * 2026-07-28+ extension. Server-decided, no `params.task`. * * Routing rules (see the dispatch matrix in the tasks restoration plan): * * | version | legacy caps | extension cap | wire | * |--------------------|-------------|---------------|-------------| * | 2025-03-26/06-18 | ignored | ignored | none | * | 2025-11-25 | present | treated absent| legacy | * | 2025-11-25 | absent | ignored | none | * | >= 2026-07-28 | ignored | present | extension | * | >= 2026-07-28 | ignored | absent | none | * * Unknown / absent versions **fail closed** to `"none"` — an unvalidated * version string must never route (see `mcp-protocol-version.ts`). */ /** The tasks wire a connection speaks. */ type TasksWire = "none" | "legacy" | "extension"; /** * Everything a caller (route, UI, CLI) needs to know about a connection's * tasks capability, derived in ONE place so no other module has to know the * per-wire rules. */ interface TasksSupport { wire: TasksWire; /** A `tools/call` may produce a task on this connection. */ toolCalls: boolean; /** `tasks/list` exists (legacy only; on the extension the client tracks). */ list: boolean; /** `tasks/cancel` may be sent. */ cancel: boolean; /** `tasks/update` exists (extension only). */ update: boolean; /** A completed `tasks/get` carries its result inline (extension only). */ inlineResult: boolean; } /** * Resolves the full tasks support matrix for a connection. This module is the * only place allowed to consult the extension capability, which is what keeps * the "treat as absent on 2025-11-25" rule honest. */ declare function resolveTasksSupport(protocolVersion: string | undefined, capabilities: ServerCapabilities | undefined): TasksSupport; /** * Skills wire dispatch — the single place that decides whether a given * connection speaks `io.modelcontextprotocol/skills` (SEP-2640). * * Unlike tasks (SEP-2663), skills has exactly ONE wire and no legacy * predecessor, so there is no era matrix here. The extension is negotiated * connection-level per SEP-2133: BOTH sides declare it in their `initialize` * capabilities, and the methods (`skills/list`, `skills/get`, * `resources/directory/read`) carry no per-request declaration. * * The gate is therefore a conjunction, and it is deliberately symmetric: * * active = client advertised the extension ∧ server declared it * * The client half is the **advertise = enforce** rule. MCPJam is a debugger: * a connection whose declared capabilities omit the extension must produce * ZERO `skills/*` frames, because the whole product claim is that what the * user sees in Tracing is what a host with those capabilities would send. * Probing an undeclared method "just to see" would be a lie about the * emulated client. * * ## No era gate * * `skills/list` / `skills/get` appear in NO protocol-version codec — neither * the 2025-11-25 in-core registry nor the 2026-07-28 one — exactly like * `tasks/update` before its era-gate shadow was needed. The tasks extension * needs `tasks-ext-era-gate.ts` because upstream gates OUTBOUND requests whose * method IS in a codec but for the wrong era; a method in no codec at all is * era-blind and rides `requestWithSchema` on every negotiated version. Two * consequences, both intentional: * * 1. no `FIRST_EXTENSION_VERSION` constant here — a 2025-06-18 server that * declares the extension is served, and `skills-ext.test.ts` pins that * dual-era behavior; * 2. nothing in this module reads the negotiated protocol version. */ /** Extension id for the SEP-2640 skills extension. */ declare const MCP_SKILLS_EXTENSION_ID: "io.modelcontextprotocol/skills"; /** * Whether a SERVER declares `io.modelcontextprotocol/skills` in its * `initialize` capabilities. */ declare function serverDeclaresSkillsExtension(capabilities: ServerCapabilities | undefined): boolean; /** * Whether the CLIENT advertised the extension on this connection. Reads the * exact capability object the manager handed to `new Client(...)`, so it can * never disagree with what went on the wire. */ declare function clientDeclaresSkillsExtension(capabilities: ClientCapabilityOptions | undefined): boolean; /** * Whether the server opted into the OPTIONAL `resources/directory/read` * method by setting `{ directoryRead: true }` in its extension settings. * * Strict `=== true`: SEP-2640 defines the setting as a boolean, and a * truthy-but-not-true value (`"yes"`, `1`) is a malformed declaration. Failing * closed here costs one optional convenience method; failing open would send a * method the server never agreed to answer. */ declare function skillsDirectoryReadEnabled(capabilities: ServerCapabilities | undefined): boolean; /** * Everything a caller (route, UI, CLI, chat tool) needs to know about a * connection's skills capability, derived in ONE place. */ interface SkillsSupport { /** The server declared the extension in its initialize capabilities. */ declared: boolean; /** THIS client advertised the extension on this connection. */ advertised: boolean; /** Server opted into `resources/directory/read`. Implies `declared`. */ directoryRead: boolean; /** * The gate every `skills/*` call site must check: mutual declaration. * `false` ⇒ the SDK refuses to send, rather than probing. */ active: boolean; } /** Resolves the full skills support matrix for a connection. */ declare function resolveSkillsSupport(clientCapabilities: ClientCapabilityOptions | undefined, serverCapabilities: ServerCapabilities | undefined): SkillsSupport; /** * Public types for `io.modelcontextprotocol/skills` (SEP-2640). * * Kept separate from the zod mirrors so app code can import the shapes without * pulling the validators. * * NO index signatures. The mirrors pass unknown keys through at RUNTIME (a * debugger must show what the server sent), but promising them in the PUBLIC * type would let consumers write code against arbitrary extension fields and * make any future tightening a breaking change. Passthrough is behavior, not * contract. */ /** One file in a skill's `resources` manifest. */ interface SkillResourceRef { /** Resource URI, resolvable via plain `resources/read`. */ uri: string; /** `:` — e.g. `sha256:ab12…`. */ digest: string; } /** * A skill as the server describes it. IDENTITY IS `uri`, not * `frontmatter.name` — the SEP is explicit that the name is a label and the * URI is the identity, and two skills served by one server may legally share * a name (`acme/billing/refunds` vs `acme/support/refunds`). */ interface SkillEntry { uri: string; /** * The SKILL.md frontmatter, VERBATIM. Typed `unknown` because the host * re-checks it field-by-field against the fetched file: a parsed-and- * re-serialized copy would not be the thing under comparison. */ frontmatter: unknown; /** * The complete set of files this skill is allowed to fetch. A read of any * URI absent from this list MUST fail (SEP-2640 integrity rules). * * Optional on the wire; MCPJam refuses to load an entry without one. */ resources?: SkillResourceRef[]; } /** `skills/list` result, with SEP-2549 caching attributes preserved. */ interface SkillsExtListResult { skills: SkillEntry[]; nextCursor?: string; /** SEP-2549: how long this listing may be cached, in milliseconds. */ ttlMs?: number; /** SEP-2549: the scope the cache entry is valid for. */ cacheScope?: string; } /** One entry from the optional `resources/directory/read`. */ interface SkillsDirectoryEntry { uri: string; name?: string; mimeType?: string; size?: number; } interface SkillsDirectoryReadResult { resources: SkillsDirectoryEntry[]; nextCursor?: string; } /** The mimeType that marks a directory resource (SEP-2640). */ declare const INODE_DIRECTORY_MIME_TYPE: "inode/directory"; /** * The frontmatter fields SEP-2640 requires a host to re-check field-by-field * against the fetched SKILL.md. `name` additionally must equal the URI's final * path segment. */ interface SkillIdentityFrontmatter { name: string; description: string; } /** * Era-neutral subscription coordinator (MCP 2026-07-28 `subscriptions/listen`). * * ## Why this is not "the legacy model plus a new RPC" * * The 2025-era model is a *set of subscribed resource URIs* plus unsolicited * list-changed notifications that arrive on whatever channel the transport * happens to keep open. There is no stream identity, no acknowledgement, no * close reason, and no way for a debugger to show what the server actually * agreed to send. * * The 2026-07-28 model is a *long-lived, explicitly-filtered stream*: the * client sends `subscriptions/listen` with a filter, the server replies with * `notifications/subscriptions/acknowledged` carrying the subset it agreed to * honor, every subsequent notification is stamped with the subscription id, * and the stream ends either gracefully (an empty `subscriptions/listen` * result), remotely (transport loss, no result), or locally (client abort). * * So the product state model here is era-neutral and adapter-specific: * * - **Desired interests** — what the user wants, independent of era: * tools/prompts/resources list-changed toggles plus a set of resource URIs * ({@link DesiredSubscriptionInterests}). * - **Streams** — zero or more {@link SubscriptionStreamRecord}s, each with a * local MCPJam id, the MCP subscription id (when known), the *requested* * filter, the *acknowledged* filter (tracked separately — they are not the * same fact), a status, lifecycle timestamps, and a reconnect attempt * counter. * - **Legacy adapter** — the existing list-changed handlers plus * `resources/subscribe` / `resources/unsubscribe` per URI, modelled as one * synthetic stream so the debugger has a single shape to render. * - **Modern adapter** — an explicit `client.listen(filter)`. Resource URIs * ride in `resourceSubscriptions`. Changing the desired filter closes and * reopens the stream (a filter is fixed for the life of a subscription); * an unexpected remote loss triggers a bounded re-*listen* — never a * resume, since there is no `Last-Event-ID`/replay for listen streams. * * ## Deliberate choices * * - **Explicit `listen()` over `ClientOptions.listChanged`.** The auto-opened * subscription hides the requested filter, the subscription identity, the * ack timing and the close reason — exactly the facts a debugger exists to * show. MCPJam always drives `listen()` itself. * - **Advertise = enforce, and show the absence.** A selection the server does * not advertise is *omitted* from the requested filter and recorded as * {@link SubscriptionInterestRejection} so the UI can render it as rejected * rather than silently dropping it. * - **Ack before active.** A stream stays `opening` until the acknowledgement * is observed; only then does it become `active` with an acknowledged filter. * - **Handlers registered once, demultiplexed by subscription id.** Multiple * concurrent subscriptions are legal, so per-stream handler registration * would fan a single notification out to the wrong streams. * - **Unrequested notification types are rejected**, recorded, and not * delivered. * - **Request-scoped notifications stay out of this store.** `notifications/ * progress` and `notifications/message` belong to the originating request * stream; the coordinator never registers handlers for them, and rejects * them if a caller asks for them. * * All exports in this module are new; nothing existing changes shape. */ /** * `_meta` key carrying the JSON-RPC id of the `subscriptions/listen` request a * notification was delivered on. Mirrors upstream `SUBSCRIPTION_ID_META_KEY`; * re-declared locally so this module has no value import from the client * package (it is type-only elsewhere) and so the constant is assertable in * tests without pulling the SDK's internal entrypoint. */ declare const SUBSCRIPTION_ID_META_KEY = "io.modelcontextprotocol/subscriptionId"; declare const SubscriptionsAcknowledgedNotificationMethod: "notifications/subscriptions/acknowledged"; /** The notification kinds this coordinator owns. */ type SubscriptionNotificationKind = "tools-list-changed" | "prompts-list-changed" | "resources-list-changed" | "resource-updated" | "tasks"; /** Product-level, era-neutral statement of what the user wants to observe. */ interface DesiredSubscriptionInterests { toolsListChanged?: boolean; promptsListChanged?: boolean; resourcesListChanged?: boolean; /** Resource URIs to watch for `notifications/resources/updated`. */ resourceUris?: readonly string[]; /** * Task IDs to watch for `notifications/tasks`. Extension wire only. The * caller is expected to drop terminal and dismissed IDs, which changes the * filter and therefore closes and re-opens the stream — a listen filter is * immutable for the life of a subscription. */ taskIds?: readonly string[]; } /** * Wire-shaped filter (structurally the SDK's `SubscriptionFilter`, plus the * extension's `taskIds`). * * `taskIds` is not in upstream's `SubscriptionFilter` type, and does not need * to be: `Client.listen` puts the filter on the wire verbatim * (`notifications: filter`, client `dist/index.mjs:3711`) with no outbound * schema strip, so the extension member survives the round trip. */ interface SubscriptionFilterShape { toolsListChanged?: boolean; promptsListChanged?: boolean; resourcesListChanged?: boolean; resourceSubscriptions?: string[]; taskIds?: string[]; } type SubscriptionStreamStatus = "opening" | "active" | "graceful-closed" | "remote-closed" | "cancelled" | "error"; /** Why a stream ended, mapped from the SDK's `McpSubscription.closed`. */ type SubscriptionCloseReason = /** Server completed the subscription intentionally (empty listen result). */ "graceful" /** Unexpected loss with no completion result — eligible for re-listen. */ | "remote" /** We closed it (desired filter changed, disposal, explicit cancel). */ | "local-abort" /** The open itself failed (pre-ack rejection, timeout, transport error). */ | "error"; /** * A selection the server does not advertise. Kept in the stream record so the * debugger can show it as *rejected*, rather than the selection just quietly * not appearing in the acknowledged filter. */ interface SubscriptionInterestRejection { interest: SubscriptionNotificationKind; /** Present for `resource-updated` rejections. */ uri?: string; /** Present for `tasks` rejections. */ taskId?: string; reason: "capability-not-advertised" | "not-acknowledged-by-server" /** * A task-filtered listen was wanted but this connection cannot put the * extension's per-request eligibility declaration on the listen request, * so sending it would earn `-32021`. Polling continues; the handle is not * lost. See `tasks-ext-listen-meta.ts`. */ | "tasks-declaration-unavailable"; } /** A notification the coordinator refused to deliver, kept for the debugger. */ interface RejectedSubscriptionNotification { method: string; subscriptionId?: string; /** Local stream id, when the notification could be attributed to one. */ localSubscriptionId?: string; reason: "unrequested-type" | "unknown-subscription-id" | "stream-not-active" | "request-scoped-notification"; at: number; } /** One long-lived notification stream, era-neutral. */ interface SubscriptionStreamRecord { /** MCPJam-local identity; stable across the record's whole lifetime. */ readonly localId: string; readonly era: "legacy" | "modern"; /** * The MCP JSON-RPC subscription id (the listen request's id). `undefined` * on the legacy era, and on the modern era until an ack/notification * reveals it. */ mcpSubscriptionId?: string; /** How `mcpSubscriptionId` became known. */ idBinding?: "reported" | "observed"; requestedFilter: SubscriptionFilterShape; /** Only set once the acknowledgement is observed. Never inferred. */ acknowledgedFilter?: SubscriptionFilterShape; rejectedInterests: SubscriptionInterestRejection[]; status: SubscriptionStreamStatus; closeReason?: SubscriptionCloseReason; error?: string; openedAt: number; acknowledgedAt?: number; closedAt?: number; /** How many re-listens have been attempted after a remote loss. */ reconnectAttempt: number; } /** A notification the coordinator accepted and attributed to a stream. */ interface DeliveredSubscriptionNotification { method: string; kind: SubscriptionNotificationKind; params?: Record; /** Resource URI for `resource-updated`. */ uri?: string; /** Task id for `tasks`. The params themselves are the full `DetailedTask`. */ taskId?: string; subscriptionId?: string; localSubscriptionId: string; at: number; } /** Minimal handle shape; upstream `McpSubscription` satisfies it structurally. */ interface McpSubscriptionHandle { readonly honoredFilter: SubscriptionFilterShape; close(): Promise; readonly closed: Promise<"local" | "graceful" | "remote">; /** * The listen request's JSON-RPC id, when the implementation exposes it. * Upstream beta.4 does not; the coordinator then binds the id from the * first stamped message on the stream. */ readonly subscriptionId?: string; } /** * The client surface the coordinator needs. `ManagedMcpClient` satisfies it * structurally (its `listen` is optional too), so the coordinator can be * driven by the managed client, by upstream `Client`, or by a test fixture. */ interface SubscriptionClientPort { getServerCapabilities(): ServerCapabilities | undefined; getProtocolEra?(): "legacy" | "modern" | undefined; setNotificationHandler(method: string, handler: (notification: { method: string; params?: Record; }) => void): void; subscribeResource(params: { uri: string; }): Promise; unsubscribeResource(params: { uri: string; }): Promise; listen?(filter: SubscriptionFilterShape): Promise; /** * Opens a listen stream carrying the `io.modelcontextprotocol/tasks` * per-request eligibility declaration. * * Separate from {@link listen} on purpose. A task-filtered listen without * the declaration MUST be answered `-32021` (`tasks.md:797-799`), so a * connection that cannot declare must not send one at all — it drops the * `taskIds` selection, records it as `tasks-declaration-unavailable`, and * keeps polling. Absent method ⇒ exactly that. */ listenWithTasksDeclaration?(filter: SubscriptionFilterShape): Promise; } /** Bounded re-listen policy. Re-listen, never resume: no replay exists. */ interface SubscriptionReconnectPolicy { /** Max re-listens after a *remote* loss. 0 disables reconnection. */ maxAttempts: number; initialDelayMs: number; factor: number; maxDelayMs: number; } declare const DEFAULT_SUBSCRIPTION_RECONNECT_POLICY: SubscriptionReconnectPolicy; interface SubscriptionCoordinatorOptions { client: SubscriptionClientPort; /** * Era override. When omitted the coordinator asks the client * (`getProtocolEra()`), defaulting to `"legacy"` when the client cannot say * — an unknown era must never opt into modern-only behavior. */ era?: "legacy" | "modern"; reconnect?: Partial; /** Injected for deterministic tests. */ now?: () => number; /** Injected for deterministic tests; must resolve after `ms`. */ sleep?: (ms: number) => Promise; /** Stream-state changes (open/ack/close/reconnect). */ onStreamChange?: (stream: SubscriptionStreamRecord) => void; /** Accepted notifications, already attributed to a stream. */ onNotification?: (event: DeliveredSubscriptionNotification) => void; /** Refused notifications, surfaced so the debugger can show the refusal. */ onRejectedNotification?: (event: RejectedSubscriptionNotification) => void; /** * Staleness hook. Invoked AFTER `onNotification`, never instead of it: a * product-level refresh policy must not be able to hide the notification * that triggered it. */ onStale?: (event: DeliveredSubscriptionNotification) => void; } /** * Splits desired interests into the filter we will actually request and the * selections the server does not advertise (shown as rejected, not dropped). * * `era` is required for `taskIds` and for nothing else. The extension * capability is a plain capability read with no era in it, so on 2025-11-25 a * server that advertises `io.modelcontextprotocol/tasks` still reads as * declaring it — while SEP-2663 says that on that revision the extension MUST * be treated as absent. Omitting `era` therefore means "not a modern * connection" and drops `taskIds`: the legacy wire must never carry an * extension-only filter member, and defaulting the other way would put one * there for every caller that has not been updated. */ declare function resolveRequestedFilter(desired: DesiredSubscriptionInterests, capabilities: ServerCapabilities | undefined, era?: "legacy" | "modern"): { requested: SubscriptionFilterShape; rejected: SubscriptionInterestRejection[]; }; /** * Selections that were requested but absent from the acknowledgement. The * server is allowed to honor a subset; the difference is a first-class, * displayable fact rather than an invisible no-op. */ declare function diffAcknowledgement(requested: SubscriptionFilterShape, acknowledged: SubscriptionFilterShape): SubscriptionInterestRejection[]; /** * Shared, era-aware subscription coordinator. One instance per connected * server; owns the desired interests, the live stream(s), and the single set * of notification handler registrations. */ declare class SubscriptionCoordinator { private readonly client; private readonly options; private readonly reconnectPolicy; private readonly now; private readonly sleep; private readonly instanceId; private desired; private handlersRegistered; private disposed; private streamSeq; /** * Local id → record. Insertion-ordered; closed records are retained up to * {@link MAX_RETAINED_STREAMS}. */ private readonly streams; /** Local id → live handle (modern only). */ private readonly handles; /** MCP subscription id → local id. */ private readonly idIndex; /** Local id of the currently intended stream, if any. */ private currentLocalId?; /** Serializes reconcile/close/reopen so filter churn cannot interleave. */ private queue; /** Bounded by {@link MAX_RETAINED_REJECTIONS}; oldest evicted on write. */ private readonly rejections; /** Legacy adapter bookkeeping: URIs currently `resources/subscribe`d. */ private legacySubscribedUris; /** Signature of the last all-rejected interest set recorded, for dedupe. */ private lastUnopenedSignature?; constructor(options: SubscriptionCoordinatorOptions); /** * The negotiated era. Unknown ⇒ `"legacy"`: modern-only behavior is never * applied on a guess. */ get era(): "legacy" | "modern"; getDesiredInterests(): DesiredSubscriptionInterests; /** Every stream this coordinator has opened, newest last. */ getStreams(): SubscriptionStreamRecord[]; getActiveStream(): SubscriptionStreamRecord | undefined; getRejectedNotifications(): RejectedSubscriptionNotification[]; /** * Declares what the user wants. Idempotent: an unchanged effective filter * leaves the live stream alone. A changed filter closes the current stream * (`local-abort`) and opens a new one — a listen filter is immutable for the * life of a subscription. */ setDesiredInterests(desired: DesiredSubscriptionInterests): Promise; /** Explicit user-driven teardown. Ends the stream as `cancelled`. */ cancel(): Promise; /** Terminal teardown. No further reconnects; handlers stay harmlessly bound. */ dispose(): Promise; private enqueue; private ensureHandlers; private readSubscriptionId; /** * Binds an MCP subscription id to a local stream. Upstream beta.4's * `McpSubscription` does not expose the listen id, so when the handle cannot * report it we adopt the id off the first stamped message of the only stream * still awaiting a binding. With more than one such stream the attribution * would be a guess, so we refuse and record the notification as * `unknown-subscription-id` instead. */ private resolveStream; private handleAcknowledgement; private markAcknowledged; private handleNotification; private recordRejection; private emitStream; private reconcile; /** * `resolveRequestedFilter` plus the tasks-declaration gate. * * Kept together so every caller — reconcile and re-listen alike — sees the * same filter. A re-listen that skipped the gate would resurrect a `taskIds` * selection this connection cannot declare and earn a `-32021` on reconnect. */ private resolveFilter; private reconcileLegacy; /** * Trims the retained stream history to {@link MAX_RETAINED_STREAMS}, oldest * first. * * Only records nothing else still points at are eligible: the intended * stream, anything holding a live handle, and anything not yet closed stay * regardless of age, because dropping one of those would strand the handle * and break `resolveStream`'s id binding. In practice the eviction set is * exactly the old terminal records — closed streams and the synthetic * never-opened ones — which is what actually accumulates. */ private pruneStreams; private safeUnsubscribe; private reconcileModern; /** * Records a stream that was never opened, purely so its rejected interests * remain visible through `getStreams()`. Terminal on arrival — there is no * transport behind it. */ private recordUnopenedStream; private openModernStream; private onHandleClosed; private scheduleRelisten; private closeStream; } /** * PIN: modelcontextprotocol/ext-tasks @ 2c1425d9a288b9b1f489430fe1e00bb392b47e48 * (`specification/draft/tasks.md`, `schema/draft/schema.ts`). Re-diff against * that commit when re-syncing; delete these files for the published * `@modelcontextprotocol/ext-tasks` package once it ships. */ /** * Vendored types for the `io.modelcontextprotocol/tasks` extension * (SEP-2663). * * VENDORED-FROM-SEP-2663-DRAFT — hand-written from the extension's * `schema/draft/schema.ts` shapes. The extension repo is spec-only and * unpublished; when `@modelcontextprotocol/ext-tasks` ships, delete this file * and import the package types (the ext-apps precedent). Type imports are * rewritten to `@modelcontextprotocol/client` — the same names `mrtr-driver.ts` * already imports — so the repo's `check:mcp-v1-runtime-imports` guard stays * green and there is exactly one source of truth for `InputRequests` / * `InputResponses`. * * Wire-level differences from the 2025-11-25 in-core utility (do not mix): * - the server decides; there is no `params.task` opt-in; * - `ttlMs` / `pollIntervalMs` (numbers, `ttlMs` nullable), not * `ttl` / `pollInterval`; * - `tasks/get` on a completed task carries the `result` INLINE — there is * no `tasks/result`, and no `tasks/list`; * - results are discriminated by a `resultType` tri-state * (`"complete" | "input_required" | "task"`). */ /** Task lifecycle states (SEP-2663). */ type TaskExtStatus = "working" | "input_required" | "completed" | "failed" | "cancelled"; /** JSON-RPC error object carried by a `failed` task. */ interface TaskExtError { code: number; message: string; data?: unknown; } /** * The task handle. `ttlMs` is `null` for a task with no expiry (distinct from * an absent field); `pollIntervalMs` is the server's requested poll floor. */ interface TaskExt { taskId: string; status: TaskExtStatus; statusMessage?: string; createdAt: string; lastUpdatedAt: string; ttlMs: number | null; pollIntervalMs?: number; _meta?: Record; } /** * The five `DetailedTask` variants (`schema/draft/schema.ts:217-222`, rendered * in `schema.json` as an `anyOf` with per-variant `required`). The status is * the discriminator and the status payload is NOT optional: * `specification/draft/tasks.md:330-336` states each as a MUST. * * Fields a variant does not carry are declared `?: undefined` rather than * omitted so that reading `task.result` / `task.error` / `task.inputRequests` * off the bare union still type-checks (narrowing on `status` gives the * precise type). Runtime passthrough is unaffected — a server that sends an * extra key still has it preserved, because a debugger must show what was * actually sent. */ interface WorkingTaskExt extends TaskExt { status: "working"; result?: undefined; error?: undefined; inputRequests?: undefined; } interface InputRequiredTaskExt extends TaskExt { status: "input_required"; /** * Keyed snapshot map re-sent on every poll (dedupe by key; partial * responses are allowed). */ inputRequests: InputRequests; result?: undefined; error?: undefined; } interface CompletedTaskExt extends TaskExt { status: "completed"; /** The original request's result, INLINE (there is no `tasks/result`). */ result: Record; error?: undefined; inputRequests?: undefined; } interface FailedTaskExt extends TaskExt { status: "failed"; /** * The JSON-RPC error that caused the failure. `failed` is ONLY for * protocol-level faults: a tool result with `isError: true` is a `completed` * task (tasks.md:837, :891-892). */ error: TaskExtError; result?: undefined; inputRequests?: undefined; } interface CancelledTaskExt extends TaskExt { status: "cancelled"; result?: undefined; error?: undefined; inputRequests?: undefined; } /** * `tasks/get` / `notifications/tasks` body — the status-discriminated union. */ type DetailedTaskExt = WorkingTaskExt | InputRequiredTaskExt | CompletedTaskExt | FailedTaskExt | CancelledTaskExt; /** * The flat `CreateTaskResult` a server MAY return in place of the requested * result. MUST NOT be returned before the task is durably readable via * `tasks/get`. * * `CreateTaskResult = Result & Task` is FLAT (schema.ts:232): it legitimately * never carries `result` / `error` / `inputRequests`, so the DetailedTask * union does NOT apply to it. `resultType: "task"` stays required — it is the * documented discriminator (tasks.md:102, MUST). */ interface CreateTaskExtResult extends TaskExt { resultType: "task"; } /** * `tasks/get` result. * * SPEC CONFLICT (pin 2c1425d9): tasks.md:338 says `resultType` **MUST** be * `"complete"` here, but `resultType` appears in `schema.json` nowhere, and * every DetailedTask variant is `additionalProperties: false` — so a validator * built from the JSON Schema would REJECT the key the prose mandates. We * therefore accept the response with OR without it and never require it. Do * not "fix" this by making it required until the two agree upstream. */ type GetTaskExtResult = DetailedTaskExt & { resultType?: "complete"; }; /** * `tasks/update` result — per SEP-2663 `UpdateTaskResult = Result`: an EMPTY, * eventually-consistent acknowledgement. It carries no task state, so callers * must re-poll `tasks/get` for the post-update status. */ type UpdateTaskExtResult = Record; /** * `notifications/tasks` body (optional; delivered via `subscriptions/listen`). * SEP-2663 carries a full `DetailedTask`, so the extra task fields are part of * the payload rather than an unrelated envelope. */ type TaskExtNotificationParams = DetailedTaskExt & { _meta?: Record; }; /** * Tool conversion utilities for integrating MCP tools with Vercel AI SDK */ /** * Input schema type for tool definitions */ type ToolInputSchema = Parameters[0]["inputSchema"]; /** * Schema overrides for specific tools * Maps tool name to custom input schema definition */ type ToolSchemaOverrides = Record; /** * Checks whether a tool is an MCP App by inspecting its _meta for a UI resource URI. * * @param toolMeta - The tool's _meta field from listTools result * @returns true if the tool is an MCP App */ declare function isMcpAppTool(toolMeta: Record | undefined): boolean; /** * Checks whether a tool is a ChatGPT App by inspecting its _meta for an output template. * * @param toolMeta - The tool's _meta field from listTools result * @returns true if the tool is a ChatGPT App */ declare function isChatGPTAppTool(toolMeta: Record | undefined): boolean; /** * Removes only the _meta field from a tool result (shallow copy). * * @param result - The full tool call result * @returns A shallow copy of the result without _meta */ declare function scrubMetaFromToolResult(result: CallToolResult): CallToolResult; /** * Returns a shallow copy of a CallToolResult with _meta and structuredContent removed. * * @param result - The full tool call result * @returns A scrubbed shallow copy without _meta and structuredContent */ declare function scrubMetaAndStructuredContentFromToolResult(result: CallToolResult): CallToolResult; /** * MCPJam's Tasks **product policy** — distinct from, and never confused with, * the wire extension. * * ```text * hostConfig.mcpProfile.extensions["com.mcpjam/tasks"] = { enabled: boolean } * ``` * * `com.mcpjam/tasks` is MCPJam configuration. It MUST NOT be advertised to an * MCP server, ever. The only thing that goes on the wire is * `io.modelcontextprotocol/tasks: {}` in a request's client capabilities. The * two are kept in separate modules for exactly this reason: nothing here * produces a capability value, and nothing in `tasks-ext.ts` reads a host * config. * * ## Why tri-state, not a boolean * * Three states are genuinely distinct and a two-state switch cannot express * them: * * - **unset** — the host has said nothing. The Tools tab keeps its existing * explicit per-call task controls, and every newly-added surface stays off. * This is what makes adding a Tasks-capable surface a non-event for hosts * that never opted in. * - **on** — enable Tasks on the *supported interactive surfaces* listed in * the matrix. Not "all surfaces": replay surfaces never create work, and * the API/CLI keep their own explicit opt-in. * - **off** — remove every task affordance and every declaration. * * A binary switch would collapse unset into one of the other two, silently * changing behavior for hosts that never expressed an opinion. So the editor * must offer On, Off, and "Use default" — the third is a real choice, not a * reset button. * * `invalid` is a fourth *observed* state, never a stored one: a malformed * value fails closed to the same behavior as `off`, and is reported so the * editor can show a repair warning instead of silently ignoring what the user * wrote. */ /** MCPJam's own extension id. Never sent to a server. */ declare const MCPJAM_TASKS_POLICY_EXTENSION_ID: "com.mcpjam/tasks"; /** What the stored config says. */ type TasksPolicy = "unset" | "on" | "off" | "invalid"; /** * What a given surface should actually do. * * - `off` — no task affordances, no declaration on any request. * - `expose` — declare eligibility, surface the handle, and let the user (or * a later turn) follow it. The call itself returns promptly. * - `await` — declare eligibility and drive the task to a bounded terminal * result before returning. For automation, where nobody is watching a tab. */ type TaskMode = "off" | "expose" | "await"; /** * The surfaces the policy distinguishes. Adding one here is deliberate: it * forces a decision about its `unset` and `on` behavior in * {@link taskModeForSurface} rather than letting it inherit something by * accident. */ type TaskSurface = /** Local or hosted Tools tab. Has its own explicit per-call controls. */ "tools" /** Local, hosted-emulated, or BYOK chat and playground. */ | "chat" /** The MCPJam agent. */ | "agent" /** Eval / simulation execution — automation, so `await` rather than `expose`. */ | "eval" /** Protocol + conformance harness. The test owns the exact wire. */ | "conformance" /** Saved-result and pinned replay. Must NEVER create new work. */ | "replay" /** Public API and CLI. Their own explicit request flag is the boundary. */ | "api"; interface TasksPolicyHost { mcpProfile?: { extensions?: Record; }; } /** * Reads the stored policy. * * Anything that is not a `{ enabled: boolean }` object is `invalid` rather * than being coerced. Coercion here is how a typo becomes silently-enabled * Tasks on every surface. */ declare function readTasksPolicy(host: TasksPolicyHost | undefined): TasksPolicy; /** Human-readable reason for an `invalid` policy, for the editor's warning. */ declare function describeInvalidTasksPolicy(host: TasksPolicyHost | undefined): string | undefined; /** * Returns a host with the policy set. Pure: the input is not mutated, so a * caller can diff before and after. */ declare function setTasksPolicy(host: T | undefined, enabled: boolean): T; /** * Returns a host with the policy removed — back to `unset`. * * Distinct from `setTasksPolicy(host, false)`: this restores default behavior * (Tools keeps its explicit controls), whereas `false` actively disables Tasks * everywhere including those controls. * * ## No residue * * `hostConfigs` are content-addressed: the stored id IS the hash of the * serialized host, so an empty container left behind by a set→clear round trip * is not cosmetic — it mints a new hostConfig row for a host that is, in every * observable way, the one that existed before the feature shipped. So the * emptied containers are removed rather than kept: `extensions` goes away when * nothing else lives in it, and `mcpProfile` goes away when `extensions` was * all it held. * * The one thing this does NOT round-trip is a host that stored an *explicitly * empty* `mcpProfile: {}` or `extensions: {}` before any policy was set — that * is normalized to absent. Deliberate: absent and empty already mean exactly * the same thing to every reader in this module, and preserving the difference * would mean keeping the residue in the far more common case. */ declare function clearTasksPolicy(host: T | undefined): T; /** * The published surface matrix. * * | Surface | unset | on | * | ----------- | ----------------- | ------------- | * | tools | explicit controls | expose | * | chat | off | expose | * | agent | off | expose | * | eval | off | await | * | conformance | explicit test | explicit test | * | replay | off | off | * | api | explicit request | explicit request | * * Two rows never move, whatever the policy says: * * - **replay** is `off` in every state. A replay surface renders a recorded * result; creating new server-side work from one would be a side effect the * user never asked for and cannot see. * - **conformance** and **api** are caller-driven. The conformance harness * owns the exact wire it is testing — a host policy that silently added a * declaration would corrupt the very thing under test — and the public API's * per-request opt-in IS its policy boundary. * * `off` and `invalid` force `off` everywhere, including the Tools tab's * explicit controls: an explicit Off is a statement, and failing closed on a * malformed value is the only safe reading of one. */ declare function taskModeForSurface(policy: TasksPolicy, surface: TaskSurface): TaskMode; /** * Whether this surface may put `io.modelcontextprotocol/tasks` on a request. * * The one predicate a route should call. It reads as the question the route * actually has, rather than making every route re-derive it from a mode. */ declare function surfaceMayDeclareTasks(policy: TasksPolicy, surface: TaskSurface): boolean; /** * The shared task lifecycle engine. * * Before this module, every Tasks surface (the Tasks tab, chat, the hosted * routes, the public API, the CLI) grew its own polling loop and its own idea * of when a task is finished. That is how the poll floor got violated: the * Tasks tab collapsed every active task to a single `Math.min(...)` across * their advertised intervals and then let a user override *replace* the * server's floor rather than be clamped by it. * * The engine owns exactly one thing per task: **when it is next allowed to be * polled, and what we last saw**. It performs no I/O. Callers ask it what is * due, do the transport work themselves, and hand the outcome back through * {@link TaskLifecycleEngine.observe} / {@link TaskLifecycleEngine.observeError}. * That keeps it usable from a browser tab, a Hono route, a Convex action, and * the CLI without any of them sharing a transport. * * ## The poll-interval rule * * ```text * effective interval = max( * server pollIntervalMs, // the floor the server asked for * user minimum, // a *preference*, never a licence to go faster * retry backoff, // exponential, after consecutive errors * Retry-After // a 429/503 hint, absolute * ) * ``` * * `pollIntervalMs` MAY change over a task's lifetime (`tasks.md:308`), in * either direction. A shrinking value is normal and must never be reported as * an anomaly. Both wires feed the same formula; only the validators differ. */ /** The two wires that can actually carry a task. `"none"` never reaches here. */ type LiveTasksWire = Exclude; /** * Stable identity for a task handle. * * `wire` is part of the identity, not a decoration: the same task ID can exist * on both wires while a developer flips protocol versions, and a stored handle * must never silently change behavior because a server was reconfigured. * `scope` is the auth/org context (hosted `projectId`), which keeps bearer-ish * task IDs from leaking between accounts sharing a browser. */ interface TaskLifecycleIdentity { scope?: string; serverId: string; wire: LiveTasksWire; taskId: string; } /** Composite key for {@link TaskLifecycleIdentity}. NUL-joined: no field may contain it. */ declare function taskLifecycleKey(identity: TaskLifecycleIdentity): string; /** * Normalized status across both wires. * * `expired` is MCPJam's own tombstone for a handle the server no longer knows * (a confirmed `-32602` on `tasks/get`), not a protocol status. `unknown` is * the pre-first-observation state of a freshly registered handle. */ type TaskLifecycleStatus = "unknown" | "working" | "input_required" | "completed" | "failed" | "cancelled" | "expired"; /** Statuses that will never change again. `expired` is terminal for MCPJam. */ declare const TERMINAL_LIFECYCLE_STATUSES: readonly TaskLifecycleStatus[]; declare function isTerminalLifecycleStatus(status: TaskLifecycleStatus): boolean; /** JSON-RPC error carried by a `failed` task. */ interface TaskLifecycleError { code: number; message: string; data?: unknown; } /** * A wire-neutral observation of a task. * * Both wires normalize into this before reaching the engine, so the scheduler * has no per-wire branches. `raw` preserves what the server actually sent, for * the debugger; the engine never interprets it. */ interface TaskLifecycleObservation { status: Exclude; statusMessage?: string; createdAt?: string; lastUpdatedAt?: string; /** `null` means "no expiry" and is distinct from an absent field. */ ttlMs?: number | null; pollIntervalMs?: number; inputRequests?: InputRequests; result?: Record; error?: TaskLifecycleError; raw?: unknown; } /** Where an observation came from. Notifications do not reset error backoff blame. */ type TaskObservationSource = "poll" | "notification" | "create"; /** Everything the engine knows about one handle. */ interface TaskLifecycleRecord { readonly key: string; readonly identity: TaskLifecycleIdentity; status: TaskLifecycleStatus; statusMessage?: string; createdAt?: string; lastUpdatedAt?: string; ttlMs: number | null; /** Latest server-advertised floor. MAY move in either direction. */ pollIntervalMs?: number; inputRequests?: InputRequests; result?: Record; error?: TaskLifecycleError; raw?: unknown; /** Epoch ms this task may next be polled. */ nextPollAt: number; /** Consecutive transport/protocol errors; drives exponential backoff. */ consecutiveErrors: number; /** Absolute epoch ms from a `Retry-After`, if one is in force. */ retryAfterUntil?: number; /** Last time ANY live read answered for this handle, including a `-32602`. */ lastObservedAt?: number; /** A `tasks/cancel` has been sent; the task may still complete normally. */ cancellationRequested: boolean; /** * Adopted from durable storage and not yet confirmed by a live read. * * Cleared by the first {@link TaskLifecycleEngine.observe}. It matters * because durable storage holds a task's *status* but never its payload: on * the extension wire the result and error ride inline on `tasks/get`, so a * handle restored as `completed` has a status and no result. A surface that * wants to show the payload has to read it once more, and this flag is how it * tells "finished, and we have the result" from "finished, and all we kept * was the word". */ restored: boolean; /** * A read is in flight for this handle right now. * * Distinct from {@link TaskLifecycleRecord.nextPollAt}, which is a *time* * reservation. Pushing the due time forward only prevents a duplicate while * the read finishes inside one interval; a read slower than the floor lets * the next poller find the handle due again and dispatch concurrently. Two * `tasks/get` in flight means the later reply wins even when it observed the * older state, so the lease covers the whole request rather than a guess at * how long it will take. */ pollInFlight: boolean; /** Input keys whose `tasks/update` acknowledgement succeeded. */ respondedInputKeys: Set; /** Provenance, for the surfaces that show it. */ toolName?: string; surface?: string; } /** Snapshot form of a record — safe to serialize and hand to a UI. */ interface TaskLifecycleSnapshot extends Omit { respondedInputKeys: string[]; terminal: boolean; } interface TaskLifecycleCallbacks { onTaskCreated?: (record: TaskLifecycleSnapshot) => void; onState?: (record: TaskLifecycleSnapshot, previous: TaskLifecycleStatus) => void; onInputRequired?: (record: TaskLifecycleSnapshot) => void; onTerminal?: (record: TaskLifecycleSnapshot) => void; } interface TaskLifecycleEngineOptions { /** * The user's *preferred minimum* interval. A preference, never permission to * poll faster than the server's floor — it enters the formula as one more * `max` term. */ userMinimumIntervalMs?: number; /** Hard floor applied to every task, whatever anyone asked for. */ absoluteFloorMs?: number; /** Cap on any single computed interval, so a hostile `pollIntervalMs` cannot park a task forever. */ maximumIntervalMs?: number; /** First backoff step after one error; doubles per consecutive error. */ errorBackoffBaseMs?: number; errorBackoffMaxMs?: number; now?: () => number; callbacks?: TaskLifecycleCallbacks; } declare function toSnapshot(record: TaskLifecycleRecord): TaskLifecycleSnapshot; /** * Per-task due-time scheduler and state store for MCP Tasks. * * Deliberately I/O-free: it decides *when* and remembers *what*, and the * caller owns the transport. See the module comment for why. */ declare class TaskLifecycleEngine { private readonly records; private readonly now; private readonly absoluteFloorMs; private readonly maximumIntervalMs; private readonly backoffBaseMs; private readonly backoffMaxMs; private userMinimumIntervalMs; private callbacks; constructor(options?: TaskLifecycleEngineOptions); setCallbacks(callbacks: TaskLifecycleCallbacks): void; /** * Updates the user's preferred minimum. * * Existing due times are recomputed in both directions, so the setting takes * effect on in-flight tasks rather than only on the next ones: a *slower* * preference extends their wait immediately, and a *faster* one shortens the * part of the wait that only the old preference caused. * * A faster preference still cannot breach a server floor — it is one `max` * term among several, and the absolute waits (`Retry-After`, error backoff) * are re-applied as floors afterwards. */ setUserMinimumIntervalMs(intervalMs: number): void; getUserMinimumIntervalMs(): number; /** * The interval this task must not be polled faster than, right now. * * Every term is a `max`. `Retry-After` participates as a *remaining * duration* so it behaves the same as the other relative terms. */ effectiveIntervalMs(record: TaskLifecycleRecord, at?: number): number; get(identity: TaskLifecycleIdentity): TaskLifecycleRecord | undefined; getByKey(key: string): TaskLifecycleRecord | undefined; snapshot(identity: TaskLifecycleIdentity): TaskLifecycleSnapshot | undefined; all(): TaskLifecycleSnapshot[]; /** * Registers a handle. Idempotent: re-registering an existing task returns the * live record rather than resetting its schedule, so a reconnect or a second * surface adopting the same handle cannot restart its backoff. */ register(identity: TaskLifecycleIdentity, init?: { createdAt?: string; ttlMs?: number | null; pollIntervalMs?: number; status?: TaskLifecycleStatus; toolName?: string; surface?: string; /** Adopted from durable storage; not re-notified as a creation. */ restored?: boolean; respondedInputKeys?: readonly string[]; /** * Due time carried over from durable storage. Without it a restored * handle is due immediately, so a reload would re-read every task before * its advertised floor elapsed — and repeated reloads would out-poll the * server. * * Applied to terminal handles too, deliberately. A restored terminal is * not the same as a terminal we watched arrive: `observe` sets * `nextPollAt` to `Infinity` because it already holds the payload, * whereas storage kept only the status. Parking a restored terminal at * `Infinity` here would make the extension wire's inline result * permanently unreachable across a reload, so the floor is honored * instead and {@link TaskLifecycleRecord.restored} marks it as still * owing one read. */ nextPollAt?: number; /** Last observation time carried over from durable storage. */ lastObservedAt?: number; }): TaskLifecycleRecord; /** Drops a handle from scheduling. Does not touch durable storage. */ forget(identity: TaskLifecycleIdentity): void; clear(): void; /** * Folds a validated observation into the record and reschedules. * * Notifications and polls are reconciled identically — the extension makes * notifications an optimization, never a separate source of truth — except * that a notification does not by itself prove the *poll* path is healthy, * so it clears the error count only when it actually carried state. */ observe(identity: TaskLifecycleIdentity, observation: TaskLifecycleObservation, source?: TaskObservationSource): TaskLifecycleRecord; /** * Records a failed read. Increments backoff and reschedules; never changes * the task's status, because a transport failure says nothing about the task. */ observeError(identity: TaskLifecycleIdentity, options?: { retryAfterMs?: number; }): TaskLifecycleRecord; /** * Applies a `Retry-After` without counting an error — for a 429 that is rate * limiting rather than failure. */ applyRetryAfter(identity: TaskLifecycleIdentity, retryAfterMs: number): void; /** * Drops the ERROR BACKOFF for a handle, for a read a **person** asked for. * * The backoff is this client's own guess about a transport that keeps * failing, and a user clicking Refresh is better evidence than that guess — * without this, a handle that has backed off to a minute is unreachable for * a minute no matter what the user does, which reads as a dead button. * * What it does NOT clear is anything the SERVER imposed. The advertised * `pollIntervalMs` still applies from the last observation, and an explicit * `Retry-After` still floors the result, so a user cannot click their way * past a rate limit or an advertised floor. No-ops for an unknown handle: * this only ever relaxes an existing schedule, never registers one. */ clearErrorBackoff(identity: TaskLifecycleIdentity): void; /** * Marks the handle unknown to the server. Only ever called from a **confirmed * `tasks/get` `-32602`** — that is the one method carrying the MUST * (`tasks.md:793-795`); update/cancel carry a SHOULD, so a `-32602` from * either is a hint that must be confirmed with a `tasks/get` first. */ markExpired(identity: TaskLifecycleIdentity): TaskLifecycleRecord; /** * Notes that a `tasks/cancel` was accepted. Cancellation is cooperative: the * ack says nothing about the task's fate, so the status is left alone and * polling continues until a real terminal state is observed. */ markCancellationRequested(identity: TaskLifecycleIdentity): void; /** * Marks an input key answered. Called only *after* the `tasks/update` * acknowledgement succeeds — an optimistic mark would silently drop the * user's answer if the update failed. */ markInputKeysResponded(identity: TaskLifecycleIdentity, keys: readonly string[]): void; /** * Input keys in the current snapshot that have not been answered yet. * Own-property checked: a hostile key such as `constructor` must not be * inherited from the prototype chain. */ pendingInputKeys(identity: TaskLifecycleIdentity): string[]; /** * Whether this handle still has a read owed to it. * * Normally that means "not terminal". The exception is a RESTORED terminal on * the extension wire, and it is a protocol fact rather than a UI preference: * durable storage keeps a task's status but never its payload, and on the * extension the result and error ride INLINE on `tasks/get` — there is no * `tasks/result` to fetch them from later. So a handle restored as * `completed` is a status with nothing behind it, and excluding it from * scheduling strands the result permanently. * * It lives HERE rather than in each caller because every scheduling method * has to agree. A caller that special-cased `due()` alone would still find * `msUntilNextDue()` ignoring the handle, so no timer would ever arm for it * and the special case would never fire. * * Bounded to one read: `observe` clears `restored`. * * Public because it is not only the scheduler's business: any caller that * short-circuits on "this handle is terminal, no read needed" has to ask the * same question, and asking it with a bare `isTerminalLifecycleStatus` is how * a restored terminal gets reported without the payload it never had. */ owesRead(record: TaskLifecycleRecord): boolean; /** * Handles due for a poll now, soonest first. Terminal handles are never due — * except a restored one that still owes its recovery read — and neither is a * handle with a read already in flight. */ due(at?: number): TaskLifecycleRecord[]; /** Handles that still owe a read, whether or not they are due. */ active(): TaskLifecycleRecord[]; /** * Milliseconds until the next handle becomes due, for a caller that wants to * arm one timer instead of ticking. `undefined` when nothing is pending. */ msUntilNextDue(at?: number): number | undefined; /** * Reserves the given handles: pushes each one's due time forward by its own * effective interval. Call this when a batch is dispatched so an in-flight * request is not re-issued by the next tick. */ reserve(records: readonly TaskLifecycleRecord[], at?: number): void; /** * Claims a handle for a read that is about to be dispatched, returning * whether the claim succeeded. * * `reserve` alone is not enough on a shared engine. It moves the due time * forward by one interval, which only covers a read that settles inside that * interval; a `tasks/get` slower than the floor leaves the handle due again * while the first request is still open, and a second poller dispatches on * top of it. Beyond the wasted request, the replies can land out of order and * an older snapshot overwrites a newer one. * * So the lease is held for the life of the request, not for a guessed * duration, and {@link due} skips a leased handle. It also still reserves, so * the floor is respected the moment the lease is released. * * Callers MUST release in a `finally` — every exit, including a throw. The * lease is not self-expiring on purpose: a timeout is exactly the case where * a stale reply may still arrive, and quietly re-admitting a second reader * would reintroduce the race this prevents. */ acquirePoll(identity: TaskLifecycleIdentity, at?: number): boolean; /** Releases a lease taken by {@link acquirePoll}. Idempotent. */ releasePoll(identity: TaskLifecycleIdentity): void; /** * The interval a *batch* must respect: the **maximum** floor among its * members, not the minimum. A batch polls every member, so honoring the * fastest member's floor would breach every slower member's. */ batchIntervalMs(records: readonly TaskLifecycleRecord[], at?: number): number; } /** * The single task-creation fan-out point, and the `await`-mode driver. * * ## Why one event * * A created task has to reach four places at once: the durable browser * tracker, the hosted chat stream, the best-effort hosted registry, and * analytics. Before this, each execution surface wired its own subset, which * is how a task created from chat could end up untracked while the same task * created from the Tools tab was tracked. {@link TaskCreatedSink} makes the * fan-out the thing surfaces share, so adding a consumer is one registration * rather than an edit in every route. * * Two rules the sink enforces, from the plan: * * 1. **A registry failure must never convert a successful tool call into a * failure.** The task exists on the server whether or not we managed to * write a recovery row. Throwing here would tell the user their call * failed while the work runs on regardless. * 2. **Tracking and client delivery are functional paths, not fire-and-forget.** * They are awaited, and a failure in one is reported rather than swallowed * inside an unobserved promise. "Best effort" describes the *registry*, not * the local tracker the user's next page load depends on. */ /** Where a task was created from. Mirrors the host-policy surface matrix. */ type TaskCreationSurface = "tools" | "chat" | "agent" | "eval" | "conformance" | "api" | "cli"; /** * A task handle, the moment it comes into existence. * * Deliberately carries no result, no input requests, and no tool arguments: * consumers of this event persist and route, they do not render. Anything * richer is read back from `tasks/get` by whoever actually needs it. */ interface TaskCreatedEvent { identity: TaskLifecycleIdentity; wire: LiveTasksWire; surface: TaskCreationSurface; /** ISO timestamp reported by the server, not a local clock reading. */ createdAt?: string; ttlMs?: number | null; pollIntervalMs?: number; /** The tool whose call produced this task, when the surface knows it. */ toolName?: string; status?: string; } /** One registered consumer. */ interface TaskCreatedConsumer { name: string; /** * `false` (the default) means a throw from this consumer propagates: it is a * functional path — durable tracking, client event delivery — and a silent * failure there loses the handle. * * `true` means a throw is captured and reported instead: for the hosted * registry, which is a recovery index rather than the source of truth. */ bestEffort?: boolean; handle: (event: TaskCreatedEvent) => void | Promise; } /** A consumer that failed. Reported, never silently dropped. */ interface TaskCreatedConsumerFailure { name: string; error: unknown; } interface TaskCreatedDispatchResult { /** Failures from `bestEffort` consumers. Non-empty is not an error. */ degraded: TaskCreatedConsumerFailure[]; } /** * Fan-out for {@link TaskCreatedEvent}. * * Consumers run in registration order and are awaited. Order matters for one * reason: durable local tracking should land before anything that might be * slow or remote, so a hung registry write cannot cost the user their handle. */ declare class TaskCreatedSink { private readonly bestEffortTimeoutMs; private readonly consumers; constructor(bestEffortTimeoutMs?: number); register(consumer: TaskCreatedConsumer): () => void; /** * Delivers to every consumer. * * @throws whatever a non-`bestEffort` consumer throws — those are the paths * whose failure genuinely means the handle was lost. */ dispatch(event: TaskCreatedEvent): Promise; } /** * `mrtr-driver.ts` — MCPJam's manual driver for the MCP 2026-07-28 * **multi-round-trip / `input_required`** interaction (spec §12; upstream * `InputRequiredResult`). A modern `tools/call`, `prompts/get`, or * `resources/read` may answer with an `input_required` result carrying embedded * elicitation requests plus an opaque `requestState`; the client collects the * input and **retries the original operation** with the responses + the echoed * state, possibly for several rounds, until a complete result comes back. * * ## Why a serializable stepper (not just an async loop) * * Hosted MCPJam is horizontally scaled: a modern `input_required` can arrive * mid-tool-execution while a worker holds an SSE stream open, and §12.5.2 * forbids blocking a worker while a human thinks. The loop is therefore split * into a **pure data state** ({@link MrtrOperationState}) and pure step * functions ({@link executeInputRequiredLeg} / {@link * resumeInputRequiredOperation}). A local/CLI surface layers the convenience * {@link runInputRequiredOperation} loop over the stepper; a hosted surface * (PR3+) persists the state to Convex between rounds and resumes on a fresh * worker. **The state never holds a `Client`, promise, closure, resolver, or * `AbortSignal`** — only JSON-serializable data. * * ## What the driver owns * * - **The round cap.** The upstream SDK's automatic driver (`maxRounds`, * `InputRequiredRoundsExceeded`) applies to `autoFulfill: true` only. MCPJam * runs manual mode (`autoFulfill: false`), so the cap is owned here: it is * {@link DEFAULT_MAX_MRTR_ROUNDS} by default, persisted in the state so a * hosted resume keeps counting, and exceeding it raises the *same typed * upstream shape* — `new SdkError(SdkErrorCode.InputRequiredRoundsExceeded, * …, { rounds })` — so existing guards work. * - **Per-round response replacement.** Each retry carries `inputResponses` * for *that round only* (never accumulated across rounds) and echoes * `requestState` byte-exact when present, omitting it when absent. * - **Undeclared-request rejection (Decision 8).** 2026 clients silently drop * inbound server→client requests, so an embedded `roots/list` / * `sampling/createMessage` (which this client never advertises) — or an * undeclared elicitation mode — is detected *here, on the result*, before any * UI is shown, and rejected with precise evidence. * - **Strict self-validation (§12.1.11).** `acceptedContent` is not exported by * the client package, so collected elicitation content is validated against * the request's `requestedSchema` before it is sent. The JSON-Schema engine * is *injected* (see {@link ElicitationContentValidator}) so this module * stays browser-safe; callers wire a **strict** dialect-aware validator whose * unknown-dialect behavior rejects rather than fails open. * * `requestState` is opaque: it is echoed verbatim and never parsed, normalized, * or logged (redact if traced). */ /** The three verbs that can enter a multi-round-trip loop (spec §12.1). */ type MrtrMethod = "tools/call" | "prompts/get" | "resources/read"; /** Elicitation delivery modes this client understands. */ type ElicitationMode = "form" | "url"; /** Default modes accepted when validating embedded `elicitation/create`. */ declare const SUPPORTED_ELICITATION_MODES: readonly ElicitationMode[]; /** * The elicitation modes an MRTR round may embed. A thunk defers the lookup to * leg time, which is what a caller deriving the set from the negotiated * `elicitation` capability needs: that capability is only known once the * connection has initialized, which happens inside the first leg. */ type MrtrSupportedModes = readonly ElicitationMode[] | (() => readonly ElicitationMode[]); /** * MCPJam owns the round cap in manual mode; this mirrors the upstream * automatic driver's `maxRounds` default so behavior is consistent across * modes. */ declare const DEFAULT_MAX_MRTR_ROUNDS = 10; /** * The complete, JSON-serializable state of one in-flight MRTR operation. * * DATA ONLY — never a `Client`, promise, closure, resolver, or `AbortSignal`. * A hosted surface persists this between rounds and rehydrates it on resume. */ interface MrtrOperationState { /** Stable id for this logical operation across all its rounds. */ readonly opId: string; /** The entry verb; drives request construction on every retry. */ readonly method: MrtrMethod; /** * The immutable original application params (e.g. `{ name, arguments }` for * a tool). Preserved byte-for-byte across every round; retries spread * `inputResponses` / `requestState` on top without mutating this. */ readonly originalParams: Record; /** * Number of retry legs performed so far. `0` before the initial send. * Persisted so a hosted resume keeps counting toward {@link maxRounds}. */ readonly round: number; /** MCPJam-owned round cap, persisted so resumes continue enforcing it. */ readonly maxRounds: number; /** * The opaque server state to echo verbatim on the next retry, present only * when the last `input_required` carried one. Never parsed, normalized, or * logged. */ readonly requestState?: string; /** * The current round's embedded requests awaiting responses, keyed by the * server's (untrusted) keys. Empty for a state-only round or before the * first `input_required`. Kept as data (includes each request's * `requestedSchema`) so a hosted resume can self-validate collected content. */ readonly pendingInputRequests: InputRequests; } /** Result of stepping one MRTR leg. */ type MrtrLegResult = { readonly status: "complete"; readonly result: TResult; } | { readonly status: "input_required"; readonly state: MrtrOperationState; }; /** * Sends exactly one wire leg. The driver builds the `{ method, params }` * request (original params + this round's `inputResponses` + echoed * `requestState`); the sender performs the wire call and returns the raw * result — either a complete result of the entry verb or an * {@link InputRequiredResult}. Implementations preserve Phase-3 helper * semantics (output-schema validation, `Mcp-Param-*` mirroring, response * cache) and their own retry/timeout wrappers; the driver is the inner leaf. */ type MrtrLegSender = (request: { readonly method: MrtrMethod; readonly params: Record; }, ctx: { readonly round: number; readonly signal?: AbortSignal; }) => Promise; /** * Validates collected elicitation content against the request's * `requestedSchema` (§12.1.11). Injected so the browser-hostile Ajv engine is * never pulled into this module's import graph; callers wire a **strict** * dialect-aware validator (unknown dialect → invalid, not fail-open). */ type ElicitationContentValidator = (requestedSchema: unknown, content: unknown) => { valid: boolean; error?: string; }; /** Collects responses for one round's embedded requests. */ type MrtrInputCollector = (request: { readonly state: MrtrOperationState; readonly inputRequests: InputRequests; readonly signal?: AbortSignal; }) => Promise; /** Validates the final complete result (e.g. tool output-schema check). */ type MrtrValidateResponse = (result: TResult) => void | Promise; /** * The server embedded a request this client never advertised (`roots/list` / * `sampling/createMessage`) or an otherwise unsupported input method. Detected * at the result (Decision 8: modern clients silently drop inbound requests, so * a `setRequestHandler` would never fire) and rejected before any UI. */ declare class MrtrUndeclaredInputError extends Error { readonly method: string; readonly inputKey: string; readonly code = "MRTR_UNDECLARED_INPUT"; constructor(method: string, inputKey: string, message: string); } /** The server requested an elicitation mode this client does not support. */ declare class MrtrUnsupportedElicitationModeError extends Error { readonly mode: string; readonly inputKey: string; /** * The modes actually allowed for this connection. Defaults to everything * this client can render; a caller that declared a narrower `elicitation` * capability passes its own set so the message names what was declared. */ readonly supportedModes: readonly ElicitationMode[]; readonly code = "MRTR_UNSUPPORTED_ELICITATION_MODE"; constructor(mode: string, inputKey: string, /** * The modes actually allowed for this connection. Defaults to everything * this client can render; a caller that declared a narrower `elicitation` * capability passes its own set so the message names what was declared. */ supportedModes?: readonly ElicitationMode[]); } /** * A collected response failed local self-validation: a missing/extra key for * the round, or accepted elicitation content that does not satisfy the * request's `requestedSchema`. */ declare class MrtrInputValidationError extends Error { readonly code = "MRTR_INPUT_VALIDATION"; constructor(message: string); } /** `true` iff `err` is the upstream typed round-cap-exceeded error. */ declare function isMaxRoundsExceeded(err: unknown): boolean; /** * `true` iff `err` is the upstream typed "unsupported result type" error — * raised by the SDK when a modern non-complete result surfaces on a call that * did not opt in with `allowInputRequired`. */ declare function isUnsupportedResultType(err: unknown): boolean; /** The default result schema for a verb's complete result. */ declare function defaultResultSchemaForMethod(_method: MrtrMethod): StandardSchemaV1>; /** * The default leg sender: the type-correct explicit-schema path * (`requestWithSchema(req, withInputRequired(resultSchema), { allowInputRequired })`). * This is the only path that correctly surfaces an `input_required` result on * *every* round — the higher-level `callTool` helper asserts a complete result * and would throw on an intermediate `input_required` leg for a tool that * declares an `outputSchema`. */ declare function makeRequestWithSchemaLegSender(client: Pick, resultSchema?: StandardSchemaV1, baseOptions?: RequestOptions): MrtrLegSender; /** * Validates the *entire* embedded-requests map before any of it is surfaced to * a UI (§12.3): rejects undeclared `roots/list` / `sampling/createMessage` * (Decision 8), unknown methods, and unsupported elicitation modes. */ declare function validateInputRequests(inputRequests: InputRequests, supportedModes?: readonly ElicitationMode[]): void; /** * Validates a round's collected responses against its pending requests: every * pending key answered, no unexpected keys, and every *accepted* form * elicitation's content self-validated against its `requestedSchema`. */ declare function validateRoundResponses(state: MrtrOperationState, responses: InputResponses, validateContent?: ElicitationContentValidator): void; /** Creates the initial state for a fresh operation (round 0, no pending input). */ declare function initInputRequiredState(args: { opId?: string; method: MrtrMethod; params: Record; maxRounds?: number; }): MrtrOperationState; /** * Steps exactly one MRTR leg: sends the wire request for `state` carrying * `currentRoundResponses` (validated first), then classifies the result. * * - complete result → `{ status: 'complete', result }`. * - `input_required` → validates the entire embedded map (undeclared / mode), * enforces the MCPJam round cap, and returns `{ status: 'input_required', * state }` with the next round's pending requests and echoed `requestState`. * * Abort: `signal` is threaded to the sender (wire-active window). Aborting the * local-pending window (input collection) is the caller's concern. On abort the * caller's `AbortError` propagates — it is never converted to a decline. */ declare function executeInputRequiredLeg(args: { sender: MrtrLegSender; state: MrtrOperationState; currentRoundResponses?: InputResponses; signal?: AbortSignal; validateContent?: ElicitationContentValidator; supportedElicitationModes?: MrtrSupportedModes; }): Promise>; /** * Resumes a suspended operation: submits `responses` for the state's current * pending requests and runs one retry leg. Rebuilds the default * `requestWithSchema` sender from `client` unless a verb-specific `sender` is * supplied (the manager passes one to preserve Phase-3 helper semantics). */ declare function resumeInputRequiredOperation(client: Pick, state: MrtrOperationState, responses: InputResponses, config?: { sender?: MrtrLegSender; resultSchema?: StandardSchemaV1; requestOptions?: RequestOptions; signal?: AbortSignal; validateContent?: ElicitationContentValidator; supportedElicitationModes?: MrtrSupportedModes; }): Promise>; interface RunInputRequiredOptions { /** * Client for the default `requestWithSchema` sender. Optional: omit it when * a verb-specific {@link sender} is supplied (the manager does this to * preserve Phase-3 helper semantics). Required otherwise. */ client?: Pick; method: MrtrMethod; params: Record; /** Collects responses for each round's embedded requests. */ collectInput: MrtrInputCollector; /** Verb-specific sender; defaults to the `requestWithSchema` path. */ sender?: MrtrLegSender; /** Result schema for the complete branch of the default sender. */ resultSchema?: StandardSchemaV1; /** Base request options threaded into every leg (timeout, cacheMode, …). */ requestOptions?: RequestOptions; /** Runs on the final complete result (e.g. tool output-schema validation). */ validateResponse?: MrtrValidateResponse; /** Strict content validator for accepted elicitation input. */ validateContent?: ElicitationContentValidator; supportedElicitationModes?: MrtrSupportedModes; /** MCPJam-owned round cap; defaults to {@link DEFAULT_MAX_MRTR_ROUNDS}. */ maxRounds?: number; /** Abort signal for both the wire-active and local-pending windows. */ signal?: AbortSignal; opId?: string; } /** * Runs an MRTR operation to completion over the stepper: initial send, then a * collect→retry loop per round until a complete result. Suitable for local and * CLI surfaces; hosted surfaces persist {@link MrtrOperationState} between * rounds instead of looping in-process. * * The wire legs and the human-input collection are separate windows: a * transient wire failure is the sender's concern (which owns retry) and never * restarts the loop at round zero, because the loop's state lives here — outside * the sender. */ declare function runInputRequiredOperation(options: RunInputRequiredOptions): Promise; /** * `input_required` driver for the tasks extension. * * A task's input channel is **not** more trusted than a direct server→client * request. `inputRequests` can carry `elicitation/create`, `roots/list`, and * `sampling/createMessage` — the same three methods a server could have asked * for out of band — so each one is routed through the same policy, consent, * schema validation, and handler as its standalone counterpart. This module is * the seam that enforces that, so no surface can accidentally grant a task * channel privileges the standalone path would have refused. * * Two rules shape everything here: * * 1. **Declare only what you can fulfil.** A surface may declare the tasks * extension only when it has a complete handler path for each standalone * capability it declares. A request for a method the client did not declare * is rejected with a typed error and surfaced as actionable — never hung on, * and never quietly marked handled. * 2. **Answered means acknowledged.** A key is marked responded only after the * `tasks/update` carrying it succeeds. Marking optimistically would silently * discard a user's answer whenever the update failed. * * The keyed `inputRequests` map is a **snapshot re-sent on every poll**, and it * may grow between polls. Partial responses are legal: a caller answers what it * can, updates, and observes the next snapshot. */ /** The three request methods an `input_required` task may embed. */ declare const TASK_INPUT_METHODS: readonly ["elicitation/create", "roots/list", "sampling/createMessage"]; type TaskInputMethod = (typeof TASK_INPUT_METHODS)[number]; /** * Why a key could not be answered. Every one of these is *displayable state*, * not a silent drop: an unanswerable key stays visible and stays unanswered. */ type TaskInputRejectionReason = /** The client never declared the capability this request needs. */ "undeclared-capability" /** The method is not one of the extension's three. */ | "unsupported-method" /** `elicitation/create` asked for a mode this client cannot render. */ | "unsupported-elicitation-mode" /** No handler was wired for a capability the client did declare. */ | "no-handler" /** A size/count limit rejected the request before it was rendered. */ | "limit-exceeded" /** The handler threw. */ | "handler-failed" /** The request's own shape was invalid. */ | "malformed-request" /** The handler's response failed the method's canonical result schema. */ | "malformed-response"; declare class TaskInputRejectedError extends Error { readonly reason: TaskInputRejectionReason; readonly inputKey: string; readonly method: string; readonly code = "TASK_INPUT_REJECTED"; constructor(reason: TaskInputRejectionReason, inputKey: string, method: string, message: string); } /** A key this driver could not answer, with the reason to show the user. */ interface TaskInputRejection { inputKey: string; method: string; reason: TaskInputRejectionReason; message: string; } interface TaskInputHandlerContext { readonly taskId: string; readonly serverId: string; readonly inputKey: string; readonly signal?: AbortSignal; } /** * Handlers for the three methods. Each must be the *same* handler the * standalone request path uses, so trust rules cannot diverge between the two * channels. An absent handler is a `no-handler` rejection, never a silent skip. */ interface TaskInputHandlers { elicitation?: (params: Record, ctx: TaskInputHandlerContext) => Promise>; roots?: (params: Record, ctx: TaskInputHandlerContext) => Promise>; sampling?: (params: Record, ctx: TaskInputHandlerContext) => Promise>; } /** * Bounds applied *before* a request is rendered or forwarded. `inputRequests` * is attacker-influenced: it comes from the server verbatim. */ interface TaskInputLimits { maxRequests?: number; maxKeyLength?: number; maxSerializedRequestBytes?: number; maxResponsesPerUpdate?: number; } declare const DEFAULT_TASK_INPUT_LIMITS: Required; interface TaskInputDriverOptions { /** What this connection actually advertised. Governs which methods are legal. */ declaredCapabilities: ClientCapabilityOptions | undefined; handlers: TaskInputHandlers; /** * Strict, dialect-aware validator for accepted elicitation content, matching * the standalone elicitation path. Absent means content is not self-validated * — acceptable only where the standalone path also does not validate. */ validateElicitationContent?: ElicitationContentValidator; supportedElicitationModes?: readonly ElicitationMode[]; limits?: TaskInputLimits; } /** * Capabilities a connection declared, as booleans. Read from the exact object * handed to `new Client(...)` so this can never disagree with the wire. */ interface DeclaredInputCapabilities { elicitation: boolean; roots: boolean; sampling: boolean; } declare function readDeclaredInputCapabilities(capabilities: ClientCapabilityOptions | undefined): DeclaredInputCapabilities; /** * Whether a surface may declare the tasks extension at all. * * The rule from the plan: a surface may declare the extension only when it has * a complete handler path for the standalone capabilities it declares. A * connection that advertises `sampling` but wires no sampling handler would * strand any task that asks for it, so declaring tasks there is a bug we can * detect statically. */ declare function canDeclareTasksExtension(declaredCapabilities: ClientCapabilityOptions | undefined, handlers: TaskInputHandlers): { ok: true; } | { ok: false; missing: TaskInputMethod[]; }; /** * `await` mode: drive a created task to a bounded terminal result. * * Used by automation surfaces — eval execution, the CLI's `tasks watch`, the * public API when a caller asks to block — where nobody is watching a tab, so * returning a handle for someone to follow later is the same as returning * nothing. * * Three properties, in the order they matter: * * 1. **It terminates.** Every exit is one of a small closed set, and the * deadline is checked before every wait. A task that needs human input * nobody can supply returns a deterministic `input-required` outcome * rather than hanging until the evaluation times out — the plan's * `TASK_INPUT_REQUIRED`. * 2. **It never polls faster than allowed.** Waiting is delegated to the * lifecycle engine, so `pollIntervalMs`, the user minimum, error backoff * and `Retry-After` all apply exactly as they do interactively. An * automation surface has no licence the interactive one lacks. * 3. **It performs no I/O of its own.** The caller supplies `getTask` and * `updateTask`, so this same driver runs over a local client, a hosted * reconnect-per-poll route, or an HTTP API client. */ /** How the drive ended. Every one is deterministic and reportable. */ type TaskAwaitOutcome = /** Reached `completed`. `task` carries the inline result. */ "completed" /** Reached `failed` — a JSON-RPC fault, NOT a tool result with `isError`. */ | "failed" /** Reached `cancelled`. */ | "cancelled" /** A confirmed `tasks/get` `-32602`: the server no longer knows the handle. */ | "expired" /** Needs input this surface cannot supply. The plan's `TASK_INPUT_REQUIRED`. */ | "input-required" /** The deadline elapsed while the task was still working. */ | "timeout" /** The caller's signal aborted. */ | "aborted" /** Reads kept failing past the retry budget. */ | "unreachable"; interface TaskAwaitResult { outcome: TaskAwaitOutcome; /** Last validated state, when there was one. */ task?: TaskLifecycleSnapshot; /** Input keys this surface could not answer, with reasons. */ unansweredInput?: TaskInputRejection[]; /** * The read error that ended an `unreachable` drive — or, on a `completed` * legacy task, the reason its result could not be fetched. The outcome stays * `completed` in that case because the task genuinely finished; this is how * the caller learns the payload is missing rather than empty. */ lastError?: unknown; } interface DriveTaskToTerminalArgs { identity: TaskLifecycleIdentity; /** Reads current state. Rejects with a `-32602` for an unknown handle. */ getTask: (identity: TaskLifecycleIdentity) => Promise; /** * Fetches a LEGACY task's result. Required on the 2025-11-25 wire, ignored on * the extension. * * The two wires differ here and the difference is not cosmetic: the extension * carries `result` inline on `tasks/get`, while 2025-11-25 returns only * status from `tasks/get` and keeps the payload behind a separate * `tasks/result` call. Without this seam a legacy drive reported `completed` * with `task.result === undefined` for every successful task — which for an * eval or a CLI consumer is the same as having failed, since the tool output * is the entire point of waiting. */ getResult?: (identity: TaskLifecycleIdentity) => Promise | null>; /** Submits input responses. Resolving means the server acknowledged them. */ updateTask?: (identity: TaskLifecycleIdentity, inputResponses: Record) => Promise; /** Answers `input_required` rounds. Absent ⇒ any input ends the drive. */ input?: TaskInputDriverOptions; /** Total wall-clock budget. The whole drive, not a single read. */ timeoutMs?: number; /** Consecutive failed reads tolerated before giving up. */ maxConsecutiveErrors?: number; /** * Cap on `input_required` rounds. A server that keeps asking for the same * thing must not turn a bounded drive into an unbounded one. */ maxInputRounds?: number; signal?: AbortSignal; /** * Timer used to bound a single I/O call. Deliberately SEPARATE from * {@link sleep}: `sleep` is the poll-pacing clock a test drives by hand, * whereas this is a wall-clock watchdog that must not fire just because a * test advanced its own clock. Injected only so the watchdog itself is * testable. */ setTimer?: (ms: number, fire: () => void) => () => void; /** * Injected engine. Pass the surface's own so an `await` drive shares the * schedule with whatever else is polling that server; omit for a private one. */ engine?: TaskLifecycleEngine; now?: () => number; sleep?: (ms: number) => Promise; /** Observability hook. Never receives payloads — status transitions only. */ onState?: (snapshot: TaskLifecycleSnapshot) => void; } /** * Polls `identity` until it reaches a terminal state or a bounded exit. * * The loop deliberately re-checks the deadline *before* sleeping rather than * after: a task whose advertised floor exceeds the remaining budget should * report `timeout` immediately instead of sleeping past its own deadline and * reporting it late. */ declare function driveTaskToTerminal(args: DriveTaskToTerminalArgs): Promise; /** * The single place a model-facing tool call may become a task. * * ## Why this is one seam and not per-surface wiring * * Chat, the agent and evals all reach their tools through the `callTool` * closure `getToolsForAiSdk` builds. Putting the task decision there means the * per-server wire is already in scope (the closure captures its own server id), * so a tool set spanning an extension server, a 2025-11-25 server and a * 2025-06-18 server does the right thing per call without any surface knowing * that mixed sets exist. A set-level decision is not merely discouraged here — * it is unrepresentable. * * ## Three invariants * * 1. **No task option ⇒ byte-identical requests.** The manager does not call * this module at all when the caller passed no `tasks` option, and * {@link toolTaskSeamOptionsFor} returns `undefined` for mode `off`. There * is exactly one no-tasks path and it is the one that existed before this * file. * 2. **Extension wire only.** The legacy (2025-11-25) wire opts in with * `task: {ttl}` per call and stays a Tools-tab affordance; letting a host * policy create legacy tasks would change behavior for every existing chat * against a 2025-11-25 server. On any non-extension wire this delegates to * the caller's plain call, unchanged. * 3. **A created task is never lost.** The fan-out fires before anything that * can fail, and a functional consumer's throw is converted into an error * *result carrying the handle* rather than an exception. See * {@link runToolTaskSeam}. */ /** * `_meta` key carrying the task handle on every result this seam synthesizes. * * Namespaced under `com.mcpjam/` for the same reason the host policy is: it is * MCPJam's own annotation on a result, not anything SEP-2663 defines, and it * must never be mistaken for server-authored wire data. */ declare const TASK_SEAM_META_KEY: "com.mcpjam/task"; /** Shape stored at {@link TASK_SEAM_META_KEY}. */ interface ToolTaskSeamMeta { taskId: string; serverId: string; wire: "extension"; status?: string; createdAt?: string; ttlMs?: number | null; pollIntervalMs?: number; /** How the drive ended. `await` mode only. */ outcome?: TaskAwaitOutcome; /** * Set when a server sent a human-readable string for the model alongside the * task handle. SEP-2663 defines no such field, so the value is recorded here * and NEVER used as the model-facing text — see * {@link synthesizeCreatedTaskResult}. */ textSource?: "server-legacy"; /** The off-spec server text itself, when there was one. */ serverText?: string; } /** What the manager supplies for one tool call. */ interface ToolTaskSeamContext { serverId: string; toolName: string; /** Resolved per server, so mixed-wire tool sets work by construction. */ wire: TasksWire; /** The pre-existing call, verbatim. Used on every non-task path. */ callPlain: () => Promise; /** A call that declares task eligibility. Extension wire only. */ callEligible: () => Promise; /** Reads task state. Required for `await`, unused by `expose`. */ getTask?: (taskId: string) => Promise; /** Submits input responses. `await` mode only. */ updateTask?: (taskId: string, inputResponses: Record) => Promise; } /** Tuning for `await` mode. Every field is optional; the driver has defaults. */ interface ToolTaskAwaitOptions { timeoutMs?: number; maxConsecutiveErrors?: number; maxInputRounds?: number; /** Answers `input_required` rounds. Absent ⇒ any input ends the drive. */ input?: TaskInputDriverOptions; /** Share the surface's engine so an await drive respects live poll floors. */ engine?: TaskLifecycleEngine; signal?: AbortSignal; } interface ToolTaskSeamOptions { /** * `off` is deliberately not representable. {@link toolTaskSeamOptionsFor} * returns `undefined` for it, which is what keeps "tasks disabled" and "no * tasks option" the same single code path. */ mode: Exclude; surface: TaskCreationSurface; /** Auth/org context (hosted `projectId`), carried into the task identity. */ scope?: string; /** * The fan-out. Fired before the result is synthesized and, in `await` mode, * before the drive starts. */ onTaskCreated: (event: TaskCreatedEvent) => void | Promise; await?: ToolTaskAwaitOptions; } /** * Resolves a surface's mode into seam options, or `undefined` for `off`. * * The mode is resolved by the CALLER and passed in. This module must not read * a host config: `mcp-client-manager` has no business knowing what a host is, * and `conformance` / `api` are caller-owned surfaces that must be able to * resolve a mode and still deliberately pass nothing. */ declare function toolTaskSeamOptionsFor(mode: TaskMode, rest: Omit): ToolTaskSeamOptions | undefined; /** * Runs one tool call under a task policy. * * @returns always a valid `CallToolResult` — every synthesized result is built * to pass `assertCallToolResult`, which the manager applies to this return * value and `tool-converters` applies again inside `execute`. */ declare function runToolTaskSeam(context: ToolTaskSeamContext, options: ToolTaskSeamOptions): Promise; declare class MCPClientManager { private readonly registeredServers; private readonly liveClientStates; private readonly toolsMetadataCache; private readonly toolsAnnotationsCache; /** * Servers whose CURRENT connection has completed a no-`cursor` * `tools/list` — the only call that writes upstream's aggregated * `tools/list` response-cache entry, which is what upstream `callTool()` * reads to run SEP-2243 `Mcp-Param-*` header mirroring (see * `@modelcontextprotocol/client` `index.d.mts`: "Pass an explicit * `{ cursor }` to fetch a single page ... does not write the response * cache"). A membership miss means "mirroring would silently no-op", which * `ensureXMcpHeaderMirroringSource` repairs before a modern tools/call. * * Lifetime is the CONNECTION, not the server registration: the response * cache is allocated per upstream `Client`, so this is cleared everywhere * `toolsMetadataCache` is (every live-state teardown) and never persists * across a reconnect. */ private readonly aggregatedToolsListWarmed; private readonly retryAbortControllers; private readonly unauthorizedRefreshInFlight; /** * Per-server modern per-request log level. A present entry means "inject * `LOG_LEVEL_META_KEY` into every request's `_meta` on the modern era"; * an ABSENT entry means opt-out (no `_meta` key). Read live by each * server's `LogLevelMetaClient` decorator via the provider closure wired * at connect, so `setPerRequestLogLevel` takes effect without reconnect. */ private readonly perRequestLogLevels; private readonly notificationManager; private readonly elicitationManager; /** * Per-server collectors for the modern multi-round-trip (`input_required`) * loop. When a collector is registered for a server, `executeTool`, * `readResource`, and `getPrompt` drive the manual MRTR loop * (`mrtr-driver.ts`) so an `input_required` result is collected and the * operation retried; when none is registered the verbs keep their exact * pre-MRTR behavior (an `input_required` from a modern server then surfaces * as the SDK's typed `UnsupportedResultType` rather than being silently * mishandled). The collector seam is what PR2 (local UI), PR6 (CLI), and the * hosted PRs plug into. */ private readonly mrtrInputCollectors; /** MCPJam-owned MRTR round cap (see `mrtr-driver.ts`). */ private readonly mrtrMaxRounds; /** * Strict self-validation of collected elicitation content against each * request's `requestedSchema` (§12.1.11). The rule itself lives in * `elicitation-content-validator.ts` so task input drivers wire the same * authority; see that module for the strictness rationale. */ private readonly mrtrElicitationContentValidator; /** * Tool output-schema validator for the MRTR path. `requestWithSchema` * bypasses upstream `callTool`'s output-schema assertion, so we reconstruct * it on the final complete result. Fail-open on an unknown dialect, matching * upstream's tool-output behavior (see `DialectAwareJsonSchemaValidator`). */ private readonly mrtrToolOutputValidator; private readonly defaultClientName; private readonly defaultClientVersion; /** * Extra `clientInfo` fields (e.g. `title`) merged into the per-connection * `clientInfo` object alongside name/version. Per-server `clientInfo` * overrides individual keys. Lets the inspector pass forward-compat MCP * spec additions (the `title` field, future fields) without an SDK bump. */ private readonly defaultClientInfoExtras; /** * Default supported protocol versions accept-list. Forwarded to the * upstream Client as `ClientOptions.supportedProtocolVersions`. Per- * server `supportedProtocolVersions` overrides this. Undefined here * preserves historical behavior (upstream Client's built-in * `SUPPORTED_PROTOCOL_VERSIONS` default). */ private readonly defaultSupportedProtocolVersions; private readonly defaultCapabilities; private readonly defaultTimeout; private readonly defaultLogJsonRpc; private readonly defaultRpcLogger?; private readonly defaultHttpLogger?; /** See `baseFetch` on `MCPClientManagerOptions`. */ private readonly defaultBaseFetch?; private readonly defaultProgressHandler?; private readonly cacheEventLogger?; /** * Optional accessor for the ambient OpenTelemetry trace context to * propagate. Unset by default — MCPJam runs no tracer, so no * `traceparent`/`tracestate`/`baggage` `_meta` key is ever emitted. */ private readonly traceContextProvider?; private readonly negotiationOutcomeLogger?; private readonly defaultRetryPolicy; private readonly lazyConnect; private readonly elicitationTimeoutExtensionMs; private progressTokenCounter; /** * Creates a new MCPClientManager. * * @param servers - Configuration map of server IDs to server configs * @param options - Global options for the manager */ constructor(servers?: MCPClientManagerConfig, options?: MCPClientManagerOptions); /** * Lists all registered server IDs. */ listServers(): string[]; /** * Checks if a server is registered. */ hasServer(serverId: string): boolean; /** * Gets summaries for all registered servers. */ getServerSummaries(): ServerSummary[]; /** * Gets replayable HTTP server configs for eval reporting. */ getServerReplayConfigs(): MCPServerReplayConfig[]; /** * Gets the connection status for a server. */ getConnectionStatus(serverId: string): MCPConnectionStatus; /** * Gets the configuration for a server. */ getServerConfig(serverId: string): MCPServerConfig | undefined; /** * Gets the capabilities reported by a server. */ getNegotiatedProtocolVersion(serverId: string): string | undefined; getServerCapabilities(serverId: string): ServerCapabilities | undefined; /** * Gets the underlying upstream MCP `Client` for a server. Returns the * legacy adapter's wrapped `Client` instance, or `undefined` for * stateless-preview connections (which have no upstream `Client`). * * **Deprecated for new code** — prefer `getManagedClient()`. Kept * because external SDK consumers reference this API; retyping it * would be a breaking change. */ getClient(serverId: string): Client | undefined; /** * Gets the `ManagedMcpClient` for a server — works for both the legacy * adapter and the 2026-07-28 stateless preview. Use this in new * code instead of `getClient()`. */ getManagedClient(serverId: string): ManagedMcpClient | undefined; /** * Gets initialization information for a connected server. */ getInitializationInfo(serverId: string): { protocolVersion: string | undefined; transport: string; serverCapabilities: { experimental?: { [x: string]: { [x: string]: string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | /*elided*/ any | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null; }; } | undefined; logging?: { [x: string]: string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | /*elided*/ any | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null; } | undefined; completions?: { [x: string]: string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | /*elided*/ any | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null; } | undefined; prompts?: { listChanged?: boolean | undefined; } | undefined; resources?: { subscribe?: boolean | undefined; listChanged?: boolean | undefined; } | undefined; tools?: { listChanged?: boolean | undefined; } | undefined; tasks?: { [x: string]: unknown; list?: { [x: string]: string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | /*elided*/ any | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null; } | undefined; cancel?: { [x: string]: string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | /*elided*/ any | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null; } | undefined; requests?: { [x: string]: unknown; tools?: { [x: string]: unknown; call?: { [x: string]: string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | /*elided*/ any | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null; } | undefined; } | undefined; } | undefined; } | undefined; extensions?: { [x: string]: { [x: string]: string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | /*elided*/ any | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null; }; } | undefined; } | undefined; serverVersion: { version: string; name: string; websiteUrl?: string | undefined; description?: string | undefined; icons?: { src: string; mimeType?: string | undefined; sizes?: string[] | undefined; theme?: "light" | "dark" | undefined; }[] | undefined; title?: string | undefined; } | undefined; instructions: string | undefined; clientCapabilities: { experimental?: { [x: string]: { [x: string]: string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | /*elided*/ any | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null; }; } | undefined; sampling?: { context?: { [x: string]: string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | /*elided*/ any | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null; } | undefined; tools?: { [x: string]: string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | /*elided*/ any | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null; } | undefined; } | undefined; elicitation?: { [x: string]: string | number | boolean | { [x: string]: string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | /*elided*/ any | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null; } | (string | number | boolean | { [x: string]: string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | /*elided*/ any | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null; } | (string | number | boolean | { [x: string]: string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | /*elided*/ any | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null; } | (string | number | boolean | { [x: string]: string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | /*elided*/ any | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null; } | (string | number | boolean | { [x: string]: string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | /*elided*/ any | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null; } | (string | number | boolean | { [x: string]: string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | /*elided*/ any | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null; } | (string | number | boolean | { [x: string]: string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | /*elided*/ any | null)[] | null)[] | null)[] | null)[] | null)[] | null; } | (string | number | boolean | { [x: string]: string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | /*elided*/ any | null)[] | null)[] | null)[] | null)[] | null; } | (string | number | boolean | { [x: string]: string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | /*elided*/ any | null)[] | null)[] | null)[] | null; } | (string | number | boolean | { [x: string]: string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | /*elided*/ any | null)[] | null)[] | null; } | (string | number | boolean | { [x: string]: string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | /*elided*/ any | null)[] | null; } | (string | number | boolean | { [x: string]: string | number | boolean | /*elided*/ any | /*elided*/ any | null; } | /*elided*/ any | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null; form?: { [x: string]: string | number | boolean | { [x: string]: string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | /*elided*/ any | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null; } | (string | number | boolean | { [x: string]: string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | /*elided*/ any | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null; } | (string | number | boolean | { [x: string]: string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | /*elided*/ any | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null; } | (string | number | boolean | { [x: string]: string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | /*elided*/ any | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null; } | (string | number | boolean | { [x: string]: string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | /*elided*/ any | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null; } | (string | number | boolean | { [x: string]: string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | /*elided*/ any | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null; } | (string | number | boolean | { [x: string]: string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | /*elided*/ any | null)[] | null)[] | null)[] | null)[] | null)[] | null; } | (string | number | boolean | { [x: string]: string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | /*elided*/ any | null)[] | null)[] | null)[] | null)[] | null; } | (string | number | boolean | { [x: string]: string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | /*elided*/ any | null)[] | null)[] | null)[] | null; } | (string | number | boolean | { [x: string]: string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | /*elided*/ any | null)[] | null)[] | null; } | (string | number | boolean | { [x: string]: string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | /*elided*/ any | null)[] | null; } | (string | number | boolean | { [x: string]: string | number | boolean | /*elided*/ any | /*elided*/ any | null; } | /*elided*/ any | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null; applyDefaults?: boolean | undefined; } | undefined; url?: { [x: string]: string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | /*elided*/ any | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null; } | undefined; } | undefined; roots?: { listChanged?: boolean | undefined; } | undefined; tasks?: { [x: string]: unknown; list?: { [x: string]: string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | /*elided*/ any | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null; } | undefined; cancel?: { [x: string]: string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | /*elided*/ any | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null; } | undefined; requests?: { [x: string]: unknown; sampling?: { [x: string]: unknown; createMessage?: { [x: string]: string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | /*elided*/ any | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null; } | undefined; } | undefined; elicitation?: { [x: string]: unknown; create?: { [x: string]: string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | /*elided*/ any | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null; } | undefined; } | undefined; } | undefined; } | undefined; extensions?: { [x: string]: { [x: string]: string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | (string | number | boolean | /*elided*/ any | /*elided*/ any | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null; }; } | undefined; }; } | undefined; /** * Connects to an MCP server. * * @param serverId - Unique identifier for the server * @param config - Server configuration * @returns The connected MCP Client */ connectToServer(serverId: string, config: MCPServerConfig): Promise; /** * Emit one auto-negotiation telemetry event for a completed connection * attempt. Fires only when a `negotiationOutcomeLogger` is wired, and NEVER * throws into the connect path (a telemetry failure must not break a * connection). Carries no request payloads. */ private emitNegotiationOutcome; /** * Disconnects from a server. */ disconnectServer(serverId: string): Promise; /** * Removes a server from the manager entirely. */ removeServer(serverId: string): Promise; /** * Disconnects from all servers. */ disconnectAllServers(): Promise; /** * Lists tools available from a server. * * A call with no `cursor` (the common case) auto-aggregates every page in * the upstream client AND writes that aggregate to its response cache — the * view upstream `callTool()` reads for SEP-2243 `Mcp-Param-*` mirroring. An * explicit-`{ cursor }` page walk deliberately writes neither, and * `cacheMode: "bypass"` returns the aggregate without writing it, so only a * no-`cursor` non-bypass call marks the connection warm here. */ listTools(serverId: string, params?: Parameters[0], options?: ClientRequestOptions): Promise; /** * Gets tools from multiple servers (or all servers if none specified). * Returns tools with execute functions pre-wired to call this manager. * * @param serverIds - Server IDs to get tools from (or all if omitted) * @returns Array of executable tools * * @example * ```typescript * const tools = await manager.getTools(["asana"]); * const agent = new HostRunner({ tools, model: "openai/gpt-4o", apiKey }); * ``` */ getTools(serverIds?: string[]): Promise; /** * Gets cached tool metadata for a server. */ getAllToolsMetadata(serverId: string): Record>; /** * Gets cached metadata for a specific tool. * Metadata is populated when tools are listed via listTools()/getTools()/getToolsForAiSdk(). */ getToolMetadata(serverId: string, toolName: string): Record | undefined; /** * Gets annotations for all tools from the most recent cached tools/list. */ getAllToolAnnotations(serverId: string): Record | undefined>; /** * Whether a complete tools/list response has populated the annotation cache. * An empty map is still a valid populated response for a server with no * tools; callers must distinguish that from a cold or invalidated cache. */ hasCachedToolAnnotations(serverId: string): boolean; /** * Gets tools formatted for Vercel AI SDK. * * @param serverIds - Server IDs to get tools from (or all if omitted) * @param options - Schema options * @returns AiSdkTool compatible with Vercel AI SDK's generateText() */ getToolsForAiSdk(serverIds?: string[] | string, options?: { schemas?: ToolSchemaOverrides | "automatic"; needsApproval?: boolean; /** * When true, include SEP-1865 app-only tools (`_meta.ui.visibility = ["app"]`) * in the returned tool set. Defaults to `false` (spec-compliant: app-only * tools are hidden from the model). Use this only when intentionally * mirroring a host that does not implement visibility filtering. */ includeAppOnly?: boolean; /** Host policy for model visibility of MCP tool-result content/resources. */ modelVisibleMcpToolResults?: ModelVisibleMcpToolResults; /** * Task policy for the tool calls this set produces. Omit (the default) * and every call takes the pre-existing path, byte-for-byte: no `_meta`, * no declaration, no extra request field. See `tool-task-seam.ts`. * * The mode is resolved by the CALLER — this class must not read host * configs. `off` is expressed by omitting the option entirely, which is * what `toolTaskSeamOptionsFor` returns for it. */ tasks?: ToolTaskSeamOptions; }): Promise; /** * Executes a tool on a server. * * @param serverId - The server ID * @param toolName - The tool name * @param args - Tool arguments * @param options - Request options * @param taskOptions - Task options for async execution */ executeTool(serverId: string, toolName: string, args?: ExecuteToolArguments, options?: ClientRequestOptions, taskOptions?: TaskOptions): Promise>; executeTool(serverId: string, toolName: string, args: ExecuteToolArguments | undefined, options: ExecuteToolRequest): Promise>; /** * Guarantees upstream `callTool()` can run SEP-2243 `Mcp-Param-*` header * mirroring (2026-07-28 Streamable HTTP, "clients **MUST** mirror the * designated parameter values into HTTP headers"). * * Upstream reads the tool's `inputSchema` from exactly two places: the * `CallToolRequestOptions.toolDefinition` escape hatch, or the aggregated * `tools/list` entry in its response cache. MCPJam passes the former * nowhere, and several surfaces reach `executeTool` with a cache that was * never written — a hosted/CLI ephemeral connection that only ever calls * the tool, and any surface that walks pagination by hand (an * explicit-`{ cursor }` `listTools()` does not write the cache). Upstream's * miss path is silent: it sends the call with no `Mcp-Param-*` headers and * relies on the server answering `HEADER_MISMATCH` to trigger its * evict-refetch-retry recovery — which a lenient server never does. So the * MUST was being skipped with no warning. * * The repair is to WARM the source rather than to synthesize a * `toolDefinition`: upstream then keeps ownership of freshness, of the * `list_changed` eviction lifecycle, and of the `HEADER_MISMATCH` recovery * retry (which it disables outright when `toolDefinition` is supplied), and * output-schema validation keeps reading the same view it does today. On * the surfaces that DO hold the tool definitions, they hold them because * they called {@link listTools} on this manager — which already warmed the * cache — so those paths pay nothing here. * * Three gates keep this off every path that cannot need it, so no legacy * (2025-*) connection changes by a single byte: * - already warmed on this connection (tracked by * `aggregatedToolsListWarmed`) — the common case, zero round trips; * - era is not `"modern"`, or is unknown (an adapter that cannot report it) * — mirroring is 2026-07-28-only and an unknown era fails closed; * - the transport is stdio — mirroring is Streamable-HTTP-only, and unlike * upstream (which cannot tell an HTTP transport from an in-memory one) we * own the server config and can skip the useless round trip. * * A failed warm-up is NOT marked warm and NOT fatal: the tool call proceeds * exactly as it does today (upstream's `HEADER_MISMATCH` recovery is still * armed because we pass no `toolDefinition`), and the next call re-attempts * the warm-up rather than disabling mirroring for the connection's life. */ private ensureXMcpHeaderMirroringSource; /** * Lists resources available from a server. */ listResources(serverId: string, params?: ListResourcesParams, options?: ClientRequestOptions): Promise<{ [x: string]: unknown; resources: { uri: string; name: string; description?: string | undefined; mimeType?: string | undefined; size?: number | undefined; annotations?: { audience?: ("user" | "assistant")[] | undefined; priority?: number | undefined; lastModified?: string | undefined; } | undefined; _meta?: { [x: string]: unknown; } | undefined; icons?: { src: string; mimeType?: string | undefined; sizes?: string[] | undefined; theme?: "light" | "dark" | undefined; }[] | undefined; title?: string | undefined; }[]; _meta?: { [x: string]: unknown; "io.modelcontextprotocol/serverInfo"?: { version: string; name: string; websiteUrl?: string | undefined; description?: string | undefined; icons?: { src: string; mimeType?: string | undefined; sizes?: string[] | undefined; theme?: "light" | "dark" | undefined; }[] | undefined; title?: string | undefined; } | undefined; } | undefined; nextCursor?: string | undefined; }>; /** * Reads a resource from a server. */ readResource(serverId: string, params: ReadResourceParams, options?: ClientRequestOptions): Promise<{ [x: string]: unknown; contents: ({ uri: string; text: string; mimeType?: string | undefined; _meta?: { [x: string]: unknown; } | undefined; } | { uri: string; blob: string; mimeType?: string | undefined; _meta?: { [x: string]: unknown; } | undefined; })[]; _meta?: { [x: string]: unknown; "io.modelcontextprotocol/serverInfo"?: { version: string; name: string; websiteUrl?: string | undefined; description?: string | undefined; icons?: { src: string; mimeType?: string | undefined; sizes?: string[] | undefined; theme?: "light" | "dark" | undefined; }[] | undefined; title?: string | undefined; } | undefined; } | undefined; }>; /** * Subscribes to resource updates. */ subscribeResource(serverId: string, params: SubscribeResourceParams, options?: ClientRequestOptions): Promise<{ _meta?: { [x: string]: unknown; "io.modelcontextprotocol/serverInfo"?: { version: string; name: string; websiteUrl?: string | undefined; description?: string | undefined; icons?: { src: string; mimeType?: string | undefined; sizes?: string[] | undefined; theme?: "light" | "dark" | undefined; }[] | undefined; title?: string | undefined; } | undefined; } | undefined; }>; /** * Unsubscribes from resource updates. */ unsubscribeResource(serverId: string, params: UnsubscribeResourceParams, options?: ClientRequestOptions): Promise<{ _meta?: { [x: string]: unknown; "io.modelcontextprotocol/serverInfo"?: { version: string; name: string; websiteUrl?: string | undefined; description?: string | undefined; icons?: { src: string; mimeType?: string | undefined; sizes?: string[] | undefined; theme?: "light" | "dark" | undefined; }[] | undefined; title?: string | undefined; } | undefined; } | undefined; }>; /** * Lists resource templates from a server. */ listResourceTemplates(serverId: string, params?: ListResourceTemplatesParams, options?: ClientRequestOptions): Promise<{ [x: string]: unknown; resourceTemplates: { uriTemplate: string; name: string; description?: string | undefined; mimeType?: string | undefined; annotations?: { audience?: ("user" | "assistant")[] | undefined; priority?: number | undefined; lastModified?: string | undefined; } | undefined; _meta?: { [x: string]: unknown; } | undefined; icons?: { src: string; mimeType?: string | undefined; sizes?: string[] | undefined; theme?: "light" | "dark" | undefined; }[] | undefined; title?: string | undefined; }[]; _meta?: { [x: string]: unknown; "io.modelcontextprotocol/serverInfo"?: { version: string; name: string; websiteUrl?: string | undefined; description?: string | undefined; icons?: { src: string; mimeType?: string | undefined; sizes?: string[] | undefined; theme?: "light" | "dark" | undefined; }[] | undefined; title?: string | undefined; } | undefined; } | undefined; nextCursor?: string | undefined; }>; /** * Lists prompts available from a server. */ listPrompts(serverId: string, params?: ListPromptsParams, options?: ClientRequestOptions): Promise<{ [x: string]: unknown; prompts: { name: string; description?: string | undefined; arguments?: { name: string; description?: string | undefined; required?: boolean | undefined; }[] | undefined; _meta?: { [x: string]: unknown; } | undefined; icons?: { src: string; mimeType?: string | undefined; sizes?: string[] | undefined; theme?: "light" | "dark" | undefined; }[] | undefined; title?: string | undefined; }[]; _meta?: { [x: string]: unknown; "io.modelcontextprotocol/serverInfo"?: { version: string; name: string; websiteUrl?: string | undefined; description?: string | undefined; icons?: { src: string; mimeType?: string | undefined; sizes?: string[] | undefined; theme?: "light" | "dark" | undefined; }[] | undefined; title?: string | undefined; } | undefined; } | undefined; nextCursor?: string | undefined; }>; /** * Gets a prompt from a server. */ getPrompt(serverId: string, params: GetPromptParams, options?: ClientRequestOptions): Promise<{ [x: string]: unknown; messages: { role: "user" | "assistant"; content: { type: "text"; text: string; annotations?: { audience?: ("user" | "assistant")[] | undefined; priority?: number | undefined; lastModified?: string | undefined; } | undefined; _meta?: { [x: string]: unknown; } | undefined; } | { type: "image"; data: string; mimeType: string; annotations?: { audience?: ("user" | "assistant")[] | undefined; priority?: number | undefined; lastModified?: string | undefined; } | undefined; _meta?: { [x: string]: unknown; } | undefined; } | { type: "audio"; data: string; mimeType: string; annotations?: { audience?: ("user" | "assistant")[] | undefined; priority?: number | undefined; lastModified?: string | undefined; } | undefined; _meta?: { [x: string]: unknown; } | undefined; } | { uri: string; name: string; type: "resource_link"; description?: string | undefined; mimeType?: string | undefined; size?: number | undefined; annotations?: { audience?: ("user" | "assistant")[] | undefined; priority?: number | undefined; lastModified?: string | undefined; } | undefined; _meta?: { [x: string]: unknown; } | undefined; icons?: { src: string; mimeType?: string | undefined; sizes?: string[] | undefined; theme?: "light" | "dark" | undefined; }[] | undefined; title?: string | undefined; } | { type: "resource"; resource: { uri: string; text: string; mimeType?: string | undefined; _meta?: { [x: string]: unknown; } | undefined; } | { uri: string; blob: string; mimeType?: string | undefined; _meta?: { [x: string]: unknown; } | undefined; }; annotations?: { audience?: ("user" | "assistant")[] | undefined; priority?: number | undefined; lastModified?: string | undefined; } | undefined; _meta?: { [x: string]: unknown; } | undefined; }; }[]; _meta?: { [x: string]: unknown; "io.modelcontextprotocol/serverInfo"?: { version: string; name: string; websiteUrl?: string | undefined; description?: string | undefined; icons?: { src: string; mimeType?: string | undefined; sizes?: string[] | undefined; theme?: "light" | "dark" | undefined; }[] | undefined; title?: string | undefined; } | undefined; } | undefined; description?: string | undefined; }>; /** * Pings a server to check connectivity — era-aware. * * `ping` was removed from the 2026-07-28 vocabulary, so the upstream * client refuses to send it on a modern-classified connection * (`MethodNotSupportedByProtocolVersion`). The modern era's universally * available request is `server/discover`, so that is the liveness probe * there; its result is discarded and the ping contract's `EmptyResult` * returned, so callers stay era-agnostic. Legacy connections keep the * wire-identical `ping`. */ pingServer(serverId: string, options?: RequestOptions): Promise>>; /** * Sets the logging level for a server. */ setLoggingLevel(serverId: string, level?: LoggingLevel): Promise; /** * Modern (2026-07-28) per-request logging opt-in. One user-facing concept * ("set a level for this server"), era-specific delivery: on the modern era * the level rides on every request as `_meta[LOG_LEVEL_META_KEY]` (injected * by the server's `LogLevelMetaClient` decorator); on the legacy era this is * inert — use {@link setLoggingLevel} there. * * Passing `undefined` opts out: the key becomes ABSENT on the wire (absence * is semantic — we never send an empty/null level). Takes effect on the next * request without a reconnect. No-op-safe before connect: the level is * stored and read live once the client exists. */ setPerRequestLogLevel(serverId: string, level: LoggingLevel | undefined): void; /** * Which logging mechanism is live for a server, for UI selection: * - `"per-request-meta"` — modern era + server advertises `logging`; set a * level with {@link setPerRequestLogLevel}. * - `"setLevel"` — legacy era + server advertises `logging`; set a level * with {@link setLoggingLevel}. * - `"none"` — no live client, or the server does not advertise `logging`. */ getLoggingMechanism(serverId: string): "setLevel" | "per-request-meta" | "none"; /** * Gets the session ID for a Streamable HTTP server. */ getSessionIdByServer(serverId: string): string | undefined; /** * Adds a notification handler for a server. */ addNotificationHandler(serverId: string, method: NotificationMethodName, handler: NotificationHandler): void; /** * Registers a handler for resource list changes. */ onResourceListChanged(serverId: string, handler: NotificationHandler): void; /** * Registers a handler for resource updates. */ onResourceUpdated(serverId: string, handler: NotificationHandler): void; /** * Registers a handler for prompt list changes. */ onPromptListChanged(serverId: string, handler: NotificationHandler): void; /** * Registers a handler for task status changes. */ onTaskStatusChanged(serverId: string, handler: NotificationHandler): void; /** * Registers a handler for server→client log records * (`notifications/message`). Works on BOTH eras: legacy servers stream * these after `logging/setLevel`; modern servers stream them inline within * the originating request's response. Follows the same * `NotificationManager.addHandler` + `applyToClient` path as the * `list_changed` registrations, so a handler registered before connect is * re-applied when the client is (re)built. */ onLogMessage(serverId: string, handler: NotificationHandler): void; /** * Sets a server-specific elicitation handler. * * Like {@link setMrtrInputCollector}, this is intentionally NOT gated on * prior registration. The two are registered together before connect (an * interactive surface answers `elicitation/create` on both eras, so both * deliveries have to be armed on the same envelope), and a gate here made * that pairing throw `Unknown MCP server` for every caller that registered * ahead of `connectToServer` — which is the only point at which `elicitation` * can still reach the connect envelope. Registering for an as-yet-unknown * server is a no-op until that server connects; the live-client update below * is already conditional on there being one. */ setElicitationHandler(serverId: string, handler: ElicitationHandler): void; /** * Clears a server-specific elicitation handler. */ clearElicitationHandler(serverId: string): void; /** * Registers the multi-round-trip (`input_required`) input collector for a * server. Once set, `executeTool` / `readResource` / `getPrompt` drive the * manual MRTR loop: an `input_required` result is validated (undeclared * roots/sampling and unsupported elicitation modes are rejected before any * UI), its embedded requests are handed to `collect`, the collected * responses are self-validated against each `requestedSchema`, and the * operation is retried — up to the MCPJam-owned round cap. `collect` MUST * reject on abort (never return a synthetic decline) and returns * `ElicitResult`-shaped responses keyed by the server's request keys. */ setMrtrInputCollector(serverId: string, collect: MrtrInputCollector): void; /** Removes a server's MRTR input collector. */ clearMrtrInputCollector(serverId: string): void; /** * Sets a global elicitation callback for all servers. */ setElicitationCallback(callback: ElicitationCallback): void; /** * Clears the global elicitation callback. */ clearElicitationCallback(): void; /** * Gets the pending elicitations map for external resolvers. */ getPendingElicitations(): Map void; reject: (error: unknown) => void; }>; /** * Responds to a pending elicitation. */ respondToElicitation(requestId: string, response: ElicitResult): boolean; /** * Lists tasks from a server. */ listTasks(serverId: string, cursor?: string, options?: ClientRequestOptions): Promise<{ [x: string]: unknown; tasks: { taskId: string; status: "failed" | "working" | "input_required" | "completed" | "cancelled"; ttl: number | null; createdAt: string; lastUpdatedAt: string; pollInterval?: number | undefined; statusMessage?: string | undefined; }[]; _meta?: { [x: string]: unknown; "io.modelcontextprotocol/serverInfo"?: { version: string; name: string; websiteUrl?: string | undefined; description?: string | undefined; icons?: { src: string; mimeType?: string | undefined; sizes?: string[] | undefined; theme?: "light" | "dark" | undefined; }[] | undefined; title?: string | undefined; } | undefined; } | undefined; nextCursor?: string | undefined; }>; /** * Gets a task by ID. */ getTask(serverId: string, taskId: string, options?: ClientRequestOptions): Promise<{ taskId: string; status: "failed" | "working" | "input_required" | "completed" | "cancelled"; ttl: number | null; createdAt: string; lastUpdatedAt: string; pollInterval?: number | undefined; statusMessage?: string | undefined; }>; /** * Gets the result of a completed task. */ getTaskResult(serverId: string, taskId: string, options?: ClientRequestOptions): Promise; /** * Cancels a task. */ cancelTask(serverId: string, taskId: string, options?: ClientRequestOptions): Promise<{ taskId: string; status: "failed" | "working" | "input_required" | "completed" | "cancelled"; ttl: number | null; createdAt: string; lastUpdatedAt: string; pollInterval?: number | undefined; statusMessage?: string | undefined; }>; /** * The tasks wire this connection speaks (`"none"` when tasks are not * available on the negotiated version / advertised capabilities). */ getTasksWire(serverId: string): TasksWire; /** * Checks if server supports task-augmented tool calls (legacy wire only). */ supportsTasksForToolCalls(serverId: string): boolean; /** * Checks if server supports listing tasks (legacy wire only). */ supportsTasksList(serverId: string): boolean; /** * Checks if server supports canceling tasks (legacy wire only). */ supportsTasksCancel(serverId: string): boolean; /** * The full tasks support matrix for a connection — the single value every * route, UI, and CLI surface should branch on. */ getTasksSupport(serverId: string): TasksSupport; /** * `tasks/get` on the SEP-2663 extension wire. A completed task carries its * `result` inline; an expired/unknown task raises the server's `-32602`. */ getTaskExt(serverId: string, taskId: string, options?: ClientRequestOptions): Promise; /** * `tasks/update` — submit (possibly partial) `inputResponses`. Resolves with * the spec's empty acknowledgement: re-poll `getTaskExt` for the new status. */ updateTask(serverId: string, taskId: string, inputResponses: InputResponses, options?: ClientRequestOptions): Promise; /** * `tasks/cancel` on the extension wire. The result is an EMPTY ack: * cancellation is cooperative, so callers must re-poll rather than render * the ack as a state. */ cancelTaskExt(serverId: string, taskId: string, options?: ClientRequestOptions): Promise>; /** * Opens a task-filtered `subscriptions/listen` carrying the extension's * per-request eligibility declaration (SEP-2663). A non-declaring * task-filtered listen MUST be answered `-32021`, so this is the ONLY way a * `taskIds` filter may reach the wire. * * Port contract: on `SubscriptionClientPort`, availability is expressed by * method PRESENCE — callers probe {@link supportsTaskDeclaredListen} and * install this method onto the port only when it answers true. A defined * method must therefore never answer `undefined`; when the underlying seam * is unavailable despite the probe, this throws instead of silently sending * nothing. * * `TasksExtListenMetaSeamError` propagates deliberately: the coordinator * records the failed open on the stream record and polling continues — * task notifications are OPTIONAL, so nothing is lost but latency. */ listenWithTasksDeclaration(serverId: string, filter: SubscriptionFilterShape): Promise; /** * Whether {@link listenWithTasksDeclaration} can put a declared, * task-filtered listen on this connection's wire: extension tasks wire ∧ * the client can listen ∧ the listen-meta seam target resolves. Consumers * building a `SubscriptionClientPort` install the opener onto the port only * when this answers true — absence of the method IS the coordinator's signal * to drop `taskIds` (recording `tasks-declaration-unavailable`) and keep * polling. */ supportsTaskDeclaredListen(serverId: string): boolean; /** * The skills support matrix for a connection — the single value every * route, UI, CLI, and chat surface branches on. * * `active` is a CONJUNCTION: this client advertised the extension AND the * server declared it. Both halves are read from what actually happened on * this connection (the initialized client capabilities, the server's * initialize result), so the answer can never disagree with the wire. */ getSkillsSupport(serverId: string): SkillsSupport; /** * Advertise = enforce. A `skills/*` request is refused BEFORE it reaches the * wire whenever the extension is not mutually declared. * * Typed, not a bare `Error`: routes map `isMCPSkillsWireError` onto a * permanent 400 rather than a 500 that hosted clients would retry against a * server that can never answer. */ private assertSkillsActive; /** * `skills/list` — one page. Drain with `listAllServerSkills` in * `operations.ts` rather than looping here. * * A listing MAY be empty or partial by spec: absence from it is never proof * a skill does not exist. Confirm disappearance with {@link getServerSkill}. */ listServerSkills(serverId: string, params?: { cursor?: string; }, options?: ClientRequestOptions): Promise; /** * `skills/get` — one skill by URI, including skills the listing never * mentioned. A conforming server answers `-32602` for a URI it does not * serve (`isSkillNotFoundError`). */ getServerSkill(serverId: string, uri: string, options?: ClientRequestOptions): Promise; /** * `resources/directory/read` — the OPTIONAL readdir, gated on the server's * `{ directoryRead: true }` setting on TOP of the mutual declaration. The * extra gate is not redundant: a server may speak skills without opting into * directory reads, and sending the method anyway is an undeclared probe. */ readServerResourceDirectory(serverId: string, params: { uri: string; cursor?: string; }, options?: ClientRequestOptions): Promise; private declaredCapabilitiesFor; private assertExtensionTasksWire; /** * Adds the per-request extension declaration when this call is eligible for * a task result. Sends nothing extra on the legacy/none wires; a task * request on `wire: "none"` is a local typed error (never a wire probe). */ private withTaskEligibilityDeclaration; /** * Unwraps a `CreateTaskResult` that the transport wrapper smuggled through * beta.4's decoder (see `wrapTransportForTaskResults`). Returns `undefined` * for an ordinary result — the server decides, and a non-task result is * always valid on the extension wire. */ private unwrapCreatedTaskExt; /** Guards the legacy-form `tasks/*` read methods against other wires. */ private assertLegacyTasksReadWire; private assertLegacyTasksWire; private registerServer; private connectToServerOnce; private performConnection; private connectViaStdio; private connectViaHttp; /** * Recognize the one connect failure whose cause is a SETTING rather than a * network condition: this connection pinned a modern protocol version and * the server does not offer it. * * Returns `undefined` unless the connection was actually in pin mode, so an * `auto` connection — which probes and falls back — can never produce this. * The mode is derived through `resolveVersionNegotiation`, the same helper * the negotiation telemetry uses, rather than re-deriving "is this modern" * from the version string: one definition of pin mode, not two. * * The pin comes from the resolved config, never from parsing the upstream * message. The manager is the thing that chose the version; reading it back * out of prose would make an upstream rewording silently produce a message * with a blank version in it. */ private protocolPinFailure; private safeCloseTransport; private getProcessEnvironment; private createStdioStderrDrain; private annotateStdioConnectError; private ensureConnected; private getClientOrThrow; private clearLiveState; private clearClosedPendingConnectionState; private destroyLiveState; private buildServerReplayConfig; private isHttpConfig; private extractReplayAccessToken; private hasReplayableRequestInit; private extractBearerAccessToken; private withTimeout; private withProgressHandler; /** * Runs a tool call under an elicitation-aware timeout budget. * * The request timeout is a *server* budget. A tool call blocked because the * server asked the user a question is not hung, so its clock stops while an * elicitation is pending — but a genuinely hung server must still die at the * base timeout, and a human who never answers must not block forever. * * Mechanics (see `elicitation-timeout.ts` for the budget accounting): * - No elicitation handler for this server ⇒ pure passthrough, no watchdog, * no behavior change whatsoever. * - Otherwise the upstream hard timeout is **overridden** to * `base + extension` to move it out of the way (`withTimeout` only * *injects* a timeout when unset, so a plain merge would not take), and * the real budget is enforced locally by the watchdog. * - The watchdog's signal is composed with the caller's — the AI SDK * forwards its own `abortSignal` into these request options and it must * keep working. */ private withElicitationTimeoutSuspension; /** * Resolves the client capabilities to advertise to `serverId`. * * `era` is the connection's classified era when it is actually KNOWN — * `undefined` at connect time (pre-negotiation), `"modern"` once the * connection has landed on a 2026-era revision. With `era: "modern"` the * config's `eraCapabilities.modern` overlay is merged over the base set; * see {@link BaseServerConfig.eraCapabilities} for the seam's contract and * {@link applyModernEraCapabilities} for when the re-resolution runs. */ private buildCapabilities; /** * The post-negotiation capability re-resolution seam: applies the config's * `eraCapabilities.modern` overlay once the connection has classified as * 2026-era, and returns the set actually in force for this connection. * * Runs exactly once per connection, between transport establishment and the * live-state publication in {@link performConnection} — so no caller-visible * request ever races the widening, and the declaration is stable for the * connection's lifetime (MRTR rounds of one logical operation always see one * consistent set). Connections that land on a 2025 era — where capabilities * were already fixed by the `initialize` handshake — return the connect-time * set unchanged, as does any config without an overlay (byte-identical wire * behavior to before this seam existed). * * ## Why this writes the upstream client's private `_capabilities` * * The upstream `Client` stamps the per-request `_meta` envelope from * `this._capabilities` LIVE at request time (`_outboundMetaEnvelope`), so * updating the field is sufficient for every subsequent frame. The public * `registerCapabilities()` refuses to run once a transport is attached — * a guard written for the legacy era, where the declaration really was * frozen by `initialize`, that upstream has not yet relaxed for the modern * era, where the wire re-declares per request and negotiation (which is * what makes the era knowable) cannot complete until after the transport * attaches. Until upstream exposes a post-negotiation capability update, * this assignment is the seam — same pattern as the tasks era-gate shadow * (`tasks-ext-era-gate.ts`). The inbound elicitation mode checks * (`getSupportedElicitationModes`) also read `_capabilities` live, so * client-side enforcement and the wire stay in lockstep. */ private applyModernEraCapabilities; /** * The elicitation modes an MRTR round may embed for this server, derived from * the `elicitation` capability actually advertised on the wire. * * The spec puts the obligation on the server ("Servers MUST NOT send * elicitation requests with modes that are not supported by the client"), so * this is the client-side backstop against a noncompliant or hostile server — * the same check `assertElicitationModeDeclared` already applies to inbound * `elicitation/create`, which the MRTR path otherwise skipped by defaulting to * every mode this client can render. Without it, a caller pinning an exact * form-only capability could still be shown a URL consent prompt. * * Returned as a thunk: the declaration is only on record after `initialize`, * and the connection is established inside the operation's first leg. */ private mrtrSupportedElicitationModes; private hasNegotiatedElicitation; /** * The `elicitation` client capability actually advertised to this server on * the wire, for `applyToClient` to enforce declared modes against. */ private negotiatedElicitationCapability; private resolveRpcLogger; /** * Unlike `resolveRpcLogger` there is no console fallback: `logJsonRpc` is * about JSON-RPC bodies, and quietly printing every HTTP header set to the * console because a caller asked for body logging would leak more than they * asked for. Opt in explicitly, per server or globally. */ private resolveHttpLogger; /** * Builds the transport `fetch`. Ordering is load-bearing: the HTTP-log * wrapper is the BASE, so it records the headers that actually leave — * including the SEP-2663 routing headers `wrapFetchForTaskRouting` injects * on top. Without a logger this is byte-identical to the previous * `wrapFetchForTaskRouting()`. */ private buildTransportFetch; private cacheToolsMetadata; private isStdioConfig; private isExecuteToolRequest; private normalizeExecuteToolRequest; private executeToolWithInputRequired; private readResourceWithInputRequired; private getPromptWithInputRequired; /** * Public reconstruction seam for the HOSTED MRTR resume path (§12.5, PR5). * * The hosted continuation transport drives an MRTR retry leg via * `resumeInputRequiredOperation` (the explicit-schema `requestWithSchema` * sender), which — like the local MRTR tool path — bypasses upstream * `callTool`'s output-schema assertion. On resume there is no * `runInputRequiredOperation` loop to carry the `validateResponse` hook, so a * fresh-request resume worker calls this to re-impose the SAME assertion the * local path applies, reusing the SAME `DialectAwareJsonSchemaValidator` * instance rather than a divergent inspector-side re-implementation. Throws a * `TypeError` on a schema mismatch / missing structured content, exactly as * the local path does; a best-effort no-op when the schema can't be resolved. */ assertMrtrToolOutputSchema(serverId: string, toolName: string, result: CallToolResult): Promise; /** * The `Mcp-Param-*` headers one MRTR `tools/call` leg must carry — the public * sibling of what {@link executeToolWithInputRequired} does internally, for * the surfaces that drive a leg themselves. * * The HOSTED resume path is why this exists. A resume leg must go through * `requestWithSchema` (only it can carry `requestState` / `inputResponses`), * which bypasses upstream `callTool` and therefore its private mirroring — so * a hosted retry went out unmirrored and a conforming 2026-07-28 server * answered `-32020`, while the local path (fixed in #3620) did not. Exposing * the seam rather than re-deriving it in `server/utils` is the same choice * {@link assertMrtrToolOutputSchema} made for output-schema validation: one * implementation, so the two surfaces cannot disagree about what a conforming * request looks like. * * Honors `mirrorToolParamHeaders: false` (host config * `toolParamHeaderMirroring: "omit"`) — the simulation has to apply to hosted * sessions too, or a user testing a non-conforming client would silently get * a conforming one. * * Built from the LEG's arguments, not the operation's: a server cross-checks * each request's headers against that request's body. Best-effort — an * unresolvable schema yields `{}` and the leg proceeds exactly as it does * today. */ mrtrToolParamHeaders(serverId: string, toolName: string, args: unknown, options?: Pick): Promise>; /** * Resolves the SEP-2243 `x-mcp-header` declarations for one tool, so an MRTR * leg can mirror them into `Mcp-Param-*` the way upstream `callTool` does. * * Gated exactly like {@link ensureXMcpHeaderMirroringSource} — modern era, * non-stdio transport — and warms the same source, so the `listTools` below * is served from the response cache without a round trip. Declarations are * resolved ONCE per operation; the header VALUES are built per leg, because * only the leg's own `arguments` may be mirrored. * * Best-effort, like the output-schema sibling: an unresolvable schema yields * no declarations and the call proceeds unmirrored, which is exactly today's * behavior. An INVALID scan also yields none — the same "proceed without * custom headers" path upstream takes, leaving the server's `-32020` as the * authority rather than inventing a client-side failure. */ private resolveXMcpHeaderDeclarations; /** * The MRTR input collector to drive rounds with, or `undefined` when this * connection simulates a client that does not drive MRTR at all * (`MCPServerConfig.supportsMrtr: false`, from the host config's * `mcpProfile.mrtrSupport: "none"`). * * Returning `undefined` is the whole enforcement: every verb gate then * takes its plain path, which does NOT pass `allowInputRequired`, so the * upstream client rejects an `input_required` result natively with * `UNSUPPORTED_RESULT_TYPE` — the same error a client that never * implemented MRTR would produce. Nothing about the round-driving code * needs a special case, and no `allowInputRequired: true` goes out on the * wire claiming a capability the simulated client disclaims. * * The collector is left REGISTERED so callers that own it (hosted bridge, * CLI) need no teardown, and so flipping the knob back does not require * re-registration. */ private resolveMrtrCollector; /** * Applies the first-page-only transport wrapper when this connection is * configured to simulate a client that stops after page one * (`MCPServerConfig.firstPageOnly`, set from the host config's * `mcpProfile.paginationTraversal: "firstPageOnly"`). * * OUTERMOST by design: the logging transport underneath still records the * frame the server really sent, so the evidence stays truthful and only the * client sees the truncated list. Applied identically on stdio, Streamable * HTTP and SSE — pagination is not transport-specific. * * Read at connect time rather than per call, because the transport is built * once per connection; changing the knob takes effect on reconnect, which is * how every other connect option behaves. */ private applyFirstPageOnly; /** * Drop `notifications/tools/list_changed` before the client sees it, for a * host whose `mcpProfile.toolListChanged.refetches` is `false`. * * Composed alongside `applyFirstPageOnly` at every transport site: both are * inbound-frame edits, and both belong outside the logging transport so the * wire log keeps what the server really sent. */ private applyDroppedListChanged; /** * Whether this connection was configured to simulate a client that does NOT * mirror `x-mcp-header` arguments into `Mcp-Param-*` * (`MCPServerConfig.mirrorToolParamHeaders: false`, set from the host * config's `mcpProfile.toolParamHeaderMirroring: "omit"`). * * `undefined` — the shape every pre-existing caller has — means mirror, so * nothing changes for anyone who never sets the knob. */ private xMcpMirroringDisabled; /** * Build the `CallToolRequestOptions.toolDefinition` that SUPPRESSES SEP-2243 * mirroring on the plain (non-MRTR) `tools/call` path: the tool's own * definition with every `x-mcp-header` annotation stripped from its * `inputSchema`. * * Two upstream behaviors make this the seam rather than a flag. `callTool` * reads a supplied `toolDefinition`'s `inputSchema` "instead of (and without * consulting) the cached `tools/list` result", so a stripped copy is the only * way to silence mirroring on a connection whose cache is already warm. And * upstream's `-32020 HEADER_MISMATCH` evict-refetch-retry recovery is * DISABLED whenever `toolDefinition` is set — which is the point here: a * simulated non-conforming client must surface the server's rejection, not * quietly recover from it on a second attempt. * * `outputSchema` is copied through untouched, because upstream validates the * result against the SAME definition; stripping it would silently weaken * output validation as a side effect of a header knob. * * Gated like {@link resolveXMcpHeaderDeclarations} — modern era, non-stdio — * since mirroring cannot happen elsewhere and a `toolDefinition` there would * only change unrelated behavior. It DOES warm the aggregated `tools/list` * (the mirroring path's own source) when the cache is cold: without the real * definition there is nothing to strip, upstream would fall into its silent * miss path, and its recovery retry — armed, because no `toolDefinition` was * passed — would refetch and then send the very headers we are simulating the * absence of. Best-effort, like its sibling: an unresolvable definition * yields `undefined` and the call proceeds unchanged. */ private resolveUnmirroredToolDefinition; /** * Reconstructs upstream `callTool`'s output-schema assertion on the final * complete result of an MRTR tool call (the `requestWithSchema` leg path * bypasses it). Best-effort schema resolution: if the tool's `outputSchema` * cannot be looked up, a successful call is not failed. */ private validateToolOutputSchema; private runRetryableReadOperation; private runRetriedOperation; private refreshAccessTokenAfterUnauthorized; private createRetrySignal; private abortRetrySignals; private throwIfAborted; private awaitWithAbort; } export { MAX_SUITE_FILE_CASES as $, type EvalSuiteFileProvenance as A, type EvalSuiteFileServer as B, type CustomProvider as C, type EvalSuiteFileTarget as D, type EvalTraceInput as E, type FailureCategory as F, type EvalSuiteFileValidity as G, type EvalTaskDecisionReason as H, type IterationStatus as I, type EvalTrialExclusionReason as J, type EvalTrialExclusions as K, type LLMProvider as L, MCPClientManager as M, type EvalValidityCoverage as N, type EvalValidityDecisionReason as O, type EvalVerdictDecisionReason as P, type EvalVerdictPolicyVersion as Q, type EvalVerdictValidity as R, type SkillEntry as S, FAILURE_CATEGORIES as T, type UserValueStage as U, IMPORT_MAPPING_STATUSES as V, ITERATION_STATUSES as W, type ImportMappingStatus as X, MAX_BATCH_CREATE_CASES as Y, MAX_CASE_ASSERTIONS as Z, MAX_REPETITIONS as _, type EvalMatchOptions as a, type TaskObservationSource as a$, MAX_SUITE_FILE_TITLE_CHARS as a0, RESERVED_CAPTURE_LEVELS as a1, RESERVED_MODES as a2, RESERVED_REPORTING_MODES as a3, type ResolvedEvalValidityPolicy as a4, STAGE_STATES as a5, USER_VALUE_STAGES as a6, evalCaseVerdictAggregationSchema as a7, evalCaseVerdictAggregationStructuralSchema as a8, evalFractionSchema as a9, isEvalValidityDecisionReason as aA, isEvalVerdictDecisionReason as aB, isEvalVerdictPolicyV2 as aC, iterationStatusSchema as aD, resolvedEvalValidityPolicySchema as aE, stageStateSchema as aF, userValueStageSchema as aG, type CompatibleProtocol as aH, Host as aI, type LiveTasksWire as aJ, MCPJAM_TASKS_POLICY_EXTENSION_ID as aK, MCP_SKILLS_EXTENSION_ID as aL, type SkillIdentityFrontmatter as aM, type SkillResourceRef as aN, type SkillsExtListResult as aO, type SkillsSupport as aP, TERMINAL_LIFECYCLE_STATUSES as aQ, type TaskLifecycleCallbacks as aR, TaskLifecycleEngine as aS, type TaskLifecycleEngineOptions as aT, type TaskLifecycleError as aU, type TaskLifecycleIdentity as aV, type TaskLifecycleObservation as aW, type TaskLifecycleRecord as aX, type TaskLifecycleSnapshot as aY, type TaskLifecycleStatus as aZ, type TaskMode as a_, evalRateMeasurementSchema as aa, evalRateMeasurementStateSchema as ab, evalRateMeasurementStructuralSchema as ac, evalRunVerdictSchema as ad, evalSuiteFileCaseImportSchema as ae, evalSuiteFileCaseSchema as af, evalSuiteFileDefaultsSchema as ag, evalSuiteFileHostSchema as ah, evalSuiteFileProvenanceSchema as ai, evalSuiteFileSchema as aj, evalSuiteFileServerSchema as ak, evalSuiteFileStructuralSchema as al, evalSuiteFileTargetSchema as am, evalSuiteFileToolPolicySchema as an, evalSuiteFileValiditySchema as ao, evalTrialExclusionReasonSchema as ap, evalTrialExclusionsSchema as aq, evalValidityCoverageSchema as ar, evalVerdictDecisionReasonSchema as as, evalVerdictDecisionSchema as at, evalVerdictDecisionStructuralSchema as au, evalVerdictPolicyVersionSchema as av, failureCategorySchema as aw, importMappingStatusSchema as ax, isEvalRunVerdict as ay, isEvalTrialExclusionReason as az, type EvalTraceSpanInput as b, type MrtrMethod as b$, type TaskSurface as b0, type TasksPolicy as b1, clearTasksPolicy as b2, clientDeclaresSkillsExtension as b3, describeInvalidTasksPolicy as b4, isTerminalLifecycleStatus as b5, readTasksPolicy as b6, resolveSkillsSupport as b7, serverDeclaresSkillsExtension as b8, setTasksPolicy as b9, type CoreToolMessage as bA, type CoreUserMessage as bB, DEFAULT_MAX_MRTR_ROUNDS as bC, DEFAULT_SUBSCRIPTION_RECONNECT_POLICY as bD, DEFAULT_TASK_INPUT_LIMITS as bE, type DeclaredInputCapabilities as bF, type DeliveredSubscriptionNotification as bG, type DesiredSubscriptionInterests as bH, type DriveTaskToTerminalArgs as bI, type EvalArgumentMismatch as bJ, type EvalOutOfOrderToolCall as bK, type EvalToolCall as bL, type EvalTraceSpanCategory as bM, type EvalWidgetCsp as bN, type EvalWidgetPermissions as bO, type EvalWidgetSnapshotInput as bP, HostRuntime as bQ, type HostRuntimeDefaults as bR, type HostRuntimeManager as bS, type HostServerRegistry as bT, INODE_DIRECTORY_MIME_TYPE as bU, type LLMConfig as bV, type McpSubscriptionHandle as bW, type MrtrInputCollector as bX, MrtrInputValidationError as bY, type MrtrLegResult as bZ, type MrtrLegSender as b_, skillsDirectoryReadEnabled as ba, surfaceMayDeclareTasks as bb, taskLifecycleKey as bc, taskModeForSurface as bd, toSnapshot as be, type DetailedTaskExt as bf, type GetTaskExtResult as bg, type TaskExtNotificationParams as bh, type TasksWire as bi, type CreateTaskExtResult as bj, type SkillsDirectoryReadResult as bk, type ElicitationContentValidator as bl, type HostExecutor as bm, type MCPJamReportingConfig as bn, type LatencyBreakdown as bo, PromptResult as bp, type HostSource as bq, type PromptOptions as br, type MCPServerReplayConfig as bs, type ToolCall as bt, type ReportEvalResultsInput as bu, type ReportEvalResultsOutput as bv, type EvalResultInput as bw, type EvalCiMetadata as bx, type CoreAssistantMessage as by, type CoreMessage as bz, type EvalExpectedToolCall as c, scrubMetaFromToolResult as c$, type MrtrOperationState as c0, type MrtrSupportedModes as c1, MrtrUndeclaredInputError as c2, MrtrUnsupportedElicitationModeError as c3, type MrtrValidateResponse as c4, type PromptResultData as c5, type RejectedSubscriptionNotification as c6, type RunInputRequiredOptions as c7, SUBSCRIPTION_ID_META_KEY as c8, SUPPORTED_ELICITATION_MODES as c9, type ToolTaskAwaitOptions as cA, type ToolTaskSeamContext as cB, type ToolTaskSeamMeta as cC, type ToolTaskSeamOptions as cD, type UpdateTaskExtResult as cE, assertHostServersKnown as cF, canDeclareTasksExtension as cG, defaultResultSchemaForMethod as cH, diffAcknowledgement as cI, driveTaskToTerminal as cJ, evaluateToolCalls as cK, executeInputRequiredLeg as cL, initInputRequiredState as cM, isChatGPTAppTool as cN, isHostJson as cO, isMaxRoundsExceeded as cP, isMcpAppTool as cQ, isUnsupportedResultType as cR, makeRequestWithSchemaLegSender as cS, readDeclaredInputCapabilities as cT, resolveKnownServerIds as cU, resolveRequestedFilter as cV, resolveTasksSupport as cW, resumeInputRequiredOperation as cX, runInputRequiredOperation as cY, runToolTaskSeam as cZ, scrubMetaAndStructuredContentFromToolResult as c_, type SkillsDirectoryEntry as ca, type SubscriptionClientPort as cb, type SubscriptionCloseReason as cc, SubscriptionCoordinator as cd, type SubscriptionCoordinatorOptions as ce, type SubscriptionFilterShape as cf, type SubscriptionInterestRejection as cg, type SubscriptionNotificationKind as ch, type SubscriptionReconnectPolicy as ci, type SubscriptionStreamRecord as cj, type SubscriptionStreamStatus as ck, SubscriptionsAcknowledgedNotificationMethod as cl, TASK_SEAM_META_KEY as cm, type TaskAwaitOutcome as cn, type TaskAwaitResult as co, type TaskCreatedConsumer as cp, type TaskCreatedEvent as cq, TaskCreatedSink as cr, type TaskCreationSurface as cs, type TaskInputDriverOptions as ct, type TaskInputHandlerContext as cu, type TaskInputHandlers as cv, TaskInputRejectedError as cw, type TaskInputRejection as cx, type TasksSupport as cy, type TokenUsage as cz, type StageState as d, snapshotHostSource as d0, toolTaskSeamOptionsFor as d1, validateInputRequests as d2, validateRoundResponses as d3, MATCH_OPTIONS_DEFAULTS as d4, assertValidMatchOptions as d5, assertValidMaxExtra as d6, resolveMatchOptions as d7, type EvalVerdictDecision as e, type EvalToolCallMatchResult as f, type EvalSuiteFileToolPolicy as g, EVAL_RATE_MEASUREMENT_STATES as h, EVAL_RUN_VERDICTS as i, EVAL_SUITE_SCHEMA_ID as j, EVAL_SUITE_SCHEMA_VERSION as k, EVAL_TASK_DECISION_REASONS as l, EVAL_TRIAL_EXCLUSION_REASONS as m, EVAL_VALIDITY_DECISION_REASONS as n, EVAL_VERDICT_DECISION_REASONS as o, EVAL_VERDICT_POLICY_SCHEMA_ID as p, EVAL_VERDICT_POLICY_VERSION as q, type EvalCaseVerdictAggregation as r, type EvalRateMeasurement as s, type EvalRateMeasurementState as t, type EvalRunVerdict as u, type EvalSuiteFile as v, type EvalSuiteFileCase as w, type EvalSuiteFileCaseImport as x, type EvalSuiteFileDefaults as y, type EvalSuiteFileHost as z };