@orkestrel/mcp 0.0.8 → 0.0.10
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/browser/index.d.ts +25 -11
- package/dist/src/browser/index.js +43 -17
- package/dist/src/browser/index.js.map +1 -1
- package/dist/src/core/index.cjs +1264 -123
- package/dist/src/core/index.cjs.map +1 -1
- package/dist/src/core/index.d.cts +1114 -168
- package/dist/src/core/index.d.ts +1114 -168
- package/dist/src/core/index.js +1221 -121
- package/dist/src/core/index.js.map +1 -1
- package/dist/src/server/index.cjs +369 -69
- package/dist/src/server/index.cjs.map +1 -1
- package/dist/src/server/index.d.cts +241 -44
- package/dist/src/server/index.d.ts +241 -44
- package/dist/src/server/index.js +360 -71
- package/dist/src/server/index.js.map +1 -1
- package/package.json +8 -8
|
@@ -6,10 +6,15 @@ 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
|
+
import { JSONRPCRequest } from '../core/index.ts';
|
|
10
|
+
import { JSONRPCResponse } from '../core/index.ts';
|
|
11
|
+
import { MCPEra } from '../core/index.ts';
|
|
9
12
|
import { MCPServerInterface } from '../core/index.ts';
|
|
10
13
|
import { MCPTransportInterface } from '../core/index.ts';
|
|
14
|
+
import { MCPVersion } from '../core/index.ts';
|
|
11
15
|
import { MiddlewareHandler } from '@orkestrel/server';
|
|
12
16
|
import { NodeWebSocketInterface } from '@orkestrel/websocket';
|
|
17
|
+
import { RouteContext } from '@orkestrel/router';
|
|
13
18
|
import { RouteInput } from '@orkestrel/router';
|
|
14
19
|
import { StreamInterface } from '@orkestrel/server';
|
|
15
20
|
import { UpgradeHandler } from '@orkestrel/server';
|
|
@@ -29,6 +34,22 @@ import { UpgradeHandler } from '@orkestrel/server';
|
|
|
29
34
|
*/
|
|
30
35
|
export declare function acceptsEventStream(request: Request): boolean;
|
|
31
36
|
|
|
37
|
+
/**
|
|
38
|
+
* Whether an HTTP request satisfies the endpoint's origin gate.
|
|
39
|
+
*
|
|
40
|
+
* @remarks
|
|
41
|
+
* Validation is enabled by default. A request without `Origin` is allowed. A canonical origin
|
|
42
|
+
* whose host is the `localhost` or `[::1]` literal, or belongs to the `127.0.0.0/8` literal
|
|
43
|
+
* range, is allowed without configuration; every other present origin must occur exactly in
|
|
44
|
+
* the caller-supplied list. Invalid and opaque (`null`) origins are denied. `enabled: false`
|
|
45
|
+
* delegates validation to an upstream layer and allows the request through this gate.
|
|
46
|
+
*
|
|
47
|
+
* @param request - The fetch-standard request to validate
|
|
48
|
+
* @param options - Shared origin validation and delegation options
|
|
49
|
+
* @returns `true` when the request may reach MCP dispatch
|
|
50
|
+
*/
|
|
51
|
+
export declare function allowsOrigin(request: Request, options?: MCPOriginOptions): boolean;
|
|
52
|
+
|
|
32
53
|
/**
|
|
33
54
|
* Bridge a message-channel {@link ClientTransportInterface} (the shape the stdio and
|
|
34
55
|
* WebSocket SERVER transports already implement) into the environment-agnostic
|
|
@@ -86,9 +107,9 @@ export declare function bridgeMessageTransport(transport: ClientTransportInterfa
|
|
|
86
107
|
* correlation. Add `options.headers` (e.g. an `Authorization` bearer) to reach a guarded
|
|
87
108
|
* server. `start` / `close` hold no connection; against a STATEFUL server it captures the
|
|
88
109
|
* `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
|
|
90
|
-
* subsequent request
|
|
91
|
-
*
|
|
110
|
+
* the initialize result's `protocolVersion` and sends `mcp-protocol-version` alone on each
|
|
111
|
+
* subsequent legacy request. Modern requests derive protocol and method headers directly
|
|
112
|
+
* from the message, plus a name header only for `tools/call`.
|
|
92
113
|
*
|
|
93
114
|
* @param options - `url` (the remote endpoint; REQUIRED), optional `headers` merged onto
|
|
94
115
|
* every request, optional `fetch` (default `globalThis.fetch`), and optional `timeout`
|
|
@@ -113,12 +134,20 @@ export declare function createHTTPClientTransport(options: HTTPClientTransportOp
|
|
|
113
134
|
* Create the Streamable-HTTP POST handler used by `createMCPRoutes`.
|
|
114
135
|
*
|
|
115
136
|
* @remarks
|
|
116
|
-
*
|
|
117
|
-
*
|
|
118
|
-
*
|
|
119
|
-
*
|
|
137
|
+
* Modern requests require matching protocol/method headers and a matching name header only
|
|
138
|
+
* for `tools/call`; mismatch returns HTTP `400` + `-32020`. Headerless `initialize` is
|
|
139
|
+
* accepted, while every other headerless request needs a live legacy session to supply its
|
|
140
|
+
* pinned version. A present origin must occur in `origin.origins` unless validation is
|
|
141
|
+
* explicitly delegated upstream. Modern dispatch errors use their protocol status map; legacy
|
|
142
|
+
* errors remain in-band at HTTP `200`. A streamed response composes the fetch-standard request
|
|
143
|
+
* signal with response-body cancellation and supplies the result to every dispatched modern
|
|
144
|
+
* handler through `MCPDispatchOptions.signal`. After every transport validation and immediately
|
|
145
|
+
* before dispatch, the optional synchronous `caller` extractor reads front-middleware state; a
|
|
146
|
+
* defined value is added to `MCPDispatchOptions`, while `undefined` is omitted.
|
|
147
|
+
*
|
|
148
|
+
* @typeParam TState - The consumer's opaque per-request route state type
|
|
120
149
|
* @param mcp - The transport-agnostic MCP server to dispatch through
|
|
121
|
-
* @param
|
|
150
|
+
* @param options - Optional streaming, origin-validation, SSE keepalive, and caller-extraction options
|
|
122
151
|
* @returns A request handler for the stateless MCP POST route
|
|
123
152
|
*
|
|
124
153
|
* @example
|
|
@@ -127,15 +156,15 @@ export declare function createHTTPClientTransport(options: HTTPClientTransportOp
|
|
|
127
156
|
* import { createMCPPostHandler } from '@orkestrel/mcp/server'
|
|
128
157
|
* import { createToolManager } from '@orkestrel/tool'
|
|
129
158
|
*
|
|
130
|
-
* const mcp = createMCPServer({ name: 'docs', version: '1.0.0', tools: createToolManager() })
|
|
131
|
-
* const handler = createMCPPostHandler(mcp, true)
|
|
159
|
+
* const mcp = createMCPServer({ identity: { name: 'docs', version: '1.0.0' }, tools: createToolManager() })
|
|
160
|
+
* const handler = createMCPPostHandler(mcp, { streaming: true })
|
|
132
161
|
* await handler(new Request('http://localhost/mcp', {
|
|
133
162
|
* method: 'POST',
|
|
134
163
|
* body: '{"jsonrpc":"2.0","method":"ping","id":1}',
|
|
135
164
|
* }))
|
|
136
165
|
* ```
|
|
137
166
|
*/
|
|
138
|
-
export declare function createMCPPostHandler(mcp: MCPServerInterface,
|
|
167
|
+
export declare function createMCPPostHandler<TState = unknown>(mcp: MCPServerInterface, options?: HTTPHandlerOptions<TState>): (request: Request, context?: RouteContext<string, TState>) => Promise<Response>;
|
|
139
168
|
|
|
140
169
|
/**
|
|
141
170
|
* Create the MCP Streamable-HTTP transport routes — mounts a transport-agnostic
|
|
@@ -152,12 +181,11 @@ export declare function createMCPPostHandler(mcp: MCPServerInterface, streaming:
|
|
|
152
181
|
* - A **transport** failure — a malformed JSON body, or a parsed value that is not a
|
|
153
182
|
* JSON-RPC REQUEST — is an HTTP `400` carrying a JSON-RPC error BODY (`-32700` Parse
|
|
154
183
|
* error / `-32600` Invalid Request, id `null`).
|
|
155
|
-
* -
|
|
156
|
-
*
|
|
157
|
-
*
|
|
158
|
-
* -
|
|
159
|
-
*
|
|
160
|
-
* envelope (the error is in-band per JSON-RPC, NOT an HTTP error).
|
|
184
|
+
* - Modern protocol/method/name headers are validated against the body; a mismatch is
|
|
185
|
+
* HTTP `400` + `-32020`. Headerless initialize is accepted, a live legacy session supplies
|
|
186
|
+
* its pinned revision, and every other headerless request is rejected.
|
|
187
|
+
* - Legacy dispatch errors stay IN-BAND at HTTP `200`; modern errors map to `400` for
|
|
188
|
+
* `-32020` / `-32021` / `-32022` / `-32602`, `404` for `-32601`, and `200` otherwise.
|
|
161
189
|
* - A **notification** (a request with no `id`, which `dispatch` resolves to
|
|
162
190
|
* `undefined`) is a `202 Accepted` with no body.
|
|
163
191
|
*
|
|
@@ -172,13 +200,15 @@ export declare function createMCPPostHandler(mcp: MCPServerInterface, streaming:
|
|
|
172
200
|
* validates the `mcp-session-id`, and serves the resumable `GET {path}` + `DELETE {path}`,
|
|
173
201
|
* leaving this route to dispatch the validated `POST`.
|
|
174
202
|
*
|
|
175
|
-
* This is MECHANISM, not policy: compose auth /
|
|
176
|
-
*
|
|
203
|
+
* This is MECHANISM, not policy: compose auth / rate-limiting (and the session middleware)
|
|
204
|
+
* IN FRONT as ordinary middleware; the optional `origin` group carries the deployment's shared
|
|
205
|
+
* allowlist or explicitly delegates validation to an upstream layer.
|
|
177
206
|
*
|
|
178
207
|
* @typeParam TState - The consumer's opaque per-request state type
|
|
179
208
|
* @param mcp - The transport-agnostic {@link MCPServerInterface} to expose over HTTP
|
|
180
209
|
* @param options - Optional `path` (default {@link DEFAULT_MCP_PATH}) and `streaming`
|
|
181
|
-
* (default `true`); see
|
|
210
|
+
* (default `true`), plus shared origin, keepalive, and synchronous caller-extraction options; see
|
|
211
|
+
* {@link HTTPTransportOptions}
|
|
182
212
|
* @returns The {@link RouteInput}s to register with the router
|
|
183
213
|
*
|
|
184
214
|
* @example
|
|
@@ -186,11 +216,11 @@ export declare function createMCPPostHandler(mcp: MCPServerInterface, streaming:
|
|
|
186
216
|
* import { createMCPServer, createToolManager } from '@src/core'
|
|
187
217
|
* import { createMCPRoutes } from '@src/server'
|
|
188
218
|
*
|
|
189
|
-
* const mcp = createMCPServer({ name: 'docs', version: '1.0.0', tools: createToolManager() })
|
|
219
|
+
* const mcp = createMCPServer({ identity: { name: 'docs', version: '1.0.0' }, tools: createToolManager() })
|
|
190
220
|
* const routes = createMCPRoutes(mcp) // POST /mcp dispatches JSON-RPC (JSON or SSE per Accept)
|
|
191
221
|
* ```
|
|
192
222
|
*/
|
|
193
|
-
export declare function createMCPRoutes<TState = unknown>(mcp: MCPServerInterface, options?: HTTPTransportOptions): readonly RouteInput<string, TState>[];
|
|
223
|
+
export declare function createMCPRoutes<TState = unknown>(mcp: MCPServerInterface, options?: HTTPTransportOptions<TState>): readonly RouteInput<string, TState>[];
|
|
194
224
|
|
|
195
225
|
/**
|
|
196
226
|
* Create the native MCP session {@link MiddlewareHandler} — the plug-and-play stateful layer
|
|
@@ -204,12 +234,17 @@ export declare function createMCPRoutes<TState = unknown>(mcp: MCPServerInterfac
|
|
|
204
234
|
* `path` (default {@link DEFAULT_MCP_PATH}); a request to any other path passes straight
|
|
205
235
|
* through (`next()`).
|
|
206
236
|
*
|
|
237
|
+
* A modern-shaped POST also passes straight through via `next()`, ignoring any session id.
|
|
238
|
+
* The remaining behavior is the legacy session layer:
|
|
239
|
+
*
|
|
207
240
|
* - **`POST {path}`.** Buffers `const text = await request.text()` (so the downstream route
|
|
208
241
|
* can re-read it via a freshly-built forwarded `Request`). Resolves a session via {@link
|
|
209
242
|
* readSessionHeader}: a VALID id touches the entry and sets `context.state.session`; an
|
|
210
243
|
* ABSENT / unknown id whose (guarded) body parses to an `initialize` request ({@link
|
|
211
244
|
* isInitializeRequest}) MINTS a fresh {@link MCPSession} (`crypto.randomUUID()`, `capacity`)
|
|
212
|
-
* and sets `context.state.session`; neither → {@link rejectUnknownSession} (`404`).
|
|
245
|
+
* and sets `context.state.session`; neither → {@link rejectUnknownSession} (`404`). The
|
|
246
|
+
* minted entry pins the negotiated legacy revision, which is supplied to a later headerless
|
|
247
|
+
* live-session request. It then
|
|
213
248
|
* FORWARDS a fresh `Request` carrying the buffered `text` (`next(forwarded)`) — never the
|
|
214
249
|
* already-consumed original — so the route re-reads the same body, and stamps the response
|
|
215
250
|
* with {@link MCP_SESSION_HEADER}.
|
|
@@ -217,8 +252,8 @@ export declare function createMCPRoutes<TState = unknown>(mcp: MCPServerInterfac
|
|
|
217
252
|
* an invalid / unknown id is the same `404`. A valid session opens the resumable
|
|
218
253
|
* server→client stream via `@orkestrel/server`'s {@link import('@orkestrel/server').openStream}:
|
|
219
254
|
* replays every event after the client's `Last-Event-ID` ({@link readLastEventId}) BEFORE
|
|
220
|
-
* attaching the stream for live pushes, then attaches;
|
|
221
|
-
* detaches it. Long-lived — never `end()`ed here.
|
|
255
|
+
* attaching the stream for live pushes, then attaches; cancellation of the streamed response
|
|
256
|
+
* body composes with `request.signal` and detaches it. Long-lived — never `end()`ed here.
|
|
222
257
|
* - **`DELETE {path}`.** Resolves the session; a valid id deletes it from the store and answers
|
|
223
258
|
* `204`; an invalid / unknown id is the same `404`.
|
|
224
259
|
*
|
|
@@ -232,7 +267,8 @@ export declare function createMCPRoutes<TState = unknown>(mcp: MCPServerInterfac
|
|
|
232
267
|
* @param options - Optional `path` (default {@link DEFAULT_MCP_PATH}), `ttl` (idle-session
|
|
233
268
|
* sweep window, ms — omit for sessions that live until an explicit `DELETE`), `capacity`
|
|
234
269
|
* (the folded per-session replay-log bound), and `clock` (the deterministic epoch-ms clock;
|
|
235
|
-
* defaults to `Date.now`); see
|
|
270
|
+
* defaults to `Date.now`), plus the shared `origin` validation options; see
|
|
271
|
+
* {@link MCPSessionOptions}
|
|
236
272
|
* @returns A {@link MiddlewareHandler} that mints / validates sessions + serves the resumable
|
|
237
273
|
* `GET` / `DELETE`
|
|
238
274
|
*
|
|
@@ -241,13 +277,22 @@ export declare function createMCPRoutes<TState = unknown>(mcp: MCPServerInterfac
|
|
|
241
277
|
* import { createMCPServer, createToolManager } from '@src/core'
|
|
242
278
|
* import { createMCPRoutes, createMCPSession } from '@src/server'
|
|
243
279
|
*
|
|
244
|
-
* const mcp = createMCPServer({ name: 'docs', version: '1.0.0', tools: createToolManager() })
|
|
280
|
+
* const mcp = createMCPServer({ identity: { name: 'docs', version: '1.0.0' }, tools: createToolManager() })
|
|
245
281
|
* router.use(createMCPSession({ ttl: 60_000 })) // stateful: mint + validate + resumable GET / DELETE
|
|
246
282
|
* router.add(createMCPRoutes(mcp)) // the route stays session-agnostic
|
|
247
283
|
* ```
|
|
248
284
|
*/
|
|
249
285
|
export declare function createMCPSession<TState extends MCPSessionState>(options?: MCPSessionOptions): MiddlewareHandler<TState>;
|
|
250
286
|
|
|
287
|
+
/**
|
|
288
|
+
* Create a readable stream from its pull and cancellation behaviours.
|
|
289
|
+
*
|
|
290
|
+
* @param pull - The behaviour that supplies the stream's next chunk
|
|
291
|
+
* @param cancel - The behaviour that releases the stream after consumer cancellation
|
|
292
|
+
* @returns A readable stream backed by the supplied behaviours
|
|
293
|
+
*/
|
|
294
|
+
export declare function createReadableStream<T>(pull: (controller: ReadableStreamDefaultController<T>) => void | PromiseLike<void>, cancel: (reason?: unknown) => void | PromiseLike<void>): ReadableStream<T>;
|
|
295
|
+
|
|
251
296
|
/**
|
|
252
297
|
* Create the stdio CLIENT transport for an {@link import('@src/core').MCPClientInterface}
|
|
253
298
|
* — a {@link ClientTransportInterface} that spawns and drives a CHILD PROCESS MCP server
|
|
@@ -307,7 +352,7 @@ export declare function createStdioClientTransport(options: StdioClientTransport
|
|
|
307
352
|
* import { createMCPServer, createToolManager } from '@src/core'
|
|
308
353
|
* import { createStdioServer } from '@src/server'
|
|
309
354
|
*
|
|
310
|
-
* const mcp = createMCPServer({ name: 'docs', version: '1.0.0', tools: createToolManager() })
|
|
355
|
+
* const mcp = createMCPServer({ identity: { name: 'docs', version: '1.0.0' }, tools: createToolManager() })
|
|
311
356
|
* createStdioServer(mcp).start() // an MCP client now connects over this process's stdio
|
|
312
357
|
* ```
|
|
313
358
|
*/
|
|
@@ -390,7 +435,7 @@ export declare function createWebSocketClientTransport(options: WebSocketClientT
|
|
|
390
435
|
* import { createMCPServer, createToolManager } from '@src/core'
|
|
391
436
|
* import { createWebSocketServer } from '@src/server'
|
|
392
437
|
*
|
|
393
|
-
* const mcp = createMCPServer({ name: 'docs', version: '1.0.0', tools: createToolManager() })
|
|
438
|
+
* const mcp = createMCPServer({ identity: { name: 'docs', version: '1.0.0' }, tools: createToolManager() })
|
|
394
439
|
* server.upgrade(createWebSocketServer(mcp)) // an MCP client now connects over ws://…/mcp
|
|
395
440
|
* ```
|
|
396
441
|
*/
|
|
@@ -410,6 +455,16 @@ export declare function createWebSocketServer(mcp: MCPServerInterface, options?:
|
|
|
410
455
|
*/
|
|
411
456
|
export declare function decodeEvent(data: string): JSONRPCMessage | undefined;
|
|
412
457
|
|
|
458
|
+
/**
|
|
459
|
+
* The default interval in milliseconds between SSE keepalive comments on held-open MCP
|
|
460
|
+
* responses.
|
|
461
|
+
*
|
|
462
|
+
* @remarks
|
|
463
|
+
* Fifteen seconds is infrequent enough to avoid chatty idle connections while bounding dead
|
|
464
|
+
* client detection and staying comfortably inside common intermediary idle windows.
|
|
465
|
+
*/
|
|
466
|
+
export declare const DEFAULT_MCP_KEEPALIVE_INTERVAL = 15000;
|
|
467
|
+
|
|
413
468
|
/** The default request path `createMCPRoutes` mounts the transport's `POST` route at. */
|
|
414
469
|
export declare const DEFAULT_MCP_PATH = "/mcp";
|
|
415
470
|
|
|
@@ -513,16 +568,17 @@ export declare function extractLines(buffer: string, chunk: string): LineExtract
|
|
|
513
568
|
* readEventStream}) — the inverse of the server's `openStream` seam, so the wire
|
|
514
569
|
* round-trips. A `202`
|
|
515
570
|
* Accepted (a notification) carries no body and emits nothing.
|
|
516
|
-
* - **Session and protocol
|
|
571
|
+
* - **Session and protocol headers.** `start()` is a no-op (a
|
|
517
572
|
* request/response transport opens no long-lived connection). The
|
|
518
573
|
* `mcp-session-id` response header, when a STATEFUL server sends one (on
|
|
519
574
|
* `initialize`), is captured into `session` and then ECHOED as the
|
|
520
575
|
* `mcp-session-id` request header on every SUBSEQUENT request — so an
|
|
521
576
|
* `MCPClient` passes a stateful server's session validation. The
|
|
522
577
|
* initialize result's `protocolVersion` is likewise captured, but only
|
|
523
|
-
* when it is a SUPPORTED value, and echoed as `mcp-protocol-version` on
|
|
524
|
-
*
|
|
525
|
-
*
|
|
578
|
+
* when it is a SUPPORTED value, and echoed as `mcp-protocol-version` alone on
|
|
579
|
+
* subsequent legacy requests. Modern requests instead derive protocol and method
|
|
580
|
+
* headers from the message, plus the name header only for `tools/call`.
|
|
581
|
+
* Before initialize returns, neither captured legacy header is sent.
|
|
526
582
|
* `close()` clears the captured protocol so a reconnect's `initialize`
|
|
527
583
|
* POST is headerless; the captured `session` persists across `close()`.
|
|
528
584
|
* - **Total at the boundary (§14).** Every reply is narrowed (`parseJSONRPCMessage`,
|
|
@@ -575,28 +631,79 @@ export declare interface HTTPClientTransportOptions {
|
|
|
575
631
|
}
|
|
576
632
|
|
|
577
633
|
/**
|
|
578
|
-
* Options
|
|
579
|
-
* response is allowed. `createMCPRoutes` is STATELESS; sessions are a separate middleware
|
|
580
|
-
* ({@link import('./middlewares.js').createMCPSession}), composed via `server.use`.
|
|
634
|
+
* Options shared by the MCP Streamable-HTTP POST handler and route factory.
|
|
581
635
|
*
|
|
582
636
|
* @remarks
|
|
583
|
-
* - `path` — the request path the single `POST` route answers; defaults to
|
|
584
|
-
* {@link import('./constants.js').DEFAULT_MCP_PATH} (`'/mcp'`). `GET` / `DELETE` to this
|
|
585
|
-
* path get the spine's automatic `405` unless a {@link
|
|
586
|
-
* import('./middlewares.js').createMCPSession} middleware (which owns the same `path`) is
|
|
587
|
-
* mounted IN FRONT to serve them.
|
|
588
637
|
* - `streaming` — when `true` (the DEFAULT) the transport MAY answer with a
|
|
589
638
|
* Server-Sent-Events response (one `data:` event carrying the JSON-RPC reply, then
|
|
590
639
|
* the stream ends) whenever the client's `Accept` header includes
|
|
591
640
|
* `text/event-stream`; when `false` it always answers with a plain JSON body. Either
|
|
592
641
|
* mode carries the SAME JSON-RPC response envelope — the choice is purely the wire
|
|
593
642
|
* framing the Streamable-HTTP spec lets the client negotiate.
|
|
643
|
+
* - `origin` — the shared origin-validation options passed to both the route and session
|
|
644
|
+
* enforcement sites. Validation is enabled by default: requests without `Origin` pass,
|
|
645
|
+
* canonical loopback-literal origins pass, and every other present `Origin` must occur
|
|
646
|
+
* exactly in `origin.origins`. Set `origin.enabled` to `false` only when validation is
|
|
647
|
+
* delegated upstream; `origins` is ignored in that mode. Both enforcement sites must receive
|
|
648
|
+
* the same value.
|
|
649
|
+
* - `keepalive` — the SSE liveness options for held-open responses. `interval` defaults to
|
|
650
|
+
* {@link import('./constants.js').DEFAULT_MCP_KEEPALIVE_INTERVAL}. Keepalives never apply
|
|
651
|
+
* to unary responses.
|
|
652
|
+
* - `caller` — the synchronous extractor for consumer-asserted caller context already resolved
|
|
653
|
+
* by front middleware. It runs only for a validated request that will dispatch. Returning
|
|
654
|
+
* `undefined` omits caller context; a throw propagates.
|
|
594
655
|
*/
|
|
595
|
-
export declare interface
|
|
596
|
-
readonly path?: string;
|
|
656
|
+
export declare interface HTTPHandlerOptions<TState = unknown> {
|
|
597
657
|
readonly streaming?: boolean;
|
|
658
|
+
/** Must match the session layer's value; `origins` is ignored when `enabled` is `false`. */
|
|
659
|
+
readonly origin?: MCPOriginOptions;
|
|
660
|
+
readonly keepalive?: MCPKeepaliveOptions;
|
|
661
|
+
readonly caller?: MCPCallerHandler<TState>;
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
/**
|
|
665
|
+
* Options for `createMCPRoutes` — the mount path plus the shared POST-handler options.
|
|
666
|
+
* `createMCPRoutes` is STATELESS; sessions are a separate middleware ({@link
|
|
667
|
+
* import('./middlewares.js').createMCPSession}), composed via `server.use`.
|
|
668
|
+
*
|
|
669
|
+
* @remarks
|
|
670
|
+
* `path` is the request path the single `POST` route answers; it defaults to {@link
|
|
671
|
+
* import('./constants.js').DEFAULT_MCP_PATH} (`'/mcp'`). `GET` / `DELETE` to this path get
|
|
672
|
+
* the spine's automatic `405` unless a {@link import('./middlewares.js').createMCPSession}
|
|
673
|
+
* middleware owning the same path is mounted in front. The remaining options are inherited
|
|
674
|
+
* from {@link HTTPHandlerOptions}.
|
|
675
|
+
*/
|
|
676
|
+
export declare interface HTTPTransportOptions<TState = unknown> extends HTTPHandlerOptions<TState> {
|
|
677
|
+
readonly path?: string;
|
|
598
678
|
}
|
|
599
679
|
|
|
680
|
+
/**
|
|
681
|
+
* Infer the legacy revision an `initialize` request negotiates.
|
|
682
|
+
*
|
|
683
|
+
* @remarks
|
|
684
|
+
* A supported legacy request is pinned exactly. A modern, malformed, absent, or unsupported
|
|
685
|
+
* request selects the newest supported legacy revision, matching the core initialize result.
|
|
686
|
+
*
|
|
687
|
+
* @param request - The legacy initialize request
|
|
688
|
+
* @returns The negotiated legacy protocol revision
|
|
689
|
+
*/
|
|
690
|
+
export declare function inferLegacyVersion(request: JSONRPCRequest): MCPVersion;
|
|
691
|
+
|
|
692
|
+
/**
|
|
693
|
+
* Infer the HTTP status for one MCP dispatch outcome without changing its JSON-RPC body.
|
|
694
|
+
*
|
|
695
|
+
* @remarks
|
|
696
|
+
* Notifications are accepted with `202`. Legacy response envelopes retain uniform `200`
|
|
697
|
+
* status semantics, including in-band errors. Modern header/capability/version/parameter
|
|
698
|
+
* failures map to `400`, method-not-found maps to `404`, and every other modern result maps
|
|
699
|
+
* to `200`.
|
|
700
|
+
*
|
|
701
|
+
* @param response - The dispatch response, or `undefined` for a notification
|
|
702
|
+
* @param era - The structurally selected request era
|
|
703
|
+
* @returns The HTTP response status
|
|
704
|
+
*/
|
|
705
|
+
export declare function inferStatus(response: JSONRPCResponse | undefined, era: MCPEra): number;
|
|
706
|
+
|
|
600
707
|
/**
|
|
601
708
|
* The result of folding one more chunk of raw stdio bytes into a newline-framed
|
|
602
709
|
* buffer — every COMPLETE line extracted (newline-terminated in the wire bytes) plus
|
|
@@ -611,6 +718,27 @@ export declare interface LineExtraction {
|
|
|
611
718
|
readonly remainder: string;
|
|
612
719
|
}
|
|
613
720
|
|
|
721
|
+
/**
|
|
722
|
+
* Whether a modern HTTP request's required standard headers match its JSON-RPC body.
|
|
723
|
+
*
|
|
724
|
+
* @remarks
|
|
725
|
+
* Requires `MCP-Protocol-Version` to equal the reserved `_meta` version and `Mcp-Method`
|
|
726
|
+
* to equal `method`. `Mcp-Name` is required only for `tools/call`, where it must equal
|
|
727
|
+
* `params.name`; discovery and listing requests need no name because none is derivable.
|
|
728
|
+
* Legacy requests return `false` because this predicate models the modern contract only.
|
|
729
|
+
*
|
|
730
|
+
* @param request - The HTTP request carrying the headers
|
|
731
|
+
* @param message - The parsed JSON-RPC request body
|
|
732
|
+
* @returns `true` only when every method-applicable modern header matches
|
|
733
|
+
*/
|
|
734
|
+
export declare function matchesModernHeaders(request: Request, message: JSONRPCRequest): boolean;
|
|
735
|
+
|
|
736
|
+
/** The modern Streamable-HTTP request header carrying the JSON-RPC method name. */
|
|
737
|
+
export declare const MCP_METHOD_HEADER = "mcp-method";
|
|
738
|
+
|
|
739
|
+
/** The modern Streamable-HTTP request header carrying a named method's target. */
|
|
740
|
+
export declare const MCP_NAME_HEADER = "mcp-name";
|
|
741
|
+
|
|
614
742
|
/**
|
|
615
743
|
* The Streamable-HTTP transport header carrying the negotiated MCP protocol version
|
|
616
744
|
* on every post-initialize client request.
|
|
@@ -643,6 +771,52 @@ export declare const MCP_SESSION_HEADER = "mcp-session-id";
|
|
|
643
771
|
*/
|
|
644
772
|
export declare const MCP_WEBSOCKET_SUBPROTOCOL = "mcp";
|
|
645
773
|
|
|
774
|
+
/**
|
|
775
|
+
* Synchronously extract consumer-asserted caller context from an HTTP request after the
|
|
776
|
+
* transport has validated it for dispatch.
|
|
777
|
+
*
|
|
778
|
+
* @remarks
|
|
779
|
+
* Authentication belongs to middleware composed in front of the MCP route. This handler only
|
|
780
|
+
* reads what that middleware already resolved. Returning `undefined` supplies no caller; a
|
|
781
|
+
* throw propagates as a route-handler throw. The result remains `unknown` because this package
|
|
782
|
+
* cannot verify caller identity.
|
|
783
|
+
*
|
|
784
|
+
* @typeParam TState - The consumer's opaque per-request route state type
|
|
785
|
+
* @param request - The validated Fetch request that will dispatch
|
|
786
|
+
* @param context - The router context, or `undefined` for direct handler invocation
|
|
787
|
+
* @returns Consumer-asserted caller context, or `undefined` for no caller
|
|
788
|
+
*/
|
|
789
|
+
export declare type MCPCallerHandler<TState = unknown> = (request: Request, context: RouteContext<string, TState> | undefined) => unknown;
|
|
790
|
+
|
|
791
|
+
/**
|
|
792
|
+
* Shared SSE keepalive options for held-open HTTP responses.
|
|
793
|
+
*
|
|
794
|
+
* @remarks
|
|
795
|
+
* - `interval` — milliseconds between SSE comment frames. Defaults to {@link
|
|
796
|
+
* import('./constants.js').DEFAULT_MCP_KEEPALIVE_INTERVAL}. Each comment keeps an idle
|
|
797
|
+
* connection live through intermediaries and bounds how long a dead client can remain
|
|
798
|
+
* unobserved by the HTTP writer.
|
|
799
|
+
*/
|
|
800
|
+
export declare interface MCPKeepaliveOptions {
|
|
801
|
+
readonly interval?: number;
|
|
802
|
+
}
|
|
803
|
+
|
|
804
|
+
/**
|
|
805
|
+
* Shared options for the protocol-required HTTP `Origin` validation at the route and session
|
|
806
|
+
* enforcement sites.
|
|
807
|
+
*
|
|
808
|
+
* @remarks
|
|
809
|
+
* - `enabled` — whether this package validates `Origin`; defaults to `true`. Set `false` only
|
|
810
|
+
* when an upstream layer performs the validation for the deployment.
|
|
811
|
+
* - `origins` — the exact serialized non-loopback origins accepted when an `Origin` header is
|
|
812
|
+
* present. Omission still accepts requests without `Origin` and canonical loopback-literal
|
|
813
|
+
* origins (`localhost`, `[::1]`, and `127.0.0.0/8`).
|
|
814
|
+
*/
|
|
815
|
+
export declare interface MCPOriginOptions {
|
|
816
|
+
readonly enabled?: boolean;
|
|
817
|
+
readonly origins?: readonly string[];
|
|
818
|
+
}
|
|
819
|
+
|
|
646
820
|
/**
|
|
647
821
|
* One MCP transport session — the per-session entity a {@link
|
|
648
822
|
* import('./middlewares.js').createMCPSession} middleware owns, keyed by its `id`, carrying the
|
|
@@ -718,10 +892,14 @@ export declare class MCPSession implements MCPSessionInterface {
|
|
|
718
892
|
* - `session` — the live {@link MCPSession} entity the store keys by session id.
|
|
719
893
|
* - `touched` — the epoch-ms instant of the last access; the entry is replaced on every
|
|
720
894
|
* resolved request so the middleware's lazy sweep can evict an idle entry past `ttl`.
|
|
895
|
+
* - `version` — the legacy revision negotiated by the session's initialize request, used
|
|
896
|
+
* when a later live-session request legitimately omits its protocol header.
|
|
721
897
|
*/
|
|
722
898
|
export declare interface MCPSessionEntry {
|
|
723
899
|
readonly session: MCPSession;
|
|
724
900
|
readonly touched: number;
|
|
901
|
+
/** The legacy revision negotiated when this session was minted. */
|
|
902
|
+
readonly version: MCPVersion;
|
|
725
903
|
}
|
|
726
904
|
|
|
727
905
|
/**
|
|
@@ -775,12 +953,22 @@ export declare interface MCPSessionInterface {
|
|
|
775
953
|
* uses directly for its own session-touch / TTL-sweep bookkeeping; defaults to `Date.now`. The
|
|
776
954
|
* deterministic clock a TTL test advances explicitly instead of racing a real idle window
|
|
777
955
|
* against wall-clock (AGENTS §16). Production never sets it.
|
|
956
|
+
* - `origin` — the same shared origin-validation options supplied to `createMCPRoutes`.
|
|
957
|
+
* Validation is enabled by default, accepts canonical loopback-literal origins, and rejects
|
|
958
|
+
* every other present origin outside its exact list before any session is minted. Set
|
|
959
|
+
* `origin.enabled` to `false` only when validation is delegated upstream; `origins` is ignored
|
|
960
|
+
* in that mode. Both enforcement sites must receive the same value.
|
|
961
|
+
* - `keepalive` — the SSE liveness options for the held-open resumable response. `interval`
|
|
962
|
+
* defaults to {@link import('./constants.js').DEFAULT_MCP_KEEPALIVE_INTERVAL}.
|
|
778
963
|
*/
|
|
779
964
|
export declare interface MCPSessionOptions {
|
|
780
965
|
readonly path?: string;
|
|
781
966
|
readonly ttl?: number;
|
|
782
967
|
readonly capacity?: number;
|
|
783
968
|
readonly clock?: () => number;
|
|
969
|
+
/** Must match the route layer's value; `origins` is ignored when `enabled` is `false`. */
|
|
970
|
+
readonly origin?: MCPOriginOptions;
|
|
971
|
+
readonly keepalive?: MCPKeepaliveOptions;
|
|
784
972
|
}
|
|
785
973
|
|
|
786
974
|
/**
|
|
@@ -857,7 +1045,7 @@ export declare function readSessionHeader(request: Request): string | undefined;
|
|
|
857
1045
|
* JSON-RPC error body.
|
|
858
1046
|
*
|
|
859
1047
|
* @remarks
|
|
860
|
-
* Returns `Response.json(
|
|
1048
|
+
* Returns `Response.json(buildJSONRPCError(null, JSONRPC_INVALID_REQUEST, 'Session not found'),
|
|
861
1049
|
* { status: 404 })`, mirroring `createMCPRoutes`'s `400` transport-failure shape (a
|
|
862
1050
|
* JSON-RPC error BODY with a `null` id) but at the session-not-found status. Shared by
|
|
863
1051
|
* every {@link import('./middlewares.js').createMCPSession} validation site — the
|
|
@@ -869,6 +1057,15 @@ export declare function readSessionHeader(request: Request): string | undefined;
|
|
|
869
1057
|
*/
|
|
870
1058
|
export declare function rejectUnknownSession(): Response;
|
|
871
1059
|
|
|
1060
|
+
/** The `X-Accel-Buffering` value that disables reverse-proxy buffering. */
|
|
1061
|
+
export declare const SSE_BUFFERING_DISABLED = "no";
|
|
1062
|
+
|
|
1063
|
+
/** The reverse-proxy response header controlling buffering of an SSE response. */
|
|
1064
|
+
export declare const SSE_BUFFERING_HEADER = "x-accel-buffering";
|
|
1065
|
+
|
|
1066
|
+
/** The comment text written by the held-open MCP response keepalive. */
|
|
1067
|
+
export declare const SSE_KEEPALIVE_COMMENT = "keepalive";
|
|
1068
|
+
|
|
872
1069
|
/**
|
|
873
1070
|
* The stdio CLIENT transport for the Model Context Protocol — a
|
|
874
1071
|
* {@link ClientTransportInterface} that drives a CHILD PROCESS MCP server over
|