@tmcp/transport-http 0.6.3 → 0.7.1

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
@@ -72,13 +72,16 @@ httpServer.listen(3000, () => {
72
72
 
73
73
  ```javascript
74
74
  const transport = new HttpTransport(server, {
75
- // Custom MCP endpoint path (default: '/mcp')
75
+ // Custom MCP endpoint path (default: '/mcp', use null to respond on every path)
76
76
  path: '/api/mcp',
77
77
  // Custom session ID generation
78
78
  getSessionId: () => {
79
79
  return `session-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
80
80
  },
81
81
  });
82
+
83
+ > [!NOTE]
84
+ > When the transport runs in development mode and you omit the `path` option, a warning is emitted. Future releases will treat an `undefined` path as "respond on every path", so set the property explicitly (for example `path: '/mcp'` or `path: null`) to lock in the behavior you want today.
82
85
  ```
83
86
 
84
87
  ### With Custom Context
@@ -162,7 +165,7 @@ const transport = new HttpTransport(server, {
162
165
  - **🌐 HTTP/SSE Communication**: Uses Server-Sent Events for real-time bidirectional communication
163
166
  - **🔄 Session Management**: Maintains client sessions with automatic session ID generation
164
167
  - **📡 Streaming Responses**: Supports streaming responses through SSE
165
- - **🛤️ Configurable Path**: Customizable MCP endpoint path with automatic filtering
168
+ - **🛤️ Configurable Path**: Customizable MCP endpoint path with automatic filtering (set `path` to `null` to respond everywhere)
166
169
  - **🔧 Framework Agnostic**: Works with any HTTP server framework (Fastify, Bun, Deno, etc.)
167
170
  - **⚡ Real-time Updates**: Server can push notifications and updates to connected clients
168
171
  - **🛡️ Error Handling**: Graceful error handling for malformed requests
@@ -190,7 +193,7 @@ Creates a new HTTP transport instance.
190
193
  ```typescript
191
194
  interface HttpTransportOptions {
192
195
  getSessionId: () => string; // Custom session ID generator
193
- path?: string; // MCP endpoint path (default: '/mcp')
196
+ path?: string | null; // MCP endpoint path (default: '/mcp', null responds on every path)
194
197
  oauth?: OAuth; // an oauth provider generated from @tmcp/auth
195
198
  sessionManager?: SessionManager; // Custom session manager (default: InMemorySessionManager)
196
199
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tmcp/transport-http",
3
- "version": "0.6.3",
3
+ "version": "0.7.1",
4
4
  "description": "Transport for TMCP using HTTP",
5
5
  "type": "module",
6
6
  "main": "src/index.js",
@@ -16,7 +16,7 @@
16
16
  "src/**/*"
17
17
  ],
18
18
  "peerDependencies": {
19
- "tmcp": "^1.14.0"
19
+ "tmcp": "^1.15.3"
20
20
  },
21
21
  "keywords": [
22
22
  "tmcp",
@@ -29,19 +29,20 @@
29
29
  "directory": "packages/transport-http"
30
30
  },
31
31
  "dependencies": {
32
+ "esm-env": "^1.2.2",
32
33
  "@tmcp/session-manager": "^0.1.2"
33
34
  },
34
35
  "devDependencies": {
35
36
  "@modelcontextprotocol/sdk": "^1.17.1",
36
- "@remix-run/node-fetch-server": "^0.8.0",
37
+ "@remix-run/node-fetch-server": "^0.11.0",
37
38
  "@types/node": "^24.0.15",
38
39
  "dts-buddy": "^0.6.2",
39
40
  "publint": "^0.3.12",
40
41
  "valibot": "^1.1.0",
41
- "vitest": "^3.2.4",
42
+ "vitest": "^4.0.0",
43
+ "@tmcp/adapter-valibot": "^0.1.4",
42
44
  "@tmcp/auth": "^0.3.3",
43
- "tmcp": "^1.14.0",
44
- "@tmcp/adapter-valibot": "^0.1.4"
45
+ "tmcp": "^1.15.3"
45
46
  },
46
47
  "publishConfig": {
47
48
  "access": "public"
package/src/index.js CHANGED
@@ -18,7 +18,7 @@
18
18
  /**
19
19
  * @typedef {{
20
20
  * getSessionId?: () => string
21
- * path?: string
21
+ * path?: string | null
22
22
  * oauth?: OAuth<"built">
23
23
  * cors?: CorsConfig | boolean,
24
24
  * sessionManager?: SessionManager
@@ -27,6 +27,7 @@
27
27
 
28
28
  import { AsyncLocalStorage } from 'node:async_hooks';
29
29
  import { InMemorySessionManager } from '@tmcp/session-manager';
30
+ import { DEV } from 'esm-env';
30
31
 
31
32
  /**
32
33
  * @template {Record<string, unknown> | undefined} [TCustom=undefined]
@@ -43,7 +44,7 @@ export class HttpTransport {
43
44
  #options;
44
45
 
45
46
  /**
46
- * @type {string}
47
+ * @type {string | null}
47
48
  */
48
49
  #path;
49
50
 
@@ -76,6 +77,13 @@ export class HttpTransport {
76
77
  getSessionId: () => crypto.randomUUID(),
77
78
  };
78
79
 
80
+ if (options?.path === undefined && DEV) {
81
+ // TODO: remove on 1.0.0 release
82
+ console.warn(
83
+ "[tmcp][transport-http] `options.path` is undefined, in future versions passing `undefined` will default to respond on all paths. To keep the current behavior, explicitly set `path` to '/mcp' or your desired path.",
84
+ );
85
+ }
86
+
79
87
  if (oauth) {
80
88
  this.#oauth = oauth;
81
89
  }
@@ -205,6 +213,7 @@ export class HttpTransport {
205
213
  */
206
214
  async #handle_get(session_id) {
207
215
  const sessions = this.#options.sessionManager;
216
+ const text_encoder = this.#text_encoder;
208
217
  // If session already exists, return error
209
218
  const existing_session = await sessions.has(session_id);
210
219
  if (existing_session) {
@@ -232,6 +241,8 @@ export class HttpTransport {
232
241
  const stream = new ReadableStream({
233
242
  async start(controller) {
234
243
  await sessions.create(session_id, controller);
244
+ // send a comment to flush the headers immediately
245
+ controller.enqueue(text_encoder.encode(': connected\n\n'));
235
246
  },
236
247
  async cancel() {
237
248
  await sessions.delete(session_id);
@@ -317,18 +328,21 @@ export class HttpTransport {
317
328
 
318
329
  const messages = Array.isArray(body) ? body : [body];
319
330
 
331
+ const has_request = messages.some((message) => message.id != null);
332
+
320
333
  // Determine status code based on response type
321
334
  // 202 Accepted for notifications/responses, 200 OK for standard requests
322
- const status = !messages.some((message) => message.id != null)
323
- ? 202
324
- : 200;
325
-
326
- return new Response(stream, {
327
- headers: {
328
- 'Content-Type': 'text/event-stream',
329
- 'Cache-Control': 'no-cache',
330
- 'mcp-session-id': session_id,
331
- },
335
+ const status = !has_request ? 202 : 200;
336
+
337
+ return new Response(has_request ? stream : null, {
338
+ headers: has_request
339
+ ? {
340
+ 'Content-Type': 'text/event-stream',
341
+ 'Cache-Control': 'no-cache',
342
+ connection: 'keep-alive',
343
+ 'mcp-session-id': session_id,
344
+ }
345
+ : undefined,
332
346
  status,
333
347
  });
334
348
  } catch (error) {
@@ -415,7 +429,7 @@ export class HttpTransport {
415
429
  }
416
430
 
417
431
  // Check if the request path matches the configured MCP path
418
- if (url.pathname !== this.#path) {
432
+ if (url.pathname !== this.#path && this.#path !== null) {
419
433
  return null;
420
434
  }
421
435
 
@@ -19,7 +19,7 @@ declare module '@tmcp/transport-http' {
19
19
  };
20
20
  export type HttpTransportOptions = {
21
21
  getSessionId?: () => string;
22
- path?: string;
22
+ path?: string | null;
23
23
  oauth?: OAuth<"built">;
24
24
  cors?: CorsConfig | boolean;
25
25
  sessionManager?: SessionManager;
@@ -10,6 +10,6 @@
10
10
  "sourcesContent": [
11
11
  null
12
12
  ],
13
- "mappings": ";;;;cAiCaA,aAAaA",
13
+ "mappings": ";;;;cAkCaA,aAAaA",
14
14
  "ignoreList": []
15
15
  }