@mastra/mcp 1.0.0-beta.0 → 1.0.0-beta.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.
Files changed (51) hide show
  1. package/CHANGELOG.md +303 -0
  2. package/README.md +250 -24
  3. package/dist/__fixtures__/tools.d.ts +8 -5
  4. package/dist/__fixtures__/tools.d.ts.map +1 -1
  5. package/dist/client/{elicitationActions.d.ts → actions/elicitation.d.ts} +2 -2
  6. package/dist/client/actions/elicitation.d.ts.map +1 -0
  7. package/dist/client/actions/progress.d.ts +23 -0
  8. package/dist/client/actions/progress.d.ts.map +1 -0
  9. package/dist/client/{promptActions.d.ts → actions/prompt.d.ts} +2 -2
  10. package/dist/client/actions/prompt.d.ts.map +1 -0
  11. package/dist/client/{resourceActions.d.ts → actions/resource.d.ts} +2 -2
  12. package/dist/client/actions/resource.d.ts.map +1 -0
  13. package/dist/client/client.d.ts +79 -132
  14. package/dist/client/client.d.ts.map +1 -1
  15. package/dist/client/configuration.d.ts +23 -1
  16. package/dist/client/configuration.d.ts.map +1 -1
  17. package/dist/client/index.d.ts +3 -1
  18. package/dist/client/index.d.ts.map +1 -1
  19. package/dist/client/oauth-provider.d.ts +230 -0
  20. package/dist/client/oauth-provider.d.ts.map +1 -0
  21. package/dist/client/types.d.ts +237 -0
  22. package/dist/client/types.d.ts.map +1 -0
  23. package/dist/docs/README.md +33 -0
  24. package/dist/docs/SKILL.md +46 -0
  25. package/dist/docs/SOURCE_MAP.json +59 -0
  26. package/dist/docs/mcp/01-overview.md +372 -0
  27. package/dist/docs/mcp/02-publishing-mcp-server.md +111 -0
  28. package/dist/docs/tools/01-reference.md +2306 -0
  29. package/dist/docs/tools-mcp/01-mcp-overview.md +384 -0
  30. package/dist/index.cjs +851 -107
  31. package/dist/index.cjs.map +1 -1
  32. package/dist/index.d.ts +1 -0
  33. package/dist/index.d.ts.map +1 -1
  34. package/dist/index.js +774 -92
  35. package/dist/index.js.map +1 -1
  36. package/dist/server/index.d.ts +1 -0
  37. package/dist/server/index.d.ts.map +1 -1
  38. package/dist/server/oauth-middleware.d.ts +142 -0
  39. package/dist/server/oauth-middleware.d.ts.map +1 -0
  40. package/dist/server/server.d.ts +20 -4
  41. package/dist/server/server.d.ts.map +1 -1
  42. package/dist/server/types.d.ts +11 -4
  43. package/dist/server/types.d.ts.map +1 -1
  44. package/dist/shared/index.d.ts +2 -0
  45. package/dist/shared/index.d.ts.map +1 -0
  46. package/dist/shared/oauth-types.d.ts +137 -0
  47. package/dist/shared/oauth-types.d.ts.map +1 -0
  48. package/package.json +17 -14
  49. package/dist/client/elicitationActions.d.ts.map +0 -1
  50. package/dist/client/promptActions.d.ts.map +0 -1
  51. package/dist/client/resourceActions.d.ts.map +0 -1
package/dist/index.cjs CHANGED
@@ -1,38 +1,39 @@
1
1
  'use strict';
2
2
 
3
+ var $RefParser = require('@apidevtools/json-schema-ref-parser');
3
4
  var base = require('@mastra/core/base');
4
5
  var error = require('@mastra/core/error');
5
- var protocol_js = require('@modelcontextprotocol/sdk/shared/protocol.js');
6
- var equal = require('fast-deep-equal');
7
- var uuid = require('uuid');
8
- var $RefParser = require('@apidevtools/json-schema-ref-parser');
9
6
  var tools = require('@mastra/core/tools');
10
7
  var utils = require('@mastra/core/utils');
11
- var index_js$1 = require('@modelcontextprotocol/sdk/client/index.js');
12
- var sse_js$1 = require('@modelcontextprotocol/sdk/client/sse.js');
13
- var stdio_js$1 = require('@modelcontextprotocol/sdk/client/stdio.js');
14
- var streamableHttp_js$1 = require('@modelcontextprotocol/sdk/client/streamableHttp.js');
8
+ var index_js = require('@modelcontextprotocol/sdk/client/index.js');
9
+ var sse_js = require('@modelcontextprotocol/sdk/client/sse.js');
10
+ var stdio_js = require('@modelcontextprotocol/sdk/client/stdio.js');
11
+ var streamableHttp_js = require('@modelcontextprotocol/sdk/client/streamableHttp.js');
12
+ var protocol_js = require('@modelcontextprotocol/sdk/shared/protocol.js');
15
13
  var types_js = require('@modelcontextprotocol/sdk/types.js');
16
14
  var exitHook = require('exit-hook');
17
15
  var zod = require('zod');
18
16
  var zodFromJsonSchema = require('zod-from-json-schema');
19
17
  var zodFromJsonSchemaV3 = require('zod-from-json-schema-v3');
18
+ var equal = require('fast-deep-equal');
19
+ var uuid = require('uuid');
20
20
  var crypto$1 = require('crypto');
21
21
  var mcp = require('@mastra/core/mcp');
22
22
  var requestContext = require('@mastra/core/request-context');
23
- var index_js = require('@modelcontextprotocol/sdk/server/index.js');
24
- var sse_js = require('@modelcontextprotocol/sdk/server/sse.js');
25
- var stdio_js = require('@modelcontextprotocol/sdk/server/stdio.js');
26
- var streamableHttp_js = require('@modelcontextprotocol/sdk/server/streamableHttp.js');
23
+ var index_js$1 = require('@modelcontextprotocol/sdk/server/index.js');
24
+ var sse_js$1 = require('@modelcontextprotocol/sdk/server/sse.js');
25
+ var stdio_js$1 = require('@modelcontextprotocol/sdk/server/stdio.js');
26
+ var streamableHttp_js$1 = require('@modelcontextprotocol/sdk/server/streamableHttp.js');
27
+ var auth_js = require('@modelcontextprotocol/sdk/client/auth.js');
27
28
 
28
29
  function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
29
30
 
30
- var equal__default = /*#__PURE__*/_interopDefault(equal);
31
31
  var $RefParser__default = /*#__PURE__*/_interopDefault($RefParser);
32
+ var equal__default = /*#__PURE__*/_interopDefault(equal);
32
33
 
33
- // src/client/configuration.ts
34
+ // src/client/client.ts
34
35
 
35
- // src/client/elicitationActions.ts
36
+ // src/client/actions/elicitation.ts
36
37
  var ElicitationClientActions = class {
37
38
  client;
38
39
  logger;
@@ -85,6 +86,23 @@ var ElicitationClientActions = class {
85
86
  this.client.setElicitationRequestHandler(handler);
86
87
  }
87
88
  };
89
+
90
+ // src/client/actions/progress.ts
91
+ var ProgressClientActions = class {
92
+ client;
93
+ logger;
94
+ constructor({ client, logger }) {
95
+ this.client = client;
96
+ this.logger = logger;
97
+ }
98
+ /**
99
+ * Set a notification handler for progress updates.
100
+ * @param handler The callback function to handle progress notifications.
101
+ */
102
+ onUpdate(handler) {
103
+ this.client.setProgressNotificationHandler(handler);
104
+ }
105
+ };
88
106
  var PromptClientActions = class {
89
107
  client;
90
108
  logger;
@@ -373,6 +391,8 @@ var ResourceClientActions = class {
373
391
  };
374
392
 
375
393
  // src/client/client.ts
394
+ var DEFAULT_SERVER_CONNECT_TIMEOUT_MSEC = 3e3;
395
+ var SSE_FALLBACK_STATUS_CODES = [400, 404, 405];
376
396
  function convertLogLevelToLoggerMethod(level) {
377
397
  switch (level) {
378
398
  case "debug":
@@ -397,15 +417,21 @@ var InternalMastraMCPClient = class extends base.MastraBase {
397
417
  timeout;
398
418
  logHandler;
399
419
  enableServerLogs;
420
+ enableProgressTracking;
400
421
  serverConfig;
401
422
  transport;
402
423
  currentOperationContext = null;
424
+ exitHookUnsubscribe;
425
+ sigTermHandler;
426
+ _roots;
403
427
  /** Provides access to resource operations (list, read, subscribe, etc.) */
404
428
  resources;
405
429
  /** Provides access to prompt operations (list, get, notifications) */
406
430
  prompts;
407
431
  /** Provides access to elicitation operations (request handling) */
408
432
  elicitation;
433
+ /** Provides access to progress operations (notifications) */
434
+ progress;
409
435
  /**
410
436
  * @internal
411
437
  */
@@ -422,8 +448,16 @@ var InternalMastraMCPClient = class extends base.MastraBase {
422
448
  this.logHandler = server.logger;
423
449
  this.enableServerLogs = server.enableServerLogs ?? true;
424
450
  this.serverConfig = server;
425
- const clientCapabilities = { ...capabilities, elicitation: {} };
426
- this.client = new index_js$1.Client(
451
+ this.enableProgressTracking = !!server.enableProgressTracking;
452
+ this._roots = server.roots ?? [];
453
+ const hasRoots = this._roots.length > 0 || !!capabilities.roots;
454
+ const clientCapabilities = {
455
+ ...capabilities,
456
+ elicitation: {},
457
+ // Auto-enable roots capability if roots are provided
458
+ ...hasRoots ? { roots: { listChanged: true, ...capabilities.roots ?? {} } } : {}
459
+ };
460
+ this.client = new index_js.Client(
427
461
  {
428
462
  name,
429
463
  version
@@ -433,9 +467,13 @@ var InternalMastraMCPClient = class extends base.MastraBase {
433
467
  }
434
468
  );
435
469
  this.setupLogging();
470
+ if (hasRoots) {
471
+ this.setupRootsHandler();
472
+ }
436
473
  this.resources = new ResourceClientActions({ client: this, logger: this.logger });
437
474
  this.prompts = new PromptClientActions({ client: this, logger: this.logger });
438
475
  this.elicitation = new ElicitationClientActions({ client: this, logger: this.logger });
476
+ this.progress = new ProgressClientActions({ client: this, logger: this.logger });
439
477
  }
440
478
  /**
441
479
  * Log a message at the specified level
@@ -474,13 +512,70 @@ var InternalMastraMCPClient = class extends base.MastraBase {
474
512
  );
475
513
  }
476
514
  }
515
+ /**
516
+ * Set up handler for roots/list requests from the server.
517
+ *
518
+ * Per MCP spec (https://modelcontextprotocol.io/specification/2025-11-25/client/roots):
519
+ * When a server sends a roots/list request, the client responds with the configured roots.
520
+ */
521
+ setupRootsHandler() {
522
+ this.log("debug", "Setting up roots/list request handler");
523
+ this.client.setRequestHandler(types_js.ListRootsRequestSchema, async () => {
524
+ this.log("debug", `Responding to roots/list request with ${this._roots.length} roots`);
525
+ return { roots: this._roots };
526
+ });
527
+ }
528
+ /**
529
+ * Get the currently configured roots.
530
+ *
531
+ * @returns Array of configured filesystem roots
532
+ */
533
+ get roots() {
534
+ return [...this._roots];
535
+ }
536
+ /**
537
+ * Update the list of filesystem roots and notify the server.
538
+ *
539
+ * Per MCP spec, when roots change, the client sends a `notifications/roots/list_changed`
540
+ * notification to inform the server that it should re-fetch the roots list.
541
+ *
542
+ * @param roots - New list of filesystem roots
543
+ *
544
+ * @example
545
+ * ```typescript
546
+ * await client.setRoots([
547
+ * { uri: 'file:///home/user/projects', name: 'Projects' },
548
+ * { uri: 'file:///tmp', name: 'Temp' }
549
+ * ]);
550
+ * ```
551
+ */
552
+ async setRoots(roots) {
553
+ this.log("debug", `Updating roots to ${roots.length} entries`);
554
+ this._roots = [...roots];
555
+ await this.sendRootsListChanged();
556
+ }
557
+ /**
558
+ * Send a roots/list_changed notification to the server.
559
+ *
560
+ * Per MCP spec, clients that support `listChanged` MUST send this notification
561
+ * when the list of roots changes. The server will then call roots/list to get
562
+ * the updated list.
563
+ */
564
+ async sendRootsListChanged() {
565
+ if (!this.transport) {
566
+ this.log("debug", "Cannot send roots/list_changed: not connected");
567
+ return;
568
+ }
569
+ this.log("debug", "Sending notifications/roots/list_changed");
570
+ await this.client.notification({ method: "notifications/roots/list_changed" });
571
+ }
477
572
  async connectStdio(command) {
478
573
  this.log("debug", `Using Stdio transport for command: ${command}`);
479
574
  try {
480
- this.transport = new stdio_js$1.StdioClientTransport({
575
+ this.transport = new stdio_js.StdioClientTransport({
481
576
  command,
482
577
  args: this.serverConfig.args,
483
- env: { ...stdio_js$1.getDefaultEnvironment(), ...this.serverConfig.env || {} }
578
+ env: { ...stdio_js.getDefaultEnvironment(), ...this.serverConfig.env || {} }
484
579
  });
485
580
  await this.client.connect(this.transport, { timeout: this.serverConfig.timeout ?? this.timeout });
486
581
  this.log("debug", `Successfully connected to MCP server via Stdio`);
@@ -490,34 +585,42 @@ var InternalMastraMCPClient = class extends base.MastraBase {
490
585
  }
491
586
  }
492
587
  async connectHttp(url) {
493
- const { requestInit, eventSourceInit, authProvider } = this.serverConfig;
588
+ const { requestInit, eventSourceInit, authProvider, connectTimeout, fetch: fetch2 } = this.serverConfig;
494
589
  this.log("debug", `Attempting to connect to URL: ${url}`);
495
590
  let shouldTrySSE = url.pathname.endsWith(`/sse`);
496
591
  if (!shouldTrySSE) {
497
592
  try {
498
593
  this.log("debug", "Trying Streamable HTTP transport...");
499
- const streamableTransport = new streamableHttp_js$1.StreamableHTTPClientTransport(url, {
594
+ const streamableTransport = new streamableHttp_js.StreamableHTTPClientTransport(url, {
500
595
  requestInit,
501
596
  reconnectionOptions: this.serverConfig.reconnectionOptions,
502
- authProvider
597
+ authProvider,
598
+ fetch: fetch2
503
599
  });
504
600
  await this.client.connect(streamableTransport, {
505
- timeout: (
506
- // this is hardcoded to 3s because the long default timeout would be extremely slow for sse backwards compat (60s)
507
- 3e3
508
- )
601
+ timeout: connectTimeout ?? DEFAULT_SERVER_CONNECT_TIMEOUT_MSEC
509
602
  });
510
603
  this.transport = streamableTransport;
511
604
  this.log("debug", "Successfully connected using Streamable HTTP transport.");
512
605
  } catch (error) {
513
606
  this.log("debug", `Streamable HTTP transport failed: ${error}`);
607
+ const status = error?.code;
608
+ if (status !== void 0 && !SSE_FALLBACK_STATUS_CODES.includes(status)) {
609
+ throw error;
610
+ }
514
611
  shouldTrySSE = true;
515
612
  }
516
613
  }
517
614
  if (shouldTrySSE) {
518
615
  this.log("debug", "Falling back to deprecated HTTP+SSE transport...");
519
616
  try {
520
- const sseTransport = new sse_js$1.SSEClientTransport(url, { requestInit, eventSourceInit, authProvider });
617
+ const sseEventSourceInit = fetch2 ? { ...eventSourceInit, fetch: fetch2 } : eventSourceInit;
618
+ const sseTransport = new sse_js.SSEClientTransport(url, {
619
+ requestInit,
620
+ eventSourceInit: sseEventSourceInit,
621
+ authProvider,
622
+ fetch: fetch2
623
+ });
521
624
  await this.client.connect(sseTransport, { timeout: this.serverConfig.timeout ?? this.timeout });
522
625
  this.transport = sseTransport;
523
626
  this.log("debug", "Successfully connected using deprecated HTTP+SSE transport.");
@@ -570,14 +673,19 @@ var InternalMastraMCPClient = class extends base.MastraBase {
570
673
  reject(e);
571
674
  }
572
675
  });
573
- exitHook.asyncExitHook(
574
- async () => {
575
- this.log("debug", `Disconnecting MCP server during exit`);
576
- await this.disconnect();
577
- },
578
- { wait: 5e3 }
579
- );
580
- process.on("SIGTERM", () => exitHook.gracefulExit());
676
+ if (!this.exitHookUnsubscribe) {
677
+ this.exitHookUnsubscribe = exitHook.asyncExitHook(
678
+ async () => {
679
+ this.log("debug", `Disconnecting MCP server during exit`);
680
+ await this.disconnect();
681
+ },
682
+ { wait: 5e3 }
683
+ );
684
+ }
685
+ if (!this.sigTermHandler) {
686
+ this.sigTermHandler = () => exitHook.gracefulExit();
687
+ process.on("SIGTERM", this.sigTermHandler);
688
+ }
581
689
  this.log("debug", `Successfully connected to MCP server`);
582
690
  return this.isConnected;
583
691
  }
@@ -591,7 +699,7 @@ var InternalMastraMCPClient = class extends base.MastraBase {
591
699
  * @internal
592
700
  */
593
701
  get sessionId() {
594
- if (this.transport instanceof streamableHttp_js$1.StreamableHTTPClientTransport) {
702
+ if (this.transport instanceof streamableHttp_js.StreamableHTTPClientTransport) {
595
703
  return this.transport.sessionId;
596
704
  }
597
705
  return void 0;
@@ -612,8 +720,64 @@ var InternalMastraMCPClient = class extends base.MastraBase {
612
720
  throw e;
613
721
  } finally {
614
722
  this.transport = void 0;
615
- this.isConnected = Promise.resolve(false);
723
+ this.isConnected = null;
724
+ if (this.exitHookUnsubscribe) {
725
+ this.exitHookUnsubscribe();
726
+ this.exitHookUnsubscribe = void 0;
727
+ }
728
+ if (this.sigTermHandler) {
729
+ process.off("SIGTERM", this.sigTermHandler);
730
+ this.sigTermHandler = void 0;
731
+ }
732
+ }
733
+ }
734
+ /**
735
+ * Checks if an error indicates a session invalidation that requires reconnection.
736
+ *
737
+ * Common session-related errors include:
738
+ * - "No valid session ID provided" (HTTP 400)
739
+ * - "Server not initialized" (HTTP 400)
740
+ * - "Not connected" (protocol state error)
741
+ * - Connection refused errors
742
+ *
743
+ * @param error - The error to check
744
+ * @returns true if the error indicates a session problem requiring reconnection
745
+ *
746
+ * @internal
747
+ */
748
+ isSessionError(error) {
749
+ if (!(error instanceof Error)) {
750
+ return false;
616
751
  }
752
+ const errorMessage = error.message.toLowerCase();
753
+ return errorMessage.includes("no valid session") || errorMessage.includes("session") || errorMessage.includes("server not initialized") || errorMessage.includes("not connected") || errorMessage.includes("http 400") || errorMessage.includes("http 401") || errorMessage.includes("http 403") || errorMessage.includes("econnrefused") || errorMessage.includes("fetch failed") || errorMessage.includes("connection refused");
754
+ }
755
+ /**
756
+ * Forces a reconnection to the MCP server by disconnecting and reconnecting.
757
+ *
758
+ * This is useful when the session becomes invalid (e.g., after server restart)
759
+ * and the client needs to establish a fresh connection.
760
+ *
761
+ * @returns Promise resolving when reconnection is complete
762
+ * @throws {Error} If reconnection fails
763
+ *
764
+ * @internal
765
+ */
766
+ async forceReconnect() {
767
+ this.log("debug", "Forcing reconnection to MCP server...");
768
+ try {
769
+ if (this.transport) {
770
+ await this.transport.close();
771
+ }
772
+ } catch (e) {
773
+ this.log("debug", "Error during force disconnect (ignored)", {
774
+ error: e instanceof Error ? e.message : String(e)
775
+ });
776
+ }
777
+ this.transport = void 0;
778
+ this.isConnected = null;
779
+ await this.connect();
780
+ this.log("debug", "Successfully reconnected to MCP server");
617
781
  }
618
782
  async listResources() {
619
783
  this.log("debug", `Requesting resources from MCP server`);
@@ -701,6 +865,12 @@ var InternalMastraMCPClient = class extends base.MastraBase {
701
865
  return handler(request.params);
702
866
  });
703
867
  }
868
+ setProgressNotificationHandler(handler) {
869
+ this.log("debug", "Setting progress notification handler");
870
+ this.client.setNotificationHandler(types_js.ProgressNotificationSchema, (notification) => {
871
+ handler(notification.params);
872
+ });
873
+ }
704
874
  async convertInputSchema(inputSchema) {
705
875
  if (utils.isZodType(inputSchema)) {
706
876
  return inputSchema;
@@ -774,7 +944,7 @@ var InternalMastraMCPClient = class extends base.MastraBase {
774
944
  }
775
945
  async tools() {
776
946
  this.log("debug", `Requesting tools from MCP server`);
777
- const { tools: tools$1 } = await this.client.listTools({ timeout: this.timeout });
947
+ const { tools: tools$1 } = await this.client.listTools({}, { timeout: this.timeout });
778
948
  const toolsRes = {};
779
949
  for (const tool of tools$1) {
780
950
  this.log("debug", `Processing tool: ${tool.name}`);
@@ -787,12 +957,14 @@ var InternalMastraMCPClient = class extends base.MastraBase {
787
957
  execute: async (input, context) => {
788
958
  const previousContext = this.currentOperationContext;
789
959
  this.currentOperationContext = context?.requestContext || null;
790
- try {
791
- this.log("debug", `Executing tool: ${tool.name}`, { toolArgs: input });
960
+ const executeToolCall = async () => {
961
+ this.log("debug", `Executing tool: ${tool.name}`, { toolArgs: input, runId: context?.runId });
792
962
  const res = await this.client.callTool(
793
963
  {
794
964
  name: tool.name,
795
- arguments: input
965
+ arguments: input,
966
+ // Use runId as progress token if available, otherwise generate a random UUID
967
+ ...this.enableProgressTracking ? { _meta: { progressToken: context?.runId || crypto.randomUUID() } } : {}
796
968
  },
797
969
  types_js.CallToolResultSchema,
798
970
  {
@@ -800,8 +972,31 @@ var InternalMastraMCPClient = class extends base.MastraBase {
800
972
  }
801
973
  );
802
974
  this.log("debug", `Tool executed successfully: ${tool.name}`);
975
+ if (res.structuredContent !== void 0) {
976
+ return res.structuredContent;
977
+ }
803
978
  return res;
979
+ };
980
+ try {
981
+ return await executeToolCall();
804
982
  } catch (e) {
983
+ if (this.isSessionError(e)) {
984
+ this.log("debug", `Session error detected for tool ${tool.name}, attempting reconnection...`, {
985
+ error: e instanceof Error ? e.message : String(e)
986
+ });
987
+ try {
988
+ await this.forceReconnect();
989
+ this.log("debug", `Retrying tool ${tool.name} after reconnection...`);
990
+ return await executeToolCall();
991
+ } catch (reconnectError) {
992
+ this.log("error", `Reconnection or retry failed for tool ${tool.name}`, {
993
+ originalError: e instanceof Error ? e.message : String(e),
994
+ reconnectError: reconnectError instanceof Error ? reconnectError.stack : String(reconnectError),
995
+ toolArgs: input
996
+ });
997
+ throw e;
998
+ }
999
+ }
805
1000
  this.log("error", `Error calling tool: ${tool.name}`, {
806
1001
  error: e instanceof Error ? e.stack : JSON.stringify(e, null, 2),
807
1002
  toolArgs: input
@@ -825,8 +1020,6 @@ var InternalMastraMCPClient = class extends base.MastraBase {
825
1020
  return toolsRes;
826
1021
  }
827
1022
  };
828
-
829
- // src/client/configuration.ts
830
1023
  var mcpClientInstances = /* @__PURE__ */ new Map();
831
1024
  var MCPClient = class extends base.MastraBase {
832
1025
  serverConfigs = {};
@@ -899,6 +1092,48 @@ To fix this you have three different options:
899
1092
  this.addToInstanceCache();
900
1093
  return this;
901
1094
  }
1095
+ /**
1096
+ * Provides access to progress-related operations for tracking long-running operations.
1097
+ *
1098
+ * Progress tracking allows MCP servers to send updates about the status of ongoing operations,
1099
+ * providing real-time feedback to users about task completion and current state.
1100
+ *
1101
+ * @example
1102
+ * ```typescript
1103
+ * // Set up handler for progress updates from a server
1104
+ * await mcp.progress.onUpdate('serverName', (params) => {
1105
+ * console.log(`Progress: ${params.progress}%`);
1106
+ * console.log(`Status: ${params.message}`);
1107
+ *
1108
+ * if (params.total) {
1109
+ * console.log(`Completed ${params.progress} of ${params.total} items`);
1110
+ * }
1111
+ * });
1112
+ * ```
1113
+ */
1114
+ get progress() {
1115
+ this.addToInstanceCache();
1116
+ return {
1117
+ onUpdate: async (serverName, handler) => {
1118
+ try {
1119
+ const internalClient = await this.getConnectedClientForServer(serverName);
1120
+ return internalClient.progress.onUpdate(handler);
1121
+ } catch (err) {
1122
+ throw new error.MastraError(
1123
+ {
1124
+ id: "MCP_CLIENT_ON_UPDATE_PROGRESS_FAILED",
1125
+ domain: error.ErrorDomain.MCP,
1126
+ category: error.ErrorCategory.THIRD_PARTY,
1127
+ details: {
1128
+ serverName
1129
+ }
1130
+ },
1131
+ err
1132
+ );
1133
+ }
1134
+ }
1135
+ };
1136
+ }
902
1137
  /**
903
1138
  * Provides access to elicitation-related operations for interactive user input collection.
904
1139
  *
@@ -1587,14 +1822,204 @@ To fix this you have three different options:
1587
1822
  }
1588
1823
  };
1589
1824
 
1590
- // ../../node_modules/.pnpm/hono@4.10.3/node_modules/hono/dist/utils/stream.js
1825
+ // src/client/oauth-provider.ts
1826
+ var InMemoryOAuthStorage = class {
1827
+ data = /* @__PURE__ */ new Map();
1828
+ set(key, value) {
1829
+ this.data.set(key, value);
1830
+ }
1831
+ get(key) {
1832
+ return this.data.get(key);
1833
+ }
1834
+ delete(key) {
1835
+ this.data.delete(key);
1836
+ }
1837
+ clear() {
1838
+ this.data.clear();
1839
+ }
1840
+ };
1841
+ var MCPOAuthClientProvider = class {
1842
+ _redirectUrl;
1843
+ _clientMetadata;
1844
+ storage;
1845
+ onRedirect;
1846
+ generateState;
1847
+ _clientInfo;
1848
+ constructor(options) {
1849
+ this._redirectUrl = options.redirectUrl;
1850
+ this._clientMetadata = options.clientMetadata;
1851
+ this._clientInfo = options.clientInformation;
1852
+ this.storage = options.storage ?? new InMemoryOAuthStorage();
1853
+ this.onRedirect = options.onRedirectToAuthorization;
1854
+ this.generateState = options.stateGenerator ?? (() => crypto.randomUUID());
1855
+ }
1856
+ /**
1857
+ * The URL to redirect the user agent to after authorization.
1858
+ */
1859
+ get redirectUrl() {
1860
+ return this._redirectUrl;
1861
+ }
1862
+ /**
1863
+ * Metadata about this OAuth client.
1864
+ */
1865
+ get clientMetadata() {
1866
+ return this._clientMetadata;
1867
+ }
1868
+ /**
1869
+ * Returns a OAuth2 state parameter.
1870
+ */
1871
+ async state() {
1872
+ return this.generateState();
1873
+ }
1874
+ /**
1875
+ * Loads information about this OAuth client.
1876
+ */
1877
+ async clientInformation() {
1878
+ if (this._clientInfo) {
1879
+ return this._clientInfo;
1880
+ }
1881
+ const stored = await this.storage.get("client_info");
1882
+ if (stored) {
1883
+ try {
1884
+ return JSON.parse(stored);
1885
+ } catch {
1886
+ }
1887
+ }
1888
+ return void 0;
1889
+ }
1890
+ /**
1891
+ * Saves dynamically registered client information.
1892
+ */
1893
+ async saveClientInformation(clientInformation) {
1894
+ this._clientInfo = clientInformation;
1895
+ await this.storage.set("client_info", JSON.stringify(clientInformation));
1896
+ }
1897
+ /**
1898
+ * Loads existing OAuth tokens.
1899
+ */
1900
+ async tokens() {
1901
+ const stored = await this.storage.get("tokens");
1902
+ if (stored) {
1903
+ try {
1904
+ return JSON.parse(stored);
1905
+ } catch {
1906
+ }
1907
+ }
1908
+ return void 0;
1909
+ }
1910
+ /**
1911
+ * Stores new OAuth tokens after successful authorization.
1912
+ */
1913
+ async saveTokens(tokens) {
1914
+ await this.storage.set("tokens", JSON.stringify(tokens));
1915
+ }
1916
+ /**
1917
+ * Invoked to redirect the user agent to the authorization URL.
1918
+ */
1919
+ async redirectToAuthorization(authorizationUrl) {
1920
+ if (this.onRedirect) {
1921
+ await this.onRedirect(authorizationUrl);
1922
+ } else {
1923
+ console.info(`Authorization required. Please visit: ${authorizationUrl.toString()}`);
1924
+ }
1925
+ }
1926
+ /**
1927
+ * Saves a PKCE code verifier before redirecting to authorization.
1928
+ */
1929
+ async saveCodeVerifier(codeVerifier) {
1930
+ await this.storage.set("code_verifier", codeVerifier);
1931
+ }
1932
+ /**
1933
+ * Loads the PKCE code verifier for validating authorization result.
1934
+ */
1935
+ async codeVerifier() {
1936
+ const verifier = await this.storage.get("code_verifier");
1937
+ if (!verifier) {
1938
+ throw new Error("No code verifier found. Authorization flow may not have started properly.");
1939
+ }
1940
+ return verifier;
1941
+ }
1942
+ /**
1943
+ * Optional: Custom client authentication for token requests.
1944
+ * Uses default behavior if not implemented.
1945
+ */
1946
+ async addClientAuthentication(_headers, _params, _url, _metadata) {
1947
+ }
1948
+ /**
1949
+ * Invalidate credentials when server indicates they're no longer valid.
1950
+ */
1951
+ async invalidateCredentials(scope) {
1952
+ switch (scope) {
1953
+ case "all":
1954
+ await this.storage.delete("tokens");
1955
+ await this.storage.delete("client_info");
1956
+ await this.storage.delete("code_verifier");
1957
+ this._clientInfo = void 0;
1958
+ break;
1959
+ case "client":
1960
+ await this.storage.delete("client_info");
1961
+ this._clientInfo = void 0;
1962
+ break;
1963
+ case "tokens":
1964
+ await this.storage.delete("tokens");
1965
+ break;
1966
+ case "verifier":
1967
+ await this.storage.delete("code_verifier");
1968
+ break;
1969
+ }
1970
+ }
1971
+ /**
1972
+ * Clear all stored OAuth data.
1973
+ * Useful for logging out or resetting state.
1974
+ */
1975
+ async clear() {
1976
+ await this.invalidateCredentials("all");
1977
+ }
1978
+ /**
1979
+ * Check if the provider has valid (non-expired) tokens.
1980
+ */
1981
+ async hasValidTokens() {
1982
+ const currentTokens = await this.tokens();
1983
+ if (!currentTokens) return false;
1984
+ if (!currentTokens.access_token) return false;
1985
+ return true;
1986
+ }
1987
+ };
1988
+ function createSimpleTokenProvider(accessToken, options) {
1989
+ const tokens = {
1990
+ access_token: accessToken,
1991
+ token_type: options.tokenType ?? "Bearer",
1992
+ refresh_token: options.refreshToken,
1993
+ expires_in: options.expiresIn,
1994
+ scope: options.scope
1995
+ };
1996
+ const storage = new InMemoryOAuthStorage();
1997
+ storage.set("tokens", JSON.stringify(tokens));
1998
+ if (options.clientInformation) {
1999
+ storage.set("client_info", JSON.stringify(options.clientInformation));
2000
+ }
2001
+ return new MCPOAuthClientProvider({
2002
+ redirectUrl: options.redirectUrl,
2003
+ clientMetadata: options.clientMetadata,
2004
+ clientInformation: options.clientInformation,
2005
+ storage
2006
+ });
2007
+ }
2008
+
2009
+ // ../../node_modules/.pnpm/hono@4.11.3/node_modules/hono/dist/utils/stream.js
1591
2010
  var StreamingApi = class {
1592
2011
  writer;
1593
2012
  encoder;
1594
2013
  writable;
1595
2014
  abortSubscribers = [];
1596
2015
  responseReadable;
2016
+ /**
2017
+ * Whether the stream has been aborted.
2018
+ */
1597
2019
  aborted = false;
2020
+ /**
2021
+ * Whether the stream has been closed normally.
2022
+ */
1598
2023
  closed = false;
1599
2024
  constructor(writable, _readable) {
1600
2025
  this.writable = writable;
@@ -1646,6 +2071,10 @@ var StreamingApi = class {
1646
2071
  onAbort(listener) {
1647
2072
  this.abortSubscribers.push(listener);
1648
2073
  }
2074
+ /**
2075
+ * Abort the stream.
2076
+ * You can call this method when stream is aborted by external event.
2077
+ */
1649
2078
  abort() {
1650
2079
  if (!this.aborted) {
1651
2080
  this.aborted = true;
@@ -1654,7 +2083,7 @@ var StreamingApi = class {
1654
2083
  }
1655
2084
  };
1656
2085
 
1657
- // ../../node_modules/.pnpm/hono@4.10.3/node_modules/hono/dist/helper/streaming/utils.js
2086
+ // ../../node_modules/.pnpm/hono@4.11.3/node_modules/hono/dist/helper/streaming/utils.js
1658
2087
  var isOldBunVersion = () => {
1659
2088
  const version = typeof Bun !== "undefined" ? Bun.version : void 0;
1660
2089
  if (version === void 0) {
@@ -1665,7 +2094,7 @@ var isOldBunVersion = () => {
1665
2094
  return result;
1666
2095
  };
1667
2096
 
1668
- // ../../node_modules/.pnpm/hono@4.10.3/node_modules/hono/dist/utils/html.js
2097
+ // ../../node_modules/.pnpm/hono@4.11.3/node_modules/hono/dist/utils/html.js
1669
2098
  var HtmlEscapedCallbackPhase = {
1670
2099
  Stringify: 1};
1671
2100
  var resolveCallback = async (str, phase, preserveCallbacks, context, buffer) => {
@@ -1696,7 +2125,7 @@ var resolveCallback = async (str, phase, preserveCallbacks, context, buffer) =>
1696
2125
  }
1697
2126
  };
1698
2127
 
1699
- // ../../node_modules/.pnpm/hono@4.10.3/node_modules/hono/dist/helper/streaming/sse.js
2128
+ // ../../node_modules/.pnpm/hono@4.11.3/node_modules/hono/dist/helper/streaming/sse.js
1700
2129
  var SSEStreamingApi = class extends StreamingApi {
1701
2130
  constructor(writable, readable) {
1702
2131
  super(writable, readable);
@@ -2144,7 +2573,16 @@ var MCPServer = class extends mcp.MCPServerBase {
2144
2573
  if (opts.prompts) {
2145
2574
  capabilities.prompts = { listChanged: true };
2146
2575
  }
2147
- this.server = new index_js.Server({ name: this.name, version: this.version }, { capabilities });
2576
+ this.server = new index_js$1.Server(
2577
+ {
2578
+ name: this.name,
2579
+ version: this.version
2580
+ },
2581
+ {
2582
+ capabilities,
2583
+ ...this.instructions ? { instructions: this.instructions } : {}
2584
+ }
2585
+ );
2148
2586
  this.logger.info(
2149
2587
  `Initialized MCPServer '${this.name}' v${this.version} (ID: ${this.id}) with tools: ${Object.keys(this.convertedTools).join(", ")} and resources. Capabilities: ${JSON.stringify(capabilities)}`
2150
2588
  );
@@ -2169,8 +2607,8 @@ var MCPServer = class extends mcp.MCPServerBase {
2169
2607
  }
2170
2608
  });
2171
2609
  this.elicitation = {
2172
- sendRequest: async (request) => {
2173
- return this.handleElicitationRequest(request);
2610
+ sendRequest: async (request, options) => {
2611
+ return this.handleElicitationRequest(request, void 0, options);
2174
2612
  }
2175
2613
  };
2176
2614
  }
@@ -2180,15 +2618,51 @@ var MCPServer = class extends mcp.MCPServerBase {
2180
2618
  *
2181
2619
  * @param request - The elicitation request containing message and schema
2182
2620
  * @param serverInstance - Optional server instance to use; defaults to main server for backward compatibility
2621
+ * @param options - Optional request options (timeout, signal, etc.)
2183
2622
  * @returns Promise that resolves to the client's response
2184
2623
  */
2185
- async handleElicitationRequest(request, serverInstance) {
2624
+ async handleElicitationRequest(request, serverInstance, options) {
2186
2625
  this.logger.debug(`Sending elicitation request: ${request.message}`);
2187
2626
  const server = serverInstance || this.server;
2188
- const response = await server.elicitInput(request);
2627
+ const response = await server.elicitInput(request, options);
2189
2628
  this.logger.debug(`Received elicitation response: ${JSON.stringify(response)}`);
2190
2629
  return response;
2191
2630
  }
2631
+ /**
2632
+ * Reads and parses the JSON body from an HTTP request.
2633
+ * If the request body was already parsed by middleware (e.g., express.json()),
2634
+ * it uses the pre-parsed body from req.body. Otherwise, it reads from the stream.
2635
+ *
2636
+ * This allows the MCP server to work with Express apps that use express.json()
2637
+ * globally without requiring special route exclusions.
2638
+ *
2639
+ * @param req - The incoming HTTP request
2640
+ * @param options - Optional configuration
2641
+ * @param options.preParsedOnly - If true, only return pre-parsed body from middleware,
2642
+ * returning undefined if not available. This allows the caller to fall back to
2643
+ * their own body reading logic (e.g., SDK's getRawBody with size limits).
2644
+ */
2645
+ async readJsonBody(req, options) {
2646
+ const reqWithBody = req;
2647
+ if (reqWithBody.body !== void 0) {
2648
+ return reqWithBody.body;
2649
+ }
2650
+ if (options?.preParsedOnly) {
2651
+ return void 0;
2652
+ }
2653
+ return new Promise((resolve, reject) => {
2654
+ let data = "";
2655
+ req.on("data", (chunk) => data += chunk);
2656
+ req.on("end", () => {
2657
+ try {
2658
+ resolve(JSON.parse(data));
2659
+ } catch (e) {
2660
+ reject(e);
2661
+ }
2662
+ });
2663
+ req.on("error", reject);
2664
+ });
2665
+ }
2192
2666
  /**
2193
2667
  * Creates a new Server instance configured with all handlers for HTTP sessions.
2194
2668
  * Each HTTP client connection gets its own Server instance to avoid routing conflicts.
@@ -2205,7 +2679,16 @@ var MCPServer = class extends mcp.MCPServerBase {
2205
2679
  if (this.promptOptions) {
2206
2680
  capabilities.prompts = { listChanged: true };
2207
2681
  }
2208
- const serverInstance = new index_js.Server({ name: this.name, version: this.version }, { capabilities });
2682
+ const serverInstance = new index_js$1.Server(
2683
+ {
2684
+ name: this.name,
2685
+ version: this.version
2686
+ },
2687
+ {
2688
+ capabilities,
2689
+ ...this.instructions ? { instructions: this.instructions } : {}
2690
+ }
2691
+ );
2209
2692
  this.registerHandlersOnServer(serverInstance);
2210
2693
  return serverInstance;
2211
2694
  }
@@ -2226,6 +2709,12 @@ var MCPServer = class extends mcp.MCPServerBase {
2226
2709
  if (tool.outputSchema) {
2227
2710
  toolSpec.outputSchema = tool.outputSchema.jsonSchema;
2228
2711
  }
2712
+ if (tool.mcp?.annotations) {
2713
+ toolSpec.annotations = tool.mcp.annotations;
2714
+ }
2715
+ if (tool.mcp?._meta) {
2716
+ toolSpec._meta = tool.mcp._meta;
2717
+ }
2229
2718
  return toolSpec;
2230
2719
  })
2231
2720
  };
@@ -2274,8 +2763,8 @@ Provided arguments: ${JSON.stringify(request.params.arguments, null, 2)}`
2274
2763
  };
2275
2764
  }
2276
2765
  const sessionElicitation = {
2277
- sendRequest: async (request2) => {
2278
- return this.handleElicitationRequest(request2, serverInstance);
2766
+ sendRequest: async (request2, options) => {
2767
+ return this.handleElicitationRequest(request2, serverInstance, options);
2279
2768
  }
2280
2769
  };
2281
2770
  const mcpOptions = {
@@ -2575,7 +3064,16 @@ Provided arguments: ${JSON.stringify(request.params.arguments, null, 2)}`
2575
3064
  `Executing agent tool '${agentToolName}' for agent '${agent.name}' with message: "${inputData.message}"`
2576
3065
  );
2577
3066
  try {
2578
- const response = await agent.generate(inputData.message, context);
3067
+ const proxiedContext = context?.requestContext || new requestContext.RequestContext();
3068
+ if (context?.mcp?.extra) {
3069
+ Object.entries(context.mcp.extra).forEach(([key, value]) => {
3070
+ proxiedContext.set(key, value);
3071
+ });
3072
+ }
3073
+ const response = await agent.generate(inputData.message, {
3074
+ ...context ?? {},
3075
+ requestContext: proxiedContext
3076
+ });
2579
3077
  return response;
2580
3078
  } catch (error) {
2581
3079
  this.logger.error(`Error executing agent tool '${agentToolName}' for agent '${agent.name}':`, error);
@@ -2639,10 +3137,16 @@ Provided arguments: ${JSON.stringify(request.params.arguments, null, 2)}`
2639
3137
  inputData
2640
3138
  );
2641
3139
  try {
2642
- const run2 = await workflow.createRun({ runId: context?.requestContext?.get("runId") });
3140
+ const proxiedContext = context?.requestContext || new requestContext.RequestContext();
3141
+ if (context?.mcp?.extra) {
3142
+ Object.entries(context.mcp.extra).forEach(([key, value]) => {
3143
+ proxiedContext.set(key, value);
3144
+ });
3145
+ }
3146
+ const run2 = await workflow.createRun({ runId: proxiedContext?.get("runId") });
2643
3147
  const response = await run2.start({
2644
3148
  inputData,
2645
- requestContext: context?.requestContext,
3149
+ requestContext: proxiedContext,
2646
3150
  tracingContext: context?.tracingContext
2647
3151
  });
2648
3152
  return response;
@@ -2761,7 +3265,7 @@ Provided arguments: ${JSON.stringify(request.params.arguments, null, 2)}`
2761
3265
  * ```
2762
3266
  */
2763
3267
  async startStdio() {
2764
- this.stdioTransport = new stdio_js.StdioServerTransport();
3268
+ this.stdioTransport = new stdio_js$1.StdioServerTransport();
2765
3269
  try {
2766
3270
  await this.server.connect(this.stdioTransport);
2767
3271
  } catch (error$1) {
@@ -2798,7 +3302,7 @@ Provided arguments: ${JSON.stringify(request.params.arguments, null, 2)}`
2798
3302
  *
2799
3303
  * @example
2800
3304
  * ```typescript
2801
- * import http from 'http';
3305
+ * import http from 'node:http';
2802
3306
  *
2803
3307
  * const httpServer = http.createServer(async (req, res) => {
2804
3308
  * await server.startSSE({
@@ -2829,7 +3333,8 @@ Provided arguments: ${JSON.stringify(request.params.arguments, null, 2)}`
2829
3333
  res.end("SSE connection not established");
2830
3334
  return;
2831
3335
  }
2832
- await this.sseTransport.handlePostMessage(req, res);
3336
+ const parsedBody = await this.readJsonBody(req, { preParsedOnly: true });
3337
+ await this.sseTransport.handlePostMessage(req, res, parsedBody);
2833
3338
  } else {
2834
3339
  this.logger.debug("Unknown path:", { path: url.pathname });
2835
3340
  res.writeHead(404);
@@ -2956,8 +3461,8 @@ Provided arguments: ${JSON.stringify(request.params.arguments, null, 2)}`
2956
3461
  *
2957
3462
  * @example
2958
3463
  * ```typescript
2959
- * import http from 'http';
2960
- * import { randomUUID } from 'crypto';
3464
+ * import http from 'node:http';
3465
+ * import { randomUUID } from 'node:crypto';
2961
3466
  *
2962
3467
  * const httpServer = http.createServer(async (req, res) => {
2963
3468
  * await server.startHTTP({
@@ -3001,7 +3506,7 @@ Provided arguments: ${JSON.stringify(request.params.arguments, null, 2)}`
3001
3506
  httpPath,
3002
3507
  req,
3003
3508
  res,
3004
- options = { sessionIdGenerator: () => crypto$1.randomUUID() }
3509
+ options
3005
3510
  }) {
3006
3511
  this.logger.debug(`startHTTP: Received ${req.method} request to ${url.pathname}`);
3007
3512
  if (url.pathname !== httpPath) {
@@ -3010,11 +3515,18 @@ Provided arguments: ${JSON.stringify(request.params.arguments, null, 2)}`
3010
3515
  res.end();
3011
3516
  return;
3012
3517
  }
3013
- if (options?.serverless) {
3014
- this.logger.debug("startHTTP: Running in serverless (stateless) mode");
3518
+ const isStatelessMode = options?.serverless || options && "sessionIdGenerator" in options && options.sessionIdGenerator === void 0;
3519
+ if (isStatelessMode) {
3520
+ this.logger.debug("startHTTP: Running in stateless mode (serverless or sessionIdGenerator: undefined)");
3015
3521
  await this.handleServerlessRequest(req, res);
3016
3522
  return;
3017
3523
  }
3524
+ const mergedOptions = {
3525
+ sessionIdGenerator: () => crypto$1.randomUUID(),
3526
+ // default: enabled
3527
+ ...options
3528
+ // user-provided overrides default
3529
+ };
3018
3530
  const sessionId = req.headers["mcp-session-id"];
3019
3531
  let transport;
3020
3532
  this.logger.debug(
@@ -3029,40 +3541,18 @@ Provided arguments: ${JSON.stringify(request.params.arguments, null, 2)}`
3029
3541
  `startHTTP: Handling GET request for existing session ${sessionId}. Calling transport.handleRequest.`
3030
3542
  );
3031
3543
  }
3032
- const body = req.method === "POST" ? await new Promise((resolve, reject) => {
3033
- let data = "";
3034
- req.on("data", (chunk) => data += chunk);
3035
- req.on("end", () => {
3036
- try {
3037
- resolve(JSON.parse(data));
3038
- } catch (e) {
3039
- reject(e);
3040
- }
3041
- });
3042
- req.on("error", reject);
3043
- }) : void 0;
3544
+ const body = req.method === "POST" ? await this.readJsonBody(req) : void 0;
3044
3545
  await transport.handleRequest(req, res, body);
3045
3546
  } else {
3046
3547
  this.logger.debug(`startHTTP: No existing Streamable HTTP session ID found. ${req.method}`);
3047
3548
  if (req.method === "POST") {
3048
- const body = await new Promise((resolve, reject) => {
3049
- let data = "";
3050
- req.on("data", (chunk) => data += chunk);
3051
- req.on("end", () => {
3052
- try {
3053
- resolve(JSON.parse(data));
3054
- } catch (e) {
3055
- reject(e);
3056
- }
3057
- });
3058
- req.on("error", reject);
3059
- });
3549
+ const body = await this.readJsonBody(req);
3060
3550
  const { isInitializeRequest } = await import('@modelcontextprotocol/sdk/types.js');
3061
3551
  if (isInitializeRequest(body)) {
3062
3552
  this.logger.debug("startHTTP: Received Streamable HTTP initialize request, creating new transport.");
3063
- transport = new streamableHttp_js.StreamableHTTPServerTransport({
3064
- ...options,
3065
- sessionIdGenerator: () => crypto$1.randomUUID(),
3553
+ transport = new streamableHttp_js$1.StreamableHTTPServerTransport({
3554
+ ...mergedOptions,
3555
+ sessionIdGenerator: mergedOptions.sessionIdGenerator,
3066
3556
  onsessioninitialized: (id) => {
3067
3557
  this.streamableHTTPTransports.set(id, transport);
3068
3558
  }
@@ -3166,24 +3656,13 @@ Provided arguments: ${JSON.stringify(request.params.arguments, null, 2)}`
3166
3656
  async handleServerlessRequest(req, res) {
3167
3657
  try {
3168
3658
  this.logger.debug(`handleServerlessRequest: Received ${req.method} request`);
3169
- const body = req.method === "POST" ? await new Promise((resolve, reject) => {
3170
- let data = "";
3171
- req.on("data", (chunk) => data += chunk);
3172
- req.on("end", () => {
3173
- try {
3174
- resolve(JSON.parse(data));
3175
- } catch (e) {
3176
- reject(new Error(`Invalid JSON in request body: ${e instanceof Error ? e.message : String(e)}`));
3177
- }
3178
- });
3179
- req.on("error", reject);
3180
- }) : void 0;
3659
+ const body = req.method === "POST" ? await this.readJsonBody(req) : void 0;
3181
3660
  this.logger.debug(`handleServerlessRequest: Processing ${req.method} request`, {
3182
3661
  method: body?.method,
3183
3662
  id: body?.id
3184
3663
  });
3185
3664
  const transientServer = this.createServerInstance();
3186
- const tempTransport = new streamableHttp_js.StreamableHTTPServerTransport({
3665
+ const tempTransport = new streamableHttp_js$1.StreamableHTTPServerTransport({
3187
3666
  sessionIdGenerator: void 0,
3188
3667
  enableJsonResponse: true
3189
3668
  });
@@ -3245,7 +3724,7 @@ Provided arguments: ${JSON.stringify(request.params.arguments, null, 2)}`
3245
3724
  }) {
3246
3725
  try {
3247
3726
  this.logger.debug("Received SSE connection");
3248
- this.sseTransport = new sse_js.SSEServerTransport(messagePath, res);
3727
+ this.sseTransport = new sse_js$1.SSEServerTransport(messagePath, res);
3249
3728
  await this.server.connect(this.sseTransport);
3250
3729
  this.server.onclose = async () => {
3251
3730
  this.sseTransport = void 0;
@@ -3607,8 +4086,273 @@ Provided arguments: ${JSON.stringify(args, null, 2)}`,
3607
4086
  }
3608
4087
  }
3609
4088
  };
4089
+ function escapeHeaderValue(value) {
4090
+ return value.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
4091
+ }
4092
+ function generateWWWAuthenticateHeader(options = {}) {
4093
+ const params = [];
4094
+ if (options.resourceMetadataUrl) {
4095
+ params.push(`resource_metadata="${escapeHeaderValue(options.resourceMetadataUrl)}"`);
4096
+ }
4097
+ if (options.additionalParams) {
4098
+ for (const [key, value] of Object.entries(options.additionalParams)) {
4099
+ params.push(`${key}="${escapeHeaderValue(value)}"`);
4100
+ }
4101
+ }
4102
+ if (params.length === 0) {
4103
+ return "Bearer";
4104
+ }
4105
+ return `Bearer ${params.join(", ")}`;
4106
+ }
4107
+ function generateProtectedResourceMetadata(config) {
4108
+ return {
4109
+ resource: config.resource,
4110
+ authorization_servers: config.authorizationServers,
4111
+ scopes_supported: config.scopesSupported ?? ["mcp:read", "mcp:write"],
4112
+ bearer_methods_supported: ["header"],
4113
+ ...config.resourceName && { resource_name: config.resourceName },
4114
+ ...config.resourceDocumentation && {
4115
+ resource_documentation: config.resourceDocumentation
4116
+ }
4117
+ };
4118
+ }
4119
+ function extractBearerToken(authHeader) {
4120
+ if (!authHeader) return void 0;
4121
+ const prefix = "bearer ";
4122
+ if (authHeader.length <= prefix.length) return void 0;
4123
+ if (authHeader.slice(0, prefix.length).toLowerCase() !== prefix) return void 0;
4124
+ const token = authHeader.slice(prefix.length).trim();
4125
+ return token || void 0;
4126
+ }
4127
+
4128
+ // src/server/oauth-middleware.ts
4129
+ function createOAuthMiddleware(options) {
4130
+ const { oauth, mcpPath = "/mcp", logger } = options;
4131
+ const protectedResourceMetadata = generateProtectedResourceMetadata(oauth);
4132
+ const wellKnownPath = "/.well-known/oauth-protected-resource";
4133
+ const resourceMetadataUrl = new URL(wellKnownPath, oauth.resource).toString();
4134
+ return async function oauthMiddleware(req, res, url) {
4135
+ logger?.debug?.(`OAuth middleware: ${req.method} ${url.pathname}`);
4136
+ if (url.pathname === wellKnownPath && req.method === "GET") {
4137
+ logger?.debug?.("OAuth middleware: Serving Protected Resource Metadata");
4138
+ res.writeHead(200, {
4139
+ "Content-Type": "application/json",
4140
+ "Cache-Control": "max-age=3600",
4141
+ "Access-Control-Allow-Origin": "*"
4142
+ });
4143
+ res.end(JSON.stringify(protectedResourceMetadata));
4144
+ return { proceed: false, handled: true };
4145
+ }
4146
+ if (url.pathname === wellKnownPath && req.method === "OPTIONS") {
4147
+ res.writeHead(204, {
4148
+ "Access-Control-Allow-Origin": "*",
4149
+ "Access-Control-Allow-Methods": "GET, OPTIONS",
4150
+ "Access-Control-Allow-Headers": "Content-Type",
4151
+ "Access-Control-Max-Age": "86400"
4152
+ });
4153
+ res.end();
4154
+ return { proceed: false, handled: true };
4155
+ }
4156
+ if (!url.pathname.startsWith(mcpPath)) {
4157
+ return { proceed: true, handled: false };
4158
+ }
4159
+ const authHeader = req.headers["authorization"];
4160
+ const token = extractBearerToken(authHeader);
4161
+ if (!token) {
4162
+ logger?.debug?.("OAuth middleware: No bearer token provided");
4163
+ res.writeHead(401, {
4164
+ "Content-Type": "application/json",
4165
+ "WWW-Authenticate": generateWWWAuthenticateHeader({ resourceMetadataUrl })
4166
+ });
4167
+ res.end(
4168
+ JSON.stringify({
4169
+ error: "unauthorized",
4170
+ error_description: "Bearer token required"
4171
+ })
4172
+ );
4173
+ return { proceed: false, handled: true };
4174
+ }
4175
+ if (oauth.validateToken) {
4176
+ logger?.debug?.("OAuth middleware: Validating token");
4177
+ const validationResult = await oauth.validateToken(token, oauth.resource);
4178
+ if (!validationResult.valid) {
4179
+ logger?.debug?.(`OAuth middleware: Token validation failed: ${validationResult.error}`);
4180
+ res.writeHead(401, {
4181
+ "Content-Type": "application/json",
4182
+ "WWW-Authenticate": generateWWWAuthenticateHeader({
4183
+ resourceMetadataUrl,
4184
+ additionalParams: {
4185
+ error: validationResult.error || "invalid_token",
4186
+ ...validationResult.errorDescription && {
4187
+ error_description: validationResult.errorDescription
4188
+ }
4189
+ }
4190
+ })
4191
+ });
4192
+ res.end(
4193
+ JSON.stringify({
4194
+ error: validationResult.error || "invalid_token",
4195
+ error_description: validationResult.errorDescription || "Token validation failed"
4196
+ })
4197
+ );
4198
+ return { proceed: false, handled: true, tokenValidation: validationResult };
4199
+ }
4200
+ logger?.debug?.("OAuth middleware: Token validated successfully");
4201
+ return { proceed: true, handled: false, tokenValidation: validationResult };
4202
+ }
4203
+ logger?.debug?.("OAuth middleware: No token validation configured, accepting token");
4204
+ return {
4205
+ proceed: true,
4206
+ handled: false,
4207
+ tokenValidation: { valid: true }
4208
+ };
4209
+ };
4210
+ }
4211
+ function createStaticTokenValidator(validTokens) {
4212
+ const tokenSet = new Set(validTokens);
4213
+ return async (token) => {
4214
+ if (tokenSet.has(token)) {
4215
+ return { valid: true, scopes: ["mcp:read", "mcp:write"] };
4216
+ }
4217
+ return {
4218
+ valid: false,
4219
+ error: "invalid_token",
4220
+ errorDescription: "Token not recognized"
4221
+ };
4222
+ };
4223
+ }
4224
+ function createIntrospectionValidator(introspectionEndpoint, clientCredentials) {
4225
+ return async (token, resource) => {
4226
+ try {
4227
+ const headers = {
4228
+ "Content-Type": "application/x-www-form-urlencoded"
4229
+ };
4230
+ if (clientCredentials) {
4231
+ if (clientCredentials.clientId.includes(":")) {
4232
+ return {
4233
+ valid: false,
4234
+ error: "invalid_request",
4235
+ errorDescription: "clientId cannot contain a colon character per RFC 7617"
4236
+ };
4237
+ }
4238
+ const credentials = Buffer.from(`${clientCredentials.clientId}:${clientCredentials.clientSecret}`).toString(
4239
+ "base64"
4240
+ );
4241
+ headers["Authorization"] = `Basic ${credentials}`;
4242
+ }
4243
+ const response = await fetch(introspectionEndpoint, {
4244
+ method: "POST",
4245
+ headers,
4246
+ body: new URLSearchParams({
4247
+ token,
4248
+ token_type_hint: "access_token"
4249
+ })
4250
+ });
4251
+ if (!response.ok) {
4252
+ return {
4253
+ valid: false,
4254
+ error: "server_error",
4255
+ errorDescription: `Introspection failed: ${response.status}`
4256
+ };
4257
+ }
4258
+ const data = await response.json();
4259
+ if (!data.active) {
4260
+ return {
4261
+ valid: false,
4262
+ error: "invalid_token",
4263
+ errorDescription: "Token is not active"
4264
+ };
4265
+ }
4266
+ if (data.aud) {
4267
+ const audiences = Array.isArray(data.aud) ? data.aud : [data.aud];
4268
+ if (!audiences.includes(resource)) {
4269
+ return {
4270
+ valid: false,
4271
+ error: "invalid_token",
4272
+ errorDescription: "Token audience does not match this resource"
4273
+ };
4274
+ }
4275
+ }
4276
+ return {
4277
+ valid: true,
4278
+ scopes: data.scope?.trim().split(" ").filter((s) => s !== "") || [],
4279
+ subject: data.sub,
4280
+ expiresAt: data.exp,
4281
+ claims: data
4282
+ };
4283
+ } catch (error) {
4284
+ return {
4285
+ valid: false,
4286
+ error: "server_error",
4287
+ errorDescription: error instanceof Error ? error.message : "Introspection failed"
4288
+ };
4289
+ }
4290
+ };
4291
+ }
3610
4292
 
4293
+ Object.defineProperty(exports, "UnauthorizedError", {
4294
+ enumerable: true,
4295
+ get: function () { return auth_js.UnauthorizedError; }
4296
+ });
4297
+ Object.defineProperty(exports, "auth", {
4298
+ enumerable: true,
4299
+ get: function () { return auth_js.auth; }
4300
+ });
4301
+ Object.defineProperty(exports, "buildDiscoveryUrls", {
4302
+ enumerable: true,
4303
+ get: function () { return auth_js.buildDiscoveryUrls; }
4304
+ });
4305
+ Object.defineProperty(exports, "discoverAuthorizationServerMetadata", {
4306
+ enumerable: true,
4307
+ get: function () { return auth_js.discoverAuthorizationServerMetadata; }
4308
+ });
4309
+ Object.defineProperty(exports, "discoverOAuthMetadata", {
4310
+ enumerable: true,
4311
+ get: function () { return auth_js.discoverOAuthMetadata; }
4312
+ });
4313
+ Object.defineProperty(exports, "discoverOAuthProtectedResourceMetadata", {
4314
+ enumerable: true,
4315
+ get: function () { return auth_js.discoverOAuthProtectedResourceMetadata; }
4316
+ });
4317
+ Object.defineProperty(exports, "exchangeAuthorization", {
4318
+ enumerable: true,
4319
+ get: function () { return auth_js.exchangeAuthorization; }
4320
+ });
4321
+ Object.defineProperty(exports, "extractResourceMetadataUrl", {
4322
+ enumerable: true,
4323
+ get: function () { return auth_js.extractResourceMetadataUrl; }
4324
+ });
4325
+ Object.defineProperty(exports, "parseErrorResponse", {
4326
+ enumerable: true,
4327
+ get: function () { return auth_js.parseErrorResponse; }
4328
+ });
4329
+ Object.defineProperty(exports, "refreshAuthorization", {
4330
+ enumerable: true,
4331
+ get: function () { return auth_js.refreshAuthorization; }
4332
+ });
4333
+ Object.defineProperty(exports, "registerClient", {
4334
+ enumerable: true,
4335
+ get: function () { return auth_js.registerClient; }
4336
+ });
4337
+ Object.defineProperty(exports, "selectResourceURL", {
4338
+ enumerable: true,
4339
+ get: function () { return auth_js.selectResourceURL; }
4340
+ });
4341
+ Object.defineProperty(exports, "startAuthorization", {
4342
+ enumerable: true,
4343
+ get: function () { return auth_js.startAuthorization; }
4344
+ });
4345
+ exports.InMemoryOAuthStorage = InMemoryOAuthStorage;
4346
+ exports.InternalMastraMCPClient = InternalMastraMCPClient;
3611
4347
  exports.MCPClient = MCPClient;
4348
+ exports.MCPOAuthClientProvider = MCPOAuthClientProvider;
3612
4349
  exports.MCPServer = MCPServer;
4350
+ exports.createIntrospectionValidator = createIntrospectionValidator;
4351
+ exports.createOAuthMiddleware = createOAuthMiddleware;
4352
+ exports.createSimpleTokenProvider = createSimpleTokenProvider;
4353
+ exports.createStaticTokenValidator = createStaticTokenValidator;
4354
+ exports.extractBearerToken = extractBearerToken;
4355
+ exports.generateProtectedResourceMetadata = generateProtectedResourceMetadata;
4356
+ exports.generateWWWAuthenticateHeader = generateWWWAuthenticateHeader;
3613
4357
  //# sourceMappingURL=index.cjs.map
3614
4358
  //# sourceMappingURL=index.cjs.map