@orkestrel/mcp 0.0.26 → 0.0.28
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 +1 -1
- package/dist/src/browser/index.d.ts +146 -275
- package/dist/src/browser/index.js +143 -393
- package/dist/src/browser/index.js.map +1 -1
- package/dist/src/core/index.cjs +1350 -247
- package/dist/src/core/index.cjs.map +1 -1
- package/dist/src/core/index.d.cts +1498 -551
- package/dist/src/core/index.d.ts +1498 -551
- package/dist/src/core/index.js +1318 -247
- package/dist/src/core/index.js.map +1 -1
- package/dist/src/server/index.cjs +425 -562
- package/dist/src/server/index.cjs.map +1 -1
- package/dist/src/server/index.d.cts +331 -357
- package/dist/src/server/index.d.ts +331 -357
- package/dist/src/server/index.js +417 -547
- package/dist/src/server/index.js.map +1 -1
- package/package.json +21 -20
|
@@ -1,17 +1,19 @@
|
|
|
1
1
|
import { EmitterInterface } from '@orkestrel/emitter';
|
|
2
|
+
import { HTTPClientTransportOptions } from '@orkestrel/mcp';
|
|
2
3
|
import { IncomingMessage } from 'node:http';
|
|
3
4
|
import { JSONRPCInvocation } from '@orkestrel/mcp';
|
|
4
5
|
import { JSONRPCMessage } from '@orkestrel/mcp';
|
|
5
6
|
import { JSONRPCMessage as JSONRPCMessage_2 } from '@orkestrel/mcp';
|
|
6
7
|
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
8
|
import { MCPContinuationInterface } from '@orkestrel/mcp';
|
|
12
9
|
import { MCPDispatcherInterface } from '@orkestrel/mcp';
|
|
13
10
|
import { MCPEra } from '@orkestrel/mcp';
|
|
11
|
+
import { MCPHeaderParameter } from '@orkestrel/mcp';
|
|
14
12
|
import { MCPLegacyVersion } from '@orkestrel/mcp';
|
|
13
|
+
import { MCPMessageTransportEventMap } from '@orkestrel/mcp';
|
|
14
|
+
import { MCPMessageTransportEventMap as MCPMessageTransportEventMap_2 } from '@orkestrel/mcp';
|
|
15
|
+
import { MCPMessageTransportInterface } from '@orkestrel/mcp';
|
|
16
|
+
import { MCPMessageTransportInterface as MCPMessageTransportInterface_2 } from '@orkestrel/mcp';
|
|
15
17
|
import { MCPStreamControllerInterface } from '@orkestrel/mcp';
|
|
16
18
|
import { MCPTransportInterface } from '@orkestrel/mcp';
|
|
17
19
|
import { MCPVersion } from '@orkestrel/mcp';
|
|
@@ -25,7 +27,7 @@ import { TokenSecret } from '@orkestrel/server';
|
|
|
25
27
|
import { UpgradeHandler } from '@orkestrel/server';
|
|
26
28
|
|
|
27
29
|
/**
|
|
28
|
-
*
|
|
30
|
+
* Checks whether the request's `Accept` header opts into a Server-Sent-Events response.
|
|
29
31
|
*
|
|
30
32
|
* @remarks
|
|
31
33
|
* Reads the fetch-standard `Request.headers.get('accept')` and returns `true` when it
|
|
@@ -35,12 +37,12 @@ import { UpgradeHandler } from '@orkestrel/server';
|
|
|
35
37
|
* — an absent / unmatched header returns `false`.
|
|
36
38
|
*
|
|
37
39
|
* @param request - The fetch-standard `Request`
|
|
38
|
-
* @returns
|
|
40
|
+
* @returns True if the client `Accept`s `text/event-stream`; false otherwise
|
|
39
41
|
*/
|
|
40
42
|
export declare function acceptsEventStream(request: Request): boolean;
|
|
41
43
|
|
|
42
44
|
/**
|
|
43
|
-
*
|
|
45
|
+
* Checks whether an HTTP request satisfies the endpoint's origin gate.
|
|
44
46
|
*
|
|
45
47
|
* @remarks
|
|
46
48
|
* Validation is enabled by default. A request without `Origin` is allowed. A canonical origin
|
|
@@ -51,18 +53,20 @@ export declare function acceptsEventStream(request: Request): boolean;
|
|
|
51
53
|
*
|
|
52
54
|
* @param request - The fetch-standard request to validate
|
|
53
55
|
* @param options - Shared origin validation and delegation options
|
|
54
|
-
* @returns
|
|
56
|
+
* @returns True if the request may reach MCP dispatch; false otherwise
|
|
55
57
|
*/
|
|
56
58
|
export declare function allowsOrigin(request: Request, options?: MCPOriginOptions): boolean;
|
|
57
59
|
|
|
58
60
|
/**
|
|
59
|
-
*
|
|
60
|
-
*
|
|
61
|
-
* {@link
|
|
62
|
-
*
|
|
63
|
-
* import('
|
|
64
|
-
*
|
|
65
|
-
*
|
|
61
|
+
* Creates the server-side mirror of
|
|
62
|
+
* {@link import('@orkestrel/mcp').createDuplexClientTransport}: the adapter that bridges a
|
|
63
|
+
* message-channel {@link MCPMessageTransportInterface}
|
|
64
|
+
* (the shape the stdio and WebSocket SERVER transports already implement) onto the
|
|
65
|
+
* environment-agnostic {@link import('@orkestrel/mcp').MCPTransportInterface} port — what
|
|
66
|
+
* {@link createStdioServer} and {@link createWebSocketServer} pipe through `bindServer`, so
|
|
67
|
+
* the request/reply/error pump those factories used to hand-roll identically now lives ONCE
|
|
68
|
+
* in the core binder. {@link import('@orkestrel/mcp').createDuplexClientTransport} adapts the
|
|
69
|
+
* same two contracts the other way.
|
|
66
70
|
*
|
|
67
71
|
* @remarks
|
|
68
72
|
* `send` decodes the already-serialized reply string back to a {@link JSONRPCMessage}
|
|
@@ -104,31 +108,22 @@ export declare function allowsOrigin(request: Request, options?: MCPOriginOption
|
|
|
104
108
|
* import { bindServer } from '@orkestrel/mcp'
|
|
105
109
|
*
|
|
106
110
|
* const transport = new StdioServerTransport(process.stdin, process.stdout)
|
|
107
|
-
* bindServer(mcp,
|
|
111
|
+
* bindServer(mcp, createDuplexServerTransport(transport))
|
|
108
112
|
* ```
|
|
109
113
|
*/
|
|
110
|
-
export declare function
|
|
111
|
-
|
|
112
|
-
/**
|
|
113
|
-
* Builds the error for a non-success HTTP response that carried no JSON-RPC message.
|
|
114
|
-
*
|
|
115
|
-
* @param response - The response whose status is reported
|
|
116
|
-
* @param type - The response's content type, or an empty string when absent
|
|
117
|
-
* @returns An error naming the HTTP status and unsupported response shape
|
|
118
|
-
*
|
|
119
|
-
* @example
|
|
120
|
-
* ```ts
|
|
121
|
-
* const error = buildResponseError(new Response('', { status: 500 }), '')
|
|
122
|
-
* ```
|
|
123
|
-
*/
|
|
124
|
-
export declare function buildResponseError(response: Response, type: string): Error;
|
|
114
|
+
export declare function createDuplexServerTransport(transport: MCPMessageTransportInterface): MCPTransportInterface;
|
|
125
115
|
|
|
126
116
|
/**
|
|
127
117
|
* Creates the HTTP CLIENT transport for an {@link import('@orkestrel/mcp').MCPClientInterface}
|
|
128
|
-
* — a {@link
|
|
118
|
+
* — a {@link MCPMessageTransportInterface} that drives a REMOTE Streamable-HTTP MCP server
|
|
129
119
|
* over `fetch`. The egress mirror of {@link createMCPRoutes}.
|
|
130
120
|
*
|
|
131
121
|
* @remarks
|
|
122
|
+
* It returns the core {@link import('@orkestrel/mcp').HTTPClientTransport}, the same class the
|
|
123
|
+
* browser face's `createHTTPClientTransport` returns, because the class touches `fetch`,
|
|
124
|
+
* `Response`, `AbortController`, `AbortSignal`, and `WeakMap` alone.
|
|
125
|
+
*
|
|
126
|
+
* @remarks
|
|
132
127
|
* Hand it to `createMCPClient({ transport })`: each JSON-RPC message the client sends is
|
|
133
128
|
* `POST`ed to `options.url` with `content-type: application/json` and an `Accept` of
|
|
134
129
|
* both `application/json` and `text/event-stream` (the server answers with EITHER — a
|
|
@@ -144,7 +139,7 @@ export declare function buildResponseError(response: Response, type: string): Er
|
|
|
144
139
|
* @param options - `url` (the remote endpoint; REQUIRED), optional `headers` merged onto
|
|
145
140
|
* every request, optional `fetch` (default `globalThis.fetch`), and optional `timeout`
|
|
146
141
|
* (ms, applied with `AbortSignal.timeout`); see {@link HTTPClientTransportOptions}
|
|
147
|
-
* @returns A working {@link
|
|
142
|
+
* @returns A working {@link MCPMessageTransportInterface} over `fetch`
|
|
148
143
|
*
|
|
149
144
|
* @example
|
|
150
145
|
* ```ts
|
|
@@ -158,7 +153,7 @@ export declare function buildResponseError(response: Response, type: string): Er
|
|
|
158
153
|
* const tools = await client.tools()
|
|
159
154
|
* ```
|
|
160
155
|
*/
|
|
161
|
-
export declare function createHTTPClientTransport(options: HTTPClientTransportOptions):
|
|
156
|
+
export declare function createHTTPClientTransport(options: HTTPClientTransportOptions): MCPMessageTransportInterface;
|
|
162
157
|
|
|
163
158
|
/**
|
|
164
159
|
* Adapts the installed server token primitives to the host-neutral MCP continuation port.
|
|
@@ -172,12 +167,17 @@ export declare function createMCPContinuation(secret: TokenSecret): MCPContinuat
|
|
|
172
167
|
* Creates the Streamable-HTTP POST handler used by `createMCPRoutes`.
|
|
173
168
|
*
|
|
174
169
|
* @remarks
|
|
175
|
-
* Modern requests require matching protocol/method headers and a matching name header
|
|
176
|
-
*
|
|
177
|
-
*
|
|
178
|
-
*
|
|
179
|
-
*
|
|
180
|
-
*
|
|
170
|
+
* Modern requests require matching protocol/method headers and a matching name header on each
|
|
171
|
+
* method carrying a named target — `tools/call` and `prompts/get` against `params.name`,
|
|
172
|
+
* `resources/read` against `params.uri` — with a Base64-sentinel value decoded before the
|
|
173
|
+
* comparison; a missing, mismatched, or invalidly encoded value returns HTTP `400` + `-32020`.
|
|
174
|
+
* A protocol header naming a MODERN revision holds the request to that revision whatever shape
|
|
175
|
+
* its body arrived in, so a body with no parsable modern `_meta` returns HTTP `400` + `-32602`.
|
|
176
|
+
* Headerless `initialize` is accepted, while every other headerless request needs a live legacy
|
|
177
|
+
* session to supply its pinned version. A legacy-shaped request carrying a protocol header is
|
|
178
|
+
* otherwise admitted only for a legacy revision; a revision this server does not implement
|
|
179
|
+
* returns HTTP `400` + `-32022` whose `supported` names the legacy revisions this door accepts.
|
|
180
|
+
* A present origin must occur in `origin.origins` unless validation is
|
|
181
181
|
* explicitly delegated upstream. Modern dispatch errors use their protocol status map; legacy
|
|
182
182
|
* errors remain in-band at HTTP `200`. A streamed response composes the fetch-standard request
|
|
183
183
|
* signal with response-body cancellation and supplies the result to every dispatched modern
|
|
@@ -232,7 +232,7 @@ export declare function createMCPPostHandler<TState = unknown>(mcp: MCPDispatche
|
|
|
232
232
|
* When `streaming` is enabled (the default) and the client `Accept`s `text/event-stream`,
|
|
233
233
|
* the `200` reply is framed as a Streamable-HTTP SSE response (one `data:` event carrying
|
|
234
234
|
* the JSON-RPC envelope, then the stream ends) through `@orkestrel/server`'s generic
|
|
235
|
-
* {@link import('@orkestrel/server').
|
|
235
|
+
* {@link import('@orkestrel/server').createStream} seam; otherwise it is a plain JSON body.
|
|
236
236
|
*
|
|
237
237
|
* **Sessions are a SEPARATE, plug-and-play middleware.** `createMCPRoutes` mints / reads no
|
|
238
238
|
* session id. To make the transport STATEFUL, mount {@link
|
|
@@ -282,8 +282,9 @@ export declare function createMCPRoutes<TState = unknown>(mcp: MCPDispatcherInte
|
|
|
282
282
|
* can re-read it from a freshly-built forwarded `Request`). Resolves a session through {@link
|
|
283
283
|
* readSessionHeader}: a VALID id touches the entry and sets `context.state.session`; an
|
|
284
284
|
* ABSENT / unknown id whose (guarded) body parses to an `initialize` request ({@link
|
|
285
|
-
* isInitializeRequest}) MINTS a fresh {@link MCPSession} (`crypto.randomUUID()`, `
|
|
286
|
-
* and sets `context.state.session`; neither → {@link rejectUnknownSession}
|
|
285
|
+
* isInitializeRequest}) MINTS a fresh {@link MCPSession} (`crypto.randomUUID()`, the `session`
|
|
286
|
+
* options group) and sets `context.state.session`; neither → {@link rejectUnknownSession}
|
|
287
|
+
* (`404`). The
|
|
287
288
|
* minted entry pins the negotiated legacy revision, which is supplied to a later headerless
|
|
288
289
|
* live-session request. It then
|
|
289
290
|
* FORWARDS a fresh `Request` carrying the buffered `text` (`next(forwarded)`) — never the
|
|
@@ -294,7 +295,7 @@ export declare function createMCPRoutes<TState = unknown>(mcp: MCPDispatcherInte
|
|
|
294
295
|
* a `DELETE` arriving while the request was suspended is not undone.
|
|
295
296
|
* - **`GET {path}`.** Resolves the session the same way (no mint — only `initialize` mints);
|
|
296
297
|
* an invalid / unknown id is the same `404`. A valid session opens the resumable
|
|
297
|
-
* server→client stream through `@orkestrel/server`'s {@link import('@orkestrel/server').
|
|
298
|
+
* server→client stream through `@orkestrel/server`'s {@link import('@orkestrel/server').createStream}:
|
|
298
299
|
* replays every event after the client's `Last-Event-ID` ({@link readLastEventId}) BEFORE
|
|
299
300
|
* attaching the stream for live pushes, then attaches; cancellation of the streamed response
|
|
300
301
|
* body composes with `request.signal` and detaches it. Long-lived — never `end()`ed here.
|
|
@@ -309,10 +310,11 @@ export declare function createMCPRoutes<TState = unknown>(mcp: MCPDispatcherInte
|
|
|
309
310
|
* @typeParam TState - The consumer's `TState`, which MUST extend {@link MCPSessionState} so
|
|
310
311
|
* the resolved session can be threaded through `context.state.session`
|
|
311
312
|
* @param options - Optional `path` (default {@link DEFAULT_MCP_PATH}), `ttl` (idle-session
|
|
312
|
-
* sweep window, ms — omit for sessions that live until an explicit `DELETE`), `
|
|
313
|
-
* (the
|
|
314
|
-
*
|
|
315
|
-
*
|
|
313
|
+
* sweep window, ms — omit for sessions that live until an explicit `DELETE`), `session`
|
|
314
|
+
* (the knobs each minted {@link MCPSession} takes — `capacity`, the log's own `ttl`, and its
|
|
315
|
+
* `clock`), and `clock` (the deterministic epoch-ms clock this middleware keeps its own
|
|
316
|
+
* bookkeeping on and hands down to a session that names none; defaults to `Date.now`), plus
|
|
317
|
+
* the shared `origin` validation options; see {@link MCPSessionMiddlewareOptions}
|
|
316
318
|
* @returns A {@link MiddlewareHandler} that mints / validates sessions + serves the resumable
|
|
317
319
|
* `GET` / `DELETE`
|
|
318
320
|
*
|
|
@@ -328,16 +330,7 @@ export declare function createMCPRoutes<TState = unknown>(mcp: MCPDispatcherInte
|
|
|
328
330
|
* router.add(createMCPRoutes(createMCPLegacy(mcp))) // answers `initialize` too; pass `mcp` alone for modern-only
|
|
329
331
|
* ```
|
|
330
332
|
*/
|
|
331
|
-
export declare function createMCPSession<TState extends MCPSessionState>(options?:
|
|
332
|
-
|
|
333
|
-
/**
|
|
334
|
-
* Creates a readable stream from its pull and cancellation behaviours.
|
|
335
|
-
*
|
|
336
|
-
* @param pull - The behaviour that supplies the stream's next chunk
|
|
337
|
-
* @param cancel - The behaviour that releases the stream after consumer cancellation
|
|
338
|
-
* @returns A readable stream backed by the supplied behaviours
|
|
339
|
-
*/
|
|
340
|
-
export declare function createReadableStream<T>(pull: (controller: ReadableStreamDefaultController<T>) => void | PromiseLike<void>, cancel: (reason?: unknown) => void | PromiseLike<void>): ReadableStream<T>;
|
|
333
|
+
export declare function createMCPSession<TState extends MCPSessionState>(options?: MCPSessionMiddlewareOptions): MiddlewareHandler<TState>;
|
|
341
334
|
|
|
342
335
|
/**
|
|
343
336
|
* Creates the stdio CLIENT transport for an {@link import('@orkestrel/mcp').MCPClientInterface}
|
|
@@ -387,7 +380,7 @@ export declare function createStdioClientTransport(options: StdioClientTransport
|
|
|
387
380
|
* Wraps `options.input` (default `process.stdin`) / `options.output` (default
|
|
388
381
|
* `process.stdout`) in a {@link import('./transports/StdioServerTransport.js').StdioServerTransport}
|
|
389
382
|
* and pipes it through the core {@link import('@orkestrel/mcp').MCPTransportInterface} port
|
|
390
|
-
* through {@link
|
|
383
|
+
* through {@link createDuplexServerTransport} + {@link
|
|
391
384
|
* import('@orkestrel/mcp').bindServer}: each inbound REQUEST runs through `mcp.dispatch`, and
|
|
392
385
|
* a defined response is written back as a newline-terminated line — a NOTIFICATION
|
|
393
386
|
* writes nothing, and a non-request message is ignored. A `dispatch` / `send` fault
|
|
@@ -397,7 +390,8 @@ export declare function createStdioClientTransport(options: StdioClientTransport
|
|
|
397
390
|
* @param mcp - The transport-agnostic {@link MCPDispatcherInterface} to expose over stdio
|
|
398
391
|
* @param options - Optional injectable `input` / `output` streams; see
|
|
399
392
|
* {@link StdioServerOptions}
|
|
400
|
-
* @returns A
|
|
393
|
+
* @returns A {@link StdioServerInterface} handle to arm / tear down the pump; `stop()` ends
|
|
394
|
+
* that handle's lifetime permanently
|
|
401
395
|
*
|
|
402
396
|
* @example
|
|
403
397
|
* ```ts
|
|
@@ -410,14 +404,11 @@ export declare function createStdioClientTransport(options: StdioClientTransport
|
|
|
410
404
|
* createStdioServer(createMCPLegacy(mcp)).start() // answers `initialize` too; pass `mcp` alone for modern-only
|
|
411
405
|
* ```
|
|
412
406
|
*/
|
|
413
|
-
export declare function createStdioServer(mcp: MCPDispatcherInterface, options?: StdioServerOptions):
|
|
414
|
-
start(): void;
|
|
415
|
-
stop(): void;
|
|
416
|
-
};
|
|
407
|
+
export declare function createStdioServer(mcp: MCPDispatcherInterface, options?: StdioServerOptions): StdioServerInterface;
|
|
417
408
|
|
|
418
409
|
/**
|
|
419
410
|
* Creates the WebSocket CLIENT transport for an {@link import('@orkestrel/mcp').MCPClientInterface}
|
|
420
|
-
* — a {@link
|
|
411
|
+
* — a {@link MCPMessageTransportInterface} that drives a REMOTE MCP server over a WebSocket. The
|
|
421
412
|
* egress mirror of {@link createWebSocketServer} and the WebSocket sibling of {@link
|
|
422
413
|
* createHTTPClientTransport}.
|
|
423
414
|
*
|
|
@@ -433,7 +424,7 @@ export declare function createStdioServer(mcp: MCPDispatcherInterface, options?:
|
|
|
433
424
|
*
|
|
434
425
|
* @param options - `url` (the remote WebSocket endpoint; REQUIRED) and optional `headers`
|
|
435
426
|
* merged onto the upgrade request; see {@link WebSocketClientTransportOptions}
|
|
436
|
-
* @returns A working {@link
|
|
427
|
+
* @returns A working {@link MCPMessageTransportInterface} over a WebSocket
|
|
437
428
|
*
|
|
438
429
|
* @example
|
|
439
430
|
* ```ts
|
|
@@ -447,7 +438,7 @@ export declare function createStdioServer(mcp: MCPDispatcherInterface, options?:
|
|
|
447
438
|
* const tools = await client.tools()
|
|
448
439
|
* ```
|
|
449
440
|
*/
|
|
450
|
-
export declare function createWebSocketClientTransport(options: WebSocketClientTransportOptions):
|
|
441
|
+
export declare function createWebSocketClientTransport(options: WebSocketClientTransportOptions): MCPMessageTransportInterface;
|
|
451
442
|
|
|
452
443
|
/**
|
|
453
444
|
* Creates the MCP WebSocket transport INGRESS — an {@link UpgradeHandler} that exposes a
|
|
@@ -469,7 +460,7 @@ export declare function createWebSocketClientTransport(options: WebSocketClientT
|
|
|
469
460
|
* only when the client's offer contains it, and sends UNMASKED frames), wraps it in a
|
|
470
461
|
* {@link WebSocketServerTransport}, and pipes it through the core {@link
|
|
471
462
|
* import('@orkestrel/mcp').MCPTransportInterface} port through {@link
|
|
472
|
-
*
|
|
463
|
+
* createDuplexServerTransport} + {@link import('@orkestrel/mcp').bindServer}:
|
|
473
464
|
* each inbound REQUEST runs through `mcp.dispatch`, and a defined response is written back
|
|
474
465
|
* as a frame — a NOTIFICATION sends nothing, and a non-request message (a stray response) is
|
|
475
466
|
* ignored. A `dispatch` / `send` fault surfaces on `mcp.emitter`'s `error` event rather than
|
|
@@ -506,21 +497,7 @@ export declare function createWebSocketClientTransport(options: WebSocketClientT
|
|
|
506
497
|
export declare function createWebSocketServer(mcp: MCPDispatcherInterface, options: WebSocketServerOptions): UpgradeHandler;
|
|
507
498
|
|
|
508
499
|
/**
|
|
509
|
-
*
|
|
510
|
-
* when it is not one — the per-event step {@link readEventStream} folds over.
|
|
511
|
-
*
|
|
512
|
-
* @remarks
|
|
513
|
-
* `JSON.parse`s the `data` (the server serializes the JSON-RPC envelope as the event's
|
|
514
|
-
* `data`) inside a try/catch and narrows the parsed value with `parseJSONRPCMessage`.
|
|
515
|
-
* Total: malformed JSON or a non-message value yields `undefined`, never throws.
|
|
516
|
-
*
|
|
517
|
-
* @param data - One SSE event's `data` payload
|
|
518
|
-
* @returns The decoded {@link JSONRPCMessage}, or `undefined`
|
|
519
|
-
*/
|
|
520
|
-
export declare function decodeEvent(data: string): JSONRPCMessage | undefined;
|
|
521
|
-
|
|
522
|
-
/**
|
|
523
|
-
* The default bound in milliseconds on one unconfirmed write to a stdio client transport's
|
|
500
|
+
* Sets the default bound in milliseconds on one unconfirmed write to a stdio client transport's
|
|
524
501
|
* child `stdin` — the `delivery` a `createStdioClientTransport` caller who supplies none gets.
|
|
525
502
|
*
|
|
526
503
|
* @remarks
|
|
@@ -533,7 +510,7 @@ export declare function decodeEvent(data: string): JSONRPCMessage | undefined;
|
|
|
533
510
|
export declare const DEFAULT_MCP_DELIVERY = 10000;
|
|
534
511
|
|
|
535
512
|
/**
|
|
536
|
-
*
|
|
513
|
+
* Sets the default interval in milliseconds between SSE keepalive comments on held-open MCP
|
|
537
514
|
* responses.
|
|
538
515
|
*
|
|
539
516
|
* @remarks
|
|
@@ -542,24 +519,24 @@ export declare const DEFAULT_MCP_DELIVERY = 10000;
|
|
|
542
519
|
*/
|
|
543
520
|
export declare const DEFAULT_MCP_KEEPALIVE_INTERVAL = 15000;
|
|
544
521
|
|
|
545
|
-
/**
|
|
522
|
+
/** Names the default request path `createMCPRoutes` mounts the transport's `POST` route at. */
|
|
546
523
|
export declare const DEFAULT_MCP_PATH = "/mcp";
|
|
547
524
|
|
|
548
525
|
/**
|
|
549
|
-
*
|
|
526
|
+
* Sets the default capacity of a session's FOLDED resumable event log (the per-{@link
|
|
550
527
|
* import('./MCPSession.js').MCPSession} replay log) — the maximum number of pushed
|
|
551
528
|
* server→client messages retained for replay before the OLDEST is evicted.
|
|
552
529
|
*
|
|
553
530
|
* @remarks
|
|
554
531
|
* Bounds the replay log's memory: only the most-recent {@link DEFAULT_MCP_SESSION_CAPACITY}
|
|
555
532
|
* pushes are retained, so a client reconnecting with a `Last-Event-ID` older than that window
|
|
556
|
-
* replays nothing (its cursor fell off the back). Override
|
|
557
|
-
* for a deeper / shallower window.
|
|
533
|
+
* replays nothing (its cursor fell off the back). Override through the `session` group of
|
|
534
|
+
* `createMCPSession`'s options (`session.capacity`) for a deeper / shallower window.
|
|
558
535
|
*/
|
|
559
536
|
export declare const DEFAULT_MCP_SESSION_CAPACITY = 1024;
|
|
560
537
|
|
|
561
538
|
/**
|
|
562
|
-
*
|
|
539
|
+
* Sets the default per-event idle lifetime (ms) of a session's folded resumable event log — an
|
|
563
540
|
* entry older than this is lazily evicted on the next access (no background timer), bounding
|
|
564
541
|
* how far back a reconnecting client may replay.
|
|
565
542
|
*
|
|
@@ -572,41 +549,22 @@ export declare const DEFAULT_MCP_SESSION_TTL = 300000;
|
|
|
572
549
|
|
|
573
550
|
/**
|
|
574
551
|
* Decodes and delivers each complete newline-framed line onto a {@link
|
|
575
|
-
*
|
|
552
|
+
* MCPMessageTransportEventMap} emitter — the shared per-chunk dispatch step both stdio
|
|
576
553
|
* transports run their framed lines through: the server transport frames with {@link
|
|
577
554
|
* extractLines}, the client transport takes its lines from the process supervisor.
|
|
578
555
|
*
|
|
579
556
|
* @remarks
|
|
580
|
-
* A blank line is skipped (a stray trailing newline). Every other line
|
|
581
|
-
*
|
|
582
|
-
* well-formed {@link JSONRPCMessage} emits `message`,
|
|
583
|
-
*
|
|
584
|
-
*
|
|
557
|
+
* A blank line is skipped (a stray trailing newline). Every other line runs through the
|
|
558
|
+
* shared {@link import('@orkestrel/mcp').deliverMessage} fold, the one inbound decode every
|
|
559
|
+
* transport in this package shares: a well-formed {@link JSONRPCMessage} emits `message`,
|
|
560
|
+
* unparsable text emits the caught parse error, and a well-formed non-message line emits
|
|
561
|
+
* `error` naming a non-JSON-RPC stdio line (total, never throws). Pure w.r.t. its own state
|
|
562
|
+
* — the emit is the caller-owned side effect.
|
|
585
563
|
*
|
|
586
564
|
* @param emitter - The transport's {@link EmitterInterface} to emit `message` / `error` onto
|
|
587
565
|
* @param lines - The complete lines to decode and deliver
|
|
588
566
|
*/
|
|
589
|
-
export declare function dispatchLines(emitter: EmitterInterface<
|
|
590
|
-
|
|
591
|
-
/**
|
|
592
|
-
* One entry of an {@link MCPSessionInterface}'s folded replay log — a single pushed {@link
|
|
593
|
-
* JSONRPCMessage} tagged with the monotone event `id` the session assigned and the `timestamp`
|
|
594
|
-
* it was appended at (for the lazy-TTL replay window).
|
|
595
|
-
*
|
|
596
|
-
* @remarks
|
|
597
|
-
* - `id` — the session-assigned, monotonically-increasing event id (a base36 string), the
|
|
598
|
-
* value a resumable client echoes back as its `Last-Event-ID` to replay from here.
|
|
599
|
-
* - `message` — the server→client {@link JSONRPCMessage} that was pushed.
|
|
600
|
-
* - `timestamp` — the epoch-ms instant the entry was appended, read by the TTL eviction.
|
|
601
|
-
*
|
|
602
|
-
* A plain value record (no behavior) — the unit {@link MCPSessionInterface.replay}
|
|
603
|
-
* returns.
|
|
604
|
-
*/
|
|
605
|
-
export declare interface EventStoreEntry {
|
|
606
|
-
readonly id: string;
|
|
607
|
-
readonly message: JSONRPCMessage;
|
|
608
|
-
readonly timestamp: number;
|
|
609
|
-
}
|
|
567
|
+
export declare function dispatchLines(emitter: EmitterInterface<MCPMessageTransportEventMap>, lines: readonly string[]): void;
|
|
610
568
|
|
|
611
569
|
/**
|
|
612
570
|
* Folds one more chunk of raw stdio bytes into a newline-framed buffer — the shared
|
|
@@ -627,97 +585,6 @@ export declare interface EventStoreEntry {
|
|
|
627
585
|
*/
|
|
628
586
|
export declare function extractLines(buffer: string, chunk: string): LineExtraction;
|
|
629
587
|
|
|
630
|
-
/**
|
|
631
|
-
* The HTTP CLIENT transport for the Model Context Protocol — a
|
|
632
|
-
* {@link MCPClientTransportInterface} that drives a REMOTE Streamable-HTTP MCP server over
|
|
633
|
-
* `fetch`, the egress mirror of the server's `createMCPRoutes`.
|
|
634
|
-
*
|
|
635
|
-
* @remarks
|
|
636
|
-
* - **Request/response over `fetch`.** `send(message)` POSTs the JSON-serialized
|
|
637
|
-
* message to `options.url` with `content-type: application/json` and an
|
|
638
|
-
* `Accept` of BOTH `application/json` and `text/event-stream` (so the server may
|
|
639
|
-
* answer with either framing) — plus any `options.headers` (for example, an `Authorization`
|
|
640
|
-
* bearer). It then decodes the reply and emits each decoded {@link JSONRPCMessage} on
|
|
641
|
-
* the `message` event the {@link import('@orkestrel/mcp').MCPClientInterface} subscribes
|
|
642
|
-
* to.
|
|
643
|
-
* - **Both reply framings.** A `200` with an `application/json` body is parsed with
|
|
644
|
-
* `parseJSONRPCMessage`; a `200` with a `text/event-stream` body is decoded with the
|
|
645
|
-
* `@orkestrel/sse` {@link import('@orkestrel/sse').SSEParserInterface} ({@link
|
|
646
|
-
* readEventStream}) — the inverse of the server's `openStream` seam, so the wire
|
|
647
|
-
* round-trips. A `202`
|
|
648
|
-
* Accepted (a notification) carries no body and emits nothing.
|
|
649
|
-
* - **Session and protocol headers.** `start()` is a no-op (a
|
|
650
|
-
* request/response transport opens no long-lived connection). The
|
|
651
|
-
* `mcp-session-id` response header, when a STATEFUL server sends one (on
|
|
652
|
-
* `initialize`), is captured into `session` and then ECHOED as the
|
|
653
|
-
* `mcp-session-id` request header on every SUBSEQUENT request — so an
|
|
654
|
-
* `MCPClient` passes a stateful server's session validation. The
|
|
655
|
-
* initialize result's `protocolVersion` is likewise captured, but only
|
|
656
|
-
* when it is a SUPPORTED value, and echoed as `mcp-protocol-version` alone on
|
|
657
|
-
* subsequent legacy requests. Modern requests instead derive protocol and method
|
|
658
|
-
* headers from the message, plus the name header only for `tools/call`.
|
|
659
|
-
* Before initialize returns, neither captured legacy header is sent.
|
|
660
|
-
* `close()` clears the captured protocol so a reconnect's `initialize`
|
|
661
|
-
* POST is headerless; the captured `session` persists across `close()`.
|
|
662
|
-
* - **`close()` releases what is in flight.** Every `fetch` this transport still has open is
|
|
663
|
-
* ABORTED, which cancels the response body a `send` is reading — an SSE reply the server
|
|
664
|
-
* never ends would otherwise outlive the transport, with nothing left able to reach it. The
|
|
665
|
-
* aborted read surfaces on `error` and the `send` reporting it resolves. `close()` is
|
|
666
|
-
* idempotent (one `close` event per connected lifetime), and `start()` opens the next one.
|
|
667
|
-
* - **Total at the boundary.** Every reply is narrowed (`parseJSONRPCMessage`,
|
|
668
|
-
* the SSE decoder). A non-message success reply is dropped, never asserted. A non-success
|
|
669
|
-
* reply that carries no valid JSON-RPC message rejects `send` with its HTTP status and body
|
|
670
|
-
* shape. A valid JSON-RPC error body is emitted at any HTTP status. A `fetch` / decode failure
|
|
671
|
-
* on a success response surfaces on the `error` event rather than escaping `send`.
|
|
672
|
-
* - **Observable.** Owns the `emitter` ({@link MCPClientTransportEventMap}); fires
|
|
673
|
-
* `message` per decoded reply, `error` on a fault, and `close` on `close()`.
|
|
674
|
-
*
|
|
675
|
-
* @example
|
|
676
|
-
* ```ts
|
|
677
|
-
* const transport = new HTTPClientTransport({ url: 'http://localhost:3000/mcp' })
|
|
678
|
-
* const client = new MCPClient({ transport })
|
|
679
|
-
* await client.connect()
|
|
680
|
-
* ```
|
|
681
|
-
*/
|
|
682
|
-
export declare class HTTPClientTransport implements MCPClientTransportInterface_2 {
|
|
683
|
-
#private;
|
|
684
|
-
constructor(options: HTTPClientTransportOptions);
|
|
685
|
-
get emitter(): EmitterInterface<MCPClientTransportEventMap_2>;
|
|
686
|
-
get session(): string | undefined;
|
|
687
|
-
get duplex(): boolean;
|
|
688
|
-
start(): Promise<void>;
|
|
689
|
-
send(message: JSONRPCMessage_2): Promise<void>;
|
|
690
|
-
close(): Promise<void>;
|
|
691
|
-
}
|
|
692
|
-
|
|
693
|
-
/**
|
|
694
|
-
* Options for `createHTTPClientTransport` — the remote MCP server's URL and any extra
|
|
695
|
-
* request headers.
|
|
696
|
-
*
|
|
697
|
-
* @remarks
|
|
698
|
-
* - `url` — the absolute URL of the remote server's Streamable-HTTP endpoint (the
|
|
699
|
-
* `POST` target every JSON-RPC message is written to, for example,
|
|
700
|
-
* `http://localhost:3000/mcp`). REQUIRED.
|
|
701
|
-
* - `headers` — extra request headers merged onto every `POST` (for example, an
|
|
702
|
-
* `Authorization` bearer for a guarded server). The transport always sets
|
|
703
|
-
* `content-type: application/json` and an `Accept` of both `application/json` and
|
|
704
|
-
* `text/event-stream` (so the server may answer with either framing); a key supplied
|
|
705
|
-
* here is merged on top.
|
|
706
|
-
* - `fetch` — the `fetch` implementation to issue each `POST` with; defaults to
|
|
707
|
-
* `globalThis.fetch`. Injectable for a test double or a non-global `fetch`.
|
|
708
|
-
* - `timeout` — an optional per-request timeout in milliseconds; when set, each
|
|
709
|
-
* `fetch` call composes that deadline with the transport's own close through
|
|
710
|
-
* `AbortSignal.any([close, AbortSignal.timeout(timeout)])`, so whichever fires first
|
|
711
|
-
* ends the request. Omit for no transport-level deadline; the close signal is passed
|
|
712
|
-
* either way.
|
|
713
|
-
*/
|
|
714
|
-
export declare interface HTTPClientTransportOptions {
|
|
715
|
-
readonly url: string;
|
|
716
|
-
readonly headers?: Readonly<Record<string, string>>;
|
|
717
|
-
readonly fetch?: typeof fetch;
|
|
718
|
-
readonly timeout?: number;
|
|
719
|
-
}
|
|
720
|
-
|
|
721
588
|
/**
|
|
722
589
|
* Composes one incoming HTTP request lifetime with one MCP-owned SSE response lifetime.
|
|
723
590
|
*
|
|
@@ -746,10 +613,10 @@ export declare interface HTTPClientTransportOptions {
|
|
|
746
613
|
* @example
|
|
747
614
|
* ```ts
|
|
748
615
|
* import { HTTPDisconnect } from '@orkestrel/mcp/server'
|
|
749
|
-
* import {
|
|
616
|
+
* import { createStream } from '@orkestrel/server'
|
|
750
617
|
*
|
|
751
618
|
* const disconnect = new HTTPDisconnect(request.signal, { interval: 15_000 })
|
|
752
|
-
* const stream =
|
|
619
|
+
* const stream = createStream()
|
|
753
620
|
* const response = disconnect.bridge(stream)
|
|
754
621
|
* ```
|
|
755
622
|
*/
|
|
@@ -764,8 +631,8 @@ export declare class HTTPDisconnect {
|
|
|
764
631
|
*/
|
|
765
632
|
constructor(signal: AbortSignal, options?: MCPKeepaliveOptions);
|
|
766
633
|
/**
|
|
767
|
-
*
|
|
768
|
-
* its graceful completion.
|
|
634
|
+
* Returns the signal aborted by the incoming request, or by any end of this response that
|
|
635
|
+
* is not its graceful completion.
|
|
769
636
|
*
|
|
770
637
|
* @returns The composed lifecycle signal
|
|
771
638
|
*/
|
|
@@ -811,7 +678,7 @@ export declare class HTTPDisconnect {
|
|
|
811
678
|
*/
|
|
812
679
|
export declare interface HTTPHandlerOptions<TState = unknown> {
|
|
813
680
|
readonly streaming?: boolean;
|
|
814
|
-
/**
|
|
681
|
+
/** Requires the session layer's value; `origins` is ignored when `enabled` is `false`. */
|
|
815
682
|
readonly origin?: MCPOriginOptions;
|
|
816
683
|
readonly keepalive?: MCPKeepaliveOptions;
|
|
817
684
|
readonly caller?: MCPCallerHandler<TState>;
|
|
@@ -834,16 +701,23 @@ export declare interface HTTPTransportOptions<TState = unknown> extends HTTPHand
|
|
|
834
701
|
}
|
|
835
702
|
|
|
836
703
|
/**
|
|
837
|
-
* Infers the first required MCP HTTP header
|
|
704
|
+
* Infers the first required MCP HTTP header a request's own body contradicts.
|
|
838
705
|
*
|
|
839
706
|
* @remarks
|
|
840
|
-
* A modern request derives its protocol, method, and
|
|
841
|
-
* the
|
|
842
|
-
*
|
|
843
|
-
*
|
|
707
|
+
* A modern request derives its protocol, method, and name expectations from the JSON-RPC body,
|
|
708
|
+
* the name expectation scoped to the methods {@link inferHeaderTarget} reads a target for. A
|
|
709
|
+
* name header carrying the Base64 sentinel is decoded through
|
|
710
|
+
* {@link import('@orkestrel/mcp').decodeSentinel} before the comparison, so a peer that had
|
|
711
|
+
* to encode its value still matches; a sentinel whose payload is invalid decodes to nothing
|
|
712
|
+
* and therefore mismatches, which is how an invalid header value is refused. A legacy request
|
|
713
|
+
* body requires a protocol header after initialization. Messages name the expected value but
|
|
714
|
+
* never echo the client-supplied one.
|
|
715
|
+
*
|
|
716
|
+
* The expectation a LIVE SESSION supplies is a different rule over a different input, so it
|
|
717
|
+
* is {@link inferSessionHeaderIssue} rather than a second arm of this one.
|
|
844
718
|
*
|
|
845
719
|
* @param request - The HTTP request carrying the headers
|
|
846
|
-
* @param
|
|
720
|
+
* @param invocation - The parsed invocation body the expectations are derived from
|
|
847
721
|
* @returns The first header issue, or `undefined` when the applicable headers agree
|
|
848
722
|
*
|
|
849
723
|
* @example
|
|
@@ -852,7 +726,32 @@ export declare interface HTTPTransportOptions<TState = unknown> extends HTTPHand
|
|
|
852
726
|
* issue?.header // 'Mcp-Method' when that field is absent or mismatched
|
|
853
727
|
* ```
|
|
854
728
|
*/
|
|
855
|
-
export declare function inferHeaderIssue(request: Request,
|
|
729
|
+
export declare function inferHeaderIssue(request: Request, invocation: JSONRPCInvocation): MCPHeaderIssue | undefined;
|
|
730
|
+
|
|
731
|
+
/**
|
|
732
|
+
* Infers the target one modern request's `Mcp-Name` header must carry.
|
|
733
|
+
*
|
|
734
|
+
* @remarks
|
|
735
|
+
* The protocol scopes the header to the methods whose body carries a name-shaped field, and
|
|
736
|
+
* names the field per method: `tools/call` and `prompts/get` carry `params.name`, and
|
|
737
|
+
* `resources/read` carries `params.uri`. Every other method — `server/discover`, `tools/list`,
|
|
738
|
+
* `resources/list`, `prompts/list` — has nothing to derive a target from, so the header is not
|
|
739
|
+
* required there and a peer that sent one anyway is not held to it.
|
|
740
|
+
*
|
|
741
|
+
* A method within the scope whose named member is absent or is not a string reads as no
|
|
742
|
+
* target. There is nothing for a header to match, and refusing the request over a body member
|
|
743
|
+
* the header rule does not own would report a parameter fault as a header fault. Total.
|
|
744
|
+
*
|
|
745
|
+
* @param request - The parsed modern invocation to read the target from
|
|
746
|
+
* @returns The target the header must carry, or `undefined` when the method carries none
|
|
747
|
+
*
|
|
748
|
+
* @example
|
|
749
|
+
* ```ts
|
|
750
|
+
* inferHeaderTarget({ jsonrpc: '2.0', id: 1, method: 'resources/read', params: { uri: 'file:///a' } })
|
|
751
|
+
* // → 'file:///a'
|
|
752
|
+
* ```
|
|
753
|
+
*/
|
|
754
|
+
export declare function inferHeaderTarget(request: JSONRPCInvocation): string | undefined;
|
|
856
755
|
|
|
857
756
|
/**
|
|
858
757
|
* Infers the legacy revision an `initialize` request negotiates.
|
|
@@ -872,6 +771,64 @@ export declare function inferHeaderIssue(request: Request, reference: JSONRPCInv
|
|
|
872
771
|
*/
|
|
873
772
|
export declare function inferLegacyVersion(request: JSONRPCInvocation): MCPLegacyVersion;
|
|
874
773
|
|
|
774
|
+
/**
|
|
775
|
+
* Infers the refusal one `tools/call` earns for a `Mcp-Param-*` header the body contradicts.
|
|
776
|
+
*
|
|
777
|
+
* @remarks
|
|
778
|
+
* The custom-header half of the standard-header seam {@link inferHeaderIssue} owns, and it
|
|
779
|
+
* takes the SERVED definition's projections rather than a header issue: SEP-2243 scopes the
|
|
780
|
+
* rule to the `Mcp-Param-*` names the server's OWN tool definitions annotate, so a name no
|
|
781
|
+
* parameter claims is another party's header and travels through untouched.
|
|
782
|
+
*
|
|
783
|
+
* For each recognized parameter the body's value at the parameter's own property path fixes
|
|
784
|
+
* the expectation. A value the call omits or supplies as `null` requires no header, and a
|
|
785
|
+
* header sent anyway is refused because it asserts something the body never said. A value the
|
|
786
|
+
* call does supply requires its header: an absent one, a Base64 sentinel whose payload is
|
|
787
|
+
* invalid, and a decoded value that disagrees are each refused. An `integer` parameter
|
|
788
|
+
* compares numerically, so a peer that padded its decimal still matches. A supplied value
|
|
789
|
+
* whose runtime shape contradicts the declared type is left alone — the tool's own argument
|
|
790
|
+
* validation owns that disagreement, and refusing it here would report an argument fault as a
|
|
791
|
+
* header fault.
|
|
792
|
+
*
|
|
793
|
+
* Messages name the field and the body path the expectation came from, and never echo the
|
|
794
|
+
* value the peer supplied.
|
|
795
|
+
*
|
|
796
|
+
* @param request - The HTTP request carrying the headers
|
|
797
|
+
* @param parameters - The projections the served tool definition declares
|
|
798
|
+
* @param values - The call's `arguments` record
|
|
799
|
+
* @returns The refusal message for the first disagreeing parameter, or `undefined`
|
|
800
|
+
*
|
|
801
|
+
* @example
|
|
802
|
+
* ```ts
|
|
803
|
+
* inferParameterRefusal(request, [{ name: 'Region', path: ['region'], primitive: 'string' }], {})
|
|
804
|
+
* // → undefined when the request carries no `Mcp-Param-Region` either
|
|
805
|
+
* ```
|
|
806
|
+
*/
|
|
807
|
+
export declare function inferParameterRefusal(request: Request, parameters: readonly MCPHeaderParameter[], values: unknown): string | undefined;
|
|
808
|
+
|
|
809
|
+
/**
|
|
810
|
+
* Infers the protocol header issue an active legacy session's pinned revision diagnoses.
|
|
811
|
+
*
|
|
812
|
+
* @remarks
|
|
813
|
+
* The session layer's rule, distinct from the body-derived one {@link inferHeaderIssue} owns:
|
|
814
|
+
* a live legacy session pinned its revision at `initialize`, so every later request on that
|
|
815
|
+
* session must name the same one. An absent header reads as `missing`, which the session
|
|
816
|
+
* middleware answers by SUPPLYING the pinned revision rather than refusing; a present header
|
|
817
|
+
* naming another revision reads as `mismatched` and is refused. The message names the session's
|
|
818
|
+
* revision and never echoes the client-supplied value.
|
|
819
|
+
*
|
|
820
|
+
* @param request - The HTTP request carrying the headers
|
|
821
|
+
* @param version - The legacy revision the active session pinned at `initialize`
|
|
822
|
+
* @returns The protocol header issue, or `undefined` when the header agrees
|
|
823
|
+
*
|
|
824
|
+
* @example
|
|
825
|
+
* ```ts
|
|
826
|
+
* const issue = inferSessionHeaderIssue(request, '2025-06-18')
|
|
827
|
+
* issue?.reason // 'missing' when the request carries no protocol header
|
|
828
|
+
* ```
|
|
829
|
+
*/
|
|
830
|
+
export declare function inferSessionHeaderIssue(request: Request, version: MCPVersion): MCPHeaderIssue | undefined;
|
|
831
|
+
|
|
875
832
|
/**
|
|
876
833
|
* Infers the HTTP status for one MCP dispatch outcome without changing its JSON-RPC body.
|
|
877
834
|
*
|
|
@@ -888,7 +845,7 @@ export declare function inferLegacyVersion(request: JSONRPCInvocation): MCPLegac
|
|
|
888
845
|
export declare function inferStatus(response: JSONRPCResponse | undefined, era: MCPEra): number;
|
|
889
846
|
|
|
890
847
|
/**
|
|
891
|
-
*
|
|
848
|
+
* Represents the result of folding one more chunk of raw stdio bytes into a newline-framed
|
|
892
849
|
* buffer — every COMPLETE line extracted (newline-terminated in the wire bytes) plus
|
|
893
850
|
* the trailing partial line carried forward as the new `remainder`.
|
|
894
851
|
*
|
|
@@ -901,44 +858,6 @@ export declare interface LineExtraction {
|
|
|
901
858
|
readonly remainder: string;
|
|
902
859
|
}
|
|
903
860
|
|
|
904
|
-
/** The modern Streamable-HTTP request header carrying the JSON-RPC method name. */
|
|
905
|
-
export declare const MCP_METHOD_HEADER = "mcp-method";
|
|
906
|
-
|
|
907
|
-
/** The modern Streamable-HTTP request header carrying a named method's target. */
|
|
908
|
-
export declare const MCP_NAME_HEADER = "mcp-name";
|
|
909
|
-
|
|
910
|
-
/**
|
|
911
|
-
* The Streamable-HTTP transport header carrying the negotiated MCP protocol version
|
|
912
|
-
* on every post-initialize client request.
|
|
913
|
-
*
|
|
914
|
-
* @remarks
|
|
915
|
-
* Required by MCP 2025-06-18 after initialization. Both HTTP client transports
|
|
916
|
-
* capture the initialize result's `protocolVersion` and send it on subsequent
|
|
917
|
-
* requests; `createMCPRoutes` rejects a present unsupported value before dispatch.
|
|
918
|
-
*/
|
|
919
|
-
export declare const MCP_PROTOCOL_VERSION_HEADER = "mcp-protocol-version";
|
|
920
|
-
|
|
921
|
-
/**
|
|
922
|
-
* The Streamable-HTTP transport header that carries the MCP session id. When a {@link
|
|
923
|
-
* import('./middlewares.js').createMCPSession} middleware is mounted, it SETS this header on
|
|
924
|
-
* the `initialize` response (the minted id) and READS it on every subsequent request
|
|
925
|
-
* (validating the session); the stateless `createMCPRoutes` default neither sets nor reads it.
|
|
926
|
-
*/
|
|
927
|
-
export declare const MCP_SESSION_HEADER = "mcp-session-id";
|
|
928
|
-
|
|
929
|
-
/**
|
|
930
|
-
* The WebSocket subprotocol the MCP-over-WebSocket transports negotiate — sent by the
|
|
931
|
-
* client in `Sec-WebSocket-Protocol`, echoed by the server in its `101` handshake.
|
|
932
|
-
*
|
|
933
|
-
* @remarks
|
|
934
|
-
* `createWebSocketServer` echoes it in the upgrade response and `createWebSocketClientTransport`
|
|
935
|
-
* requests it, so an MCP WebSocket endpoint is distinguishable from any other WebSocket on the
|
|
936
|
-
* same path. The default WebSocket upgrade path is {@link DEFAULT_MCP_PATH} (the same `'/mcp'`
|
|
937
|
-
* the HTTP transport mounts at) — the upgrade is selected by the `Upgrade: websocket` header,
|
|
938
|
-
* not a separate path.
|
|
939
|
-
*/
|
|
940
|
-
export declare const MCP_WEBSOCKET_SUBPROTOCOL = "mcp";
|
|
941
|
-
|
|
942
861
|
/**
|
|
943
862
|
* Extracts consumer-asserted caller context synchronously from an HTTP request after the
|
|
944
863
|
* transport has validated it for dispatch.
|
|
@@ -957,7 +876,8 @@ export declare const MCP_WEBSOCKET_SUBPROTOCOL = "mcp";
|
|
|
957
876
|
export declare type MCPCallerHandler<TState = unknown> = (request: Request, context: RouteContext<string, TState> | undefined) => unknown;
|
|
958
877
|
|
|
959
878
|
/**
|
|
960
|
-
*
|
|
879
|
+
* Reports one required MCP HTTP header that is absent or disagrees with its server-derived
|
|
880
|
+
* value.
|
|
961
881
|
*
|
|
962
882
|
* @remarks
|
|
963
883
|
* - `header` — the canonical HTTP field name safe to show to an integrator.
|
|
@@ -972,7 +892,7 @@ export declare interface MCPHeaderIssue {
|
|
|
972
892
|
}
|
|
973
893
|
|
|
974
894
|
/**
|
|
975
|
-
*
|
|
895
|
+
* Configures the shared SSE keepalive for held-open HTTP responses.
|
|
976
896
|
*
|
|
977
897
|
* @remarks
|
|
978
898
|
* - `interval` — milliseconds between SSE comment frames. Defaults to {@link
|
|
@@ -985,7 +905,7 @@ export declare interface MCPKeepaliveOptions {
|
|
|
985
905
|
}
|
|
986
906
|
|
|
987
907
|
/**
|
|
988
|
-
*
|
|
908
|
+
* Configures the protocol-required HTTP `Origin` validation shared by the route and session
|
|
989
909
|
* enforcement sites.
|
|
990
910
|
*
|
|
991
911
|
* @remarks
|
|
@@ -1001,12 +921,12 @@ export declare interface MCPOriginOptions {
|
|
|
1001
921
|
}
|
|
1002
922
|
|
|
1003
923
|
/**
|
|
1004
|
-
*
|
|
924
|
+
* Represents one MCP transport session — the per-session entity a {@link
|
|
1005
925
|
* import('./middlewares.js').createMCPSession} middleware owns, keyed by its `id`, carrying the
|
|
1006
926
|
* resumable server→client push channel with its bounded replay log FOLDED IN.
|
|
1007
927
|
*
|
|
1008
928
|
* @remarks
|
|
1009
|
-
*
|
|
929
|
+
* One entity carries the whole session: it holds the
|
|
1010
930
|
* session `id`, its OWN bounded, replayable log of pushed server→client messages (the
|
|
1011
931
|
* resumable GET-SSE channel — a private `#events` `Map` + a monotone `#counter`, with
|
|
1012
932
|
* `capacity` / `ttl` eviction, not a separate store), and the set of open
|
|
@@ -1018,7 +938,7 @@ export declare interface MCPOriginOptions {
|
|
|
1018
938
|
* SSE event (`stream.write({ id, data })`). A push with NO attached stream is still logged,
|
|
1019
939
|
* so a client that connects (or reconnects with a `Last-Event-ID`) LATER replays it from the
|
|
1020
940
|
* log. A `write` to a closed stream is a safe no-op (the {@link
|
|
1021
|
-
* `@orkestrel/server`'s `
|
|
941
|
+
* `@orkestrel/server`'s `createStream` contract), so a just-disconnected stream that
|
|
1022
942
|
* has not yet been `detach`ed never throws. A replayed event and the live one carry the
|
|
1023
943
|
* IDENTICAL id (the log assigns it once).
|
|
1024
944
|
*
|
|
@@ -1043,9 +963,10 @@ export declare interface MCPOriginOptions {
|
|
|
1043
963
|
* The middleware opens the stream (the spine seam) and registers it here; this class only
|
|
1044
964
|
* serializes a message onto the already-open streams.
|
|
1045
965
|
*
|
|
1046
|
-
* - **Injected clock.**
|
|
1047
|
-
* `Date.now
|
|
1048
|
-
* timer
|
|
966
|
+
* - **Injected clock.** {@link import('./types.js').MCPSessionOptions.clock} supplies the
|
|
967
|
+
* epoch-ms clock the lazy TTL sweep reads, defaulting to `Date.now` — so a test drives TTL
|
|
968
|
+
* eviction with an elapsed clock rather than a real timer, and the middleware that mints a
|
|
969
|
+
* session hands its own clock down instead of leaving the log on wall-clock time.
|
|
1049
970
|
*
|
|
1050
971
|
* @example
|
|
1051
972
|
* ```ts
|
|
@@ -1061,15 +982,15 @@ export declare class MCPSession implements MCPSessionInterface {
|
|
|
1061
982
|
get id(): string;
|
|
1062
983
|
attach(stream: StreamInterface): void;
|
|
1063
984
|
detach(stream: StreamInterface): void;
|
|
1064
|
-
push(message: JSONRPCMessage
|
|
1065
|
-
replay(afterId: string
|
|
985
|
+
push(message: JSONRPCMessage): string;
|
|
986
|
+
replay(afterId: string): readonly MCPSessionEvent[];
|
|
1066
987
|
}
|
|
1067
988
|
|
|
1068
989
|
/**
|
|
1069
|
-
*
|
|
1070
|
-
* keeps per minted session — the live {@link MCPSession} entity plus the epoch-ms
|
|
1071
|
-
* was last touched (the lazy-TTL sweep's idle clock, independent of the session's
|
|
1072
|
-
* replay-log TTL).
|
|
990
|
+
* Represents the closure store entry a {@link import('./middlewares.js').createMCPSession}
|
|
991
|
+
* middleware keeps per minted session — the live {@link MCPSession} entity plus the epoch-ms
|
|
992
|
+
* instant it was last touched (the lazy-TTL sweep's idle clock, independent of the session's
|
|
993
|
+
* own replay-log TTL).
|
|
1073
994
|
*
|
|
1074
995
|
* @remarks
|
|
1075
996
|
* - `session` — the live {@link MCPSession} entity the store keys by session id.
|
|
@@ -1081,12 +1002,32 @@ export declare class MCPSession implements MCPSessionInterface {
|
|
|
1081
1002
|
export declare interface MCPSessionEntry {
|
|
1082
1003
|
readonly session: MCPSession;
|
|
1083
1004
|
readonly touched: number;
|
|
1084
|
-
/**
|
|
1005
|
+
/** Holds the legacy revision negotiated when this session was minted. */
|
|
1085
1006
|
readonly version: MCPVersion;
|
|
1086
1007
|
}
|
|
1087
1008
|
|
|
1088
1009
|
/**
|
|
1089
|
-
*
|
|
1010
|
+
* Represents one entry of an {@link MCPSessionInterface}'s folded replay log — a single pushed
|
|
1011
|
+
* {@link JSONRPCMessage} tagged with the monotone event `id` the session assigned and the
|
|
1012
|
+
* `timestamp` it was appended at (for the lazy-TTL replay window).
|
|
1013
|
+
*
|
|
1014
|
+
* @remarks
|
|
1015
|
+
* - `id` — the session-assigned, monotonically-increasing event id (a base36 string), the
|
|
1016
|
+
* value a resumable client echoes back as its `Last-Event-ID` to replay from here.
|
|
1017
|
+
* - `message` — the server→client {@link JSONRPCMessage} that was pushed.
|
|
1018
|
+
* - `timestamp` — the epoch-ms instant the entry was appended, read by the TTL eviction.
|
|
1019
|
+
*
|
|
1020
|
+
* A plain value record (no behavior) — the unit {@link MCPSessionInterface.replay}
|
|
1021
|
+
* returns.
|
|
1022
|
+
*/
|
|
1023
|
+
export declare interface MCPSessionEvent {
|
|
1024
|
+
readonly id: string;
|
|
1025
|
+
readonly message: JSONRPCMessage;
|
|
1026
|
+
readonly timestamp: number;
|
|
1027
|
+
}
|
|
1028
|
+
|
|
1029
|
+
/**
|
|
1030
|
+
* Represents one MCP transport session — the per-session entity a {@link
|
|
1090
1031
|
* import('./middlewares.js').createMCPSession} middleware owns (the {@link
|
|
1091
1032
|
* import('./MCPSession.js').MCPSession} entity), carrying the resumable server→client push
|
|
1092
1033
|
* channel with its bounded replay log FOLDED IN.
|
|
@@ -1111,7 +1052,7 @@ export declare interface MCPSessionInterface {
|
|
|
1111
1052
|
attach(stream: StreamInterface): void;
|
|
1112
1053
|
detach(stream: StreamInterface): void;
|
|
1113
1054
|
push(message: JSONRPCMessage): string;
|
|
1114
|
-
replay(afterId: string): readonly
|
|
1055
|
+
replay(afterId: string): readonly MCPSessionEvent[];
|
|
1115
1056
|
}
|
|
1116
1057
|
|
|
1117
1058
|
/**
|
|
@@ -1126,12 +1067,11 @@ export declare interface MCPSessionInterface {
|
|
|
1126
1067
|
* is treated as ABSENT and lazily evicted on the next access (no background timer — the
|
|
1127
1068
|
* `createRateLimiter` lazy-window idiom). Omit it for sessions that live until an explicit
|
|
1128
1069
|
* `DELETE`.
|
|
1129
|
-
* - `
|
|
1130
|
-
*
|
|
1131
|
-
*
|
|
1132
|
-
*
|
|
1133
|
-
*
|
|
1134
|
-
* the session; this `capacity` bounds its replay log — independent knobs.)
|
|
1070
|
+
* - `session` — the knobs forwarded to each minted {@link MCPSession}: `capacity` bounds its
|
|
1071
|
+
* replay log and `ttl` is that log's per-event lifetime. This type's own `ttl` bounds the
|
|
1072
|
+
* SESSION instead. An omitted leaf takes its {@link MCPSessionOptions} default, and an
|
|
1073
|
+
* omitted `session.clock` inherits this type's own `clock`, so one injected clock governs
|
|
1074
|
+
* both the store sweep and the log sweep unless a caller names a different one.
|
|
1135
1075
|
* - `clock` — the `() => number` epoch-ms clock {@link import('./middlewares.js').createMCPSession}
|
|
1136
1076
|
* uses directly for its own session-touch / TTL-sweep bookkeeping; defaults to `Date.now`. The
|
|
1137
1077
|
* deterministic clock a TTL test advances explicitly instead of racing a real idle window
|
|
@@ -1144,18 +1084,44 @@ export declare interface MCPSessionInterface {
|
|
|
1144
1084
|
* - `keepalive` — the SSE liveness options for the held-open resumable response. `interval`
|
|
1145
1085
|
* defaults to {@link import('./constants.js').DEFAULT_MCP_KEEPALIVE_INTERVAL}.
|
|
1146
1086
|
*/
|
|
1147
|
-
export declare interface
|
|
1087
|
+
export declare interface MCPSessionMiddlewareOptions {
|
|
1148
1088
|
readonly path?: string;
|
|
1149
1089
|
readonly ttl?: number;
|
|
1150
|
-
readonly
|
|
1090
|
+
readonly session?: MCPSessionOptions;
|
|
1151
1091
|
readonly clock?: () => number;
|
|
1152
|
-
/**
|
|
1092
|
+
/** Requires the route layer's value; `origins` is ignored when `enabled` is `false`. */
|
|
1153
1093
|
readonly origin?: MCPOriginOptions;
|
|
1154
1094
|
readonly keepalive?: MCPKeepaliveOptions;
|
|
1155
1095
|
}
|
|
1156
1096
|
|
|
1157
1097
|
/**
|
|
1158
|
-
*
|
|
1098
|
+
* Options for the {@link MCPSession} entity — its folded replay log's capacity and per-event
|
|
1099
|
+
* lifetime.
|
|
1100
|
+
*
|
|
1101
|
+
* @remarks
|
|
1102
|
+
* - `capacity` — the maximum number of pushed server→client messages retained for replay
|
|
1103
|
+
* before the OLDEST is evicted. Omit it for the {@link
|
|
1104
|
+
* import('./constants.js').DEFAULT_MCP_SESSION_CAPACITY} default.
|
|
1105
|
+
* - `ttl` — the PER-EVENT idle lifetime in milliseconds: a log entry older than `ttl` is
|
|
1106
|
+
* dropped by the lazy sweep `push` and `replay` run, which bounds how far a reconnecting
|
|
1107
|
+
* client may replay. Omit it for the {@link
|
|
1108
|
+
* import('./constants.js').DEFAULT_MCP_SESSION_TTL} default; a non-positive value means no
|
|
1109
|
+
* entry ever ages out by time.
|
|
1110
|
+
* - `clock` — the `() => number` epoch-ms clock the log's lazy TTL sweep reads; defaults to
|
|
1111
|
+
* `Date.now`.
|
|
1112
|
+
*
|
|
1113
|
+
* The middleware's own knobs — the owned path, the idle-SESSION sweep window, origin
|
|
1114
|
+
* validation, and keepalive — live on {@link MCPSessionMiddlewareOptions}. The two `ttl`
|
|
1115
|
+
* values measure different things, which is why they sit on different types.
|
|
1116
|
+
*/
|
|
1117
|
+
export declare interface MCPSessionOptions {
|
|
1118
|
+
readonly capacity?: number;
|
|
1119
|
+
readonly ttl?: number;
|
|
1120
|
+
readonly clock?: () => number;
|
|
1121
|
+
}
|
|
1122
|
+
|
|
1123
|
+
/**
|
|
1124
|
+
* Declares the `context.state` slice a {@link import('./middlewares.js').createMCPSession}
|
|
1159
1125
|
* middleware sets on a validated / minted request — a consumer's `TState` extends
|
|
1160
1126
|
* this so the downstream route handler can read `context.state.session` to `push`
|
|
1161
1127
|
* a server-initiated message onto the session's resumable stream.
|
|
@@ -1170,27 +1136,6 @@ export declare interface MCPSessionState {
|
|
|
1170
1136
|
readonly session?: MCPSessionInterface;
|
|
1171
1137
|
}
|
|
1172
1138
|
|
|
1173
|
-
/**
|
|
1174
|
-
* Decodes a `fetch` Response's Server-Sent-Events body into the JSON-RPC messages it
|
|
1175
|
-
* carried — the CLIENT-side inverse of the server's Streamable-HTTP SSE response.
|
|
1176
|
-
*
|
|
1177
|
-
* @remarks
|
|
1178
|
-
* Reads the whole `response.body` stream chunk-by-chunk through a `TextDecoder({
|
|
1179
|
-
* stream: true })` (handling a multi-byte char split across reads) and `@orkestrel/sse`'s
|
|
1180
|
-
* {@link SSEParserInterface} (handling a partial line / in-progress event split across
|
|
1181
|
-
* reads), then narrows each dispatched event's `data` to a {@link JSONRPCMessage} with
|
|
1182
|
-
* `parseJSONRPCMessage` (so a non-message / non-JSON `data:` event is DROPPED, never
|
|
1183
|
-
* thrown — total). It reuses the SAME `SSEParser` the server's `openStream` seam
|
|
1184
|
-
* serializes against, so the wire round-trips. A `null` body (no stream) yields no
|
|
1185
|
-
* messages; the {@link import('./transports/HTTPClientTransport.js').HTTPClientTransport}
|
|
1186
|
-
* reads a request/response SSE reply (the server sends one `data:` event then ends), so
|
|
1187
|
-
* this drains to completion.
|
|
1188
|
-
*
|
|
1189
|
-
* @param response - The SSE `fetch` Response to decode (its `body` is read to completion)
|
|
1190
|
-
* @returns Every {@link JSONRPCMessage} the stream carried, in order
|
|
1191
|
-
*/
|
|
1192
|
-
export declare function readEventStream(response: Response): Promise<readonly JSONRPCMessage[]>;
|
|
1193
|
-
|
|
1194
1139
|
/**
|
|
1195
1140
|
* Reads the request's `Last-Event-ID` header — the SSE resume cursor a client sends when it
|
|
1196
1141
|
* reconnects to the resumable `GET {path}` stream, or `undefined` when absent.
|
|
@@ -1268,27 +1213,26 @@ export declare function rejectUnknownSession(): Response;
|
|
|
1268
1213
|
* ```ts
|
|
1269
1214
|
* const answer = await mcp.dispatch(invocation, { signal: disconnect.signal })
|
|
1270
1215
|
* if (answer !== undefined && Symbol.asyncIterator in answer) {
|
|
1271
|
-
* const sse =
|
|
1216
|
+
* const sse = createStream()
|
|
1272
1217
|
* queueMicrotask(() => void sendEventStream(answer, sse))
|
|
1273
1218
|
* }
|
|
1274
1219
|
* ```
|
|
1275
1220
|
*/
|
|
1276
1221
|
export declare function sendEventStream(stream: MCPStreamControllerInterface, sse: StreamInterface): Promise<void>;
|
|
1277
1222
|
|
|
1278
|
-
/**
|
|
1223
|
+
/** Names the `X-Accel-Buffering` value that disables reverse-proxy buffering. */
|
|
1279
1224
|
export declare const SSE_BUFFERING_DISABLED = "no";
|
|
1280
1225
|
|
|
1281
|
-
/**
|
|
1226
|
+
/** Names the reverse-proxy response header controlling buffering of an SSE response. */
|
|
1282
1227
|
export declare const SSE_BUFFERING_HEADER = "x-accel-buffering";
|
|
1283
1228
|
|
|
1284
|
-
/**
|
|
1229
|
+
/** Names the comment text written by the held-open MCP response keepalive. */
|
|
1285
1230
|
export declare const SSE_KEEPALIVE_COMMENT = "keepalive";
|
|
1286
1231
|
|
|
1287
1232
|
/**
|
|
1288
|
-
*
|
|
1289
|
-
* {@link StdioClientTransportInterface}
|
|
1290
|
-
*
|
|
1291
|
-
* import('./HTTPClientTransport.js').HTTPClientTransport} and {@link
|
|
1233
|
+
* Drives a CHILD PROCESS MCP server over newline-delimited JSON-RPC on `stdin`/`stdout` —
|
|
1234
|
+
* a {@link StdioClientTransportInterface}, the stdio sibling of {@link
|
|
1235
|
+
* import('@orkestrel/mcp').HTTPClientTransport} and {@link
|
|
1292
1236
|
* import('./WebSocketClientTransport.js').WebSocketClientTransport}.
|
|
1293
1237
|
*
|
|
1294
1238
|
* @remarks
|
|
@@ -1329,7 +1273,7 @@ export declare const SSE_KEEPALIVE_COMMENT = "keepalive";
|
|
|
1329
1273
|
* never moves again, so a detached descendant writing to the inherited stderr after the cutoff
|
|
1330
1274
|
* cannot grow it. See {@link StdioClientTransportInterface.evidence} for the readings and the
|
|
1331
1275
|
* byte bound.
|
|
1332
|
-
* - **Observable.** Owns the `emitter` ({@link
|
|
1276
|
+
* - **Observable.** Owns the `emitter` ({@link MCPMessageTransportEventMap}); the
|
|
1333
1277
|
* emitter isolates a listener throw; `error` is a DOMAIN event (a transport-level
|
|
1334
1278
|
* fault, including the child spawn cause the supervisor surfaces and the notice that this
|
|
1335
1279
|
* lifetime's `evidence` was cut off at the `drain` bound), distinct from the emitter's own
|
|
@@ -1345,7 +1289,7 @@ export declare const SSE_KEEPALIVE_COMMENT = "keepalive";
|
|
|
1345
1289
|
export declare class StdioClientTransport implements StdioClientTransportInterface {
|
|
1346
1290
|
#private;
|
|
1347
1291
|
constructor(options: StdioClientTransportOptions);
|
|
1348
|
-
get emitter(): EmitterInterface<
|
|
1292
|
+
get emitter(): EmitterInterface<MCPMessageTransportEventMap_2>;
|
|
1349
1293
|
get session(): string | undefined;
|
|
1350
1294
|
get duplex(): boolean;
|
|
1351
1295
|
get evidence(): string | undefined;
|
|
@@ -1365,23 +1309,23 @@ export declare class StdioClientTransport implements StdioClientTransportInterfa
|
|
|
1365
1309
|
}
|
|
1366
1310
|
|
|
1367
1311
|
/**
|
|
1368
|
-
*
|
|
1369
|
-
* that also reports the supervised child's stderr tail, the
|
|
1370
|
-
* startup leaves behind.
|
|
1312
|
+
* Declares the contract `createStdioClientTransport` returns — a
|
|
1313
|
+
* {@link MCPMessageTransportInterface} that also reports the supervised child's stderr tail, the
|
|
1314
|
+
* diagnostic a child that dies at startup leaves behind.
|
|
1371
1315
|
*
|
|
1372
1316
|
* @remarks
|
|
1373
1317
|
* This contract adds `evidence` and changes nothing else: `emitter`, `session`, `duplex`,
|
|
1374
1318
|
* `start`, `send`, and `close` are the shared client-transport surface, unchanged. It sits here
|
|
1375
|
-
* rather than on {@link
|
|
1319
|
+
* rather than on {@link MCPMessageTransportInterface} because a transport that supervises no child —
|
|
1376
1320
|
* Streamable HTTP, WebSocket, a `MessagePort` pair — has no such tail, and a member every one
|
|
1377
1321
|
* of them answers `undefined` to forever is a stdio detail rather than a shared contract. A
|
|
1378
|
-
* consumer that widens this value back to {@link
|
|
1322
|
+
* consumer that widens this value back to {@link MCPMessageTransportInterface}, including by
|
|
1379
1323
|
* reading `client.transport`, loses the reader and must keep the original reference.
|
|
1380
1324
|
*/
|
|
1381
|
-
export declare interface StdioClientTransportInterface extends
|
|
1325
|
+
export declare interface StdioClientTransportInterface extends MCPMessageTransportInterface {
|
|
1382
1326
|
/**
|
|
1383
|
-
*
|
|
1384
|
-
* captured at that child's end afterwards.
|
|
1327
|
+
* Reports the supervised child's decoded stderr tail — live while a child is held, and the
|
|
1328
|
+
* value captured at that child's end afterwards.
|
|
1385
1329
|
*
|
|
1386
1330
|
* @remarks
|
|
1387
1331
|
* - **Readings.** `undefined` while no child has run and none has been captured — before the
|
|
@@ -1474,6 +1418,30 @@ export declare interface StdioClientTransportOptions {
|
|
|
1474
1418
|
readonly delivery?: number;
|
|
1475
1419
|
}
|
|
1476
1420
|
|
|
1421
|
+
/**
|
|
1422
|
+
* Arms and tears down the newline-delimited JSON-RPC pump over the {@link StdioServerOptions}
|
|
1423
|
+
* stream pair — the stdio INGRESS handle {@link import('./factories.js').createStdioServer}
|
|
1424
|
+
* returns.
|
|
1425
|
+
*
|
|
1426
|
+
* @remarks
|
|
1427
|
+
* - `start()` — arm the pump: subscribe to `input`, and dispatch every complete line through
|
|
1428
|
+
* the bound {@link import('@src/core').MCPDispatcherInterface}, writing each defined
|
|
1429
|
+
* response back to `output`. The subscriptions are attached by the time the call returns.
|
|
1430
|
+
* The pump arms ONCE, so a repeated `start()` attaches nothing further and an inbound
|
|
1431
|
+
* request still draws exactly one reply.
|
|
1432
|
+
* - `stop()` — unbind the pump and close the transport: the listeners `start()` put on
|
|
1433
|
+
* `input` / `output` are removed, every pending `send` rejects, and `input` is released so
|
|
1434
|
+
* the process can exit. The release is complete by the time the call returns, and a
|
|
1435
|
+
* repeated `stop()` does nothing.
|
|
1436
|
+
* - **One lifetime per handle.** `stop()` ends it permanently: a `start()` issued afterwards
|
|
1437
|
+
* arms nothing, and serving again takes a fresh
|
|
1438
|
+
* {@link import('./factories.js').createStdioServer} over a live stream pair.
|
|
1439
|
+
*/
|
|
1440
|
+
export declare interface StdioServerInterface {
|
|
1441
|
+
start(): void;
|
|
1442
|
+
stop(): void;
|
|
1443
|
+
}
|
|
1444
|
+
|
|
1477
1445
|
/**
|
|
1478
1446
|
* Options for `createStdioServer` — the injectable stdin/stdout streams the server
|
|
1479
1447
|
* transport reads newline-delimited JSON-RPC requests from and writes responses to.
|
|
@@ -1490,15 +1458,14 @@ export declare interface StdioServerOptions {
|
|
|
1490
1458
|
}
|
|
1491
1459
|
|
|
1492
1460
|
/**
|
|
1493
|
-
*
|
|
1494
|
-
*
|
|
1495
|
-
*
|
|
1496
|
-
*
|
|
1497
|
-
* `mcp.dispatch` over, the stdio mirror of {@link
|
|
1461
|
+
* Wraps an injectable readable/writable stream pair (`process.stdin`/`process.stdout` in
|
|
1462
|
+
* production, a test double in tests) as a {@link MCPMessageTransportInterface} — the
|
|
1463
|
+
* newline-delimited JSON-RPC channel {@link import('../factories.js').createStdioServer}
|
|
1464
|
+
* pumps `mcp.dispatch` over, the stdio mirror of {@link
|
|
1498
1465
|
* import('./WebSocketServerTransport.js').WebSocketServerTransport}.
|
|
1499
1466
|
*
|
|
1500
1467
|
* @remarks
|
|
1501
|
-
* - **Reuses `
|
|
1468
|
+
* - **Reuses `MCPMessageTransportInterface`.** The same generic carrier the HTTP
|
|
1502
1469
|
* and WebSocket server transports implement — `emitter` (`message` / `close` /
|
|
1503
1470
|
* `error`), `start`, `send`, `close`. `session` is `undefined` (the stateless v1).
|
|
1504
1471
|
* - **Inbound (`message`).** `start()` subscribes to `input`'s `data` event; each
|
|
@@ -1524,14 +1491,14 @@ export declare interface StdioServerOptions {
|
|
|
1524
1491
|
* listener receives data. The injected streams are owned by the caller (typically
|
|
1525
1492
|
* `process.stdin`/`process.stdout`), so the transport never destroys, ends, or blanket-clears
|
|
1526
1493
|
* them.
|
|
1527
|
-
* - **Observable.** Owns the `emitter` ({@link
|
|
1494
|
+
* - **Observable.** Owns the `emitter` ({@link MCPMessageTransportEventMap}); the
|
|
1528
1495
|
* emitter isolates a listener throw; `error` is a DOMAIN event (a transport-level
|
|
1529
1496
|
* fault), distinct from the emitter's own listener-error channel.
|
|
1530
1497
|
*/
|
|
1531
|
-
export declare class StdioServerTransport implements
|
|
1498
|
+
export declare class StdioServerTransport implements MCPMessageTransportInterface_2 {
|
|
1532
1499
|
#private;
|
|
1533
1500
|
constructor(input: NodeJS.ReadableStream, output: NodeJS.WritableStream);
|
|
1534
|
-
get emitter(): EmitterInterface<
|
|
1501
|
+
get emitter(): EmitterInterface<MCPMessageTransportEventMap_2>;
|
|
1535
1502
|
get session(): string | undefined;
|
|
1536
1503
|
get duplex(): boolean;
|
|
1537
1504
|
start(): Promise<void>;
|
|
@@ -1570,10 +1537,10 @@ export declare class StdioServerTransport implements MCPClientTransportInterface
|
|
|
1570
1537
|
export declare function upgradeRequestPath(request: IncomingMessage): string;
|
|
1571
1538
|
|
|
1572
1539
|
/**
|
|
1573
|
-
*
|
|
1574
|
-
* {@link
|
|
1540
|
+
* Drives a REMOTE MCP server over a WebSocket — a CLIENT
|
|
1541
|
+
* {@link MCPMessageTransportInterface} for the Model Context Protocol, the
|
|
1575
1542
|
* egress mirror of {@link import('./factories.js').createWebSocketServer} and the WebSocket
|
|
1576
|
-
* sibling of {@link import('
|
|
1543
|
+
* sibling of {@link import('@orkestrel/mcp').HTTPClientTransport}.
|
|
1577
1544
|
*
|
|
1578
1545
|
* @remarks
|
|
1579
1546
|
* - **Persistent bidirectional channel (unlike the HTTP transport).** `start()` performs the
|
|
@@ -1591,16 +1558,18 @@ export declare function upgradeRequestPath(request: IncomingMessage): string;
|
|
|
1591
1558
|
* transport while the handshake was on the wire, both WIN — the socket that arrives late is
|
|
1592
1559
|
* DESTROYED and never bound, so no orphan is left re-emitting frames at nobody. Both
|
|
1593
1560
|
* `start()` calls still resolve; exactly one socket is ever bound.
|
|
1594
|
-
* - **Inbound (`message`).** Each decoded text frame
|
|
1595
|
-
*
|
|
1561
|
+
* - **Inbound (`message`).** Each decoded text frame runs through the shared `deliverMessage`
|
|
1562
|
+
* fold (parse, then narrow) — a {@link JSONRPCMessage} re-emits on this transport's `message`
|
|
1596
1563
|
* event (the reply the {@link import('@orkestrel/mcp').MCPClientInterface} correlates by `id`); a
|
|
1597
1564
|
* non-JSON / non-message frame surfaces on `error` and is dropped. The socket's `close`
|
|
1598
1565
|
* / `error` bridge to this transport's events.
|
|
1599
1566
|
* - **Outbound (`send`).** `send(message)` writes one masked text frame. A socket write is not
|
|
1600
|
-
* confirmed, so this transport answers a closed channel from its
|
|
1601
|
-
* bound socket — before `start()`, after `close()`, or after the
|
|
1602
|
-
*
|
|
1603
|
-
*
|
|
1567
|
+
* confirmed, so this transport answers a closed channel from its own state AND the socket's
|
|
1568
|
+
* `readyState`: a `send` with no bound socket — before `start()`, after `close()`, or after the
|
|
1569
|
+
* peer ended the socket — and a `send` on a bound socket that is not `OPEN` both REJECT with
|
|
1570
|
+
* `WebSocket transport is not connected`. It neither drops the message nor queues it for a
|
|
1571
|
+
* connection this transport is not holding — the browser face queues a pre-open send, and this
|
|
1572
|
+
* one, holding no connection to flush it onto, rejects that too.
|
|
1604
1573
|
* - **`close()`** unsubscribes from the socket, closes it, and fires `close` (idempotent). An
|
|
1605
1574
|
* upgrade still on the wire is DESTROYED, so a `close()` during the handshake ends the
|
|
1606
1575
|
* transport at once instead of waiting for a peer that may never answer — the suspended
|
|
@@ -1608,7 +1577,7 @@ export declare function upgradeRequestPath(request: IncomingMessage): string;
|
|
|
1608
1577
|
* - **URL scheme.** `options.url` accepts a `ws://` / `wss://` URL or an `http://` / `https://`
|
|
1609
1578
|
* one; a `ws(s)` scheme is converted to `http(s)` for the underlying upgrade request (`wss`
|
|
1610
1579
|
* → TLS through `node:https`). Either reaches the same endpoint.
|
|
1611
|
-
* - **Observable.** Owns the `emitter` ({@link
|
|
1580
|
+
* - **Observable.** Owns the `emitter` ({@link MCPMessageTransportEventMap}); every emit
|
|
1612
1581
|
* the emitter isolates a listener throw (a buggy observer never corrupts the transport);
|
|
1613
1582
|
* `error` is a DOMAIN event (a transport-level fault).
|
|
1614
1583
|
*
|
|
@@ -1619,10 +1588,10 @@ export declare function upgradeRequestPath(request: IncomingMessage): string;
|
|
|
1619
1588
|
* await client.connect() // start() handshakes, then the MCP initialize runs over WS frames
|
|
1620
1589
|
* ```
|
|
1621
1590
|
*/
|
|
1622
|
-
export declare class WebSocketClientTransport implements
|
|
1591
|
+
export declare class WebSocketClientTransport implements MCPMessageTransportInterface_2 {
|
|
1623
1592
|
#private;
|
|
1624
1593
|
constructor(options: WebSocketClientTransportOptions);
|
|
1625
|
-
get emitter(): EmitterInterface<
|
|
1594
|
+
get emitter(): EmitterInterface<MCPMessageTransportEventMap_2>;
|
|
1626
1595
|
get session(): string | undefined;
|
|
1627
1596
|
get duplex(): boolean;
|
|
1628
1597
|
start(): Promise<void>;
|
|
@@ -1672,7 +1641,7 @@ export declare interface WebSocketClientTransportOptions {
|
|
|
1672
1641
|
* transport mounts at). A protocol-upgrade request to any OTHER path is DECLINED
|
|
1673
1642
|
* (the handler returns `false`, so the spine fans it to the next handler or destroys it).
|
|
1674
1643
|
* - `subprotocol` — the WebSocket subprotocol selected in the `101` handshake's
|
|
1675
|
-
* `Sec-WebSocket-Protocol`; defaults to {@link import('
|
|
1644
|
+
* `Sec-WebSocket-Protocol`; defaults to {@link import('@orkestrel/mcp').MCP_WEBSOCKET_SUBPROTOCOL}
|
|
1676
1645
|
* (`'mcp'`). It is sent only when the client's offer contains that token.
|
|
1677
1646
|
*
|
|
1678
1647
|
* Auth / origin policy is deliberately ABSENT: like the HTTP transport, the WebSocket
|
|
@@ -1686,41 +1655,46 @@ export declare interface WebSocketServerOptions {
|
|
|
1686
1655
|
}
|
|
1687
1656
|
|
|
1688
1657
|
/**
|
|
1689
|
-
*
|
|
1690
|
-
* {@link
|
|
1691
|
-
*
|
|
1658
|
+
* Wraps a {@link NodeWebSocketInterface} (the RFC 6455 wire wrapper) as a
|
|
1659
|
+
* {@link MCPMessageTransportInterface} — the per-connection JSON-RPC-over-WebSocket SERVER
|
|
1660
|
+
* bridge, the bidirectional JSON-RPC message channel
|
|
1692
1661
|
* `createWebSocketServer` pumps `mcp.dispatch` over and the egress mirror's
|
|
1693
1662
|
* {@link import('./WebSocketClientTransport.js').WebSocketClientTransport} reuses.
|
|
1694
1663
|
*
|
|
1695
1664
|
* @remarks
|
|
1696
|
-
* - **Reuses `
|
|
1665
|
+
* - **Reuses `MCPMessageTransportInterface`.** It IS the same generic carrier the HTTP
|
|
1697
1666
|
* client transport implements — `emitter` (`message` / `close` / `error`), `start`,
|
|
1698
1667
|
* `send`, `close` — so the WebSocket server and client both speak ONE transport contract,
|
|
1699
1668
|
* no near-duplicate sibling interface. `session` is `undefined` (the stateless v1; a
|
|
1700
1669
|
* session id is the deferred sessions tier). The name keeps the role explicit even though
|
|
1701
1670
|
* the shape is shared.
|
|
1702
1671
|
* - **Inbound (`message`).** `start()` subscribes to the socket's `message` event; each text
|
|
1703
|
-
* frame
|
|
1672
|
+
* frame runs through the shared `deliverMessage` fold (parse, then narrow) — a
|
|
1704
1673
|
* well-formed {@link JSONRPCMessage} is re-emitted on this transport's `message` event (the
|
|
1705
1674
|
* parsed envelope the {@link import('@orkestrel/mcp').MCPServerInterface} pump dispatches), while
|
|
1706
1675
|
* a non-JSON or non-message frame is surfaced on `error` and DROPPED, never thrown. It
|
|
1707
1676
|
* also bridges the socket's `close` → this transport's `close`, and the socket's `error`.
|
|
1708
1677
|
* - **Outbound (`send`).** `send(message)` writes one text frame
|
|
1709
|
-
* (`nodeWs.send(JSON.stringify(message))`)
|
|
1710
|
-
* non-open socket, so a closed
|
|
1678
|
+
* (`nodeWs.send(JSON.stringify(message))`). The underlying wrapper no-ops a write on a
|
|
1679
|
+
* non-open socket and confirms nothing, so this bridge answers a closed channel from its own
|
|
1680
|
+
* state and the socket's `readyState`: a `send` after `close()`, after the peer's close, or on
|
|
1681
|
+
* a socket that is not `OPEN` REJECTS with `WebSocket transport is not connected` rather than
|
|
1682
|
+
* resolving on a frame nobody wrote. `bindServer` catches that rejection and routes it to the
|
|
1683
|
+
* dispatcher's `error` event, and it aborts every in-flight request the moment this transport's
|
|
1684
|
+
* `close` fires — so a peer that disconnects mid-request is answered by no write at all.
|
|
1711
1685
|
* - **`close()`** removes the subscriptions `start()` installed on the socket, closes the
|
|
1712
1686
|
* underlying socket (the RFC 6455 close handshake), and fires the transport's `close` event
|
|
1713
1687
|
* (idempotent — a second `close`, or a socket-driven close, emits once). A frame that arrives
|
|
1714
1688
|
* between that release and the peer's close echo reaches nothing: the socket-driven close path
|
|
1715
1689
|
* releases the same way, so a closed transport is never subscribed to a live socket.
|
|
1716
|
-
* - **Observable.** Owns the `emitter` ({@link
|
|
1690
|
+
* - **Observable.** Owns the `emitter` ({@link MCPMessageTransportEventMap}); the emitter
|
|
1717
1691
|
* isolates a listener throw (a buggy observer never corrupts the bridge). `error` is a
|
|
1718
1692
|
* DOMAIN event (a transport-level fault), distinct from the emitter's listener-error channel.
|
|
1719
1693
|
*/
|
|
1720
|
-
export declare class WebSocketServerTransport implements
|
|
1694
|
+
export declare class WebSocketServerTransport implements MCPMessageTransportInterface_2 {
|
|
1721
1695
|
#private;
|
|
1722
1696
|
constructor(socket: NodeWebSocketInterface);
|
|
1723
|
-
get emitter(): EmitterInterface<
|
|
1697
|
+
get emitter(): EmitterInterface<MCPMessageTransportEventMap_2>;
|
|
1724
1698
|
get session(): string | undefined;
|
|
1725
1699
|
get duplex(): boolean;
|
|
1726
1700
|
start(): Promise<void>;
|