@tmcp/transport-http 0.8.3 → 0.8.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -212,11 +212,24 @@ interface HttpTransportOptions {
212
212
  streams?: StreamSessionManager;
213
213
  info?: InfoSessionManager;
214
214
  }; // Provide custom managers; defaults to in-memory implementations
215
+ disableSse?: boolean; // Disable SSE stream endpoint (GET returns 405)
215
216
  }
216
217
  ```
217
218
 
218
219
  If you omit `sessionManager` the transport creates `InMemoryStreamSessionManager` and `InMemoryInfoSessionManager` instances for you. You can override either field independently (for example, Redis streams with in-memory metadata during development).
219
220
 
221
+ ### Disabling SSE Streams
222
+
223
+ If your deployment doesn't support long-lived SSE connections (e.g., some serverless environments), you can disable the GET endpoint:
224
+
225
+ ```javascript
226
+ const transport = new HttpTransport(server, {
227
+ disableSse: true,
228
+ });
229
+ ```
230
+
231
+ When `disableSse` is `true`, GET requests to the MCP endpoint will return a `405 Method Not Allowed` response. Clients can still communicate via POST requests, but won't receive server-initiated notifications.
232
+
220
233
  #### Methods
221
234
 
222
235
  ##### `respond(request: Request, customContext?: T): Promise<Response | null>`
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tmcp/transport-http",
3
- "version": "0.8.3",
3
+ "version": "0.8.5",
4
4
  "description": "Transport for TMCP using HTTP",
5
5
  "type": "module",
6
6
  "main": "src/index.js",
@@ -40,7 +40,7 @@
40
40
  },
41
41
  "devDependencies": {
42
42
  "@modelcontextprotocol/sdk": "^1.17.1",
43
- "@remix-run/node-fetch-server": "^0.12.0",
43
+ "@remix-run/node-fetch-server": "^0.13.0",
44
44
  "@types/node": "^24.0.15",
45
45
  "dts-buddy": "^0.6.2",
46
46
  "publint": "^0.3.12",
@@ -48,7 +48,7 @@
48
48
  "vitest": "^4.0.0",
49
49
  "@tmcp/adapter-valibot": "^0.1.5",
50
50
  "@tmcp/auth": "^0.4.0",
51
- "tmcp": "^1.18.1"
51
+ "tmcp": "^1.19.3"
52
52
  },
53
53
  "publishConfig": {
54
54
  "access": "public"
package/src/index.js CHANGED
@@ -23,6 +23,7 @@
23
23
  * oauth?: OAuth<"built">
24
24
  * cors?: CorsConfig | boolean,
25
25
  * sessionManager?: { streams?: StreamSessionManager, info?: OptionalizeSessionManager<InfoSessionManager> }
26
+ * disableSse?: boolean
26
27
  * }} HttpTransportOptions
27
28
  */
28
29
 
@@ -47,7 +48,7 @@ export class HttpTransport {
47
48
  #server;
48
49
 
49
50
  /**
50
- * @type {Required<Omit<HttpTransportOptions, 'oauth' | 'cors' | 'sessionManager'>> & { cors?: CorsConfig | boolean, sessionManager: SessionManager }}
51
+ * @type {Required<Omit<HttpTransportOptions, 'oauth' | 'cors' | 'sessionManager' | 'disableSse'>> & { cors?: CorsConfig | boolean, sessionManager: SessionManager, disableSse?: boolean }}
51
52
  */
52
53
  #options;
53
54
 
@@ -85,6 +86,7 @@ export class HttpTransport {
85
86
  path = '/mcp',
86
87
  oauth,
87
88
  cors,
89
+ disableSse,
88
90
  sessionManager: _sessionManager = {
89
91
  streams: new InMemoryStreamSessionManager(),
90
92
  info: new InMemoryInfoSessionManager(),
@@ -113,7 +115,13 @@ export class HttpTransport {
113
115
  this.#oauth = oauth;
114
116
  }
115
117
 
116
- this.#options = { getSessionId, path, cors, sessionManager };
118
+ this.#options = {
119
+ getSessionId,
120
+ path,
121
+ cors,
122
+ sessionManager,
123
+ disableSse,
124
+ };
117
125
  this.#path = path;
118
126
 
119
127
  this.#server.on('initialize', ({ capabilities, clientInfo }) => {
@@ -281,6 +289,15 @@ export class HttpTransport {
281
289
  * @returns
282
290
  */
283
291
  async #handle_get(session_id) {
292
+ if (this.#options.disableSse) {
293
+ return new Response(null, {
294
+ status: 405,
295
+ headers: {
296
+ Allow: 'POST, DELETE, OPTIONS',
297
+ },
298
+ });
299
+ }
300
+
284
301
  const sessions = this.#options.sessionManager;
285
302
  const text_encoder = this.#text_encoder;
286
303
  // If session already exists, return error
@@ -376,17 +393,28 @@ export class HttpTransport {
376
393
 
377
394
  const session_id_storage = this.#session_id_storage;
378
395
 
396
+ const messages = Array.isArray(body) ? body : [body];
397
+
379
398
  const handle = async () => {
380
- const client_capabilities =
381
- await this.#options.sessionManager.info
382
- .getClientCapabilities(session_id)
383
- .catch(() => undefined);
384
- const client_info = await this.#options.sessionManager.info
385
- .getClientInfo(session_id)
386
- .catch(() => undefined);
387
- const log_level = await this.#options.sessionManager.info
388
- .getLogLevel(session_id)
389
- .catch(() => undefined);
399
+ const init_message = messages.find(
400
+ (/** @type {any} */ m) => m.method === 'initialize',
401
+ );
402
+
403
+ const client_capabilities = init_message
404
+ ? init_message.params?.capabilities
405
+ : await this.#options.sessionManager.info
406
+ .getClientCapabilities(session_id)
407
+ .catch(() => undefined);
408
+ const client_info = init_message
409
+ ? init_message.params?.clientInfo
410
+ : await this.#options.sessionManager.info
411
+ .getClientInfo(session_id)
412
+ .catch(() => undefined);
413
+ const log_level = init_message
414
+ ? undefined
415
+ : await this.#options.sessionManager.info
416
+ .getLogLevel(session_id)
417
+ .catch(() => undefined);
390
418
 
391
419
  const response = await this.#controller_storage.run(
392
420
  controller,
@@ -417,8 +445,6 @@ export class HttpTransport {
417
445
 
418
446
  handle();
419
447
 
420
- const messages = Array.isArray(body) ? body : [body];
421
-
422
448
  const has_request = messages.some((message) => message.id != null);
423
449
 
424
450
  // Determine status code based on response type
@@ -26,6 +26,7 @@ declare module '@tmcp/transport-http' {
26
26
  streams?: StreamSessionManager;
27
27
  info?: OptionalizeSessionManager<InfoSessionManager>;
28
28
  };
29
+ disableSse?: boolean;
29
30
  };
30
31
  type ToOmit = 'removeSubscription';
31
32
 
@@ -14,6 +14,6 @@
14
14
  null,
15
15
  null
16
16
  ],
17
- "mappings": ";;;;cAsCaA,aAAaA;;;;;;;;;;;;;;;;;;;;;;;;;MCnCrBC,MAAMA;;MAECC,yBAAyBA",
17
+ "mappings": ";;;;cAuCaA,aAAaA;;;;;;;;;;;;;;;;;;;;;;;;;;MCpCrBC,MAAMA;;MAECC,yBAAyBA",
18
18
  "ignoreList": []
19
19
  }