import type { MCPDiscoverResult, MCPEraCache, MCPTransportUnion, McpEra, McpModernVersion } from '../../types/connector/index.js'; import { MCPProtocolError } from './errors.js'; /** * Is this the answer of a server that speaks the modern protocol? * * The spec is explicit that a fallback MUST NOT be keyed to one specific * error code, because a legacy server refuses an unknown method with * whatever its implementation happens to use — commonly `-32601`, commonly * `-32602`, sometimes something else entirely, sometimes silence. So the * question is never "was this `-32601`?"; it is "did the peer answer in a * vocabulary only a modern server has?" — which is what these three codes * are. Everything else, including a code this client has never seen, means * fall back. */ export declare function isRecognizedModernError(error: unknown): error is MCPProtocolError; /** * Read a failed HTTP response for evidence of a modern server. * * `400`, `404` and `405` are the statuses a legacy origin answers a modern * request with — and also the statuses a MODERN origin uses for a request * it understands but will not serve. The body decides. A `404` whose body * is a JSON-RPC `-32601` is a modern server saying "no such method", which * the spec calls out precisely so it is not mistaken for the `404` of an * origin that has never heard of the protocol. * * `-32601` counts ONLY on a `404`. On a `400` or `405` it is an ordinary * unimplemented-method answer that any server of any era can send, and * reading it as proof of modernity there would strand this client on a * legacy origin with no way back. */ export declare function classifyModernHttpFailure(status: number, bodyText: string): { readonly kind: 'modern'; readonly error: MCPProtocolError; } | { readonly kind: 'legacy'; }; /** A cache that remembers nothing outside this object. */ export declare function createMcpEraCache(): MCPEraCache; /** * The cache every `MCPClient` shares unless it was given its own. * * Process-wide on purpose: two clients reaching the same origin should not * each pay a probe. Injectable on purpose too — a suite that shared this * one would leak era state between cases and become order-dependent, which * is the failure mode a conformance suite can least afford. */ export declare const defaultMcpEraCache: MCPEraCache; /** * What a cached era belongs to: an HTTP origin, or a stdio process * identity. * * Path and query are deliberately dropped from an HTTP URL. Era is a * property of the server behind the origin, not of one endpoint on it, and * keying per URL would re-probe every path of the same deployment. * * Two things the key deliberately does and does not fold together: * * - `streamable_http` and `streamable-http` are two spellings of ONE * transport, so they key together. An origin probed under one spelling is * the same origin under the other, and keying them apart would probe it * twice and let one half of the process believe something the other half * had already disproved. * - `http-sse` keys APART from those two, on the same origin. It is the * 2024-11-05 transport and is never probed at all, so it records a legacy * era it never tested; a Streamable HTTP client reading that entry would * skip its own probe on the strength of an answer nobody asked for. */ export declare function mcpEraCacheKey(transport: MCPTransportUnion): string; /** What one probe round trip came back with. */ export type McpEraProbeAnswer = { readonly kind: 'result'; readonly result: unknown; } | { readonly kind: 'error'; readonly error: unknown; } | { readonly kind: 'timeout'; }; /** * Send `server/discover` at one modern version and report what came back, * without throwing. * * A timeout is an ANSWER here, not a failure: the stdio spec says in so * many words that a legacy server may simply not respond, so silence is * evidence about the era rather than an error to propagate. */ export type McpEraProbe = (version: McpModernVersion) => Promise; export interface McpEraResolution { readonly era: McpEra; /** Present only when a modern probe actually returned a `DiscoverResult`. */ readonly discover?: MCPDiscoverResult; /** Probe round trips this resolution spent. `0` when the cache answered. */ readonly probes: number; readonly fromCache: boolean; } export interface McpEraResolutionInput { readonly probe: McpEraProbe; readonly cache: MCPEraCache; readonly key: string; /** * `false` for the 2024-11-05 HTTP+SSE transport, which is a legacy * transport by definition — an origin that speaks it is not a modern * origin, and probing it would spend a round trip to learn something the * transport choice already said. */ readonly probeSupported: boolean; /** Named in a refusal so the failure says who could not agree with whom. */ readonly serverName: string; } /** The server info a modern peer carries under the reserved `_meta` key. */ export declare function serverInfoFromDiscover(discover: MCPDiscoverResult | undefined): { name: string; version?: string; } | undefined; /** * Decide which era a peer speaks, probing at most twice. * * Modern first, per the spec's own guidance and because the alternative is * worse than a wasted round trip: a legacy server handed an era-ambiguous * method processes it under legacy semantics and fails confusingly, where a * probe fails cleanly. The cache makes the cost one round trip per origin * or per command rather than one per connection. * * The two probes are NOT the same algorithm — an HTTP probe reads a status * and a body, a stdio probe reads a reply or a silence — but both reduce to * one {@link McpEraProbe} answer, so this state machine is written once and * neither transport carries a copy of it. */ export declare function resolveMcpEra(input: McpEraResolutionInput): Promise; //# sourceMappingURL=era.d.ts.map