@orkestrel/mcp 0.0.35 → 0.0.37
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/dist/src/core/index.cjs +50 -6
- package/dist/src/core/index.cjs.map +1 -1
- package/dist/src/core/index.d.cts +52 -1
- package/dist/src/core/index.d.ts +52 -1
- package/dist/src/core/index.js +50 -7
- package/dist/src/core/index.js.map +1 -1
- package/dist/src/server/index.cjs +81 -34
- package/dist/src/server/index.cjs.map +1 -1
- package/dist/src/server/index.d.cts +58 -23
- package/dist/src/server/index.d.ts +58 -23
- package/dist/src/server/index.js +84 -38
- package/dist/src/server/index.js.map +1 -1
- package/package.json +9 -9
|
@@ -170,8 +170,8 @@ export declare function createMCPContinuation(secret: TokenSecret): MCPContinuat
|
|
|
170
170
|
* comparison; a missing, mismatched, or invalidly encoded value returns HTTP `400` + `-32020`.
|
|
171
171
|
* A protocol header naming a modern revision holds the request to that revision whatever shape
|
|
172
172
|
* its body arrived in, so a body with no parsable modern `_meta` returns HTTP `400` + `-32602`.
|
|
173
|
-
* Headerless `initialize`
|
|
174
|
-
* session to supply its pinned version. A legacy-shaped request carrying a protocol header is
|
|
173
|
+
* Headerless `initialize` and id-bearing legacy `ping` are accepted; other headerless requests
|
|
174
|
+
* need a live legacy session to supply its pinned version. A legacy-shaped request carrying a protocol header is
|
|
175
175
|
* otherwise admitted only for a legacy revision; a revision this server does not implement
|
|
176
176
|
* returns HTTP `400` + `-32022` whose `supported` names the legacy revisions this door accepts.
|
|
177
177
|
* A present origin must occur in `origin.origins` unless validation is
|
|
@@ -181,6 +181,8 @@ export declare function createMCPContinuation(secret: TokenSecret): MCPContinuat
|
|
|
181
181
|
* handler through `MCPDispatchOptions.signal`. After every transport validation and immediately
|
|
182
182
|
* before dispatch, the optional synchronous `caller` extractor reads front-middleware state; a
|
|
183
183
|
* defined value is added to `MCPDispatchOptions`, while `undefined` is omitted.
|
|
184
|
+
* For legacy `initialize`, the dispatch response is recorded as `initialization` before
|
|
185
|
+
* JSON or SSE framing only when consumer state is an object with `'session' in context.state`.
|
|
184
186
|
*
|
|
185
187
|
* @typeParam TState - The consumer's opaque per-request route state type
|
|
186
188
|
* @param mcp - The transport-agnostic MCP dispatcher to dispatch through
|
|
@@ -280,14 +282,20 @@ export declare function createMCPRoutes<TState = unknown>(mcp: MCPDispatcherInte
|
|
|
280
282
|
* readSessionHeader}: a valid id touches the entry and sets `context.state.session`; an
|
|
281
283
|
* absent / unknown id whose (guarded) body parses to an `initialize` request ({@link
|
|
282
284
|
* isInitializeRequest}) mints a fresh {@link MCPSession} (`crypto.randomUUID()`, the `session`
|
|
283
|
-
* options group) and sets `context.state.session
|
|
285
|
+
* options group) and sets `context.state.session`. An id-bearing legacy `ping` with no
|
|
286
|
+
* session header passes through without session state, a supplied protocol header, or a
|
|
287
|
+
* response stamp. Other unresolved requests → {@link rejectUnknownSession}
|
|
284
288
|
* (`404`). The
|
|
285
289
|
* minted entry pins the negotiated legacy revision, which is supplied to a later headerless
|
|
286
290
|
* live-session request. It then
|
|
287
291
|
* forwards a fresh `Request` carrying the buffered `text` (`next(forwarded)`) — never the
|
|
288
292
|
* already-consumed original — so the route re-reads the same body, and stamps the response
|
|
289
|
-
* with {@link MCP_SESSION_HEADER}.
|
|
290
|
-
*
|
|
293
|
+
* with {@link MCP_SESSION_HEADER}. A candidate entry is stored and advertised only when
|
|
294
|
+
* `context.state.initialization` carries a result. An `initialize` that would mint a session
|
|
295
|
+
* stores none and advertises none when refused; a live session's header is returned unchanged.
|
|
296
|
+
* The POST handler records that dispatch response before JSON or SSE framing.
|
|
297
|
+
* The entry's `touched` instant is read after the downstream response, because it
|
|
298
|
+
* means the last access: a request slower than `ttl` would
|
|
291
299
|
* otherwise store a session that is already expired, and the write-back RE-ASKS the store, so
|
|
292
300
|
* a `DELETE` arriving while the request was suspended is not undone.
|
|
293
301
|
* - **`GET {path}`.** Resolves the session the same way (no mint — only `initialize` mints);
|
|
@@ -548,7 +556,7 @@ export declare const DEFAULT_MCP_SESSION_TTL = 300000;
|
|
|
548
556
|
* Decodes and delivers each complete newline-framed line onto a {@link
|
|
549
557
|
* MCPMessageTransportEventMap} emitter — the shared per-chunk dispatch step both stdio
|
|
550
558
|
* transports run their framed lines through: the server transport frames with {@link
|
|
551
|
-
* extractLines}, the client transport
|
|
559
|
+
* extractLines}, the client transport frames the supervisor's stdout with Node's `readline`.
|
|
552
560
|
*
|
|
553
561
|
* @remarks
|
|
554
562
|
* A blank line is skipped (a stray trailing newline). Every other line runs through the
|
|
@@ -707,8 +715,8 @@ export declare interface HTTPTransportOptions<TState = unknown> extends HTTPHand
|
|
|
707
715
|
* {@link import('@orkestrel/mcp').decodeSentinel} before the comparison, so a peer that had
|
|
708
716
|
* to encode its value still matches; a sentinel whose payload is invalid decodes to nothing
|
|
709
717
|
* and therefore mismatches, which is how an invalid header value is refused. A legacy request
|
|
710
|
-
* body requires a protocol header
|
|
711
|
-
* never echo the client-supplied one.
|
|
718
|
+
* body requires a protocol header except for `initialize` and an id-bearing legacy `ping`.
|
|
719
|
+
* Messages name the expected value but never echo the client-supplied one.
|
|
712
720
|
*
|
|
713
721
|
* The expectation a live session supplies is a different rule over a different input, so it
|
|
714
722
|
* is {@link inferSessionHeaderIssue} rather than a second arm of this one.
|
|
@@ -855,6 +863,16 @@ export declare interface LineExtraction {
|
|
|
855
863
|
readonly remainder: string;
|
|
856
864
|
}
|
|
857
865
|
|
|
866
|
+
/**
|
|
867
|
+
* Sets the bound in milliseconds for a stdio server to exit after the client ends its input.
|
|
868
|
+
*
|
|
869
|
+
* @remarks
|
|
870
|
+
* Gives EOF cleanup half the process supervisor's 5,000 ms signal grace before escalation gets
|
|
871
|
+
* its existing full window. This bounds the MCP lifecycle's reasonable-time wait without spending
|
|
872
|
+
* another full signal grace before termination. Pending input flushes share this bound.
|
|
873
|
+
*/
|
|
874
|
+
export declare const MCP_STDIO_GRACE: number;
|
|
875
|
+
|
|
858
876
|
/**
|
|
859
877
|
* Extracts consumer-asserted caller context synchronously from an HTTP request after the
|
|
860
878
|
* transport has validated it for dispatch.
|
|
@@ -1130,13 +1148,16 @@ export declare interface MCPSessionOptions {
|
|
|
1130
1148
|
* a server-initiated message onto the session's resumable stream.
|
|
1131
1149
|
*
|
|
1132
1150
|
* @remarks
|
|
1133
|
-
* `session` is set on `initialize` (the
|
|
1151
|
+
* `session` is set on `initialize` (the candidate or resolved live session) and on every validated
|
|
1134
1152
|
* non-`initialize` `POST` (the resolved one); absent when the request never
|
|
1135
1153
|
* reached a resolved session (the middleware short-circuits those as a `404`
|
|
1136
|
-
* before calling `next`).
|
|
1154
|
+
* before calling `next`). A refused candidate remains in request state but is never
|
|
1155
|
+
* stored or advertised. A live session's response header is returned unchanged.
|
|
1137
1156
|
*/
|
|
1138
1157
|
export declare interface MCPSessionState {
|
|
1139
1158
|
readonly session?: MCPSessionInterface;
|
|
1159
|
+
/** Holds the initialization dispatch response before HTTP framing, used to admit a session. */
|
|
1160
|
+
readonly initialization?: JSONRPCResponse;
|
|
1140
1161
|
}
|
|
1141
1162
|
|
|
1142
1163
|
/**
|
|
@@ -1240,32 +1261,37 @@ export declare const SSE_KEEPALIVE_COMMENT = "keepalive";
|
|
|
1240
1261
|
*
|
|
1241
1262
|
* @remarks
|
|
1242
1263
|
* - **Composes `@orkestrel/process`.** `start()` builds one supervised
|
|
1243
|
-
* {@link import('@orkestrel/process/server').
|
|
1264
|
+
* {@link import('@orkestrel/process/server').Supervisor} with `writable: true`, so the child's
|
|
1244
1265
|
* `stdin`/`stdout` are the JSON-RPC channel and its `stderr` is retained as bounded evidence
|
|
1245
|
-
* rather than parsed as protocol. The supervisor owns spawn
|
|
1246
|
-
* - **Inbound (`message`).** Standard output is drained eagerly through
|
|
1247
|
-
*
|
|
1266
|
+
* rather than parsed as protocol. The supervisor owns spawn and termination.
|
|
1267
|
+
* - **Inbound (`message`).** Standard output is drained eagerly through Node's `readline`,
|
|
1268
|
+
* so a multi-byte UTF-8 sequence split across two reads is
|
|
1248
1269
|
* decoded whole and a final line written without a trailing newline still arrives. Each framed
|
|
1249
1270
|
* line is decoded and delivered through the shared {@link dispatchLines} helper — a well-formed
|
|
1250
1271
|
* {@link JSONRPCMessage} emits `message`, a malformed line emits `error` (never throws).
|
|
1251
1272
|
* - **Outbound (`send`).** `send(message)` writes one newline-terminated `JSON.stringify`d line
|
|
1252
|
-
* through the supervisor's `
|
|
1273
|
+
* through the supervisor's `deliver` and awaits its answer, so this promise settles only after the
|
|
1253
1274
|
* host reports the line handled rather than the moment the write is queued. The supervisor never
|
|
1254
1275
|
* rejects — it answers `false` for a channel that was closed, destroyed, or ended, for a write
|
|
1255
1276
|
* that failed, or for one that remained unconfirmed through `delivery`. A call made without a
|
|
1256
1277
|
* live child rejects as not connected; a `false` answer from a live child rejects as unable to
|
|
1257
1278
|
* deliver. The supervisor does not disclose which cause produced that answer.
|
|
1258
|
-
* - **`close()`**
|
|
1259
|
-
*
|
|
1260
|
-
* `evidence`,
|
|
1261
|
-
*
|
|
1262
|
-
* A
|
|
1279
|
+
* - **`close()`** ends the child's input and waits up to {@link MCP_STDIO_GRACE} for native exit,
|
|
1280
|
+
* including any pending input flush. Only after that wait does the supervisor terminate a child
|
|
1281
|
+
* that remains alive. Teardown freezes `evidence`, closes the reader, and settles `exit`, then
|
|
1282
|
+
* fires `close` once (idempotent).
|
|
1283
|
+
* A child that exits on input end within the grace receives no termination signal, so it must
|
|
1284
|
+
* end its own child processes on input end; only escalation reaches its process tree.
|
|
1285
|
+
* If an `MCPClient` request `timeout` is shorter than the grace and the close outlasts that
|
|
1286
|
+
* timeout, `disconnect()` rejects with `MCP transport close timed out after <timeout>ms`.
|
|
1287
|
+
* The close keeps running, and a later caller joins it while it remains pending.
|
|
1288
|
+
* A line already framed behind the one being delivered is dropped rather than
|
|
1263
1289
|
* emitted onto a transport whose teardown has begun. A `close()` issued while that teardown runs
|
|
1264
1290
|
* joins it rather than opening a second one, so it resolves only after `close` has fired, and a
|
|
1265
1291
|
* `start()` issued while it runs waits behind the same barrier, so lifetimes never overlap. A
|
|
1266
1292
|
* descendant can retain an inherited stdout pipe after the child exits; the supervisor's `drain`
|
|
1267
|
-
* bound cuts that wait off
|
|
1268
|
-
*
|
|
1293
|
+
* bound cuts that wait off independently of the input grace. Escalation belongs to the host:
|
|
1294
|
+
* a POSIX host signals the
|
|
1269
1295
|
* child's own process group `SIGTERM`, waits the grace window, then `SIGKILL`s through the same
|
|
1270
1296
|
* route, so the kill reaches grandchildren rather than orphaning them, while Windows ends the
|
|
1271
1297
|
* tree with `taskkill /F /T`, which nothing in the child can intercept.
|
|
@@ -1324,6 +1350,14 @@ export declare class StdioClientTransport implements StdioClientTransportInterfa
|
|
|
1324
1350
|
* of them answers `undefined` to forever is a stdio detail rather than a shared contract. A
|
|
1325
1351
|
* consumer that widens this value back to {@link MCPMessageTransportInterface}, including by
|
|
1326
1352
|
* reading `client.transport`, loses the reader and must keep the original reference.
|
|
1353
|
+
* Closing ends the child's input, waits up to {@link import('./constants.js').MCP_STDIO_GRACE}
|
|
1354
|
+
* for native exit, and then uses the supervisor's bounded termination if the child remains alive.
|
|
1355
|
+
* The input flush shares that bound; the supervisor's stream-drain bound follows native exit.
|
|
1356
|
+
* A child that exits on input end within the grace receives no termination signal and must end
|
|
1357
|
+
* its own child processes on input end; only escalation reaches its process tree. If an
|
|
1358
|
+
* `MCPClient` request `timeout` is shorter than the grace and the close outlasts that timeout,
|
|
1359
|
+
* `disconnect()` rejects with `MCP transport close timed out after <timeout>ms`. The close keeps
|
|
1360
|
+
* running, and a later caller joins it while it remains pending.
|
|
1327
1361
|
*/
|
|
1328
1362
|
export declare interface StdioClientTransportInterface extends MCPMessageTransportInterface {
|
|
1329
1363
|
/**
|
|
@@ -1357,7 +1391,8 @@ export declare interface StdioClientTransportInterface extends MCPMessageTranspo
|
|
|
1357
1391
|
* would have read.
|
|
1358
1392
|
* - **What the close path carries.** The frozen value is what the supervisor had received by
|
|
1359
1393
|
* that terminal moment, not the child's complete output.
|
|
1360
|
-
* Windows ends the tree with `taskkill /F /T`, which nothing
|
|
1394
|
+
* After the input grace expires, Windows ends the tree with `taskkill /F /T`, which nothing
|
|
1395
|
+
* in the child can intercept: a
|
|
1361
1396
|
* `SIGTERM` handler never runs there, so the bytes it would have written never exist. A
|
|
1362
1397
|
* child that ends on its own closes its stderr first, and that tail is complete.
|
|
1363
1398
|
* Where that moment arrived at the supervisor's `drain` bound rather than at the child's
|
|
@@ -170,8 +170,8 @@ export declare function createMCPContinuation(secret: TokenSecret): MCPContinuat
|
|
|
170
170
|
* comparison; a missing, mismatched, or invalidly encoded value returns HTTP `400` + `-32020`.
|
|
171
171
|
* A protocol header naming a modern revision holds the request to that revision whatever shape
|
|
172
172
|
* its body arrived in, so a body with no parsable modern `_meta` returns HTTP `400` + `-32602`.
|
|
173
|
-
* Headerless `initialize`
|
|
174
|
-
* session to supply its pinned version. A legacy-shaped request carrying a protocol header is
|
|
173
|
+
* Headerless `initialize` and id-bearing legacy `ping` are accepted; other headerless requests
|
|
174
|
+
* need a live legacy session to supply its pinned version. A legacy-shaped request carrying a protocol header is
|
|
175
175
|
* otherwise admitted only for a legacy revision; a revision this server does not implement
|
|
176
176
|
* returns HTTP `400` + `-32022` whose `supported` names the legacy revisions this door accepts.
|
|
177
177
|
* A present origin must occur in `origin.origins` unless validation is
|
|
@@ -181,6 +181,8 @@ export declare function createMCPContinuation(secret: TokenSecret): MCPContinuat
|
|
|
181
181
|
* handler through `MCPDispatchOptions.signal`. After every transport validation and immediately
|
|
182
182
|
* before dispatch, the optional synchronous `caller` extractor reads front-middleware state; a
|
|
183
183
|
* defined value is added to `MCPDispatchOptions`, while `undefined` is omitted.
|
|
184
|
+
* For legacy `initialize`, the dispatch response is recorded as `initialization` before
|
|
185
|
+
* JSON or SSE framing only when consumer state is an object with `'session' in context.state`.
|
|
184
186
|
*
|
|
185
187
|
* @typeParam TState - The consumer's opaque per-request route state type
|
|
186
188
|
* @param mcp - The transport-agnostic MCP dispatcher to dispatch through
|
|
@@ -280,14 +282,20 @@ export declare function createMCPRoutes<TState = unknown>(mcp: MCPDispatcherInte
|
|
|
280
282
|
* readSessionHeader}: a valid id touches the entry and sets `context.state.session`; an
|
|
281
283
|
* absent / unknown id whose (guarded) body parses to an `initialize` request ({@link
|
|
282
284
|
* isInitializeRequest}) mints a fresh {@link MCPSession} (`crypto.randomUUID()`, the `session`
|
|
283
|
-
* options group) and sets `context.state.session
|
|
285
|
+
* options group) and sets `context.state.session`. An id-bearing legacy `ping` with no
|
|
286
|
+
* session header passes through without session state, a supplied protocol header, or a
|
|
287
|
+
* response stamp. Other unresolved requests → {@link rejectUnknownSession}
|
|
284
288
|
* (`404`). The
|
|
285
289
|
* minted entry pins the negotiated legacy revision, which is supplied to a later headerless
|
|
286
290
|
* live-session request. It then
|
|
287
291
|
* forwards a fresh `Request` carrying the buffered `text` (`next(forwarded)`) — never the
|
|
288
292
|
* already-consumed original — so the route re-reads the same body, and stamps the response
|
|
289
|
-
* with {@link MCP_SESSION_HEADER}.
|
|
290
|
-
*
|
|
293
|
+
* with {@link MCP_SESSION_HEADER}. A candidate entry is stored and advertised only when
|
|
294
|
+
* `context.state.initialization` carries a result. An `initialize` that would mint a session
|
|
295
|
+
* stores none and advertises none when refused; a live session's header is returned unchanged.
|
|
296
|
+
* The POST handler records that dispatch response before JSON or SSE framing.
|
|
297
|
+
* The entry's `touched` instant is read after the downstream response, because it
|
|
298
|
+
* means the last access: a request slower than `ttl` would
|
|
291
299
|
* otherwise store a session that is already expired, and the write-back RE-ASKS the store, so
|
|
292
300
|
* a `DELETE` arriving while the request was suspended is not undone.
|
|
293
301
|
* - **`GET {path}`.** Resolves the session the same way (no mint — only `initialize` mints);
|
|
@@ -548,7 +556,7 @@ export declare const DEFAULT_MCP_SESSION_TTL = 300000;
|
|
|
548
556
|
* Decodes and delivers each complete newline-framed line onto a {@link
|
|
549
557
|
* MCPMessageTransportEventMap} emitter — the shared per-chunk dispatch step both stdio
|
|
550
558
|
* transports run their framed lines through: the server transport frames with {@link
|
|
551
|
-
* extractLines}, the client transport
|
|
559
|
+
* extractLines}, the client transport frames the supervisor's stdout with Node's `readline`.
|
|
552
560
|
*
|
|
553
561
|
* @remarks
|
|
554
562
|
* A blank line is skipped (a stray trailing newline). Every other line runs through the
|
|
@@ -707,8 +715,8 @@ export declare interface HTTPTransportOptions<TState = unknown> extends HTTPHand
|
|
|
707
715
|
* {@link import('@orkestrel/mcp').decodeSentinel} before the comparison, so a peer that had
|
|
708
716
|
* to encode its value still matches; a sentinel whose payload is invalid decodes to nothing
|
|
709
717
|
* and therefore mismatches, which is how an invalid header value is refused. A legacy request
|
|
710
|
-
* body requires a protocol header
|
|
711
|
-
* never echo the client-supplied one.
|
|
718
|
+
* body requires a protocol header except for `initialize` and an id-bearing legacy `ping`.
|
|
719
|
+
* Messages name the expected value but never echo the client-supplied one.
|
|
712
720
|
*
|
|
713
721
|
* The expectation a live session supplies is a different rule over a different input, so it
|
|
714
722
|
* is {@link inferSessionHeaderIssue} rather than a second arm of this one.
|
|
@@ -855,6 +863,16 @@ export declare interface LineExtraction {
|
|
|
855
863
|
readonly remainder: string;
|
|
856
864
|
}
|
|
857
865
|
|
|
866
|
+
/**
|
|
867
|
+
* Sets the bound in milliseconds for a stdio server to exit after the client ends its input.
|
|
868
|
+
*
|
|
869
|
+
* @remarks
|
|
870
|
+
* Gives EOF cleanup half the process supervisor's 5,000 ms signal grace before escalation gets
|
|
871
|
+
* its existing full window. This bounds the MCP lifecycle's reasonable-time wait without spending
|
|
872
|
+
* another full signal grace before termination. Pending input flushes share this bound.
|
|
873
|
+
*/
|
|
874
|
+
export declare const MCP_STDIO_GRACE: number;
|
|
875
|
+
|
|
858
876
|
/**
|
|
859
877
|
* Extracts consumer-asserted caller context synchronously from an HTTP request after the
|
|
860
878
|
* transport has validated it for dispatch.
|
|
@@ -1130,13 +1148,16 @@ export declare interface MCPSessionOptions {
|
|
|
1130
1148
|
* a server-initiated message onto the session's resumable stream.
|
|
1131
1149
|
*
|
|
1132
1150
|
* @remarks
|
|
1133
|
-
* `session` is set on `initialize` (the
|
|
1151
|
+
* `session` is set on `initialize` (the candidate or resolved live session) and on every validated
|
|
1134
1152
|
* non-`initialize` `POST` (the resolved one); absent when the request never
|
|
1135
1153
|
* reached a resolved session (the middleware short-circuits those as a `404`
|
|
1136
|
-
* before calling `next`).
|
|
1154
|
+
* before calling `next`). A refused candidate remains in request state but is never
|
|
1155
|
+
* stored or advertised. A live session's response header is returned unchanged.
|
|
1137
1156
|
*/
|
|
1138
1157
|
export declare interface MCPSessionState {
|
|
1139
1158
|
readonly session?: MCPSessionInterface;
|
|
1159
|
+
/** Holds the initialization dispatch response before HTTP framing, used to admit a session. */
|
|
1160
|
+
readonly initialization?: JSONRPCResponse;
|
|
1140
1161
|
}
|
|
1141
1162
|
|
|
1142
1163
|
/**
|
|
@@ -1240,32 +1261,37 @@ export declare const SSE_KEEPALIVE_COMMENT = "keepalive";
|
|
|
1240
1261
|
*
|
|
1241
1262
|
* @remarks
|
|
1242
1263
|
* - **Composes `@orkestrel/process`.** `start()` builds one supervised
|
|
1243
|
-
* {@link import('@orkestrel/process/server').
|
|
1264
|
+
* {@link import('@orkestrel/process/server').Supervisor} with `writable: true`, so the child's
|
|
1244
1265
|
* `stdin`/`stdout` are the JSON-RPC channel and its `stderr` is retained as bounded evidence
|
|
1245
|
-
* rather than parsed as protocol. The supervisor owns spawn
|
|
1246
|
-
* - **Inbound (`message`).** Standard output is drained eagerly through
|
|
1247
|
-
*
|
|
1266
|
+
* rather than parsed as protocol. The supervisor owns spawn and termination.
|
|
1267
|
+
* - **Inbound (`message`).** Standard output is drained eagerly through Node's `readline`,
|
|
1268
|
+
* so a multi-byte UTF-8 sequence split across two reads is
|
|
1248
1269
|
* decoded whole and a final line written without a trailing newline still arrives. Each framed
|
|
1249
1270
|
* line is decoded and delivered through the shared {@link dispatchLines} helper — a well-formed
|
|
1250
1271
|
* {@link JSONRPCMessage} emits `message`, a malformed line emits `error` (never throws).
|
|
1251
1272
|
* - **Outbound (`send`).** `send(message)` writes one newline-terminated `JSON.stringify`d line
|
|
1252
|
-
* through the supervisor's `
|
|
1273
|
+
* through the supervisor's `deliver` and awaits its answer, so this promise settles only after the
|
|
1253
1274
|
* host reports the line handled rather than the moment the write is queued. The supervisor never
|
|
1254
1275
|
* rejects — it answers `false` for a channel that was closed, destroyed, or ended, for a write
|
|
1255
1276
|
* that failed, or for one that remained unconfirmed through `delivery`. A call made without a
|
|
1256
1277
|
* live child rejects as not connected; a `false` answer from a live child rejects as unable to
|
|
1257
1278
|
* deliver. The supervisor does not disclose which cause produced that answer.
|
|
1258
|
-
* - **`close()`**
|
|
1259
|
-
*
|
|
1260
|
-
* `evidence`,
|
|
1261
|
-
*
|
|
1262
|
-
* A
|
|
1279
|
+
* - **`close()`** ends the child's input and waits up to {@link MCP_STDIO_GRACE} for native exit,
|
|
1280
|
+
* including any pending input flush. Only after that wait does the supervisor terminate a child
|
|
1281
|
+
* that remains alive. Teardown freezes `evidence`, closes the reader, and settles `exit`, then
|
|
1282
|
+
* fires `close` once (idempotent).
|
|
1283
|
+
* A child that exits on input end within the grace receives no termination signal, so it must
|
|
1284
|
+
* end its own child processes on input end; only escalation reaches its process tree.
|
|
1285
|
+
* If an `MCPClient` request `timeout` is shorter than the grace and the close outlasts that
|
|
1286
|
+
* timeout, `disconnect()` rejects with `MCP transport close timed out after <timeout>ms`.
|
|
1287
|
+
* The close keeps running, and a later caller joins it while it remains pending.
|
|
1288
|
+
* A line already framed behind the one being delivered is dropped rather than
|
|
1263
1289
|
* emitted onto a transport whose teardown has begun. A `close()` issued while that teardown runs
|
|
1264
1290
|
* joins it rather than opening a second one, so it resolves only after `close` has fired, and a
|
|
1265
1291
|
* `start()` issued while it runs waits behind the same barrier, so lifetimes never overlap. A
|
|
1266
1292
|
* descendant can retain an inherited stdout pipe after the child exits; the supervisor's `drain`
|
|
1267
|
-
* bound cuts that wait off
|
|
1268
|
-
*
|
|
1293
|
+
* bound cuts that wait off independently of the input grace. Escalation belongs to the host:
|
|
1294
|
+
* a POSIX host signals the
|
|
1269
1295
|
* child's own process group `SIGTERM`, waits the grace window, then `SIGKILL`s through the same
|
|
1270
1296
|
* route, so the kill reaches grandchildren rather than orphaning them, while Windows ends the
|
|
1271
1297
|
* tree with `taskkill /F /T`, which nothing in the child can intercept.
|
|
@@ -1324,6 +1350,14 @@ export declare class StdioClientTransport implements StdioClientTransportInterfa
|
|
|
1324
1350
|
* of them answers `undefined` to forever is a stdio detail rather than a shared contract. A
|
|
1325
1351
|
* consumer that widens this value back to {@link MCPMessageTransportInterface}, including by
|
|
1326
1352
|
* reading `client.transport`, loses the reader and must keep the original reference.
|
|
1353
|
+
* Closing ends the child's input, waits up to {@link import('./constants.js').MCP_STDIO_GRACE}
|
|
1354
|
+
* for native exit, and then uses the supervisor's bounded termination if the child remains alive.
|
|
1355
|
+
* The input flush shares that bound; the supervisor's stream-drain bound follows native exit.
|
|
1356
|
+
* A child that exits on input end within the grace receives no termination signal and must end
|
|
1357
|
+
* its own child processes on input end; only escalation reaches its process tree. If an
|
|
1358
|
+
* `MCPClient` request `timeout` is shorter than the grace and the close outlasts that timeout,
|
|
1359
|
+
* `disconnect()` rejects with `MCP transport close timed out after <timeout>ms`. The close keeps
|
|
1360
|
+
* running, and a later caller joins it while it remains pending.
|
|
1327
1361
|
*/
|
|
1328
1362
|
export declare interface StdioClientTransportInterface extends MCPMessageTransportInterface {
|
|
1329
1363
|
/**
|
|
@@ -1357,7 +1391,8 @@ export declare interface StdioClientTransportInterface extends MCPMessageTranspo
|
|
|
1357
1391
|
* would have read.
|
|
1358
1392
|
* - **What the close path carries.** The frozen value is what the supervisor had received by
|
|
1359
1393
|
* that terminal moment, not the child's complete output.
|
|
1360
|
-
* Windows ends the tree with `taskkill /F /T`, which nothing
|
|
1394
|
+
* After the input grace expires, Windows ends the tree with `taskkill /F /T`, which nothing
|
|
1395
|
+
* in the child can intercept: a
|
|
1361
1396
|
* `SIGTERM` handler never runs there, so the bytes it would have written never exist. A
|
|
1362
1397
|
* child that ends on its own closes its stderr first, and that tail is complete.
|
|
1363
1398
|
* Where that moment arrived at the supervisor's `drain` bound rather than at the child's
|
package/dist/src/server/index.js
CHANGED
|
@@ -1,15 +1,25 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { PROCESS_GRACE } from "@orkestrel/process";
|
|
2
|
+
import { HTTPClientTransport, JSONRPC_INVALID_PARAMS, JSONRPC_INVALID_REQUEST, JSONRPC_METHOD_NOT_FOUND, JSONRPC_PARSE_ERROR, MCP_HANDSHAKE_VERSION, MCP_HEADER_MISMATCH, MCP_LOOKUP_PAGES, MCP_META_VERSION, MCP_METHOD_HEADER, MCP_MISSING_CAPABILITY, MCP_NAME_HEADER, MCP_PARAM_PREFIX, MCP_PROTOCOL_VERSION_HEADER, MCP_SESSION_HEADER, MCP_UNSUPPORTED_VERSION, MCP_WEBSOCKET_SUBPROTOCOL, SUPPORTED_LEGACY_PROTOCOL_VERSIONS, bindServer, buildHeaderParameters, buildJSONRPCError, decodeEvent, decodeSentinel, deliverMessage, extractToolSchema, inferRequestEra, isInitializeRequest, isJSONRPCInvocation, isMCPLegacyVersion, isMCPModernVersion, isModernRequest, isPingRequest, parseJSONRPCMessage, parseRequestContext, renderHeaderValue } from "../core/index.js";
|
|
3
|
+
import { isError, isObject, isRecord, isString, parseJSON, sanitizeBudget } from "@orkestrel/contract";
|
|
3
4
|
import { createStream, signToken, verifyToken } from "@orkestrel/server";
|
|
4
5
|
import { Emitter } from "@orkestrel/emitter";
|
|
5
6
|
import { WEBSOCKET_READY_OPEN, WEBSOCKET_VERSION, computeWebSocketAccept, createNodeWebSocket } from "@orkestrel/websocket";
|
|
6
7
|
import { randomBytes } from "node:crypto";
|
|
7
8
|
import { request } from "node:http";
|
|
8
9
|
import { request as request$1 } from "node:https";
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
10
|
+
import { createInterface } from "node:readline";
|
|
11
|
+
import { Supervisor } from "@orkestrel/process/server";
|
|
11
12
|
import { Readable } from "node:stream";
|
|
12
13
|
//#region src/server/constants.ts
|
|
14
|
+
/**
|
|
15
|
+
* Sets the bound in milliseconds for a stdio server to exit after the client ends its input.
|
|
16
|
+
*
|
|
17
|
+
* @remarks
|
|
18
|
+
* Gives EOF cleanup half the process supervisor's 5,000 ms signal grace before escalation gets
|
|
19
|
+
* its existing full window. This bounds the MCP lifecycle's reasonable-time wait without spending
|
|
20
|
+
* another full signal grace before termination. Pending input flushes share this bound.
|
|
21
|
+
*/
|
|
22
|
+
var MCP_STDIO_GRACE = PROCESS_GRACE / 2;
|
|
13
23
|
/** Names the reverse-proxy response header controlling buffering of an SSE response. */
|
|
14
24
|
var SSE_BUFFERING_HEADER = "x-accel-buffering";
|
|
15
25
|
/** Names the `X-Accel-Buffering` value that disables reverse-proxy buffering. */
|
|
@@ -297,7 +307,7 @@ function writeLine(output, line) {
|
|
|
297
307
|
* Decodes and delivers each complete newline-framed line onto a {@link
|
|
298
308
|
* MCPMessageTransportEventMap} emitter — the shared per-chunk dispatch step both stdio
|
|
299
309
|
* transports run their framed lines through: the server transport frames with {@link
|
|
300
|
-
* extractLines}, the client transport
|
|
310
|
+
* extractLines}, the client transport frames the supervisor's stdout with Node's `readline`.
|
|
301
311
|
*
|
|
302
312
|
* @remarks
|
|
303
313
|
* A blank line is skipped (a stray trailing newline). Every other line runs through the
|
|
@@ -361,8 +371,8 @@ function inferHeaderTarget(request) {
|
|
|
361
371
|
* {@link import('@orkestrel/mcp').decodeSentinel} before the comparison, so a peer that had
|
|
362
372
|
* to encode its value still matches; a sentinel whose payload is invalid decodes to nothing
|
|
363
373
|
* and therefore mismatches, which is how an invalid header value is refused. A legacy request
|
|
364
|
-
* body requires a protocol header
|
|
365
|
-
* never echo the client-supplied one.
|
|
374
|
+
* body requires a protocol header except for `initialize` and an id-bearing legacy `ping`.
|
|
375
|
+
* Messages name the expected value but never echo the client-supplied one.
|
|
366
376
|
*
|
|
367
377
|
* The expectation a live session supplies is a different rule over a different input, so it
|
|
368
378
|
* is {@link inferSessionHeaderIssue} rather than a second arm of this one.
|
|
@@ -380,7 +390,7 @@ function inferHeaderTarget(request) {
|
|
|
380
390
|
function inferHeaderIssue(request, invocation) {
|
|
381
391
|
const protocol = request.headers.get(MCP_PROTOCOL_VERSION_HEADER);
|
|
382
392
|
if (!isModernRequest(invocation)) {
|
|
383
|
-
if (isInitializeRequest(invocation) || protocol !== null) return void 0;
|
|
393
|
+
if (isInitializeRequest(invocation) || isPingRequest(invocation) || protocol !== null) return void 0;
|
|
384
394
|
return {
|
|
385
395
|
header: "MCP-Protocol-Version",
|
|
386
396
|
reason: "missing",
|
|
@@ -708,8 +718,8 @@ var HTTPDisconnect = class {
|
|
|
708
718
|
* comparison; a missing, mismatched, or invalidly encoded value returns HTTP `400` + `-32020`.
|
|
709
719
|
* A protocol header naming a modern revision holds the request to that revision whatever shape
|
|
710
720
|
* its body arrived in, so a body with no parsable modern `_meta` returns HTTP `400` + `-32602`.
|
|
711
|
-
* Headerless `initialize`
|
|
712
|
-
* session to supply its pinned version. A legacy-shaped request carrying a protocol header is
|
|
721
|
+
* Headerless `initialize` and id-bearing legacy `ping` are accepted; other headerless requests
|
|
722
|
+
* need a live legacy session to supply its pinned version. A legacy-shaped request carrying a protocol header is
|
|
713
723
|
* otherwise admitted only for a legacy revision; a revision this server does not implement
|
|
714
724
|
* returns HTTP `400` + `-32022` whose `supported` names the legacy revisions this door accepts.
|
|
715
725
|
* A present origin must occur in `origin.origins` unless validation is
|
|
@@ -719,6 +729,8 @@ var HTTPDisconnect = class {
|
|
|
719
729
|
* handler through `MCPDispatchOptions.signal`. After every transport validation and immediately
|
|
720
730
|
* before dispatch, the optional synchronous `caller` extractor reads front-middleware state; a
|
|
721
731
|
* defined value is added to `MCPDispatchOptions`, while `undefined` is omitted.
|
|
732
|
+
* For legacy `initialize`, the dispatch response is recorded as `initialization` before
|
|
733
|
+
* JSON or SSE framing only when consumer state is an object with `'session' in context.state`.
|
|
722
734
|
*
|
|
723
735
|
* @typeParam TState - The consumer's opaque per-request route state type
|
|
724
736
|
* @param mcp - The transport-agnostic MCP dispatcher to dispatch through
|
|
@@ -811,6 +823,9 @@ function createMCPPostHandler(mcp, options) {
|
|
|
811
823
|
queueMicrotask(() => void sendEventStream(response, stream));
|
|
812
824
|
return disconnect.bridge(stream);
|
|
813
825
|
}
|
|
826
|
+
if (era === "legacy" && invocation.method === "initialize" && isObject(context?.state) && "session" in context.state) {
|
|
827
|
+
if (!Reflect.set(context.state, "initialization", response)) throw new Error("MCP initialization state is not writable");
|
|
828
|
+
}
|
|
814
829
|
const status = inferStatus(response, era);
|
|
815
830
|
if (response === void 0) return new Response(null, { status });
|
|
816
831
|
if (status === 200 && streaming && acceptsEventStream(request)) {
|
|
@@ -1228,32 +1243,37 @@ var WebSocketClientTransport = class {
|
|
|
1228
1243
|
*
|
|
1229
1244
|
* @remarks
|
|
1230
1245
|
* - **Composes `@orkestrel/process`.** `start()` builds one supervised
|
|
1231
|
-
* {@link import('@orkestrel/process/server').
|
|
1246
|
+
* {@link import('@orkestrel/process/server').Supervisor} with `writable: true`, so the child's
|
|
1232
1247
|
* `stdin`/`stdout` are the JSON-RPC channel and its `stderr` is retained as bounded evidence
|
|
1233
|
-
* rather than parsed as protocol. The supervisor owns spawn
|
|
1234
|
-
* - **Inbound (`message`).** Standard output is drained eagerly through
|
|
1235
|
-
*
|
|
1248
|
+
* rather than parsed as protocol. The supervisor owns spawn and termination.
|
|
1249
|
+
* - **Inbound (`message`).** Standard output is drained eagerly through Node's `readline`,
|
|
1250
|
+
* so a multi-byte UTF-8 sequence split across two reads is
|
|
1236
1251
|
* decoded whole and a final line written without a trailing newline still arrives. Each framed
|
|
1237
1252
|
* line is decoded and delivered through the shared {@link dispatchLines} helper — a well-formed
|
|
1238
1253
|
* {@link JSONRPCMessage} emits `message`, a malformed line emits `error` (never throws).
|
|
1239
1254
|
* - **Outbound (`send`).** `send(message)` writes one newline-terminated `JSON.stringify`d line
|
|
1240
|
-
* through the supervisor's `
|
|
1255
|
+
* through the supervisor's `deliver` and awaits its answer, so this promise settles only after the
|
|
1241
1256
|
* host reports the line handled rather than the moment the write is queued. The supervisor never
|
|
1242
1257
|
* rejects — it answers `false` for a channel that was closed, destroyed, or ended, for a write
|
|
1243
1258
|
* that failed, or for one that remained unconfirmed through `delivery`. A call made without a
|
|
1244
1259
|
* live child rejects as not connected; a `false` answer from a live child rejects as unable to
|
|
1245
1260
|
* deliver. The supervisor does not disclose which cause produced that answer.
|
|
1246
|
-
* - **`close()`**
|
|
1247
|
-
*
|
|
1248
|
-
* `evidence`,
|
|
1249
|
-
*
|
|
1250
|
-
* A
|
|
1261
|
+
* - **`close()`** ends the child's input and waits up to {@link MCP_STDIO_GRACE} for native exit,
|
|
1262
|
+
* including any pending input flush. Only after that wait does the supervisor terminate a child
|
|
1263
|
+
* that remains alive. Teardown freezes `evidence`, closes the reader, and settles `exit`, then
|
|
1264
|
+
* fires `close` once (idempotent).
|
|
1265
|
+
* A child that exits on input end within the grace receives no termination signal, so it must
|
|
1266
|
+
* end its own child processes on input end; only escalation reaches its process tree.
|
|
1267
|
+
* If an `MCPClient` request `timeout` is shorter than the grace and the close outlasts that
|
|
1268
|
+
* timeout, `disconnect()` rejects with `MCP transport close timed out after <timeout>ms`.
|
|
1269
|
+
* The close keeps running, and a later caller joins it while it remains pending.
|
|
1270
|
+
* A line already framed behind the one being delivered is dropped rather than
|
|
1251
1271
|
* emitted onto a transport whose teardown has begun. A `close()` issued while that teardown runs
|
|
1252
1272
|
* joins it rather than opening a second one, so it resolves only after `close` has fired, and a
|
|
1253
1273
|
* `start()` issued while it runs waits behind the same barrier, so lifetimes never overlap. A
|
|
1254
1274
|
* descendant can retain an inherited stdout pipe after the child exits; the supervisor's `drain`
|
|
1255
|
-
* bound cuts that wait off
|
|
1256
|
-
*
|
|
1275
|
+
* bound cuts that wait off independently of the input grace. Escalation belongs to the host:
|
|
1276
|
+
* a POSIX host signals the
|
|
1257
1277
|
* child's own process group `SIGTERM`, waits the grace window, then `SIGKILL`s through the same
|
|
1258
1278
|
* route, so the kill reaches grandchildren rather than orphaning them, while Windows ends the
|
|
1259
1279
|
* tree with `taskkill /F /T`, which nothing in the child can intercept.
|
|
@@ -1315,7 +1335,7 @@ var StdioClientTransport = class {
|
|
|
1315
1335
|
}
|
|
1316
1336
|
if (this.#process !== void 0 && !this.#closed) return;
|
|
1317
1337
|
this.#closed = false;
|
|
1318
|
-
const child = new
|
|
1338
|
+
const child = new Supervisor({
|
|
1319
1339
|
command: {
|
|
1320
1340
|
file: this.#command,
|
|
1321
1341
|
arguments: [...this.#args],
|
|
@@ -1325,11 +1345,23 @@ var StdioClientTransport = class {
|
|
|
1325
1345
|
grace: PROCESS_GRACE,
|
|
1326
1346
|
delivery: this.#delivery,
|
|
1327
1347
|
writable: true
|
|
1348
|
+
}, {
|
|
1349
|
+
chunk: () => void 0,
|
|
1350
|
+
fault: (cause) => this.#emitter.emit("error", cause),
|
|
1351
|
+
close: () => reader.close(),
|
|
1352
|
+
terminal: () => void 0,
|
|
1353
|
+
teardown: () => reader.close()
|
|
1354
|
+
});
|
|
1355
|
+
const reader = createInterface({
|
|
1356
|
+
input: child.stdout,
|
|
1357
|
+
crlfDelay: Infinity
|
|
1328
1358
|
});
|
|
1329
1359
|
this.#process = child;
|
|
1330
|
-
|
|
1360
|
+
reader.on("line", (line) => {
|
|
1361
|
+
if (this.#closed || this.#process !== child) return;
|
|
1362
|
+
dispatchLines(this.#emitter, [line]);
|
|
1363
|
+
});
|
|
1331
1364
|
child.exit.then((exit) => this.#onExit(child, exit));
|
|
1332
|
-
this.#pump(child);
|
|
1333
1365
|
}
|
|
1334
1366
|
/**
|
|
1335
1367
|
* Sends one newline-delimited JSON-RPC message to the live child.
|
|
@@ -1344,7 +1376,7 @@ var StdioClientTransport = class {
|
|
|
1344
1376
|
async send(message) {
|
|
1345
1377
|
const child = this.#closed ? void 0 : this.#process;
|
|
1346
1378
|
if (child === void 0) throw new Error("stdio transport is not connected");
|
|
1347
|
-
if (!await child.
|
|
1379
|
+
if (!await child.deliver(Buffer.from(`${JSON.stringify(message)}\n`, "utf8"))) throw new Error("stdio transport could not deliver the message");
|
|
1348
1380
|
}
|
|
1349
1381
|
async close() {
|
|
1350
1382
|
if (this.#closed && this.#closing === void 0) return;
|
|
@@ -1356,17 +1388,19 @@ var StdioClientTransport = class {
|
|
|
1356
1388
|
this.#closed = true;
|
|
1357
1389
|
const child = this.#process;
|
|
1358
1390
|
if (child !== void 0) {
|
|
1391
|
+
const expired = Promise.withResolvers();
|
|
1392
|
+
const timer = setTimeout(expired.resolve, MCP_STDIO_GRACE);
|
|
1393
|
+
try {
|
|
1394
|
+
child.end();
|
|
1395
|
+
await Promise.race([child.ending, expired.promise]);
|
|
1396
|
+
} finally {
|
|
1397
|
+
clearTimeout(timer);
|
|
1398
|
+
}
|
|
1359
1399
|
await child.destroy();
|
|
1360
1400
|
this.#report(await child.exit);
|
|
1361
1401
|
}
|
|
1362
1402
|
this.#emitter.emit("close");
|
|
1363
1403
|
}
|
|
1364
|
-
async #pump(child) {
|
|
1365
|
-
for await (const line of child.lines) {
|
|
1366
|
-
if (this.#closed || this.#process !== child) return;
|
|
1367
|
-
dispatchLines(this.#emitter, [line]);
|
|
1368
|
-
}
|
|
1369
|
-
}
|
|
1370
1404
|
#onExit(child, exit) {
|
|
1371
1405
|
if (this.#process !== child) return;
|
|
1372
1406
|
if (this.#closed) return;
|
|
@@ -1944,14 +1978,20 @@ function createStdioServer(mcp, options) {
|
|
|
1944
1978
|
* readSessionHeader}: a valid id touches the entry and sets `context.state.session`; an
|
|
1945
1979
|
* absent / unknown id whose (guarded) body parses to an `initialize` request ({@link
|
|
1946
1980
|
* isInitializeRequest}) mints a fresh {@link MCPSession} (`crypto.randomUUID()`, the `session`
|
|
1947
|
-
* options group) and sets `context.state.session
|
|
1981
|
+
* options group) and sets `context.state.session`. An id-bearing legacy `ping` with no
|
|
1982
|
+
* session header passes through without session state, a supplied protocol header, or a
|
|
1983
|
+
* response stamp. Other unresolved requests → {@link rejectUnknownSession}
|
|
1948
1984
|
* (`404`). The
|
|
1949
1985
|
* minted entry pins the negotiated legacy revision, which is supplied to a later headerless
|
|
1950
1986
|
* live-session request. It then
|
|
1951
1987
|
* forwards a fresh `Request` carrying the buffered `text` (`next(forwarded)`) — never the
|
|
1952
1988
|
* already-consumed original — so the route re-reads the same body, and stamps the response
|
|
1953
|
-
* with {@link MCP_SESSION_HEADER}.
|
|
1954
|
-
*
|
|
1989
|
+
* with {@link MCP_SESSION_HEADER}. A candidate entry is stored and advertised only when
|
|
1990
|
+
* `context.state.initialization` carries a result. An `initialize` that would mint a session
|
|
1991
|
+
* stores none and advertises none when refused; a live session's header is returned unchanged.
|
|
1992
|
+
* The POST handler records that dispatch response before JSON or SSE framing.
|
|
1993
|
+
* The entry's `touched` instant is read after the downstream response, because it
|
|
1994
|
+
* means the last access: a request slower than `ttl` would
|
|
1955
1995
|
* otherwise store a session that is already expired, and the write-back RE-ASKS the store, so
|
|
1956
1996
|
* a `DELETE` arriving while the request was suspended is not undone.
|
|
1957
1997
|
* - **`GET {path}`.** Resolves the session the same way (no mint — only `initialize` mints);
|
|
@@ -2069,7 +2109,13 @@ function createMCPSession(options) {
|
|
|
2069
2109
|
version: inferLegacyVersion(parsed)
|
|
2070
2110
|
};
|
|
2071
2111
|
entry = created;
|
|
2072
|
-
} else return
|
|
2112
|
+
} else if (id === void 0 && isPingRequest(parsed)) return next(new Request(context.url, {
|
|
2113
|
+
method: "POST",
|
|
2114
|
+
headers: request.headers,
|
|
2115
|
+
body: text,
|
|
2116
|
+
signal: request.signal
|
|
2117
|
+
}));
|
|
2118
|
+
else return rejectUnknownSession();
|
|
2073
2119
|
}
|
|
2074
2120
|
if (!Reflect.set(context.state, "session", entry.session)) throw new Error("MCP session state is not writable");
|
|
2075
2121
|
const headers = new Headers(request.headers);
|
|
@@ -2088,7 +2134,7 @@ function createMCPSession(options) {
|
|
|
2088
2134
|
signal: request.signal
|
|
2089
2135
|
}));
|
|
2090
2136
|
if (created !== void 0) {
|
|
2091
|
-
if (!response.ok) return response;
|
|
2137
|
+
if (!response.ok || context.state.initialization?.result === void 0) return response;
|
|
2092
2138
|
store.set(created.session.id, {
|
|
2093
2139
|
...created,
|
|
2094
2140
|
touched: clock()
|
|
@@ -2102,6 +2148,6 @@ function createMCPSession(options) {
|
|
|
2102
2148
|
};
|
|
2103
2149
|
}
|
|
2104
2150
|
//#endregion
|
|
2105
|
-
export { DEFAULT_MCP_DELIVERY, DEFAULT_MCP_KEEPALIVE_INTERVAL, DEFAULT_MCP_PATH, DEFAULT_MCP_SESSION_CAPACITY, DEFAULT_MCP_SESSION_TTL, HTTPDisconnect, MCPSession, SSE_BUFFERING_DISABLED, SSE_BUFFERING_HEADER, SSE_KEEPALIVE_COMMENT, StdioClientTransport, StdioServerTransport, WebSocketClientTransport, WebSocketServerTransport, acceptsEventStream, allowsOrigin, createDuplexServerTransport, createHTTPClientTransport, createMCPContinuation, createMCPPostHandler, createMCPRoutes, createMCPSession, createStdioClientTransport, createStdioServer, createWebSocketClientTransport, createWebSocketServer, dispatchLines, extractLines, inferHeaderIssue, inferHeaderTarget, inferLegacyVersion, inferParameterRefusal, inferSessionHeaderIssue, inferStatus, readLastEventId, readSessionHeader, rejectUnknownSession, sendEventStream, upgradeRequestPath, writeLine };
|
|
2151
|
+
export { DEFAULT_MCP_DELIVERY, DEFAULT_MCP_KEEPALIVE_INTERVAL, DEFAULT_MCP_PATH, DEFAULT_MCP_SESSION_CAPACITY, DEFAULT_MCP_SESSION_TTL, HTTPDisconnect, MCPSession, MCP_STDIO_GRACE, SSE_BUFFERING_DISABLED, SSE_BUFFERING_HEADER, SSE_KEEPALIVE_COMMENT, StdioClientTransport, StdioServerTransport, WebSocketClientTransport, WebSocketServerTransport, acceptsEventStream, allowsOrigin, createDuplexServerTransport, createHTTPClientTransport, createMCPContinuation, createMCPPostHandler, createMCPRoutes, createMCPSession, createStdioClientTransport, createStdioServer, createWebSocketClientTransport, createWebSocketServer, dispatchLines, extractLines, inferHeaderIssue, inferHeaderTarget, inferLegacyVersion, inferParameterRefusal, inferSessionHeaderIssue, inferStatus, readLastEventId, readSessionHeader, rejectUnknownSession, sendEventStream, upgradeRequestPath, writeLine };
|
|
2106
2152
|
|
|
2107
2153
|
//# sourceMappingURL=index.js.map
|