@orkestrel/mcp 0.0.4 → 0.0.6

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.
@@ -1,12 +1,13 @@
1
1
  import { ClientTransportEventMap } from '../core/index.ts';
2
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';
3
+ import { ClientTransportInterface } from '../core/index.ts';
4
+ import { ClientTransportInterface as ClientTransportInterface_2 } from '../../core/index.ts';
5
5
  import { EmitterInterface } from '@orkestrel/emitter';
6
6
  import { IncomingMessage } from 'node:http';
7
7
  import { JSONRPCMessage } from '../core/index.ts';
8
8
  import { JSONRPCMessage as JSONRPCMessage_2 } from '../../core/index.ts';
9
9
  import { MCPServerInterface } from '../core/index.ts';
10
+ import { MCPTransportInterface } from '../core/index.ts';
10
11
  import { MiddlewareHandler } from '@orkestrel/server';
11
12
  import { NodeWebSocketInterface } from '@orkestrel/websocket';
12
13
  import { RouteInput } from '@orkestrel/router';
@@ -28,6 +29,49 @@ import { UpgradeHandler } from '@orkestrel/server';
28
29
  */
29
30
  export declare function acceptsEventStream(request: Request): boolean;
30
31
 
32
+ /**
33
+ * Bridge a message-channel {@link ClientTransportInterface} (the shape the stdio and
34
+ * WebSocket SERVER transports already implement) into the environment-agnostic
35
+ * {@link import('@src/core').MCPTransportInterface} port — the adapter
36
+ * {@link import('./factories.js').createStdioServer} and {@link
37
+ * import('./factories.js').createWebSocketServer} pipe through `bindServer`, so the
38
+ * request/reply/error pump those two factories used to hand-roll identically now
39
+ * lives ONCE in the core binder.
40
+ *
41
+ * @remarks
42
+ * `send` decodes the already-serialized reply string back to a {@link JSONRPCMessage}
43
+ * and writes it via `transport.send` (the SAME `JSON.stringify` the underlying
44
+ * transport already performs, so the wire bytes are unchanged). `listen` filters
45
+ * `transport`'s `message` event to REQUESTS ONLY — a stray response is ignored,
46
+ * exactly as the prior hand-rolled pumps did — and re-serializes each one back to a
47
+ * string for `bindServer`. `closed` bridges `transport`'s `close` event. `close`
48
+ * closes the underlying `transport`.
49
+ *
50
+ * @remarks Per {@link import('@src/core').MCPTransportInterface}, `listen`/`closed`
51
+ * each hold THE SINGLE current handler (a second call REPLACES the first, never adds).
52
+ * Since the underlying `transport.emitter` is ADD-based (`on` subscribes, never
53
+ * replaces), this bridge installs ONE stable emitter listener per event on first use
54
+ * and re-routes it to whichever handler is CURRENTLY registered (`undefined` while
55
+ * none is), so rebinding never double-dispatches.
56
+ *
57
+ * @remarks A response whose `result` serializes away (e.g. `undefined`) is dropped by
58
+ * the message validators on the wire's decode side — an asymmetry the stdio/WS carrier
59
+ * shares with the streamable-HTTP face, since both round-trip through `JSON.stringify`
60
+ * / `JSON.parse` before re-validation.
61
+ *
62
+ * @param transport - The message-channel transport to bridge (stdio or WebSocket)
63
+ * @returns An {@link import('@src/core').MCPTransportInterface} `bindServer` can drive
64
+ *
65
+ * @example
66
+ * ```ts
67
+ * import { bindServer } from '@src/core'
68
+ *
69
+ * const transport = new StdioServerTransport(process.stdin, process.stdout)
70
+ * bindServer(mcp, bridgeMessageTransport(transport))
71
+ * ```
72
+ */
73
+ export declare function bridgeMessageTransport(transport: ClientTransportInterface): MCPTransportInterface;
74
+
31
75
  /**
32
76
  * Create the HTTP CLIENT transport for an {@link import('@src/core').MCPClientInterface}
33
77
  * — a {@link ClientTransportInterface} that drives a REMOTE Streamable-HTTP MCP server
@@ -41,8 +85,10 @@ export declare function acceptsEventStream(request: Request): boolean;
41
85
  * and the reply is surfaced on the transport's `message` event for the client's id
42
86
  * correlation. Add `options.headers` (e.g. an `Authorization` bearer) to reach a guarded
43
87
  * server. `start` / `close` hold no connection; against a STATEFUL server it captures the
44
- * `mcp-session-id` from `initialize` and echoes it on later requests, so the same
45
- * `MCPClient` passes session validation (a stateless server sends none).
88
+ * `mcp-session-id` from `initialize` and echoes it on later requests. It also captures
89
+ * the initialize result's `protocolVersion` and sends `mcp-protocol-version` on every
90
+ * subsequent request, so the same `MCPClient` passes the session and 2025-06-18
91
+ * protocol gates without caller wiring.
46
92
  *
47
93
  * @param options - `url` (the remote endpoint; REQUIRED), optional `headers` merged onto
48
94
  * every request, optional `fetch` (default `globalThis.fetch`), and optional `timeout`
@@ -61,7 +107,35 @@ export declare function acceptsEventStream(request: Request): boolean;
61
107
  * const tools = await client.tools()
62
108
  * ```
63
109
  */
64
- export declare function createHTTPClientTransport(options: HTTPClientTransportOptions): ClientTransportInterface_2;
110
+ export declare function createHTTPClientTransport(options: HTTPClientTransportOptions): ClientTransportInterface;
111
+
112
+ /**
113
+ * Create the Streamable-HTTP POST handler used by `createMCPRoutes`.
114
+ *
115
+ * @remarks
116
+ * A present `mcp-protocol-version` header must name a supported revision; an
117
+ * unsupported value returns an HTTP `400` JSON-RPC invalid-request error without
118
+ * dispatching. An absent header is accepted for the initialize/bootstrap request.
119
+ *
120
+ * @param mcp - The transport-agnostic MCP server to dispatch through
121
+ * @param streaming - Whether an event-stream response may be negotiated
122
+ * @returns A request handler for the stateless MCP POST route
123
+ *
124
+ * @example
125
+ * ```ts
126
+ * import { createMCPServer } from '@orkestrel/mcp'
127
+ * import { createMCPPostHandler } from '@orkestrel/mcp/server'
128
+ * import { createToolManager } from '@orkestrel/agent'
129
+ *
130
+ * const mcp = createMCPServer({ name: 'docs', version: '1.0.0', tools: createToolManager() })
131
+ * const handler = createMCPPostHandler(mcp, true)
132
+ * await handler(new Request('http://localhost/mcp', {
133
+ * method: 'POST',
134
+ * body: '{"jsonrpc":"2.0","method":"ping","id":1}',
135
+ * }))
136
+ * ```
137
+ */
138
+ export declare function createMCPPostHandler(mcp: MCPServerInterface, streaming: boolean): (request: Request) => Promise<Response>;
65
139
 
66
140
  /**
67
141
  * Create the MCP Streamable-HTTP transport routes — mounts a transport-agnostic
@@ -78,6 +152,9 @@ export declare function createHTTPClientTransport(options: HTTPClientTransportOp
78
152
  * - A **transport** failure — a malformed JSON body, or a parsed value that is not a
79
153
  * JSON-RPC REQUEST — is an HTTP `400` carrying a JSON-RPC error BODY (`-32700` Parse
80
154
  * error / `-32600` Invalid Request, id `null`).
155
+ * - A present `mcp-protocol-version` header is validated before dispatch: a supported
156
+ * value proceeds, while an unsupported value returns HTTP `400` with a JSON-RPC
157
+ * `-32600` body. An absent value proceeds for initialize/bootstrap compatibility.
81
158
  * - A **dispatch** result — a success OR an IN-BAND JSON-RPC error from `mcp.dispatch`
82
159
  * (e.g. `-32601` method-not-found) — is an HTTP `200` carrying the JSON-RPC response
83
160
  * envelope (the error is in-band per JSON-RPC, NOT an HTTP error).
@@ -202,7 +279,7 @@ export declare function createMCPSession<TState extends MCPSessionState>(options
202
279
  * const tools = await client.tools()
203
280
  * ```
204
281
  */
205
- export declare function createStdioClientTransport(options: StdioClientTransportOptions): ClientTransportInterface_2;
282
+ export declare function createStdioClientTransport(options: StdioClientTransportOptions): ClientTransportInterface;
206
283
 
207
284
  /**
208
285
  * Create the MCP stdio transport INGRESS — pumps a transport-agnostic {@link
@@ -212,12 +289,13 @@ export declare function createStdioClientTransport(options: StdioClientTransport
212
289
  * @remarks
213
290
  * Wraps `options.input` (default `process.stdin`) / `options.output` (default
214
291
  * `process.stdout`) in a {@link import('./transports/StdioServerTransport.js').StdioServerTransport}
215
- * and PUMPS: each inbound {@link import('@src/core').JSONRPCMessage} that is a
216
- * REQUEST runs through `mcp.dispatch`, and a defined response is written back as a
217
- * newline-terminated line a NOTIFICATION (`dispatch` → `undefined`) writes
218
- * nothing. A non-request message is ignored. The dispatch is guarded so a
219
- * `dispatch` / `send` fault surfaces on the transport's `error` event rather than
220
- * escaping the (async) message listener.
292
+ * and pipes it through the core {@link import('@src/core').MCPTransportInterface} port
293
+ * via {@link import('./helpers.js').bridgeMessageTransport} + {@link
294
+ * import('@src/core').bindServer}: each inbound REQUEST runs through `mcp.dispatch`, and
295
+ * a defined response is written back as a newline-terminated line a NOTIFICATION
296
+ * writes nothing, and a non-request message is ignored. A `dispatch` / `send` fault
297
+ * surfaces on `mcp.emitter`'s `error` event rather than escaping the (async) message
298
+ * pump.
221
299
  *
222
300
  * @param mcp - The transport-agnostic {@link MCPServerInterface} to expose over stdio
223
301
  * @param options - Optional injectable `input` / `output` streams; see
@@ -270,7 +348,7 @@ export declare function createStdioServer(mcp: MCPServerInterface, options?: Std
270
348
  * const tools = await client.tools()
271
349
  * ```
272
350
  */
273
- export declare function createWebSocketClientTransport(options: WebSocketClientTransportOptions): ClientTransportInterface_2;
351
+ export declare function createWebSocketClientTransport(options: WebSocketClientTransportOptions): ClientTransportInterface;
274
352
 
275
353
  /**
276
354
  * Create the MCP WebSocket transport INGRESS — an {@link UpgradeHandler} that exposes a
@@ -290,12 +368,13 @@ export declare function createWebSocketClientTransport(options: WebSocketClientT
290
368
  * - **Claims (returns `true`)** otherwise: it builds `createNodeWebSocket({ socket, key, head,
291
369
  * protocol })` (SERVER mode → writes the `101` handshake, echoing the `subprotocol`, default
292
370
  * {@link MCP_WEBSOCKET_SUBPROTOCOL} `'mcp'`, and sends UNMASKED frames), wraps it in a
293
- * {@link WebSocketServerTransport}, and PUMPS: each inbound {@link
294
- * import('@src/core').JSONRPCMessage} that is a REQUEST runs through `mcp.dispatch`, and a
295
- * defined response is written back as a frame — a NOTIFICATION (`dispatch` → `undefined`)
296
- * sends nothing. A non-request message (a stray response) is ignored. The dispatch is
297
- * guarded so a `dispatch` / `send` fault surfaces on the transport's `error` event rather
298
- * than escaping the (async) message listener.
371
+ * {@link WebSocketServerTransport}, and pipes it through the core {@link
372
+ * import('@src/core').MCPTransportInterface} port via {@link
373
+ * import('./helpers.js').bridgeMessageTransport} + {@link import('@src/core').bindServer}:
374
+ * each inbound REQUEST runs through `mcp.dispatch`, and a defined response is written back
375
+ * as a frame — a NOTIFICATION sends nothing, and a non-request message (a stray response) is
376
+ * ignored. A `dispatch` / `send` fault surfaces on `mcp.emitter`'s `error` event rather than
377
+ * escaping the (async) message pump.
299
378
  *
300
379
  * It is MECHANISM, not policy: compose an auth guard IN FRONT by registering an upgrade
301
380
  * handler BEFORE this one — that handler can claim (decline + destroy) an unauthenticated
@@ -422,7 +501,7 @@ export declare function extractLines(buffer: string, chunk: string): LineExtract
422
501
  *
423
502
  * @remarks
424
503
  * - **Request/response over `fetch`.** `send(message)` POSTs the JSON-serialized
425
- * message (or batch) to `options.url` with `content-type: application/json` and an
504
+ * message to `options.url` with `content-type: application/json` and an
426
505
  * `Accept` of BOTH `application/json` and `text/event-stream` (so the server may
427
506
  * answer with either framing) — plus any `options.headers` (e.g. an `Authorization`
428
507
  * bearer). It then decodes the reply and emits each decoded {@link JSONRPCMessage} on
@@ -434,13 +513,18 @@ export declare function extractLines(buffer: string, chunk: string): LineExtract
434
513
  * readEventStream}) — the inverse of the server's `openStream` seam, so the wire
435
514
  * round-trips. A `202`
436
515
  * Accepted (a notification) carries no body and emits nothing.
437
- * - **Session echo.** `start()` / `close()` are no-ops (a request/response transport
438
- * holds no long-lived connection). The `mcp-session-id` response header, when a
439
- * STATEFUL server sends one (on `initialize`), is captured into `session` and then
440
- * ECHOED as the `mcp-session-id` request header on every SUBSEQUENT request — so an
441
- * `MCPClient` passes a stateful server's session validation. Before initialize returns
442
- * an id, `session` is `undefined` and no header is sent (safe against a stateless
443
- * server, which neither sends nor expects one).
516
+ * - **Session and protocol echo.** `start()` is a no-op (a
517
+ * request/response transport opens no long-lived connection). The
518
+ * `mcp-session-id` response header, when a STATEFUL server sends one (on
519
+ * `initialize`), is captured into `session` and then ECHOED as the
520
+ * `mcp-session-id` request header on every SUBSEQUENT request so an
521
+ * `MCPClient` passes a stateful server's session validation. The
522
+ * initialize result's `protocolVersion` is likewise captured, but only
523
+ * when it is a SUPPORTED value, and echoed as `mcp-protocol-version` on
524
+ * every subsequent request, as required by the 2025-06-18 Streamable-HTTP
525
+ * transport. Before initialize returns, neither captured header is sent.
526
+ * `close()` clears the captured protocol so a reconnect's `initialize`
527
+ * POST is headerless; the captured `session` persists across `close()`.
444
528
  * - **Total at the boundary (§14).** Every reply is narrowed (`parseJSONRPCMessage`,
445
529
  * the SSE decoder) — a non-message reply is dropped, never asserted; a `fetch` /
446
530
  * decode failure surfaces on the `error` event rather than escaping `send`.
@@ -454,13 +538,13 @@ export declare function extractLines(buffer: string, chunk: string): LineExtract
454
538
  * await client.connect()
455
539
  * ```
456
540
  */
457
- export declare class HTTPClientTransport implements ClientTransportInterface {
541
+ export declare class HTTPClientTransport implements ClientTransportInterface_2 {
458
542
  #private;
459
543
  constructor(options: HTTPClientTransportOptions);
460
544
  get emitter(): EmitterInterface<ClientTransportEventMap_2>;
461
545
  get session(): string | undefined;
462
546
  start(): Promise<void>;
463
- send(message: JSONRPCMessage_2 | readonly JSONRPCMessage_2[]): Promise<void>;
547
+ send(message: JSONRPCMessage_2): Promise<void>;
464
548
  close(): Promise<void>;
465
549
  }
466
550
 
@@ -528,10 +612,13 @@ export declare interface LineExtraction {
528
612
  }
529
613
 
530
614
  /**
531
- * The Streamable-HTTP transport header that carries the negotiated MCP protocol version
532
- * on a subsequent request. The version is negotiated in the `initialize` JSON-RPC result
533
- * body; a stateful transport MAY additionally read this header to pin the per-request
534
- * protocol version (optional — the result body remains the source of truth).
615
+ * The Streamable-HTTP transport header carrying the negotiated MCP protocol version
616
+ * on every post-initialize client request.
617
+ *
618
+ * @remarks
619
+ * Required by MCP 2025-06-18 after initialization. Both HTTP client transports
620
+ * capture the initialize result's `protocolVersion` and send it on subsequent
621
+ * requests; `createMCPRoutes` rejects a present unsupported value before dispatch.
535
622
  */
536
623
  export declare const MCP_PROTOCOL_VERSION_HEADER = "mcp-protocol-version";
537
624
 
@@ -629,12 +716,12 @@ export declare class MCPSession implements MCPSessionInterface {
629
716
  *
630
717
  * @remarks
631
718
  * - `session` — the live {@link MCPSession} entity the store keys by session id.
632
- * - `touched` — the epoch-ms instant of the last access; mutated (not replaced) on every
719
+ * - `touched` — the epoch-ms instant of the last access; the entry is replaced on every
633
720
  * resolved request so the middleware's lazy sweep can evict an idle entry past `ttl`.
634
721
  */
635
722
  export declare interface MCPSessionEntry {
636
723
  readonly session: MCPSession;
637
- touched: number;
724
+ readonly touched: number;
638
725
  }
639
726
 
640
727
  /**
@@ -709,7 +796,7 @@ export declare interface MCPSessionOptions {
709
796
  * before calling `next`).
710
797
  */
711
798
  export declare interface MCPSessionState {
712
- session?: MCPSessionInterface;
799
+ readonly session?: MCPSessionInterface;
713
800
  }
714
801
 
715
802
  /**
@@ -800,8 +887,8 @@ export declare function rejectUnknownSession(): Response;
800
887
  * {@link dispatchLines} helper — a well-formed {@link JSONRPCMessage} emits
801
888
  * `message`, a malformed line emits `error` (§14, never throws). The child's
802
889
  * `close` bridges to this transport's `close`.
803
- * - **Outbound (`send`).** `send(message | messages)` writes ONE newline-terminated
804
- * `JSON.stringify`d line per message to the child's `stdin`.
890
+ * - **Outbound (`send`).** `send(message)` writes one newline-terminated
891
+ * `JSON.stringify`d line to the child's `stdin`.
805
892
  * - **`close()`** kills the child process and fires `close` (idempotent).
806
893
  * - **Observable (§13).** Owns the `emitter` ({@link ClientTransportEventMap}); the
807
894
  * emitter isolates a listener throw; `error` is a DOMAIN event (a transport-level
@@ -814,13 +901,13 @@ export declare function rejectUnknownSession(): Response;
814
901
  * await client.connect() // start() spawns the child, then the MCP initialize runs over stdio
815
902
  * ```
816
903
  */
817
- export declare class StdioClientTransport implements ClientTransportInterface {
904
+ export declare class StdioClientTransport implements ClientTransportInterface_2 {
818
905
  #private;
819
906
  constructor(options: StdioClientTransportOptions);
820
907
  get emitter(): EmitterInterface<ClientTransportEventMap_2>;
821
908
  get session(): string | undefined;
822
909
  start(): Promise<void>;
823
- send(message: JSONRPCMessage_2 | readonly JSONRPCMessage_2[]): Promise<void>;
910
+ send(message: JSONRPCMessage_2): Promise<void>;
824
911
  close(): Promise<void>;
825
912
  }
826
913
 
@@ -877,8 +964,8 @@ export declare interface StdioServerOptions {
877
964
  * well-formed {@link JSONRPCMessage} re-emits on `message`, a malformed line
878
965
  * emits `error` (§14, never throws). `input`'s `close` bridges to this
879
966
  * transport's `close`.
880
- * - **Outbound (`send`).** `send(message | messages)` writes ONE newline-terminated
881
- * `JSON.stringify`d line per message to `output`.
967
+ * - **Outbound (`send`).** `send(message)` writes one newline-terminated
968
+ * `JSON.stringify`d line to `output`.
882
969
  * - **`close()`** fires this transport's `close` (idempotent) — the injected streams
883
970
  * are owned by the caller (typically `process.stdin`/`process.stdout`, which must
884
971
  * never be closed out from under the process) and are not torn down here.
@@ -886,13 +973,13 @@ export declare interface StdioServerOptions {
886
973
  * emitter isolates a listener throw; `error` is a DOMAIN event (a transport-level
887
974
  * fault), distinct from the emitter's own listener-error channel.
888
975
  */
889
- export declare class StdioServerTransport implements ClientTransportInterface {
976
+ export declare class StdioServerTransport implements ClientTransportInterface_2 {
890
977
  #private;
891
978
  constructor(input: NodeJS.ReadableStream, output: NodeJS.WritableStream);
892
979
  get emitter(): EmitterInterface<ClientTransportEventMap_2>;
893
980
  get session(): string | undefined;
894
981
  start(): Promise<void>;
895
- send(message: JSONRPCMessage_2 | readonly JSONRPCMessage_2[]): Promise<void>;
982
+ send(message: JSONRPCMessage_2): Promise<void>;
896
983
  close(): Promise<void>;
897
984
  }
898
985
 
@@ -933,7 +1020,7 @@ export declare function upgradeRequestPath(request: IncomingMessage): string;
933
1020
  * event (the reply the {@link import('@src/core').MCPClientInterface} correlates by `id`); a
934
1021
  * non-JSON / non-message frame surfaces on `error` and is dropped (§14). The socket's `close`
935
1022
  * / `error` bridge to this transport's events.
936
- * - **Outbound (`send`).** `send(message | messages)` writes ONE masked text frame per message.
1023
+ * - **Outbound (`send`).** `send(message)` writes one masked text frame.
937
1024
  * - **`close()`** closes the underlying socket and fires `close` (idempotent).
938
1025
  * - **URL scheme.** `options.url` accepts a `ws://` / `wss://` URL or an `http://` / `https://`
939
1026
  * one; a `ws(s)` scheme is converted to `http(s)` for the underlying upgrade request (`wss`
@@ -949,13 +1036,13 @@ export declare function upgradeRequestPath(request: IncomingMessage): string;
949
1036
  * await client.connect() // start() handshakes, then the MCP initialize runs over WS frames
950
1037
  * ```
951
1038
  */
952
- export declare class WebSocketClientTransport implements ClientTransportInterface {
1039
+ export declare class WebSocketClientTransport implements ClientTransportInterface_2 {
953
1040
  #private;
954
1041
  constructor(options: WebSocketClientTransportOptions);
955
1042
  get emitter(): EmitterInterface<ClientTransportEventMap_2>;
956
1043
  get session(): string | undefined;
957
1044
  start(): Promise<void>;
958
- send(message: JSONRPCMessage_2 | readonly JSONRPCMessage_2[]): Promise<void>;
1045
+ send(message: JSONRPCMessage_2): Promise<void>;
959
1046
  close(): Promise<void>;
960
1047
  }
961
1048
 
@@ -1021,8 +1108,8 @@ export declare interface WebSocketServerOptions {
1021
1108
  * parsed envelope the {@link import('@src/core').MCPServerInterface} pump dispatches), while
1022
1109
  * a non-JSON or non-message frame is surfaced on `error` and DROPPED, never thrown (§14). It
1023
1110
  * also bridges the socket's `close` → this transport's `close`, and the socket's `error`.
1024
- * - **Outbound (`send`).** `send(message | messages)` writes ONE text frame per message
1025
- * (`nodeWs.send(JSON.stringify(...))`); the underlying wrapper no-ops a write on a
1111
+ * - **Outbound (`send`).** `send(message)` writes one text frame
1112
+ * (`nodeWs.send(JSON.stringify(message))`); the underlying wrapper no-ops a write on a
1026
1113
  * non-open socket, so a closed connection drops silently rather than throwing.
1027
1114
  * - **`close()`** closes the underlying socket (the RFC 6455 close handshake) and fires the
1028
1115
  * transport's `close` event (idempotent — a second `close`, or a socket-driven close, emits
@@ -1031,13 +1118,13 @@ export declare interface WebSocketServerOptions {
1031
1118
  * isolates a listener throw (a buggy observer never corrupts the bridge). `error` is a
1032
1119
  * DOMAIN event (a transport-level fault), distinct from the emitter's listener-error channel.
1033
1120
  */
1034
- export declare class WebSocketServerTransport implements ClientTransportInterface {
1121
+ export declare class WebSocketServerTransport implements ClientTransportInterface_2 {
1035
1122
  #private;
1036
1123
  constructor(socket: NodeWebSocketInterface);
1037
1124
  get emitter(): EmitterInterface<ClientTransportEventMap_2>;
1038
1125
  get session(): string | undefined;
1039
1126
  start(): Promise<void>;
1040
- send(message: JSONRPCMessage_2 | readonly JSONRPCMessage_2[]): Promise<void>;
1127
+ send(message: JSONRPCMessage_2): Promise<void>;
1041
1128
  close(): Promise<void>;
1042
1129
  }
1043
1130