@combycode/llm-sdk 1.7.0 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +430 -1
- package/MIGRATION.md +93 -0
- package/README.md +17 -2
- package/dist/agent/loop-config.d.ts +22 -0
- package/dist/agent/loop-step-state.d.ts +2 -0
- package/dist/agent/loop.d.ts +7 -0
- package/dist/agent/reflect-retry.d.ts +56 -0
- package/dist/agent/tool-key.d.ts +3 -0
- package/dist/helpers/mcp.d.ts +24 -2
- package/dist/helpers/provenance-types.d.ts +63 -0
- package/dist/helpers/provenance.d.ts +12 -0
- package/dist/helpers/transcribe.d.ts +35 -6
- package/dist/index.browser.js +2996 -707
- package/dist/index.d.ts +19 -7
- package/dist/index.js +2996 -707
- package/dist/llm/providers/anthropic/constants.d.ts +2 -0
- package/dist/llm/providers/openai/completions.d.ts +9 -0
- package/dist/llm/providers/openai/provenance.d.ts +26 -0
- package/dist/llm/providers/openai/responses.d.ts +4 -2
- package/dist/llm/providers/openai/transcription.d.ts +39 -2
- package/dist/llm/types/audio.d.ts +31 -0
- package/dist/llm/types/messages.d.ts +89 -1
- package/dist/llm/types/options.d.ts +15 -0
- package/dist/llm/types/request.d.ts +13 -1
- package/dist/llm/types/response.d.ts +31 -1
- package/dist/llm/types/stream.d.ts +14 -1
- package/dist/llm/types/tiers.d.ts +6 -6
- package/dist/network/queue-state-config.d.ts +5 -0
- package/dist/network/queue-state.d.ts +7 -0
- package/dist/network/types.d.ts +23 -0
- package/dist/plugins/context-guard/strategies/anchored.d.ts +47 -0
- package/dist/plugins/context-measurer/counter/hybrid.d.ts +4 -1
- package/dist/plugins/context-measurer/counter/tiktoken.d.ts +8 -1
- package/dist/plugins/mcp/base-transport.d.ts +16 -0
- package/dist/plugins/mcp/client.d.ts +144 -7
- package/dist/plugins/mcp/input-required.d.ts +35 -0
- package/dist/plugins/mcp/jsonrpc.d.ts +7 -0
- package/dist/plugins/mcp/oauth.d.ts +21 -1
- package/dist/plugins/mcp/protocol-version.d.ts +61 -0
- package/dist/plugins/mcp/result-cache.d.ts +31 -0
- package/dist/plugins/mcp/subscriptions.d.ts +69 -0
- package/dist/plugins/mcp/transport-http.d.ts +31 -0
- package/dist/plugins/mcp/transport-stdio.d.ts +2 -0
- package/dist/plugins/mcp/transport-ws.d.ts +11 -1
- package/dist/plugins/mcp/transport.d.ts +11 -0
- package/dist/plugins/mcp/types.d.ts +54 -2
- package/dist/plugins/telemetry/telemetry.d.ts +12 -0
- package/dist/util/http.d.ts +8 -0
- package/package.json +9 -6
|
@@ -5,7 +5,9 @@
|
|
|
5
5
|
import type { HookBus } from '../../bus/hook-bus';
|
|
6
6
|
import type { McpTransport } from './transport';
|
|
7
7
|
import type { TraceContext } from '../../network/types';
|
|
8
|
-
import
|
|
8
|
+
import { type McpEra } from './protocol-version';
|
|
9
|
+
import { McpSubscription, type McpServerEvent, type McpSubscriptionFilter } from './subscriptions';
|
|
10
|
+
import type { McpCallResult, McpCompletionRef, McpCompletionResult, McpDiscoverResult, McpGetPromptResult, McpInitializeResult, McpLogLevel, McpPrompt, McpResource, McpResourceContent, McpResourceTemplate, McpTask, McpTaskMetadata, McpToolDef } from './types';
|
|
9
11
|
export interface McpClientOptions {
|
|
10
12
|
clientInfo?: {
|
|
11
13
|
name: string;
|
|
@@ -29,23 +31,92 @@ export interface McpClientOptions {
|
|
|
29
31
|
hooks: HookBus;
|
|
30
32
|
server: string;
|
|
31
33
|
};
|
|
32
|
-
/** Send a `ping` every N ms to keep the connection alive (0/undefined = off).
|
|
34
|
+
/** Send a `ping` every N ms to keep the connection alive (0/undefined = off).
|
|
35
|
+
* Ignored on a 2026-07-28 session, where `ping` no longer exists. */
|
|
33
36
|
keepAliveMs?: number;
|
|
37
|
+
/** How to negotiate the protocol revision.
|
|
38
|
+
*
|
|
39
|
+
* - `'auto'` (default) — probe `server/discover`; anything that is not positive evidence of a
|
|
40
|
+
* modern server falls back to the `initialize` handshake.
|
|
41
|
+
* - `'legacy'` — skip the probe and run the handshake, byte-identical to pre-2.0 behaviour.
|
|
42
|
+
* Use this for a server that mishandles unknown methods.
|
|
43
|
+
* - a version string (e.g. `'2026-07-28'`) — adopt that revision directly, no probe.
|
|
44
|
+
*
|
|
45
|
+
* Fallback is a DENYLIST: every JSON-RPC error falls back to the handshake except a
|
|
46
|
+
* `-32022` naming only modern versions we do not share. Transport/network errors are never
|
|
47
|
+
* treated as an era verdict — an outage must not silently downgrade the wire. */
|
|
48
|
+
protocolMode?: 'auto' | 'legacy' | (string & {});
|
|
49
|
+
/** Cap on `input_required` retry rounds before giving up (default 10, matching every other SDK).
|
|
50
|
+
* A handler that never satisfies the server would otherwise loop forever. */
|
|
51
|
+
inputRequiredMaxRounds?: number;
|
|
52
|
+
/** Honour the server's `ttlMs` / `cacheScope` hints on list and read results (2026-07-28).
|
|
53
|
+
*
|
|
54
|
+
* **Off by default.** Caching changes when a caller observes a server-side change, which is the
|
|
55
|
+
* caller's call to make. A server that sends no hints caches nothing either way, so this is a
|
|
56
|
+
* no-op against every pre-2026 server. Cache entries are dropped automatically on the matching
|
|
57
|
+
* `*_changed` notification. */
|
|
58
|
+
cacheResults?: boolean;
|
|
34
59
|
}
|
|
35
60
|
export declare class McpClient {
|
|
36
61
|
private readonly transport;
|
|
37
62
|
private readonly opts;
|
|
38
63
|
private serverInfo;
|
|
39
64
|
private pingTimer;
|
|
65
|
+
private negotiatedVersion;
|
|
66
|
+
private discovery;
|
|
67
|
+
private readonly cache;
|
|
68
|
+
private readonly subscriptions;
|
|
40
69
|
constructor(transport: McpTransport, opts?: McpClientOptions);
|
|
41
|
-
/**
|
|
70
|
+
/** Drop cached list/read results. Called automatically on the matching `*_changed`
|
|
71
|
+
* notification; exposed because a caller may know the server moved before it says so. */
|
|
72
|
+
invalidateCache(method?: string): void;
|
|
73
|
+
/** The server's `initialize` result, or null before `connect()`.
|
|
74
|
+
*
|
|
75
|
+
* On a 2026-07-28 session there is no `initialize`, so this is SYNTHESISED from the
|
|
76
|
+
* `server/discover` result (capabilities, instructions, and the `_meta` serverInfo stamp).
|
|
77
|
+
* Deliberate: the shape a caller reads must not depend on which wire was negotiated
|
|
78
|
+
* (CONSTITUTION.md R2 — absorb the difference, never expose a union). */
|
|
42
79
|
get info(): McpInitializeResult | null;
|
|
80
|
+
/** The revision actually negotiated. Defaults to the handshake-era version until `connect()`. */
|
|
81
|
+
get protocolVersion(): string;
|
|
82
|
+
/** Which wire this session speaks. `'handshake'` for 2025-11-25 and earlier. */
|
|
83
|
+
get era(): McpEra;
|
|
84
|
+
/** The raw `server/discover` result on a modern session; `null` on a handshake session.
|
|
85
|
+
* Carries the fields that have no handshake equivalent (`supportedVersions`, `ttlMs`,
|
|
86
|
+
* `cacheScope`). */
|
|
87
|
+
get discoverResult(): McpDiscoverResult | null;
|
|
43
88
|
/** Open the transport, run the initialize handshake, and start listening for
|
|
44
89
|
* server-initiated messages. */
|
|
45
90
|
connect(): Promise<McpInitializeResult>;
|
|
91
|
+
/** The pre-2026 path, unchanged: `initialize` + `notifications/initialized`. */
|
|
92
|
+
private handshake;
|
|
93
|
+
/** One `server/discover` at `version`. No retry, no adoption — the caller decides.
|
|
94
|
+
*
|
|
95
|
+
* The transport is told the version FIRST because on HTTP it is not just bookkeeping:
|
|
96
|
+
* a modern server routes by the `MCP-Protocol-Version` header, so a probe sent without
|
|
97
|
+
* it lands on the legacy handler and is rejected with `400 Missing session ID` — the
|
|
98
|
+
* connection fails outright instead of negotiating. Invisible over stdio, which has no
|
|
99
|
+
* headers; found against mcp-py 2.0.0 Streamable HTTP (2026-08-09). */
|
|
100
|
+
private sendDiscover;
|
|
101
|
+
/** Install a discover result as the live session. */
|
|
102
|
+
private adoptModern;
|
|
103
|
+
/** `mode: 'auto'` — probe modern, fall back to the handshake on anything that is not positive
|
|
104
|
+
* evidence of a modern server.
|
|
105
|
+
*
|
|
106
|
+
* The fallback is a DENYLIST, matching mcp-py 2.0.0 `client/_probe.py`: every JSON-RPC error
|
|
107
|
+
* falls back EXCEPT a `-32022` whose `supported` list is modern-only and disjoint from ours —
|
|
108
|
+
* that one is a real incompatibility and must surface. Transport/network failures are rethrown
|
|
109
|
+
* untouched: an outage is not an era verdict, and silently downgrading the wire because a
|
|
110
|
+
* socket blipped would be the worst possible failure mode. */
|
|
111
|
+
private negotiateAuto;
|
|
46
112
|
/** List every tool the server exposes (follows cursor pagination). */
|
|
47
113
|
listTools(): Promise<McpToolDef[]>;
|
|
48
|
-
/** Follow cursor pagination for a list method, collecting `field` from each page.
|
|
114
|
+
/** Follow cursor pagination for a list method, collecting `field` from each page.
|
|
115
|
+
*
|
|
116
|
+
* When result caching is enabled and the server sent a `ttlMs`, the assembled list is reused
|
|
117
|
+
* until it expires. A paginated list is only as fresh as its shortest-lived page, so the
|
|
118
|
+
* effective TTL is the MINIMUM across pages — taking the last page's value would let an early,
|
|
119
|
+
* more volatile page go stale unnoticed. */
|
|
49
120
|
private paginate;
|
|
50
121
|
/** Invoke a tool by its (un-namespaced) server name.
|
|
51
122
|
* Pass `trace` when the call originates from an AgentLoop run so that
|
|
@@ -57,14 +128,35 @@ export declare class McpClient {
|
|
|
57
128
|
listResourceTemplates(): Promise<McpResourceTemplate[]>;
|
|
58
129
|
/** Read a resource's contents by URI. */
|
|
59
130
|
readResource(uri: string): Promise<McpResourceContent[]>;
|
|
60
|
-
/** Subscribe to updates for a resource (server sends `notifications/resources/updated`).
|
|
131
|
+
/** Subscribe to updates for a resource (server sends `notifications/resources/updated`).
|
|
132
|
+
*
|
|
133
|
+
* Handshake era only — 2026-07-28 replaces per-resource subscription with the single
|
|
134
|
+
* `subscriptions/listen` stream. */
|
|
61
135
|
subscribeResource(uri: string): Promise<void>;
|
|
62
136
|
unsubscribeResource(uri: string): Promise<void>;
|
|
137
|
+
/** Open a `subscriptions/listen` stream (2026-07-28) — the single channel that replaces
|
|
138
|
+
* `resources/subscribe` and the standalone notification stream.
|
|
139
|
+
*
|
|
140
|
+
* Every kind is **opt-in**: the server may not send what was not requested, and it acknowledges
|
|
141
|
+
* with the subset it actually honoured — which can be narrower than what was asked for. Check
|
|
142
|
+
* `subscription.honored` / `isHonored(kind)` rather than assuming the request was granted.
|
|
143
|
+
*
|
|
144
|
+
* Events are level triggers ("this changed, re-fetch if you care"), so they carry no payload
|
|
145
|
+
* beyond the change itself. When result caching is on, the matching entries are dropped before
|
|
146
|
+
* `onEvent` runs, so a handler that immediately re-lists sees fresh data.
|
|
147
|
+
*
|
|
148
|
+
* Requires a modern session and a duplex transport (stdio / WebSocket) — see
|
|
149
|
+
* `sendLongLivedRequest` on the HTTP transport for why. */
|
|
150
|
+
listen(filter: McpSubscriptionFilter, onEvent: (event: McpServerEvent) => void): Promise<McpSubscription>;
|
|
63
151
|
/** List the server's prompts (follows cursor pagination). */
|
|
64
152
|
listPrompts(): Promise<McpPrompt[]>;
|
|
65
153
|
/** Render a prompt by name with arguments → its messages. */
|
|
66
154
|
getPrompt(name: string, args?: Record<string, string>): Promise<McpGetPromptResult>;
|
|
67
|
-
/** Set the server's log verbosity (it then sends `notifications/message`).
|
|
155
|
+
/** Set the server's log verbosity (it then sends `notifications/message`).
|
|
156
|
+
*
|
|
157
|
+
* Handshake era only — `logging/setLevel` was removed at 2026-07-28, where verbosity rides in
|
|
158
|
+
* each request's `_meta` instead. Throws rather than silently no-opping: a caller who asked for
|
|
159
|
+
* debug logging and got none would have no way to tell. */
|
|
68
160
|
setLogLevel(level: McpLogLevel): Promise<void>;
|
|
69
161
|
/** Argument autocompletion for a prompt or resource template. */
|
|
70
162
|
completeArgument(ref: McpCompletionRef, argument: {
|
|
@@ -85,8 +177,53 @@ export declare class McpClient {
|
|
|
85
177
|
pollIntervalMs?: number;
|
|
86
178
|
timeoutMs?: number;
|
|
87
179
|
}): Promise<McpTask>;
|
|
88
|
-
/** Low-level escape hatch: send any request method.
|
|
180
|
+
/** Low-level escape hatch: send any request method. Carries the modern-era identity
|
|
181
|
+
* envelope like every other request. */
|
|
89
182
|
request(method: string, params?: unknown): Promise<unknown>;
|
|
183
|
+
/** Every request goes through here.
|
|
184
|
+
*
|
|
185
|
+
* At 2026-07-28 there is no handshake and no session id, so each request states its
|
|
186
|
+
* own identity: `_meta` MUST carry the protocol version and the client capabilities
|
|
187
|
+
* (client info is optional). Only `server/discover` used to build that envelope, so
|
|
188
|
+
* every later call on a modern session was rejected:
|
|
189
|
+
*
|
|
190
|
+
* -32602 params._meta must be an object carrying the required
|
|
191
|
+
* 'io.modelcontextprotocol/protocolVersion' and
|
|
192
|
+
* 'io.modelcontextprotocol/clientCapabilities' envelope keys
|
|
193
|
+
*
|
|
194
|
+
* Unreachable without a real 2026-07-28 server — no public one exists yet; found by
|
|
195
|
+
* running mcp-py 2.0.0 over stdio (2026-08-09).
|
|
196
|
+
*
|
|
197
|
+
* Handshake-era sessions are untouched: identity lives in `initialize` there, and an
|
|
198
|
+
* unexpected `_meta` is exactly the sort of thing an older server can reject. */
|
|
199
|
+
private send;
|
|
200
|
+
/** Stamp the modern identity envelope onto a request's params.
|
|
201
|
+
*
|
|
202
|
+
* Shared by `send()` and the long-lived `subscriptions/listen`, because EVERY request
|
|
203
|
+
* needs it — and the long-lived path is the one where a missing envelope hides: the
|
|
204
|
+
* rejection arrives as the stream's end rather than a thrown error, so `listen()`
|
|
205
|
+
* returned a subscription that looked alive and delivered nothing. */
|
|
206
|
+
private withEnvelope;
|
|
90
207
|
close(): Promise<void>;
|
|
208
|
+
/** Resolve an `input_required` result to a terminal one.
|
|
209
|
+
*
|
|
210
|
+
* The dispatcher is `handleServerRequest` — the SAME path that serves a handshake-era server
|
|
211
|
+
* pushing `sampling/createMessage` at us. That is the whole point of routing MRTR through here:
|
|
212
|
+
* a caller wires up sampling once and it works on either wire, without knowing which is in play.
|
|
213
|
+
*
|
|
214
|
+
* Skips instantly when `resultType` is absent or `'complete'`, so the handshake path pays
|
|
215
|
+
* nothing. */
|
|
216
|
+
private driveInputRequired;
|
|
217
|
+
/** Guard a method the 2026-07-28 revision removed. Naming the negotiated version and the
|
|
218
|
+
* replacement matters: without it the caller sees a bare -32601 from the server and has no way
|
|
219
|
+
* to know the method existed until the session happened to negotiate modern. */
|
|
220
|
+
private requireHandshakeEra;
|
|
221
|
+
/** Map a change notification onto the cache entries it invalidates. Same vocabulary on both
|
|
222
|
+
* eras — these methods ride the `subscriptions/listen` stream at 2026-07-28 and the
|
|
223
|
+
* back-channel before it, but the meaning ("refetch if you care") is identical. */
|
|
224
|
+
private invalidateOnChange;
|
|
225
|
+
/** Same invalidation as `invalidateOnChange`, driven from a typed listen-stream event. */
|
|
226
|
+
private invalidateForEvent;
|
|
227
|
+
private clientInfo;
|
|
91
228
|
private handleServerRequest;
|
|
92
229
|
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/** Multi-round-trip requests (MRTR, SEP-2322) — the 2026-07-28 replacement for the server→client
|
|
2
|
+
* back-channel.
|
|
3
|
+
*
|
|
4
|
+
* On the handshake wire a server that needs sampling/elicitation/roots PUSHES a request at us
|
|
5
|
+
* mid-call. At 2026-07-28 there is no back-channel: the server instead RETURNS
|
|
6
|
+
* `resultType: 'input_required'` carrying the requests it needs answered, and the client re-issues
|
|
7
|
+
* the *same* call with the answers plus the server's opaque `requestState`.
|
|
8
|
+
*
|
|
9
|
+
* Both mechanisms feed the SAME callbacks — a caller who wired up sampling once gets it on either
|
|
10
|
+
* wire without knowing which is in play. Algorithm mirrors mcp-py 2.0.0 `client/_input_required.py`.
|
|
11
|
+
*/
|
|
12
|
+
import type { McpInputRequest } from './types';
|
|
13
|
+
/** Cap on retry rounds before the driver gives up. Matches the TypeScript SDK's default; the C#
|
|
14
|
+
* and Go SDKs use the same value as a hard constant. */
|
|
15
|
+
export declare const DEFAULT_INPUT_REQUIRED_MAX_ROUNDS = 10;
|
|
16
|
+
/** Answer one embedded request through the client's sampling / elicitation / roots handling. */
|
|
17
|
+
export type InputRequestDispatcher = (key: string, request: McpInputRequest) => Promise<unknown>;
|
|
18
|
+
/** Re-issue the original call with the collected answers and the latest state. */
|
|
19
|
+
export type InputRequiredRetry<T> = (responses: Record<string, unknown> | undefined, requestState: string | undefined) => Promise<T>;
|
|
20
|
+
/** True when a result is the server asking for more input rather than a final answer.
|
|
21
|
+
*
|
|
22
|
+
* The discriminant is `resultType`. Per spec an ABSENT `resultType` MUST be read as `'complete'`:
|
|
23
|
+
* servers on earlier revisions never send the field, and treating absent as anything else would
|
|
24
|
+
* make every legacy result look like a question. */
|
|
25
|
+
export declare function isInputRequired(result: unknown): boolean;
|
|
26
|
+
/** Drive an `input_required` result to a terminal one.
|
|
27
|
+
*
|
|
28
|
+
* Each round either answers every embedded request and retries with the responses, or — when the
|
|
29
|
+
* server sent state but no questions — backs off and retries empty. `requestState` is echoed back
|
|
30
|
+
* byte-exact and never inspected: it is the server's sealed continuation token. */
|
|
31
|
+
export declare function runInputRequiredDriver<T>(first: T, opts: {
|
|
32
|
+
dispatch: InputRequestDispatcher;
|
|
33
|
+
retry: InputRequiredRetry<T>;
|
|
34
|
+
maxRounds?: number;
|
|
35
|
+
}): Promise<T>;
|
|
@@ -9,6 +9,13 @@ export declare const McpErrorCode: {
|
|
|
9
9
|
readonly MethodNotFound: -32601;
|
|
10
10
|
readonly InvalidParams: -32602;
|
|
11
11
|
readonly InternalError: -32603;
|
|
12
|
+
/** A routing header disagrees with the request body. */
|
|
13
|
+
readonly HeaderMismatch: -32020;
|
|
14
|
+
/** The server requires a client capability this client did not declare. */
|
|
15
|
+
readonly MissingRequiredClientCapability: -32021;
|
|
16
|
+
/** The server does not speak the requested revision; `data.supported` lists the ones it does.
|
|
17
|
+
* The ONE error carrying era information, so negotiation reads it specifically. */
|
|
18
|
+
readonly UnsupportedProtocolVersion: -32022;
|
|
12
19
|
};
|
|
13
20
|
/** A JSON-RPC / transport level error (server unreachable, error response,
|
|
14
21
|
* timeout). Tool execution failures arrive as a normal result with
|
|
@@ -27,6 +27,9 @@ export interface McpOAuthClientMetadata {
|
|
|
27
27
|
grant_types?: string[];
|
|
28
28
|
response_types?: string[];
|
|
29
29
|
token_endpoint_auth_method?: string;
|
|
30
|
+
/** OIDC Registration §2 application type (SEP-837). Defaults to `'native'` at registration, since
|
|
31
|
+
* an MCP client is normally a local process with a loopback redirect. Set explicitly to override. */
|
|
32
|
+
application_type?: 'web' | 'native';
|
|
30
33
|
}
|
|
31
34
|
/** Consumer-implemented storage + interactive redirect. */
|
|
32
35
|
export interface McpAuthProvider {
|
|
@@ -51,7 +54,24 @@ export interface AuthServerMetadata {
|
|
|
51
54
|
authorization_endpoint: string;
|
|
52
55
|
token_endpoint: string;
|
|
53
56
|
registration_endpoint?: string;
|
|
57
|
+
/** The authorization server's issuer identifier (RFC 8414), used to validate the RFC 9207 `iss`
|
|
58
|
+
* returned with the authorization code. */
|
|
59
|
+
issuer?: string;
|
|
60
|
+
/** RFC 9207: the server states it returns `iss` on authorization responses. When true, a response
|
|
61
|
+
* WITHOUT `iss` is rejected — otherwise an attacker could simply strip the parameter to dodge
|
|
62
|
+
* the check. */
|
|
63
|
+
authorization_response_iss_parameter_supported?: boolean;
|
|
54
64
|
}
|
|
65
|
+
/** Validate the RFC 9207 authorization-response issuer.
|
|
66
|
+
*
|
|
67
|
+
* This is the mix-up-attack defence: without it a malicious authorization server can hand back a
|
|
68
|
+
* code minted by a DIFFERENT server, and the client will dutifully redeem it — replaying the
|
|
69
|
+
* user's credentials against a party they never intended to authorize.
|
|
70
|
+
*
|
|
71
|
+
* Comparison is **exact string equality** per RFC 9207 §2.4 (RFC 3986 §6.2.1) — deliberately NOT
|
|
72
|
+
* URL-normalised. Normalising would make `https://as.example.com` and `https://as.example.com/`
|
|
73
|
+
* compare equal, and that leniency is precisely what an attacker looks for. */
|
|
74
|
+
export declare function validateAuthorizationResponseIss(iss: string | undefined, meta: Pick<AuthServerMetadata, 'issuer' | 'authorization_response_iss_parameter_supported'>): void;
|
|
55
75
|
/** Security options for the OAuth flow. All fields default to the most
|
|
56
76
|
* restrictive posture. Re-exported from `url-guard` for consumer convenience. */
|
|
57
77
|
export type { SsrfGuardOptions as McpOAuthSecurityOptions };
|
|
@@ -114,7 +134,7 @@ export declare class McpOAuth {
|
|
|
114
134
|
reauthorize(): Promise<boolean>;
|
|
115
135
|
/** Finish the interactive flow: exchange the callback code for tokens.
|
|
116
136
|
* The `returnedState` MUST match the state persisted during redirect (CSRF guard). */
|
|
117
|
-
finish(code: string, returnedState: string): Promise<void>;
|
|
137
|
+
finish(code: string, returnedState: string, iss?: string): Promise<void>;
|
|
118
138
|
private startRedirect;
|
|
119
139
|
private tryRefresh;
|
|
120
140
|
private ensureMetadata;
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/** MCP protocol-version registry and era helpers.
|
|
2
|
+
*
|
|
3
|
+
* MCP has two ERAS, not just two versions:
|
|
4
|
+
* - **handshake** (2024-11-05 … 2025-11-25) — `initialize` + `notifications/initialized`, an
|
|
5
|
+
* `Mcp-Session-Id`, and a server→client back-channel (ping, logging/setLevel,
|
|
6
|
+
* resources/subscribe, push sampling / roots / elicitation).
|
|
7
|
+
* - **modern** (2026-07-28+) — no handshake and no session id. One `server/discover` probe,
|
|
8
|
+
* then every request carries its own identity in `_meta` and routing headers.
|
|
9
|
+
*
|
|
10
|
+
* Both are supported and neither is preferred: real servers overwhelmingly still speak
|
|
11
|
+
* 2025-11-25, so the handshake path must keep working byte-for-byte. See CONSTITUTION.md
|
|
12
|
+
* standing decisions (2026-08-08).
|
|
13
|
+
*/
|
|
14
|
+
/** Every released revision, oldest to newest. Verified against mcp-py 2.0.0
|
|
15
|
+
* (`mcp_types/version.py`, KNOWN_PROTOCOL_VERSIONS). */
|
|
16
|
+
export declare const MCP_KNOWN_PROTOCOL_VERSIONS: readonly ["2024-11-05", "2025-03-26", "2025-06-18", "2025-11-25", "2026-07-28"];
|
|
17
|
+
/** Revisions reachable via the `initialize` handshake. */
|
|
18
|
+
export declare const MCP_HANDSHAKE_PROTOCOL_VERSIONS: readonly ["2024-11-05", "2025-03-26", "2025-06-18", "2025-11-25"];
|
|
19
|
+
/** Revisions that use the stateless per-request envelope (`server/discover`). */
|
|
20
|
+
export declare const MCP_MODERN_PROTOCOL_VERSIONS: readonly ["2026-07-28"];
|
|
21
|
+
/** Newest revision reachable via the handshake — what we offer in `initialize`. */
|
|
22
|
+
export declare const MCP_LATEST_HANDSHAKE_VERSION = "2025-11-25";
|
|
23
|
+
/** Newest per-request-envelope revision — what the `server/discover` probe asks for. */
|
|
24
|
+
export declare const MCP_LATEST_MODERN_VERSION = "2026-07-28";
|
|
25
|
+
/** Which wire a negotiated version implies. */
|
|
26
|
+
export type McpEra = 'handshake' | 'modern';
|
|
27
|
+
/** Version strings are an ENUMERATED SET, not an ordered scalar.
|
|
28
|
+
*
|
|
29
|
+
* Released revisions happen to be dates that sort lexicographically, but future identifiers are
|
|
30
|
+
* not guaranteed to be date-shaped, and an unrecognised peer string must compare conservatively
|
|
31
|
+
* rather than accidentally (`'zzz' > '2025-11-25'` is true and meaningless). So era questions go
|
|
32
|
+
* through the lists above — never through `<` / `>`. Upstream calls this out explicitly, having
|
|
33
|
+
* been bitten by it. An unknown version reads as `handshake`: the older, safer wire. */
|
|
34
|
+
export declare function mcpEraOf(version: string): McpEra;
|
|
35
|
+
/** True when `version` is a revision this client can speak on the modern wire. */
|
|
36
|
+
export declare function isModernMcpVersion(version: string): boolean;
|
|
37
|
+
/** True when `version` is a revision reachable via the `initialize` handshake. */
|
|
38
|
+
export declare function isHandshakeMcpVersion(version: string): boolean;
|
|
39
|
+
/** The newest modern revision BOTH sides speak, or undefined if they share none. */
|
|
40
|
+
export declare function newestMutualModernVersion(theirs: readonly string[]): string | undefined;
|
|
41
|
+
/** Required on every modern request: the revision this request is written at. */
|
|
42
|
+
export declare const MCP_PROTOCOL_VERSION_META_KEY = "io.modelcontextprotocol/protocolVersion";
|
|
43
|
+
/** Required on every modern request: what the client can do (replaces the handshake exchange). */
|
|
44
|
+
export declare const MCP_CLIENT_CAPABILITIES_META_KEY = "io.modelcontextprotocol/clientCapabilities";
|
|
45
|
+
/** Optional client identity, display-only. */
|
|
46
|
+
export declare const MCP_CLIENT_INFO_META_KEY = "io.modelcontextprotocol/clientInfo";
|
|
47
|
+
/** Server identity stamped on modern results, display-only (absent and malformed both read as
|
|
48
|
+
* "unknown" rather than failing the connection). */
|
|
49
|
+
export declare const MCP_SERVER_INFO_META_KEY = "io.modelcontextprotocol/serverInfo";
|
|
50
|
+
/** Stamped on every `subscriptions/listen` stream frame; the value is that request's JSON-RPC id,
|
|
51
|
+
* which is how a frame is attributed to one subscription. */
|
|
52
|
+
export declare const MCP_SUBSCRIPTION_ID_META_KEY = "io.modelcontextprotocol/subscriptionId";
|
|
53
|
+
export declare const MCP_PROTOCOL_VERSION_HEADER = "mcp-protocol-version";
|
|
54
|
+
export declare const MCP_METHOD_HEADER = "mcp-method";
|
|
55
|
+
export declare const MCP_NAME_HEADER = "mcp-name";
|
|
56
|
+
/** Methods whose primary subject goes in the `Mcp-Name` routing header, and the param it comes
|
|
57
|
+
* from. Lets an intermediary route/authorize without parsing the body. */
|
|
58
|
+
export declare const MCP_NAME_BEARING_METHODS: Readonly<Record<string, string>>;
|
|
59
|
+
/** Header values must be ASCII; a tool name or URI may not be. Encode out-of-range characters
|
|
60
|
+
* rather than emitting a header the runtime will reject. */
|
|
61
|
+
export declare function encodeMcpHeaderValue(value: string): string;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/** Opt-in response cache honouring the 2026-07-28 `ttlMs` / `cacheScope` hints (SEP-2578).
|
|
2
|
+
*
|
|
3
|
+
* A server tells the client how long a list/read result stays fresh. Without this, `listTools()`
|
|
4
|
+
* re-fetches on every call — the churn this hint exists to remove.
|
|
5
|
+
*
|
|
6
|
+
* Deliberately conservative:
|
|
7
|
+
* - **Off unless asked for.** Caching changes when a caller observes a server change; that is the
|
|
8
|
+
* caller's decision, not ours.
|
|
9
|
+
* - **No hint, no caching.** `ttlMs` absent (every pre-2026 server) means nothing is stored, so
|
|
10
|
+
* behaviour is byte-identical to before.
|
|
11
|
+
* - **`ttlMs: 0` means immediately stale**, which is a real instruction — not a missing value to
|
|
12
|
+
* be replaced with a default.
|
|
13
|
+
* - **`cacheScope` is recorded, never used to widen sharing.** This cache lives inside one client
|
|
14
|
+
* with one credential, so `public` buys nothing here; storing the scope keeps the entry honest
|
|
15
|
+
* if the cache is ever shared.
|
|
16
|
+
*/
|
|
17
|
+
import type { McpCacheHints } from './types';
|
|
18
|
+
export declare class McpResultCache {
|
|
19
|
+
private readonly entries;
|
|
20
|
+
/** Cache key: the method plus its arguments. Two `resources/read` calls for different URIs are
|
|
21
|
+
* different entries. */
|
|
22
|
+
static key(method: string, params?: unknown): string;
|
|
23
|
+
get(key: string, now?: number): unknown | undefined;
|
|
24
|
+
/** Store only when the server actually asked for it. Returns whether anything was stored. */
|
|
25
|
+
set(key: string, value: unknown, hints: McpCacheHints | undefined, now?: number): boolean;
|
|
26
|
+
/** Drop everything — e.g. after a `*_changed` notification says the server moved on. */
|
|
27
|
+
clear(): void;
|
|
28
|
+
/** Drop every entry for one method, leaving the others. */
|
|
29
|
+
clearMethod(method: string): void;
|
|
30
|
+
get size(): number;
|
|
31
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/** `subscriptions/listen` (2026-07-28, SEP-2575) — one stream for every change notification.
|
|
2
|
+
*
|
|
3
|
+
* At 2026-07-28 the per-resource `resources/subscribe` RPC and the standalone notification channel
|
|
4
|
+
* are both replaced by a single long-lived `subscriptions/listen` request. The client names the
|
|
5
|
+
* notification kinds it wants; **every kind is opt-in and the server MUST NOT send one that was
|
|
6
|
+
* not asked for**. The server acknowledges with the subset it actually honoured, which can be
|
|
7
|
+
* smaller than what was requested — so "I asked for it" never implies "I will receive it".
|
|
8
|
+
*
|
|
9
|
+
* Every frame on the stream is stamped with the listen request's id under
|
|
10
|
+
* `io.modelcontextprotocol/subscriptionId`, which is how frames are attributed to a subscription.
|
|
11
|
+
*
|
|
12
|
+
* Events are **level triggers**: "this changed, re-fetch if you care". They carry no payload
|
|
13
|
+
* beyond the fact of the change, so consecutive identical events collapse safely.
|
|
14
|
+
*/
|
|
15
|
+
/** The notification kinds a client may opt into. Mirrors the wire `SubscriptionFilter`. */
|
|
16
|
+
export interface McpSubscriptionFilter {
|
|
17
|
+
toolsListChanged?: boolean;
|
|
18
|
+
promptsListChanged?: boolean;
|
|
19
|
+
resourcesListChanged?: boolean;
|
|
20
|
+
/** Resource URIs to watch — the replacement for the `resources/subscribe` RPC. */
|
|
21
|
+
resourceSubscriptions?: string[];
|
|
22
|
+
}
|
|
23
|
+
/** A change the server announced. Level triggers: re-fetch if you care. */
|
|
24
|
+
export type McpServerEvent = {
|
|
25
|
+
type: 'tools_list_changed';
|
|
26
|
+
} | {
|
|
27
|
+
type: 'prompts_list_changed';
|
|
28
|
+
} | {
|
|
29
|
+
type: 'resources_list_changed';
|
|
30
|
+
} | {
|
|
31
|
+
type: 'resource_updated';
|
|
32
|
+
uri: string;
|
|
33
|
+
};
|
|
34
|
+
/** The notification methods that ride a listen stream at 2026-07-28. */
|
|
35
|
+
export declare const MCP_LISTEN_STREAM_METHODS: readonly ["notifications/tools/list_changed", "notifications/prompts/list_changed", "notifications/resources/list_changed", "notifications/resources/updated"];
|
|
36
|
+
/** The ack frame that carries the honoured subset. */
|
|
37
|
+
export declare const MCP_SUBSCRIPTIONS_ACKNOWLEDGED = "notifications/subscriptions/acknowledged";
|
|
38
|
+
/** The event a raw stream frame announces, or undefined when it carries none. */
|
|
39
|
+
export declare function eventFromWire(method: string, params: unknown): McpServerEvent | undefined;
|
|
40
|
+
/** The subscription id stamped on a frame, or undefined when it is not a listen frame. */
|
|
41
|
+
export declare function subscriptionIdFrom(params: unknown): string | number | undefined;
|
|
42
|
+
/** A live subscription: the honoured filter once acknowledged, plus event delivery. */
|
|
43
|
+
export declare class McpSubscription {
|
|
44
|
+
readonly id: string | number;
|
|
45
|
+
readonly requested: McpSubscriptionFilter;
|
|
46
|
+
private readonly onEvent;
|
|
47
|
+
private readonly onClose;
|
|
48
|
+
/** The subset the server agreed to send. `null` until the ack arrives — and it can be NARROWER
|
|
49
|
+
* than what was requested, so check it rather than assuming. */
|
|
50
|
+
honored: McpSubscriptionFilter | null;
|
|
51
|
+
private closed;
|
|
52
|
+
private endState;
|
|
53
|
+
constructor(id: string | number, requested: McpSubscriptionFilter, onEvent: (event: McpServerEvent) => void, onClose: () => void);
|
|
54
|
+
/** Feed a raw stream frame. Returns true when it belonged to this subscription. */
|
|
55
|
+
handleFrame(method: string, params: unknown): boolean;
|
|
56
|
+
/** True when the server acknowledged this kind. Unacknowledged ⇒ do not expect events. */
|
|
57
|
+
isHonored(kind: keyof McpSubscriptionFilter): boolean;
|
|
58
|
+
/** Set once the stream ends: `undefined` for a clean server-side teardown, otherwise the error
|
|
59
|
+
* that killed it. A subscription that stopped delivering is otherwise indistinguishable from one
|
|
60
|
+
* where nothing has changed yet. */
|
|
61
|
+
get ended(): {
|
|
62
|
+
error?: unknown;
|
|
63
|
+
} | null;
|
|
64
|
+
/** True while the subscription can still deliver. */
|
|
65
|
+
get active(): boolean;
|
|
66
|
+
/** Called by the transport when the underlying stream finishes. */
|
|
67
|
+
markEnded(error?: unknown): void;
|
|
68
|
+
close(): void;
|
|
69
|
+
}
|
|
@@ -26,11 +26,18 @@ export declare class HttpTransport extends BaseJsonRpcTransport implements McpTr
|
|
|
26
26
|
private nextHttpId;
|
|
27
27
|
private sessionId;
|
|
28
28
|
private protocolVersion;
|
|
29
|
+
/** Handshake until negotiation says otherwise — an un-negotiated connection must behave exactly
|
|
30
|
+
* as it did before 2026 support existed. */
|
|
31
|
+
private era;
|
|
29
32
|
private eventAbort;
|
|
30
33
|
private lastEventId;
|
|
34
|
+
/** Live `subscriptions/listen` streams, so `close()` can tear them down. Without this a closed
|
|
35
|
+
* client would leave the POST hanging until the server gave up on it. */
|
|
36
|
+
private readonly streamAborts;
|
|
31
37
|
constructor(config: McpHttpConfig, deps: HttpTransportDeps);
|
|
32
38
|
start(): Promise<void>;
|
|
33
39
|
setProtocolVersion(version: string): void;
|
|
40
|
+
setEra(era: 'handshake' | 'modern'): void;
|
|
34
41
|
/** Open the server->client GET SSE stream (best-effort: a 405 means the server
|
|
35
42
|
* is request/response only). Reconnects with backoff + Last-Event-ID until
|
|
36
43
|
* close(). Runs in the background. */
|
|
@@ -43,8 +50,32 @@ export declare class HttpTransport extends BaseJsonRpcTransport implements McpTr
|
|
|
43
50
|
close(): Promise<void>;
|
|
44
51
|
/** Send a JSON-RPC response back via POST (used by handleRequest from base). */
|
|
45
52
|
protected sendMessage(obj: unknown): Promise<void>;
|
|
53
|
+
/** Open a long-lived request whose RESPONSE BODY is the event stream.
|
|
54
|
+
*
|
|
55
|
+
* This is how `subscriptions/listen` works on Streamable HTTP: unlike an ordinary call, the POST
|
|
56
|
+
* does not return a single JSON-RPC message — the server holds the response open and writes
|
|
57
|
+
* notification frames as they occur, closing it only when the subscription ends. So it goes
|
|
58
|
+
* through `fetchStream` (streaming response) rather than the buffered `post()` path, which would
|
|
59
|
+
* surface the frames only after the stream closed.
|
|
60
|
+
*
|
|
61
|
+
* Frames are routed exactly like the GET channel's, so the client sees no difference between the
|
|
62
|
+
* two. The eventual JSON-RPC response has no pending entry to settle — its only meaning is "the
|
|
63
|
+
* stream ended", which is reported through `onEnd`. */
|
|
64
|
+
sendLongLivedRequest(method: string, params?: unknown, onEnd?: (error?: unknown) => void): Promise<number>;
|
|
46
65
|
private headers;
|
|
66
|
+
/** Modern-era routing headers: `Mcp-Method` on every request, plus `Mcp-Name` carrying the
|
|
67
|
+
* method's subject (tool name / prompt name / resource URI) so a gateway can route and
|
|
68
|
+
* authorize without parsing the body. No-op on the handshake wire. */
|
|
69
|
+
private routingHeaders;
|
|
47
70
|
/** Base headers + any OAuth bearer + per-call extras. */
|
|
48
71
|
private authedHeaders;
|
|
49
72
|
private post;
|
|
50
73
|
}
|
|
74
|
+
/** Extract the JSON-RPC response matching `id` from a JSON or SSE body. */
|
|
75
|
+
/** The media-type ESSENCE of a Content-Type: type/subtype, lowercased, parameters stripped.
|
|
76
|
+
*
|
|
77
|
+
* A substring test misroutes anything that merely *contains* the token — `application/json` with a
|
|
78
|
+
* vendor parameter mentioning it, or a hypothetical `application/vnd.text/event-stream+json` —
|
|
79
|
+
* while still needing to cope with the ordinary `text/event-stream; charset=utf-8`. Comparing the
|
|
80
|
+
* essence handles both. Matches the fix upstream shipped in mcp-ts 1.30. */
|
|
81
|
+
export declare function mediaTypeEssence(contentType: string): string;
|
|
@@ -9,8 +9,10 @@ export declare class StdioTransport extends BaseJsonRpcTransport implements McpT
|
|
|
9
9
|
private proc;
|
|
10
10
|
private buffer;
|
|
11
11
|
private readonly timeoutMs;
|
|
12
|
+
private readonly maxBufferSize;
|
|
12
13
|
constructor(config: McpStdioConfig, opts?: {
|
|
13
14
|
timeoutMs?: number;
|
|
15
|
+
maxBufferSize?: number;
|
|
14
16
|
});
|
|
15
17
|
start(): Promise<void>;
|
|
16
18
|
setProtocolVersion(): void;
|
|
@@ -1,6 +1,16 @@
|
|
|
1
1
|
/** WebSocket MCP transport — JSON-RPC messages as text frames over a duplex
|
|
2
2
|
* socket (naturally bidirectional). Uses the engine's `connect` so it shares
|
|
3
|
-
* the engine's WebSocket factory + hooks. Cross-env (browser + Node/Bun).
|
|
3
|
+
* the engine's WebSocket factory + hooks. Cross-env (browser + Node/Bun).
|
|
4
|
+
*
|
|
5
|
+
* **NON-STANDARD, and deliberately kept.** The MCP SDK removed its own WebSocket transport in
|
|
6
|
+
* `mcp` 2.0.0, reasoning that it "was never part of the MCP specification". Ours stays: it is
|
|
7
|
+
* public API we shipped, and deleting an exported transport on an upstream style call would break
|
|
8
|
+
* consumers for no protocol reason (CONSTITUTION.md R7 — an upstream deletion is not our
|
|
9
|
+
* deletion). Treat it as a supported extra rather than a spec transport: the server has to opt
|
|
10
|
+
* into JSON-RPC over a socket, and the standard transports remain stdio and Streamable HTTP.
|
|
11
|
+
*
|
|
12
|
+
* Practical upside of keeping it: being duplex, it supports `subscriptions/listen` today —
|
|
13
|
+
* which Streamable HTTP does not yet. */
|
|
4
14
|
import type { EngineConnect } from '../../network/types';
|
|
5
15
|
import type { McpTransport } from './transport';
|
|
6
16
|
import { BaseJsonRpcTransport } from './base-transport';
|
|
@@ -21,6 +21,17 @@ export interface McpTransport {
|
|
|
21
21
|
setHandlers(handlers: IncomingMcpHandlers): void;
|
|
22
22
|
/** Record the negotiated protocol version (HTTP sets a header; stdio ignores). */
|
|
23
23
|
setProtocolVersion?(version: string): void;
|
|
24
|
+
/** Record the negotiated ERA. At `'modern'` (2026-07-28+) the wire is stateless: no
|
|
25
|
+
* `Mcp-Session-Id`, and every request carries `Mcp-Method` / `Mcp-Name` routing headers so an
|
|
26
|
+
* intermediary can route without parsing the body. Optional — stdio has no headers to set. */
|
|
27
|
+
setEra?(era: 'handshake' | 'modern'): void;
|
|
28
|
+
/** Send a long-lived request (`subscriptions/listen`) and return its id, without arming the
|
|
29
|
+
* normal response timeout.
|
|
30
|
+
*
|
|
31
|
+
* `onEnd` fires when the stream finishes — cleanly (the server tore the subscription down) or
|
|
32
|
+
* with the error that killed it. A subscription that stops delivering is otherwise invisible to
|
|
33
|
+
* the caller. */
|
|
34
|
+
sendLongLivedRequest?(method: string, params?: unknown, onEnd?: (error?: unknown) => void): Promise<string | number>;
|
|
24
35
|
/** Open the server->client channel (HTTP GET SSE stream). Stdio is already
|
|
25
36
|
* duplex, so this is a no-op there. Call after the initialize handshake. */
|
|
26
37
|
listen?(): Promise<void> | void;
|
|
@@ -52,7 +52,40 @@ export type McpContentBlock = {
|
|
|
52
52
|
type: string;
|
|
53
53
|
[k: string]: unknown;
|
|
54
54
|
};
|
|
55
|
-
|
|
55
|
+
/** One server→client request embedded in an `input_required` result: a `sampling/createMessage`,
|
|
56
|
+
* `elicitation/create` or `roots/list`, in JSON-RPC request shape. Identical in content to what a
|
|
57
|
+
* handshake-era server pushes over the back-channel — only the delivery differs. */
|
|
58
|
+
export interface McpInputRequest {
|
|
59
|
+
method: string;
|
|
60
|
+
params?: unknown;
|
|
61
|
+
}
|
|
62
|
+
/** Client-side caching directives carried by 2026-07-28 list/read results (`CacheableResult`).
|
|
63
|
+
*
|
|
64
|
+
* Optional here because every pre-2026 server omits them, and a server that sends no hints must
|
|
65
|
+
* behave exactly as before (CONSTITUTION.md R3). */
|
|
66
|
+
export interface McpCacheHints {
|
|
67
|
+
/** How long (ms) the client MAY reuse this result. **`0` means immediately stale** — re-fetch
|
|
68
|
+
* every time — so it is NOT the same as "absent" and must not be coerced to a default. */
|
|
69
|
+
ttlMs?: number;
|
|
70
|
+
/** `'public'`: no user-specific data, any cache may serve it across authorization contexts.
|
|
71
|
+
* `'private'`: reusable only within the same authorization context. */
|
|
72
|
+
cacheScope?: 'private' | 'public';
|
|
73
|
+
}
|
|
74
|
+
/** The 2026-07-28 multi-round-trip fields (SEP-2322).
|
|
75
|
+
*
|
|
76
|
+
* Upstream models this as a separate `InputRequiredResult` type, making every result a union. We
|
|
77
|
+
* attach it as OPTIONAL fields on the existing results instead (CONSTITUTION.md R2): code reading
|
|
78
|
+
* `result.content` keeps compiling, and callers who never meet a modern server never see them. */
|
|
79
|
+
export interface McpInputRequiredFields {
|
|
80
|
+
/** `'complete'` | `'input_required'`. **Absent MUST be read as `'complete'`** — earlier revisions
|
|
81
|
+
* never send it. Open by R1: a future revision may add a third kind. */
|
|
82
|
+
resultType?: 'complete' | 'input_required' | (string & {});
|
|
83
|
+
/** Server-assigned key → the request to answer. Present when the server has questions. */
|
|
84
|
+
inputRequests?: Record<string, McpInputRequest>;
|
|
85
|
+
/** Opaque continuation token. Echoed back byte-exact and never inspected. */
|
|
86
|
+
requestState?: string;
|
|
87
|
+
}
|
|
88
|
+
export interface McpCallResult extends McpInputRequiredFields {
|
|
56
89
|
content: McpContentBlock[];
|
|
57
90
|
isError?: boolean;
|
|
58
91
|
structuredContent?: Record<string, unknown>;
|
|
@@ -92,7 +125,7 @@ export interface McpPromptMessage {
|
|
|
92
125
|
role: 'user' | 'assistant';
|
|
93
126
|
content: McpContentBlock;
|
|
94
127
|
}
|
|
95
|
-
export interface McpGetPromptResult {
|
|
128
|
+
export interface McpGetPromptResult extends McpInputRequiredFields {
|
|
96
129
|
description?: string;
|
|
97
130
|
messages: McpPromptMessage[];
|
|
98
131
|
}
|
|
@@ -180,6 +213,25 @@ export interface McpInitializeResult {
|
|
|
180
213
|
serverInfo: McpServerInfo;
|
|
181
214
|
instructions?: string;
|
|
182
215
|
}
|
|
216
|
+
/** Result of the 2026-07-28 `server/discover` probe — the modern replacement for the `initialize`
|
|
217
|
+
* handshake. Shape verified against mcp-py 2.0.0 (`_v2026_07_28.DiscoverResult`).
|
|
218
|
+
*
|
|
219
|
+
* `McpClient.info` synthesises an `McpInitializeResult` from this, so a caller never has to branch
|
|
220
|
+
* on the era (CONSTITUTION.md R2). This type exposes the fields that have no handshake equivalent. */
|
|
221
|
+
export interface McpDiscoverResult {
|
|
222
|
+
capabilities: Record<string, unknown>;
|
|
223
|
+
/** Revisions the server speaks; the client picks one from this list. */
|
|
224
|
+
supportedVersions: string[];
|
|
225
|
+
/** How long (ms) the client MAY cache this result. `0` = treat as immediately stale. */
|
|
226
|
+
ttlMs?: number;
|
|
227
|
+
/** `public` = cacheable across authorization contexts; `private` = same context only. */
|
|
228
|
+
cacheScope?: 'private' | 'public';
|
|
229
|
+
instructions?: string;
|
|
230
|
+
/** Absent on servers implementing an earlier revision, which MUST be read as `'complete'`. */
|
|
231
|
+
resultType?: string;
|
|
232
|
+
/** Carries the display-only `io.modelcontextprotocol/serverInfo` stamp. */
|
|
233
|
+
_meta?: Record<string, unknown>;
|
|
234
|
+
}
|
|
183
235
|
export interface McpHttpConfig {
|
|
184
236
|
/** Streamable-HTTP MCP endpoint URL. Cross-env (browser needs server CORS). */
|
|
185
237
|
url: string;
|