@tmcp/transport-http 0.4.2 → 0.5.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tmcp/transport-http",
3
- "version": "0.4.2",
3
+ "version": "0.5.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.8.1"
19
+ "tmcp": "^1.9.0"
20
20
  },
21
21
  "keywords": [
22
22
  "tmcp",
@@ -36,7 +36,8 @@
36
36
  "valibot": "^1.1.0",
37
37
  "vitest": "^3.2.4",
38
38
  "@tmcp/adapter-valibot": "^0.1.3",
39
- "tmcp": "^1.8.1"
39
+ "@tmcp/auth": "^0.3.1",
40
+ "tmcp": "^1.9.0"
40
41
  },
41
42
  "publishConfig": {
42
43
  "access": "public"
package/src/index.js CHANGED
@@ -1,16 +1,17 @@
1
1
  /**
2
- * @import { McpServer, ClientCapabilities } from "tmcp";
2
+ * @import { AuthInfo, McpServer } from "tmcp";
3
+ * @import { OAuth } from "@tmcp/auth";
3
4
  */
4
5
 
5
6
  /**
6
7
  * @typedef {{
7
8
  * getSessionId?: () => string
8
9
  * path?: string
10
+ * oauth?: OAuth<"built">
9
11
  * }} HttpTransportOptions
10
12
  */
11
13
 
12
14
  import { AsyncLocalStorage } from 'node:async_hooks';
13
-
14
15
  export class HttpTransport {
15
16
  /**
16
17
  * @type {McpServer<any>}
@@ -18,7 +19,7 @@ export class HttpTransport {
18
19
  #server;
19
20
 
20
21
  /**
21
- * @type {Required<HttpTransportOptions>}
22
+ * @type {Required<Omit<HttpTransportOptions, 'oauth'>>}
22
23
  */
23
24
  #options;
24
25
 
@@ -41,6 +42,11 @@ export class HttpTransport {
41
42
  */
42
43
  #controller_storage = new AsyncLocalStorage();
43
44
 
45
+ /**
46
+ * @type {OAuth<"built"> | undefined}
47
+ */
48
+ #oauth;
49
+
44
50
  /**
45
51
  *
46
52
  * @param {McpServer<any>} server
@@ -48,18 +54,25 @@ export class HttpTransport {
48
54
  */
49
55
  constructor(server, options) {
50
56
  this.#server = server;
51
- const { getSessionId = () => crypto.randomUUID(), path = '/mcp' } =
52
- options ?? {
53
- getSessionId: () => crypto.randomUUID(),
54
- };
57
+ const {
58
+ getSessionId = () => crypto.randomUUID(),
59
+ path = '/mcp',
60
+ oauth,
61
+ } = options ?? {
62
+ getSessionId: () => crypto.randomUUID(),
63
+ };
64
+
65
+ if (oauth) {
66
+ this.#oauth = oauth;
67
+ }
68
+
55
69
  this.#options = { getSessionId, path };
56
70
  this.#path = path;
57
71
  this.#server.on('send', ({ request, context: { sessions } }) => {
58
72
  // use the current controller if the request has an id (it means it's a request and not a notification)
59
73
  if (request.id != null) {
60
74
  const controller = this.#controller_storage.getStore();
61
- if (!controller)
62
- throw new Error('Controller not found in storage');
75
+ if (!controller) return;
63
76
 
64
77
  controller.enqueue('data: ' + JSON.stringify(request) + '\n\n');
65
78
  return;
@@ -150,8 +163,9 @@ export class HttpTransport {
150
163
  *
151
164
  * @param {string} session_id
152
165
  * @param {Request} request
166
+ * @param {AuthInfo | null} auth_info
153
167
  */
154
- async #handle_post(session_id, request) {
168
+ async #handle_post(session_id, request, auth_info) {
155
169
  // Check Content-Type header
156
170
  const content_type = request.headers.get('content-type');
157
171
  if (!content_type || !content_type.includes('application/json')) {
@@ -192,7 +206,11 @@ export class HttpTransport {
192
206
  const handle = async () => {
193
207
  const response = await this.#controller_storage.run(
194
208
  controller,
195
- () => this.#server.receive(body, session_id),
209
+ () =>
210
+ this.#server.receive(body, {
211
+ sessionId: session_id,
212
+ auth: auth_info ?? undefined,
213
+ }),
196
214
  );
197
215
 
198
216
  controller?.enqueue(
@@ -274,10 +292,38 @@ export class HttpTransport {
274
292
  async respond(request) {
275
293
  const url = new URL(request.url);
276
294
 
295
+ /**
296
+ * @type {AuthInfo | null}
297
+ */
298
+ let auth_info = null;
299
+
300
+ // Check if OAuth helper should handle this request
301
+ if (this.#oauth) {
302
+ try {
303
+ const response = await this.#oauth.respond(request);
304
+ if (response) {
305
+ return response;
306
+ }
307
+ } catch (error) {
308
+ return new Response(
309
+ JSON.stringify({
310
+ error: 'server_error',
311
+ error_description: /** @type {Error} */ (error).message,
312
+ }),
313
+ {
314
+ status: 500,
315
+ headers: { 'Content-Type': 'application/json' },
316
+ },
317
+ );
318
+ }
319
+ auth_info = await this.#oauth.verify(request);
320
+ }
321
+
277
322
  // Check if the request path matches the configured MCP path
278
323
  if (url.pathname !== this.#path) {
279
324
  return null;
280
325
  }
326
+
281
327
  const method = request.method;
282
328
  const session_id =
283
329
  request.headers.get('mcp-session-id') ||
@@ -295,7 +341,7 @@ export class HttpTransport {
295
341
 
296
342
  // Handle POST request - process message and respond through event stream
297
343
  if (method === 'POST') {
298
- return this.#handle_post(session_id, request);
344
+ return this.#handle_post(session_id, request, auth_info);
299
345
  }
300
346
 
301
347
  // Method not supported
@@ -1,5 +1,6 @@
1
1
  declare module '@tmcp/transport-http' {
2
2
  import type { McpServer } from 'tmcp';
3
+ import type { OAuth } from '@tmcp/auth';
3
4
  export class HttpTransport {
4
5
 
5
6
  constructor(server: McpServer<any>, options?: HttpTransportOptions);
@@ -10,6 +11,7 @@ declare module '@tmcp/transport-http' {
10
11
  export type HttpTransportOptions = {
11
12
  getSessionId?: () => string;
12
13
  path?: string;
14
+ oauth?: OAuth<"built">;
13
15
  };
14
16
 
15
17
  export {};
@@ -10,6 +10,6 @@
10
10
  "sourcesContent": [
11
11
  null
12
12
  ],
13
- "mappings": ";;cAaaA,aAAaA",
13
+ "mappings": ";;;cAcaA,aAAaA",
14
14
  "ignoreList": []
15
15
  }