@orkestrel/mcp 0.0.11 → 0.0.13
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/README.md +47 -1
- package/dist/src/browser/index.d.ts +36 -24
- package/dist/src/browser/index.js +19 -13
- package/dist/src/browser/index.js.map +1 -1
- package/dist/src/core/index.cjs +4061 -983
- package/dist/src/core/index.cjs.map +1 -1
- package/dist/src/core/index.d.cts +4024 -1069
- package/dist/src/core/index.d.ts +4024 -1069
- package/dist/src/core/index.js +3982 -973
- package/dist/src/core/index.js.map +1 -1
- package/dist/src/server/index.cjs +352 -135
- package/dist/src/server/index.cjs.map +1 -1
- package/dist/src/server/index.d.cts +254 -89
- package/dist/src/server/index.d.ts +254 -89
- package/dist/src/server/index.js +352 -138
- package/dist/src/server/index.js.map +1 -1
- package/package.json +10 -9
|
@@ -1,22 +1,25 @@
|
|
|
1
|
-
import { ClientTransportEventMap } from '../core/index.ts';
|
|
2
|
-
import { ClientTransportEventMap as ClientTransportEventMap_2 } from '../../core/index.ts';
|
|
3
|
-
import { ClientTransportInterface } from '../core/index.ts';
|
|
4
|
-
import { ClientTransportInterface as ClientTransportInterface_2 } from '../../core/index.ts';
|
|
5
1
|
import { EmitterInterface } from '@orkestrel/emitter';
|
|
6
2
|
import { IncomingMessage } from 'node:http';
|
|
7
|
-
import {
|
|
8
|
-
import { JSONRPCMessage
|
|
9
|
-
import {
|
|
10
|
-
import { JSONRPCResponse } from '
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
3
|
+
import { JSONRPCInvocation } from '@orkestrel/mcp';
|
|
4
|
+
import { JSONRPCMessage } from '@orkestrel/mcp';
|
|
5
|
+
import { JSONRPCMessage as JSONRPCMessage_2 } from '@orkestrel/mcp';
|
|
6
|
+
import { JSONRPCResponse } from '@orkestrel/mcp';
|
|
7
|
+
import { MCPClientTransportEventMap } from '@orkestrel/mcp';
|
|
8
|
+
import { MCPClientTransportEventMap as MCPClientTransportEventMap_2 } from '@orkestrel/mcp';
|
|
9
|
+
import { MCPClientTransportInterface } from '@orkestrel/mcp';
|
|
10
|
+
import { MCPClientTransportInterface as MCPClientTransportInterface_2 } from '@orkestrel/mcp';
|
|
11
|
+
import { MCPContinuationInterface } from '@orkestrel/mcp';
|
|
12
|
+
import { MCPDispatcherInterface } from '@orkestrel/mcp';
|
|
13
|
+
import { MCPEra } from '@orkestrel/mcp';
|
|
14
|
+
import { MCPStreamControllerInterface } from '@orkestrel/mcp';
|
|
15
|
+
import { MCPTransportInterface } from '@orkestrel/mcp';
|
|
16
|
+
import { MCPVersion } from '@orkestrel/mcp';
|
|
15
17
|
import { MiddlewareHandler } from '@orkestrel/server';
|
|
16
18
|
import { NodeWebSocketInterface } from '@orkestrel/websocket';
|
|
17
19
|
import { RouteContext } from '@orkestrel/router';
|
|
18
20
|
import { RouteInput } from '@orkestrel/router';
|
|
19
21
|
import { StreamInterface } from '@orkestrel/server';
|
|
22
|
+
import { TokenSecret } from '@orkestrel/server';
|
|
20
23
|
import { UpgradeHandler } from '@orkestrel/server';
|
|
21
24
|
|
|
22
25
|
/**
|
|
@@ -51,7 +54,7 @@ export declare function acceptsEventStream(request: Request): boolean;
|
|
|
51
54
|
export declare function allowsOrigin(request: Request, options?: MCPOriginOptions): boolean;
|
|
52
55
|
|
|
53
56
|
/**
|
|
54
|
-
* Bridge a message-channel {@link
|
|
57
|
+
* Bridge a message-channel {@link MCPClientTransportInterface} (the shape the stdio and
|
|
55
58
|
* WebSocket SERVER transports already implement) into the environment-agnostic
|
|
56
59
|
* {@link import('@src/core').MCPTransportInterface} port — the adapter
|
|
57
60
|
* {@link import('./factories.js').createStdioServer} and {@link
|
|
@@ -63,11 +66,22 @@ export declare function allowsOrigin(request: Request, options?: MCPOriginOption
|
|
|
63
66
|
* `send` decodes the already-serialized reply string back to a {@link JSONRPCMessage}
|
|
64
67
|
* and writes it via `transport.send` (the SAME `JSON.stringify` the underlying
|
|
65
68
|
* transport already performs, so the wire bytes are unchanged). `listen` filters
|
|
66
|
-
* `transport`'s `message` event to
|
|
67
|
-
* exactly as the prior hand-rolled pumps did — and re-serializes each one
|
|
68
|
-
* string for `bindServer`. `closed` bridges `transport`'s `close` event. `close`
|
|
69
|
+
* `transport`'s `message` event to INVOCATIONS ONLY — requests and notifications, never a
|
|
70
|
+
* stray response, exactly as the prior hand-rolled pumps did — and re-serializes each one
|
|
71
|
+
* back to a string for `bindServer`. `closed` bridges `transport`'s `close` event. `close`
|
|
69
72
|
* closes the underlying `transport`.
|
|
70
73
|
*
|
|
74
|
+
* @remarks A message crossing this bridge is decoded and re-encoded TWICE, and that is
|
|
75
|
+
* ACCEPTED rather than accidental. Inbound: the carrier already parsed the frame into a
|
|
76
|
+
* {@link JSONRPCMessage}, and `listen` re-serializes it so `bindServer` can decode it again
|
|
77
|
+
* under the server's own `limit`. Outbound: `bindServer` serialized the reply, `send` parses
|
|
78
|
+
* it back, and the carrier stringifies it once more. The cost is two extra `JSON.parse` /
|
|
79
|
+
* `JSON.stringify` round trips per message, paid to keep ONE pump in the core binder instead
|
|
80
|
+
* of a hand-rolled one per carrier. It is BOUNDED rather than unbounded because the binder
|
|
81
|
+
* decodes within `server.limit.message`, so an oversized frame is refused before the second
|
|
82
|
+
* decode rather than after it. Removing the cost means giving `MCPTransportInterface` a
|
|
83
|
+
* message-shaped face beside its string one, which every transport would then carry.
|
|
84
|
+
*
|
|
71
85
|
* @remarks Per {@link import('@src/core').MCPTransportInterface}, `listen`/`closed`
|
|
72
86
|
* each hold THE SINGLE current handler (a second call REPLACES the first, never adds).
|
|
73
87
|
* Since the underlying `transport.emitter` is ADD-based (`on` subscribes, never
|
|
@@ -91,11 +105,11 @@ export declare function allowsOrigin(request: Request, options?: MCPOriginOption
|
|
|
91
105
|
* bindServer(mcp, bridgeMessageTransport(transport))
|
|
92
106
|
* ```
|
|
93
107
|
*/
|
|
94
|
-
export declare function bridgeMessageTransport(transport:
|
|
108
|
+
export declare function bridgeMessageTransport(transport: MCPClientTransportInterface): MCPTransportInterface;
|
|
95
109
|
|
|
96
110
|
/**
|
|
97
111
|
* Create the HTTP CLIENT transport for an {@link import('@src/core').MCPClientInterface}
|
|
98
|
-
* — a {@link
|
|
112
|
+
* — a {@link MCPClientTransportInterface} that drives a REMOTE Streamable-HTTP MCP server
|
|
99
113
|
* over `fetch`. The egress mirror of {@link createMCPRoutes}.
|
|
100
114
|
*
|
|
101
115
|
* @remarks
|
|
@@ -114,7 +128,7 @@ export declare function bridgeMessageTransport(transport: ClientTransportInterfa
|
|
|
114
128
|
* @param options - `url` (the remote endpoint; REQUIRED), optional `headers` merged onto
|
|
115
129
|
* every request, optional `fetch` (default `globalThis.fetch`), and optional `timeout`
|
|
116
130
|
* (ms, applied via `AbortSignal.timeout`); see {@link HTTPClientTransportOptions}
|
|
117
|
-
* @returns A working {@link
|
|
131
|
+
* @returns A working {@link MCPClientTransportInterface} over `fetch`
|
|
118
132
|
*
|
|
119
133
|
* @example
|
|
120
134
|
* ```ts
|
|
@@ -128,7 +142,15 @@ export declare function bridgeMessageTransport(transport: ClientTransportInterfa
|
|
|
128
142
|
* const tools = await client.tools()
|
|
129
143
|
* ```
|
|
130
144
|
*/
|
|
131
|
-
export declare function createHTTPClientTransport(options: HTTPClientTransportOptions):
|
|
145
|
+
export declare function createHTTPClientTransport(options: HTTPClientTransportOptions): MCPClientTransportInterface;
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Adapt the installed server token primitives to the host-neutral MCP continuation port.
|
|
149
|
+
*
|
|
150
|
+
* @param secret - Current signing secret or `[current, ...older]` rotation list
|
|
151
|
+
* @returns A continuation port that seals and opens opaque canonical state strings
|
|
152
|
+
*/
|
|
153
|
+
export declare function createMCPContinuation(secret: TokenSecret): MCPContinuationInterface;
|
|
132
154
|
|
|
133
155
|
/**
|
|
134
156
|
* Create the Streamable-HTTP POST handler used by `createMCPRoutes`.
|
|
@@ -146,29 +168,29 @@ export declare function createHTTPClientTransport(options: HTTPClientTransportOp
|
|
|
146
168
|
* defined value is added to `MCPDispatchOptions`, while `undefined` is omitted.
|
|
147
169
|
*
|
|
148
170
|
* @typeParam TState - The consumer's opaque per-request route state type
|
|
149
|
-
* @param mcp - The transport-agnostic MCP
|
|
171
|
+
* @param mcp - The transport-agnostic MCP dispatcher to dispatch through
|
|
150
172
|
* @param options - Optional streaming, origin-validation, SSE keepalive, and caller-extraction options
|
|
151
173
|
* @returns A request handler for the stateless MCP POST route
|
|
152
174
|
*
|
|
153
175
|
* @example
|
|
154
176
|
* ```ts
|
|
155
|
-
* import { createMCPServer } from '@orkestrel/mcp'
|
|
177
|
+
* import { createMCPLegacy, createMCPServer } from '@orkestrel/mcp'
|
|
156
178
|
* import { createMCPPostHandler } from '@orkestrel/mcp/server'
|
|
157
179
|
* import { createToolManager } from '@orkestrel/tool'
|
|
158
180
|
*
|
|
159
181
|
* const mcp = createMCPServer({ identity: { name: 'docs', version: '1.0.0' }, tools: createToolManager() })
|
|
160
|
-
* const handler = createMCPPostHandler(mcp, { streaming: true })
|
|
182
|
+
* const handler = createMCPPostHandler(createMCPLegacy(mcp), { streaming: true })
|
|
161
183
|
* await handler(new Request('http://localhost/mcp', {
|
|
162
184
|
* method: 'POST',
|
|
163
185
|
* body: '{"jsonrpc":"2.0","method":"ping","id":1}',
|
|
164
186
|
* }))
|
|
165
187
|
* ```
|
|
166
188
|
*/
|
|
167
|
-
export declare function createMCPPostHandler<TState = unknown>(mcp:
|
|
189
|
+
export declare function createMCPPostHandler<TState = unknown>(mcp: MCPDispatcherInterface, options?: HTTPHandlerOptions<TState>): (request: Request, context?: RouteContext<string, TState>) => Promise<Response>;
|
|
168
190
|
|
|
169
191
|
/**
|
|
170
192
|
* Create the MCP Streamable-HTTP transport routes — mounts a transport-agnostic
|
|
171
|
-
* {@link
|
|
193
|
+
* {@link MCPDispatcherInterface} (the `@src/core` dispatch boundary) on the fetch-standard router
|
|
172
194
|
* spine, pumping each `POST` body through `mcp.dispatch`. Returns the {@link RouteInput}s to
|
|
173
195
|
* hand to `router.add(...)`.
|
|
174
196
|
*
|
|
@@ -179,14 +201,14 @@ export declare function createMCPPostHandler<TState = unknown>(mcp: MCPServerInt
|
|
|
179
201
|
* DISPATCH-level outcomes:
|
|
180
202
|
*
|
|
181
203
|
* - A **transport** failure — a malformed JSON body, or a parsed value that is not a
|
|
182
|
-
* JSON-RPC
|
|
183
|
-
* error / `-32600` Invalid Request,
|
|
204
|
+
* JSON-RPC INVOCATION — is an HTTP `400` carrying a JSON-RPC error BODY (`-32700` Parse
|
|
205
|
+
* error / `-32600` Invalid Request), with the `id` it could not read OMITTED.
|
|
184
206
|
* - Modern protocol/method/name headers are validated against the body; a mismatch is
|
|
185
207
|
* HTTP `400` + `-32020`. Headerless initialize is accepted, a live legacy session supplies
|
|
186
208
|
* its pinned revision, and every other headerless request is rejected.
|
|
187
209
|
* - Legacy dispatch errors stay IN-BAND at HTTP `200`; modern errors map to `400` for
|
|
188
210
|
* `-32020` / `-32021` / `-32022` / `-32602`, `404` for `-32601`, and `200` otherwise.
|
|
189
|
-
* - A **notification** (
|
|
211
|
+
* - A **notification** (an invocation with no `id`, which `dispatch` resolves to
|
|
190
212
|
* `undefined`) is a `202 Accepted` with no body.
|
|
191
213
|
*
|
|
192
214
|
* When `streaming` is enabled (the default) and the client `Accept`s `text/event-stream`,
|
|
@@ -205,7 +227,7 @@ export declare function createMCPPostHandler<TState = unknown>(mcp: MCPServerInt
|
|
|
205
227
|
* allowlist or explicitly delegates validation to an upstream layer.
|
|
206
228
|
*
|
|
207
229
|
* @typeParam TState - The consumer's opaque per-request state type
|
|
208
|
-
* @param mcp - The transport-agnostic {@link
|
|
230
|
+
* @param mcp - The transport-agnostic {@link MCPDispatcherInterface} to expose over HTTP
|
|
209
231
|
* @param options - Optional `path` (default {@link DEFAULT_MCP_PATH}) and `streaming`
|
|
210
232
|
* (default `true`), plus shared origin, keepalive, and synchronous caller-extraction options; see
|
|
211
233
|
* {@link HTTPTransportOptions}
|
|
@@ -213,14 +235,14 @@ export declare function createMCPPostHandler<TState = unknown>(mcp: MCPServerInt
|
|
|
213
235
|
*
|
|
214
236
|
* @example
|
|
215
237
|
* ```ts
|
|
216
|
-
* import { createMCPServer, createToolManager } from '@src/core'
|
|
238
|
+
* import { createMCPLegacy, createMCPServer, createToolManager } from '@src/core'
|
|
217
239
|
* import { createMCPRoutes } from '@src/server'
|
|
218
240
|
*
|
|
219
241
|
* const mcp = createMCPServer({ identity: { name: 'docs', version: '1.0.0' }, tools: createToolManager() })
|
|
220
|
-
* const routes = createMCPRoutes(mcp) //
|
|
242
|
+
* const routes = createMCPRoutes(createMCPLegacy(mcp)) // both eras; pass `mcp` for modern only
|
|
221
243
|
* ```
|
|
222
244
|
*/
|
|
223
|
-
export declare function createMCPRoutes<TState = unknown>(mcp:
|
|
245
|
+
export declare function createMCPRoutes<TState = unknown>(mcp: MCPDispatcherInterface, options?: HTTPTransportOptions<TState>): readonly RouteInput<string, TState>[];
|
|
224
246
|
|
|
225
247
|
/**
|
|
226
248
|
* Create the native MCP session {@link MiddlewareHandler} — the plug-and-play stateful layer
|
|
@@ -247,7 +269,10 @@ export declare function createMCPRoutes<TState = unknown>(mcp: MCPServerInterfac
|
|
|
247
269
|
* live-session request. It then
|
|
248
270
|
* FORWARDS a fresh `Request` carrying the buffered `text` (`next(forwarded)`) — never the
|
|
249
271
|
* already-consumed original — so the route re-reads the same body, and stamps the response
|
|
250
|
-
* with {@link MCP_SESSION_HEADER}.
|
|
272
|
+
* with {@link MCP_SESSION_HEADER}. The entry's `touched` instant is read AFTER that
|
|
273
|
+
* downstream response, because it means the LAST ACCESS: a request slower than `ttl` would
|
|
274
|
+
* otherwise store a session that is already expired, and the write-back RE-ASKS the store, so
|
|
275
|
+
* a `DELETE` arriving while the request was suspended is not undone.
|
|
251
276
|
* - **`GET {path}`.** Resolves the session the same way (no mint — only `initialize` mints);
|
|
252
277
|
* an invalid / unknown id is the same `404`. A valid session opens the resumable
|
|
253
278
|
* server→client stream via `@orkestrel/server`'s {@link import('@orkestrel/server').openStream}:
|
|
@@ -295,7 +320,7 @@ export declare function createReadableStream<T>(pull: (controller: ReadableStrea
|
|
|
295
320
|
|
|
296
321
|
/**
|
|
297
322
|
* Create the stdio CLIENT transport for an {@link import('@src/core').MCPClientInterface}
|
|
298
|
-
* — a {@link
|
|
323
|
+
* — a {@link MCPClientTransportInterface} that spawns and drives a CHILD PROCESS MCP server
|
|
299
324
|
* over newline-delimited JSON-RPC on `stdin`/`stdout`, the stdio sibling of {@link
|
|
300
325
|
* createHTTPClientTransport} and {@link createWebSocketClientTransport}.
|
|
301
326
|
*
|
|
@@ -310,7 +335,7 @@ export declare function createReadableStream<T>(pull: (controller: ReadableStrea
|
|
|
310
335
|
*
|
|
311
336
|
* @param options - `command` (the executable to spawn; REQUIRED), optional `args`,
|
|
312
337
|
* and optional `env`; see {@link StdioClientTransportOptions}
|
|
313
|
-
* @returns A working {@link
|
|
338
|
+
* @returns A working {@link MCPClientTransportInterface} over a child process's stdio
|
|
314
339
|
*
|
|
315
340
|
* @example
|
|
316
341
|
* ```ts
|
|
@@ -324,11 +349,11 @@ export declare function createReadableStream<T>(pull: (controller: ReadableStrea
|
|
|
324
349
|
* const tools = await client.tools()
|
|
325
350
|
* ```
|
|
326
351
|
*/
|
|
327
|
-
export declare function createStdioClientTransport(options: StdioClientTransportOptions):
|
|
352
|
+
export declare function createStdioClientTransport(options: StdioClientTransportOptions): MCPClientTransportInterface;
|
|
328
353
|
|
|
329
354
|
/**
|
|
330
355
|
* Create the MCP stdio transport INGRESS — pumps a transport-agnostic {@link
|
|
331
|
-
*
|
|
356
|
+
* MCPDispatcherInterface} over newline-delimited JSON-RPC on `stdin`/`stdout` (or an
|
|
332
357
|
* injected stream pair), the stdio mirror of {@link createWebSocketServer}.
|
|
333
358
|
*
|
|
334
359
|
* @remarks
|
|
@@ -342,7 +367,7 @@ export declare function createStdioClientTransport(options: StdioClientTransport
|
|
|
342
367
|
* surfaces on `mcp.emitter`'s `error` event rather than escaping the (async) message
|
|
343
368
|
* pump.
|
|
344
369
|
*
|
|
345
|
-
* @param mcp - The transport-agnostic {@link
|
|
370
|
+
* @param mcp - The transport-agnostic {@link MCPDispatcherInterface} to expose over stdio
|
|
346
371
|
* @param options - Optional injectable `input` / `output` streams; see
|
|
347
372
|
* {@link StdioServerOptions}
|
|
348
373
|
* @returns A `{ start(): void; stop(): void }` handle to arm / tear down the pump
|
|
@@ -356,14 +381,14 @@ export declare function createStdioClientTransport(options: StdioClientTransport
|
|
|
356
381
|
* createStdioServer(mcp).start() // an MCP client now connects over this process's stdio
|
|
357
382
|
* ```
|
|
358
383
|
*/
|
|
359
|
-
export declare function createStdioServer(mcp:
|
|
384
|
+
export declare function createStdioServer(mcp: MCPDispatcherInterface, options?: StdioServerOptions): {
|
|
360
385
|
start(): void;
|
|
361
386
|
stop(): void;
|
|
362
387
|
};
|
|
363
388
|
|
|
364
389
|
/**
|
|
365
390
|
* Create the WebSocket CLIENT transport for an {@link import('@src/core').MCPClientInterface}
|
|
366
|
-
* — a {@link
|
|
391
|
+
* — a {@link MCPClientTransportInterface} that drives a REMOTE MCP server over a WebSocket. The
|
|
367
392
|
* egress mirror of {@link createWebSocketServer} and the WebSocket sibling of {@link
|
|
368
393
|
* createHTTPClientTransport}.
|
|
369
394
|
*
|
|
@@ -379,7 +404,7 @@ export declare function createStdioServer(mcp: MCPServerInterface, options?: Std
|
|
|
379
404
|
*
|
|
380
405
|
* @param options - `url` (the remote WebSocket endpoint; REQUIRED) and optional `headers`
|
|
381
406
|
* merged onto the upgrade request; see {@link WebSocketClientTransportOptions}
|
|
382
|
-
* @returns A working {@link
|
|
407
|
+
* @returns A working {@link MCPClientTransportInterface} over a WebSocket
|
|
383
408
|
*
|
|
384
409
|
* @example
|
|
385
410
|
* ```ts
|
|
@@ -393,11 +418,11 @@ export declare function createStdioServer(mcp: MCPServerInterface, options?: Std
|
|
|
393
418
|
* const tools = await client.tools()
|
|
394
419
|
* ```
|
|
395
420
|
*/
|
|
396
|
-
export declare function createWebSocketClientTransport(options: WebSocketClientTransportOptions):
|
|
421
|
+
export declare function createWebSocketClientTransport(options: WebSocketClientTransportOptions): MCPClientTransportInterface;
|
|
397
422
|
|
|
398
423
|
/**
|
|
399
424
|
* Create the MCP WebSocket transport INGRESS — an {@link UpgradeHandler} that exposes a
|
|
400
|
-
* transport-agnostic {@link
|
|
425
|
+
* transport-agnostic {@link MCPDispatcherInterface} over a WebSocket, the WebSocket mirror of
|
|
401
426
|
* {@link createMCPRoutes}. Register it on the spine's upgrade seam.
|
|
402
427
|
*
|
|
403
428
|
* @remarks
|
|
@@ -425,7 +450,7 @@ export declare function createWebSocketClientTransport(options: WebSocketClientT
|
|
|
425
450
|
* handler BEFORE this one — that handler can claim (decline + destroy) an unauthenticated
|
|
426
451
|
* upgrade so it never reaches this pump.
|
|
427
452
|
*
|
|
428
|
-
* @param mcp - The transport-agnostic {@link
|
|
453
|
+
* @param mcp - The transport-agnostic {@link MCPDispatcherInterface} to expose over WebSocket
|
|
429
454
|
* @param options - Optional `path` (default {@link DEFAULT_MCP_PATH}) and `subprotocol`
|
|
430
455
|
* (default {@link MCP_WEBSOCKET_SUBPROTOCOL}); see {@link WebSocketServerOptions}
|
|
431
456
|
* @returns An {@link UpgradeHandler} to register with the spine's `upgrade` seam
|
|
@@ -439,7 +464,7 @@ export declare function createWebSocketClientTransport(options: WebSocketClientT
|
|
|
439
464
|
* server.upgrade(createWebSocketServer(mcp)) // an MCP client now connects over ws://…/mcp
|
|
440
465
|
* ```
|
|
441
466
|
*/
|
|
442
|
-
export declare function createWebSocketServer(mcp:
|
|
467
|
+
export declare function createWebSocketServer(mcp: MCPDispatcherInterface, options?: WebSocketServerOptions): UpgradeHandler;
|
|
443
468
|
|
|
444
469
|
/**
|
|
445
470
|
* Decode one SSE event's `data` string into a {@link JSONRPCMessage}, or `undefined`
|
|
@@ -495,7 +520,7 @@ export declare const DEFAULT_MCP_SESSION_TTL = 300000;
|
|
|
495
520
|
|
|
496
521
|
/**
|
|
497
522
|
* Decode and deliver each complete newline-framed line onto a {@link
|
|
498
|
-
*
|
|
523
|
+
* MCPClientTransportEventMap} emitter — the shared per-chunk dispatch step both stdio
|
|
499
524
|
* transports (client and server) run their {@link extractLines} output through.
|
|
500
525
|
*
|
|
501
526
|
* @remarks
|
|
@@ -508,7 +533,7 @@ export declare const DEFAULT_MCP_SESSION_TTL = 300000;
|
|
|
508
533
|
* @param emitter - The transport's {@link EmitterInterface} to emit `message` / `error` onto
|
|
509
534
|
* @param lines - The complete lines (from {@link extractLines}) to decode and deliver
|
|
510
535
|
*/
|
|
511
|
-
export declare function dispatchLines(emitter: EmitterInterface<
|
|
536
|
+
export declare function dispatchLines(emitter: EmitterInterface<MCPClientTransportEventMap>, lines: readonly string[]): void;
|
|
512
537
|
|
|
513
538
|
/**
|
|
514
539
|
* One entry of an {@link MCPSessionInterface}'s folded replay log — a single pushed {@link
|
|
@@ -551,7 +576,7 @@ export declare function extractLines(buffer: string, chunk: string): LineExtract
|
|
|
551
576
|
|
|
552
577
|
/**
|
|
553
578
|
* The HTTP CLIENT transport for the Model Context Protocol — a
|
|
554
|
-
* {@link
|
|
579
|
+
* {@link MCPClientTransportInterface} that drives a REMOTE Streamable-HTTP MCP server over
|
|
555
580
|
* `fetch`, the egress mirror of the server's `createMCPRoutes`.
|
|
556
581
|
*
|
|
557
582
|
* @remarks
|
|
@@ -584,7 +609,7 @@ export declare function extractLines(buffer: string, chunk: string): LineExtract
|
|
|
584
609
|
* - **Total at the boundary (§14).** Every reply is narrowed (`parseJSONRPCMessage`,
|
|
585
610
|
* the SSE decoder) — a non-message reply is dropped, never asserted; a `fetch` /
|
|
586
611
|
* decode failure surfaces on the `error` event rather than escaping `send`.
|
|
587
|
-
* - **Observable (§13).** Owns the `emitter` ({@link
|
|
612
|
+
* - **Observable (§13).** Owns the `emitter` ({@link MCPClientTransportEventMap}); fires
|
|
588
613
|
* `message` per decoded reply, `error` on a fault, and `close` on `close()`.
|
|
589
614
|
*
|
|
590
615
|
* @example
|
|
@@ -594,11 +619,12 @@ export declare function extractLines(buffer: string, chunk: string): LineExtract
|
|
|
594
619
|
* await client.connect()
|
|
595
620
|
* ```
|
|
596
621
|
*/
|
|
597
|
-
export declare class HTTPClientTransport implements
|
|
622
|
+
export declare class HTTPClientTransport implements MCPClientTransportInterface_2 {
|
|
598
623
|
#private;
|
|
599
624
|
constructor(options: HTTPClientTransportOptions);
|
|
600
|
-
get emitter(): EmitterInterface<
|
|
625
|
+
get emitter(): EmitterInterface<MCPClientTransportEventMap_2>;
|
|
601
626
|
get session(): string | undefined;
|
|
627
|
+
get duplex(): boolean;
|
|
602
628
|
start(): Promise<void>;
|
|
603
629
|
send(message: JSONRPCMessage_2): Promise<void>;
|
|
604
630
|
close(): Promise<void>;
|
|
@@ -630,6 +656,72 @@ export declare interface HTTPClientTransportOptions {
|
|
|
630
656
|
readonly timeout?: number;
|
|
631
657
|
}
|
|
632
658
|
|
|
659
|
+
/**
|
|
660
|
+
* Compose one incoming HTTP request lifetime with one MCP-owned SSE response lifetime.
|
|
661
|
+
*
|
|
662
|
+
* @remarks
|
|
663
|
+
* The composed {@link signal} observes request abort and EVERY way this response can end
|
|
664
|
+
* without one: consumer cancellation of the bridged body, a forwarding failure mid-pump, and a
|
|
665
|
+
* keepalive tick that finds the SSE stream already closed. That last pair is the whole point of
|
|
666
|
+
* the composition — a client that vanishes mid-stream aborts nothing by itself, so unless this
|
|
667
|
+
* object raises the signal on its own failure paths, the handler, the controlled stream, and
|
|
668
|
+
* the producer behind them all keep running for a response that can no longer be written.
|
|
669
|
+
* Graceful upstream completion is the one terminal that does NOT abort: the body simply closes,
|
|
670
|
+
* because the exchange finished rather than ended.
|
|
671
|
+
*
|
|
672
|
+
* {@link bridge} preserves the source response status and headers, forwards its body bytes, and
|
|
673
|
+
* owns keepalive comments plus listener/timer cleanup until upstream completion, request abort,
|
|
674
|
+
* or consumer cancellation. This is a single-response lifecycle object, not a reusable bridge.
|
|
675
|
+
* It supplies no handler or session policy.
|
|
676
|
+
*
|
|
677
|
+
* The keepalive interval is a BUDGET, sanitized like every other numeric knob in this package:
|
|
678
|
+
* anything that is not a positive integer — `0`, a negative, a fractional value, `NaN`,
|
|
679
|
+
* `Infinity` — falls back to {@link DEFAULT_MCP_KEEPALIVE_INTERVAL}, and a larger value clamps
|
|
680
|
+
* to Node's `2_147_483_647` ms timer maximum. None may reach the platform's timer floor, where
|
|
681
|
+
* an idle-liveness tick becomes the polling this package forbids everywhere else.
|
|
682
|
+
*
|
|
683
|
+
* @example
|
|
684
|
+
* ```ts
|
|
685
|
+
* import { HTTPDisconnect } from '@orkestrel/mcp/server'
|
|
686
|
+
* import { openStream } from '@orkestrel/server'
|
|
687
|
+
*
|
|
688
|
+
* const disconnect = new HTTPDisconnect(request.signal, { interval: 15_000 })
|
|
689
|
+
* const stream = openStream()
|
|
690
|
+
* const response = disconnect.bridge(stream)
|
|
691
|
+
* ```
|
|
692
|
+
*/
|
|
693
|
+
export declare class HTTPDisconnect {
|
|
694
|
+
#private;
|
|
695
|
+
/**
|
|
696
|
+
* Create the lifecycle composition for one request and its future SSE response.
|
|
697
|
+
*
|
|
698
|
+
* @param signal - The incoming request signal
|
|
699
|
+
* @param options - Optional keepalive `interval` in milliseconds; an invalid value falls back
|
|
700
|
+
* to {@link DEFAULT_MCP_KEEPALIVE_INTERVAL}, and one above Node's timer maximum clamps to it
|
|
701
|
+
*/
|
|
702
|
+
constructor(signal: AbortSignal, options?: MCPKeepaliveOptions);
|
|
703
|
+
/**
|
|
704
|
+
* The signal aborted by the incoming request, or by any end of this response that is not
|
|
705
|
+
* its graceful completion.
|
|
706
|
+
*
|
|
707
|
+
* @returns The composed lifecycle signal
|
|
708
|
+
*/
|
|
709
|
+
get signal(): AbortSignal;
|
|
710
|
+
/**
|
|
711
|
+
* Bridge one open SSE response through cancellation-aware byte forwarding and keepalives.
|
|
712
|
+
*
|
|
713
|
+
* Consumer cancellation, a read failure while forwarding, and a keepalive tick that finds the
|
|
714
|
+
* SSE stream already closed each abort {@link signal}; consumer cancellation also cancels the
|
|
715
|
+
* upstream reader. Upstream completion closes the returned body without inventing an abort.
|
|
716
|
+
* Every terminal path clears the keepalive timer and detaches the bridge-owned abort listener.
|
|
717
|
+
*
|
|
718
|
+
* @param stream - The open SSE stream whose response will be consumed by the HTTP writer
|
|
719
|
+
* @returns A one-use response preserving status, status text, headers, and SSE body bytes
|
|
720
|
+
* @throws When the supplied SSE response has no body
|
|
721
|
+
*/
|
|
722
|
+
bridge(stream: StreamInterface): Response;
|
|
723
|
+
}
|
|
724
|
+
|
|
633
725
|
/**
|
|
634
726
|
* Options shared by the MCP Streamable-HTTP POST handler and route factory.
|
|
635
727
|
*
|
|
@@ -677,6 +769,27 @@ export declare interface HTTPTransportOptions<TState = unknown> extends HTTPHand
|
|
|
677
769
|
readonly path?: string;
|
|
678
770
|
}
|
|
679
771
|
|
|
772
|
+
/**
|
|
773
|
+
* Infer the first required MCP HTTP header that is missing or mismatched.
|
|
774
|
+
*
|
|
775
|
+
* @remarks
|
|
776
|
+
* A modern request derives its protocol, method, and tools/call-only name expectations from
|
|
777
|
+
* the JSON-RPC body. A legacy request body requires a protocol header after initialization,
|
|
778
|
+
* while a supplied legacy session version additionally diagnoses a header that disagrees with
|
|
779
|
+
* the active session. Messages name the expected value but never echo the client-supplied one.
|
|
780
|
+
*
|
|
781
|
+
* @param request - The HTTP request carrying the headers
|
|
782
|
+
* @param reference - The parsed invocation body, or the active legacy session version
|
|
783
|
+
* @returns The first header issue, or `undefined` when the applicable headers agree
|
|
784
|
+
*
|
|
785
|
+
* @example
|
|
786
|
+
* ```ts
|
|
787
|
+
* const issue = inferHeaderIssue(request, rpcRequest)
|
|
788
|
+
* issue?.header // 'Mcp-Method' when that field is absent or mismatched
|
|
789
|
+
* ```
|
|
790
|
+
*/
|
|
791
|
+
export declare function inferHeaderIssue(request: Request, reference: JSONRPCInvocation | MCPVersion): MCPHeaderIssue | undefined;
|
|
792
|
+
|
|
680
793
|
/**
|
|
681
794
|
* Infer the legacy revision an `initialize` request negotiates.
|
|
682
795
|
*
|
|
@@ -684,10 +797,10 @@ export declare interface HTTPTransportOptions<TState = unknown> extends HTTPHand
|
|
|
684
797
|
* A supported legacy request is pinned exactly. A modern, malformed, absent, or unsupported
|
|
685
798
|
* request selects the newest supported legacy revision, matching the core initialize result.
|
|
686
799
|
*
|
|
687
|
-
* @param request - The legacy initialize
|
|
800
|
+
* @param request - The legacy initialize invocation
|
|
688
801
|
* @returns The negotiated legacy protocol revision
|
|
689
802
|
*/
|
|
690
|
-
export declare function inferLegacyVersion(request:
|
|
803
|
+
export declare function inferLegacyVersion(request: JSONRPCInvocation): MCPVersion;
|
|
691
804
|
|
|
692
805
|
/**
|
|
693
806
|
* Infer the HTTP status for one MCP dispatch outcome without changing its JSON-RPC body.
|
|
@@ -718,21 +831,6 @@ export declare interface LineExtraction {
|
|
|
718
831
|
readonly remainder: string;
|
|
719
832
|
}
|
|
720
833
|
|
|
721
|
-
/**
|
|
722
|
-
* Whether a modern HTTP request's required standard headers match its JSON-RPC body.
|
|
723
|
-
*
|
|
724
|
-
* @remarks
|
|
725
|
-
* Requires `MCP-Protocol-Version` to equal the reserved `_meta` version and `Mcp-Method`
|
|
726
|
-
* to equal `method`. `Mcp-Name` is required only for `tools/call`, where it must equal
|
|
727
|
-
* `params.name`; discovery and listing requests need no name because none is derivable.
|
|
728
|
-
* Legacy requests return `false` because this predicate models the modern contract only.
|
|
729
|
-
*
|
|
730
|
-
* @param request - The HTTP request carrying the headers
|
|
731
|
-
* @param message - The parsed JSON-RPC request body
|
|
732
|
-
* @returns `true` only when every method-applicable modern header matches
|
|
733
|
-
*/
|
|
734
|
-
export declare function matchesModernHeaders(request: Request, message: JSONRPCRequest): boolean;
|
|
735
|
-
|
|
736
834
|
/** The modern Streamable-HTTP request header carrying the JSON-RPC method name. */
|
|
737
835
|
export declare const MCP_METHOD_HEADER = "mcp-method";
|
|
738
836
|
|
|
@@ -788,6 +886,21 @@ export declare const MCP_WEBSOCKET_SUBPROTOCOL = "mcp";
|
|
|
788
886
|
*/
|
|
789
887
|
export declare type MCPCallerHandler<TState = unknown> = (request: Request, context: RouteContext<string, TState> | undefined) => unknown;
|
|
790
888
|
|
|
889
|
+
/**
|
|
890
|
+
* One required MCP HTTP header that is absent or disagrees with its server-derived value.
|
|
891
|
+
*
|
|
892
|
+
* @remarks
|
|
893
|
+
* - `header` — the canonical HTTP field name safe to show to an integrator.
|
|
894
|
+
* - `reason` — whether the field is absent or carries a mismatched value.
|
|
895
|
+
* - `message` — the refusal message naming the expected body, session, or server value without
|
|
896
|
+
* echoing the client-supplied header value.
|
|
897
|
+
*/
|
|
898
|
+
export declare interface MCPHeaderIssue {
|
|
899
|
+
readonly header: 'MCP-Protocol-Version' | 'Mcp-Method' | 'Mcp-Name';
|
|
900
|
+
readonly reason: 'missing' | 'mismatched';
|
|
901
|
+
readonly message: string;
|
|
902
|
+
}
|
|
903
|
+
|
|
791
904
|
/**
|
|
792
905
|
* Shared SSE keepalive options for held-open HTTP responses.
|
|
793
906
|
*
|
|
@@ -1045,9 +1158,9 @@ export declare function readSessionHeader(request: Request): string | undefined;
|
|
|
1045
1158
|
* JSON-RPC error body.
|
|
1046
1159
|
*
|
|
1047
1160
|
* @remarks
|
|
1048
|
-
* Returns `Response.json(buildJSONRPCError(
|
|
1049
|
-
* { status: 404 })`, mirroring `createMCPRoutes`'s `400` transport-failure shape (a
|
|
1050
|
-
* JSON-RPC error BODY with
|
|
1161
|
+
* Returns `Response.json(buildJSONRPCError(undefined, JSONRPC_INVALID_REQUEST, 'Session not
|
|
1162
|
+
* found'), { status: 404 })`, mirroring `createMCPRoutes`'s `400` transport-failure shape (a
|
|
1163
|
+
* JSON-RPC error BODY with NO id) but at the session-not-found status. Shared by
|
|
1051
1164
|
* every {@link import('./middlewares.js').createMCPSession} validation site — the
|
|
1052
1165
|
* non-`initialize` `POST` path, the resumable `GET {path}` open, and the `DELETE {path}`
|
|
1053
1166
|
* session-end (each a missing / unknown / TTL-evicted id) — so the single `404` envelope
|
|
@@ -1057,6 +1170,41 @@ export declare function readSessionHeader(request: Request): string | undefined;
|
|
|
1057
1170
|
*/
|
|
1058
1171
|
export declare function rejectUnknownSession(): Response;
|
|
1059
1172
|
|
|
1173
|
+
/**
|
|
1174
|
+
* Pump a controlled held-open exchange onto an open SSE stream — one `data:` event per
|
|
1175
|
+
* notification in order, then the terminating response — and END the exchange however the
|
|
1176
|
+
* pump leaves.
|
|
1177
|
+
*
|
|
1178
|
+
* @remarks
|
|
1179
|
+
* The Streamable-HTTP twin of {@link import('@src/core').sendStream}, and it owns exactly what
|
|
1180
|
+
* that owns. The `finally` releases the exchange on EVERY exit — the normal terminal, a
|
|
1181
|
+
* producer that threw, a `write` that threw, and an abort alike — because nothing else will:
|
|
1182
|
+
* a request whose client vanished cancels nothing by itself, so an exchange this pump walks
|
|
1183
|
+
* away from keeps its producer, its request lifetime, and its live subscription slot forever.
|
|
1184
|
+
* The exchange is released BEFORE the body ends, so the slot is already back when the response
|
|
1185
|
+
* completes.
|
|
1186
|
+
*
|
|
1187
|
+
* Total (§14) — never throws and never rejects. A held-open SSE response has already sent its
|
|
1188
|
+
* headers and part of its body, so there is no failure the transport could still convert into
|
|
1189
|
+
* a different answer; the honest end of a broken stream is a closed one, and the fault itself
|
|
1190
|
+
* is already legible on `server.emitter`'s `error` event, which is where a contained fault
|
|
1191
|
+
* belongs.
|
|
1192
|
+
*
|
|
1193
|
+
* @param stream - The controlled held-open answer to write out and then end
|
|
1194
|
+
* @param sse - The open SSE stream to write each serialized message onto
|
|
1195
|
+
* @returns Resolves once the exchange has ended and the SSE body has been closed
|
|
1196
|
+
*
|
|
1197
|
+
* @example
|
|
1198
|
+
* ```ts
|
|
1199
|
+
* const answer = await mcp.dispatch(invocation, { signal: disconnect.signal })
|
|
1200
|
+
* if (answer !== undefined && Symbol.asyncIterator in answer) {
|
|
1201
|
+
* const sse = openStream()
|
|
1202
|
+
* queueMicrotask(() => void sendEventStream(answer, sse))
|
|
1203
|
+
* }
|
|
1204
|
+
* ```
|
|
1205
|
+
*/
|
|
1206
|
+
export declare function sendEventStream(stream: MCPStreamControllerInterface, sse: StreamInterface): Promise<void>;
|
|
1207
|
+
|
|
1060
1208
|
/** The `X-Accel-Buffering` value that disables reverse-proxy buffering. */
|
|
1061
1209
|
export declare const SSE_BUFFERING_DISABLED = "no";
|
|
1062
1210
|
|
|
@@ -1068,7 +1216,7 @@ export declare const SSE_KEEPALIVE_COMMENT = "keepalive";
|
|
|
1068
1216
|
|
|
1069
1217
|
/**
|
|
1070
1218
|
* The stdio CLIENT transport for the Model Context Protocol — a
|
|
1071
|
-
* {@link
|
|
1219
|
+
* {@link MCPClientTransportInterface} that drives a CHILD PROCESS MCP server over
|
|
1072
1220
|
* newline-delimited JSON-RPC on `stdin`/`stdout`, the stdio sibling of {@link
|
|
1073
1221
|
* import('./HTTPClientTransport.js').HTTPClientTransport} and {@link
|
|
1074
1222
|
* import('./WebSocketClientTransport.js').WebSocketClientTransport}.
|
|
@@ -1087,7 +1235,7 @@ export declare const SSE_KEEPALIVE_COMMENT = "keepalive";
|
|
|
1087
1235
|
* - **Outbound (`send`).** `send(message)` writes one newline-terminated
|
|
1088
1236
|
* `JSON.stringify`d line to the child's `stdin`.
|
|
1089
1237
|
* - **`close()`** kills the child process and fires `close` (idempotent).
|
|
1090
|
-
* - **Observable (§13).** Owns the `emitter` ({@link
|
|
1238
|
+
* - **Observable (§13).** Owns the `emitter` ({@link MCPClientTransportEventMap}); the
|
|
1091
1239
|
* emitter isolates a listener throw; `error` is a DOMAIN event (a transport-level
|
|
1092
1240
|
* fault), distinct from the emitter's own listener-error channel.
|
|
1093
1241
|
*
|
|
@@ -1098,11 +1246,12 @@ export declare const SSE_KEEPALIVE_COMMENT = "keepalive";
|
|
|
1098
1246
|
* await client.connect() // start() spawns the child, then the MCP initialize runs over stdio
|
|
1099
1247
|
* ```
|
|
1100
1248
|
*/
|
|
1101
|
-
export declare class StdioClientTransport implements
|
|
1249
|
+
export declare class StdioClientTransport implements MCPClientTransportInterface_2 {
|
|
1102
1250
|
#private;
|
|
1103
1251
|
constructor(options: StdioClientTransportOptions);
|
|
1104
|
-
get emitter(): EmitterInterface<
|
|
1252
|
+
get emitter(): EmitterInterface<MCPClientTransportEventMap_2>;
|
|
1105
1253
|
get session(): string | undefined;
|
|
1254
|
+
get duplex(): boolean;
|
|
1106
1255
|
start(): Promise<void>;
|
|
1107
1256
|
send(message: JSONRPCMessage_2): Promise<void>;
|
|
1108
1257
|
close(): Promise<void>;
|
|
@@ -1145,13 +1294,13 @@ export declare interface StdioServerOptions {
|
|
|
1145
1294
|
/**
|
|
1146
1295
|
* The stdio SERVER transport for the Model Context Protocol — wraps an injectable
|
|
1147
1296
|
* readable/writable stream pair (`process.stdin`/`process.stdout` in production, a
|
|
1148
|
-
* test double in tests) as a {@link
|
|
1297
|
+
* test double in tests) as a {@link MCPClientTransportInterface}, the newline-delimited
|
|
1149
1298
|
* JSON-RPC channel {@link import('../factories.js').createStdioServer} pumps
|
|
1150
1299
|
* `mcp.dispatch` over, the stdio mirror of {@link
|
|
1151
1300
|
* import('./WebSocketServerTransport.js').WebSocketServerTransport}.
|
|
1152
1301
|
*
|
|
1153
1302
|
* @remarks
|
|
1154
|
-
* - **Reuses `
|
|
1303
|
+
* - **Reuses `MCPClientTransportInterface` (§21).** The same generic carrier the HTTP
|
|
1155
1304
|
* and WebSocket server transports implement — `emitter` (`message` / `close` /
|
|
1156
1305
|
* `error`), `start`, `send`, `close`. `session` is `undefined` (the stateless v1).
|
|
1157
1306
|
* - **Inbound (`message`).** `start()` subscribes to `input`'s `data` event; each
|
|
@@ -1166,15 +1315,16 @@ export declare interface StdioServerOptions {
|
|
|
1166
1315
|
* - **`close()`** fires this transport's `close` (idempotent) — the injected streams
|
|
1167
1316
|
* are owned by the caller (typically `process.stdin`/`process.stdout`, which must
|
|
1168
1317
|
* never be closed out from under the process) and are not torn down here.
|
|
1169
|
-
* - **Observable (§13).** Owns the `emitter` ({@link
|
|
1318
|
+
* - **Observable (§13).** Owns the `emitter` ({@link MCPClientTransportEventMap}); the
|
|
1170
1319
|
* emitter isolates a listener throw; `error` is a DOMAIN event (a transport-level
|
|
1171
1320
|
* fault), distinct from the emitter's own listener-error channel.
|
|
1172
1321
|
*/
|
|
1173
|
-
export declare class StdioServerTransport implements
|
|
1322
|
+
export declare class StdioServerTransport implements MCPClientTransportInterface_2 {
|
|
1174
1323
|
#private;
|
|
1175
1324
|
constructor(input: NodeJS.ReadableStream, output: NodeJS.WritableStream);
|
|
1176
|
-
get emitter(): EmitterInterface<
|
|
1325
|
+
get emitter(): EmitterInterface<MCPClientTransportEventMap_2>;
|
|
1177
1326
|
get session(): string | undefined;
|
|
1327
|
+
get duplex(): boolean;
|
|
1178
1328
|
start(): Promise<void>;
|
|
1179
1329
|
send(message: JSONRPCMessage_2): Promise<void>;
|
|
1180
1330
|
close(): Promise<void>;
|
|
@@ -1199,7 +1349,7 @@ export declare function upgradeRequestPath(request: IncomingMessage): string;
|
|
|
1199
1349
|
|
|
1200
1350
|
/**
|
|
1201
1351
|
* The WebSocket CLIENT transport for the Model Context Protocol — a
|
|
1202
|
-
* {@link
|
|
1352
|
+
* {@link MCPClientTransportInterface} that drives a REMOTE MCP server over a WebSocket, the
|
|
1203
1353
|
* egress mirror of {@link import('./factories.js').createWebSocketServer} and the WebSocket
|
|
1204
1354
|
* sibling of {@link import('./HTTPClientTransport.js').HTTPClientTransport}.
|
|
1205
1355
|
*
|
|
@@ -1212,6 +1362,12 @@ export declare function upgradeRequestPath(request: IncomingMessage): string;
|
|
|
1212
1362
|
* — a mismatch (or a non-`101` response, or a request error) REJECTS `start()` and the socket
|
|
1213
1363
|
* is destroyed. On success it wraps the raw upgraded socket in `createNodeWebSocket({ socket,
|
|
1214
1364
|
* head })` (CLIENT mode — no key → frames are MASKED per §5.3) and bridges its `message`.
|
|
1365
|
+
* - **The arriving socket is RE-ASKED for, never assumed.** `start()` suspends across that
|
|
1366
|
+
* connect and upgrade, so it re-checks the transport's state before installing anything: a
|
|
1367
|
+
* concurrent `start()` that already installed a socket, or a {@link close} that ended the
|
|
1368
|
+
* transport while the handshake was on the wire, both WIN — the socket that arrives late is
|
|
1369
|
+
* DESTROYED and never bound, so no orphan is left re-emitting frames at nobody. Both
|
|
1370
|
+
* `start()` calls still resolve; exactly one socket is ever bound.
|
|
1215
1371
|
* - **Inbound (`message`).** Each decoded text frame is `JSON.parse`d (guarded) and narrowed
|
|
1216
1372
|
* with `parseJSONRPCMessage` — a {@link JSONRPCMessage} re-emits on this transport's `message`
|
|
1217
1373
|
* event (the reply the {@link import('@src/core').MCPClientInterface} correlates by `id`); a
|
|
@@ -1222,7 +1378,7 @@ export declare function upgradeRequestPath(request: IncomingMessage): string;
|
|
|
1222
1378
|
* - **URL scheme.** `options.url` accepts a `ws://` / `wss://` URL or an `http://` / `https://`
|
|
1223
1379
|
* one; a `ws(s)` scheme is converted to `http(s)` for the underlying upgrade request (`wss`
|
|
1224
1380
|
* → TLS via `node:https`). Either reaches the same endpoint.
|
|
1225
|
-
* - **Observable (§13).** Owns the `emitter` ({@link
|
|
1381
|
+
* - **Observable (§13).** Owns the `emitter` ({@link MCPClientTransportEventMap}); every emit
|
|
1226
1382
|
* the emitter isolates a listener throw (a buggy observer never corrupts the transport);
|
|
1227
1383
|
* `error` is a DOMAIN event (a transport-level fault).
|
|
1228
1384
|
*
|
|
@@ -1233,11 +1389,12 @@ export declare function upgradeRequestPath(request: IncomingMessage): string;
|
|
|
1233
1389
|
* await client.connect() // start() handshakes, then the MCP initialize runs over WS frames
|
|
1234
1390
|
* ```
|
|
1235
1391
|
*/
|
|
1236
|
-
export declare class WebSocketClientTransport implements
|
|
1392
|
+
export declare class WebSocketClientTransport implements MCPClientTransportInterface_2 {
|
|
1237
1393
|
#private;
|
|
1238
1394
|
constructor(options: WebSocketClientTransportOptions);
|
|
1239
|
-
get emitter(): EmitterInterface<
|
|
1395
|
+
get emitter(): EmitterInterface<MCPClientTransportEventMap_2>;
|
|
1240
1396
|
get session(): string | undefined;
|
|
1397
|
+
get duplex(): boolean;
|
|
1241
1398
|
start(): Promise<void>;
|
|
1242
1399
|
send(message: JSONRPCMessage_2): Promise<void>;
|
|
1243
1400
|
close(): Promise<void>;
|
|
@@ -1256,6 +1413,13 @@ export declare class WebSocketClientTransport implements ClientTransportInterfac
|
|
|
1256
1413
|
* bearer for a guarded server). The transport always sets `Connection: Upgrade`,
|
|
1257
1414
|
* `Upgrade: websocket`, a random `Sec-WebSocket-Key`, `Sec-WebSocket-Version: 13`, and
|
|
1258
1415
|
* `Sec-WebSocket-Protocol: mcp`; a header supplied here is merged on top.
|
|
1416
|
+
*
|
|
1417
|
+
* **`headers` exists HERE and not on the browser face's `{ url, protocols }`, and that
|
|
1418
|
+
* divergence is deliberate rather than a lag: the host performs the WebSocket handshake.**
|
|
1419
|
+
* This face owns its own `node:http(s)` upgrade request, so it can set any header on it. A
|
|
1420
|
+
* page cannot — the native `WebSocket` constructor takes a URL and subprotocols and nothing
|
|
1421
|
+
* else — so a `headers` key over there would be an option that silently did nothing. Do not
|
|
1422
|
+
* "harmonize" the two shapes.
|
|
1259
1423
|
*/
|
|
1260
1424
|
export declare interface WebSocketClientTransportOptions {
|
|
1261
1425
|
readonly url: string;
|
|
@@ -1288,12 +1452,12 @@ export declare interface WebSocketServerOptions {
|
|
|
1288
1452
|
/**
|
|
1289
1453
|
* The per-connection JSON-RPC-over-WebSocket SERVER bridge — wraps a
|
|
1290
1454
|
* {@link NodeWebSocketInterface} (the RFC 6455 wire wrapper) as a
|
|
1291
|
-
* {@link
|
|
1455
|
+
* {@link MCPClientTransportInterface}, the bidirectional JSON-RPC message channel
|
|
1292
1456
|
* `createWebSocketServer` pumps `mcp.dispatch` over and the egress mirror's
|
|
1293
1457
|
* {@link import('./WebSocketClientTransport.js').WebSocketClientTransport} reuses.
|
|
1294
1458
|
*
|
|
1295
1459
|
* @remarks
|
|
1296
|
-
* - **Reuses `
|
|
1460
|
+
* - **Reuses `MCPClientTransportInterface` (§21).** It IS the same generic carrier the HTTP
|
|
1297
1461
|
* client transport implements — `emitter` (`message` / `close` / `error`), `start`,
|
|
1298
1462
|
* `send`, `close` — so the WebSocket server and client both speak ONE transport contract,
|
|
1299
1463
|
* no near-duplicate sibling interface. `session` is `undefined` (the stateless v1; a
|
|
@@ -1311,15 +1475,16 @@ export declare interface WebSocketServerOptions {
|
|
|
1311
1475
|
* - **`close()`** closes the underlying socket (the RFC 6455 close handshake) and fires the
|
|
1312
1476
|
* transport's `close` event (idempotent — a second `close`, or a socket-driven close, emits
|
|
1313
1477
|
* once).
|
|
1314
|
-
* - **Observable (§13).** Owns the `emitter` ({@link
|
|
1478
|
+
* - **Observable (§13).** Owns the `emitter` ({@link MCPClientTransportEventMap}); the emitter
|
|
1315
1479
|
* isolates a listener throw (a buggy observer never corrupts the bridge). `error` is a
|
|
1316
1480
|
* DOMAIN event (a transport-level fault), distinct from the emitter's listener-error channel.
|
|
1317
1481
|
*/
|
|
1318
|
-
export declare class WebSocketServerTransport implements
|
|
1482
|
+
export declare class WebSocketServerTransport implements MCPClientTransportInterface_2 {
|
|
1319
1483
|
#private;
|
|
1320
1484
|
constructor(socket: NodeWebSocketInterface);
|
|
1321
|
-
get emitter(): EmitterInterface<
|
|
1485
|
+
get emitter(): EmitterInterface<MCPClientTransportEventMap_2>;
|
|
1322
1486
|
get session(): string | undefined;
|
|
1487
|
+
get duplex(): boolean;
|
|
1323
1488
|
start(): Promise<void>;
|
|
1324
1489
|
send(message: JSONRPCMessage_2): Promise<void>;
|
|
1325
1490
|
close(): Promise<void>;
|