@mastra/mcp 1.8.1 → 1.9.0-alpha.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/dist/index.js CHANGED
@@ -418,6 +418,7 @@ function isReconnectableMCPError(error) {
418
418
 
419
419
  // src/client/client.ts
420
420
  var DEFAULT_SERVER_CONNECT_TIMEOUT_MSEC = 3e3;
421
+ var DEFAULT_INSTRUCTIONS_MAX_LENGTH = 512;
421
422
  var require2 = createRequire(import.meta.url);
422
423
  var SSE_FALLBACK_STATUS_CODES = [400, 404, 405];
423
424
  var DATADOG_TRACER_TEST_SYMBOL = /* @__PURE__ */ Symbol.for("mastra.mcp.dd-trace-test-tracer");
@@ -500,6 +501,7 @@ var InternalMastraMCPClient = class extends MastraBase {
500
501
  exitHookUnsubscribe;
501
502
  sigTermHandler;
502
503
  sigHupHandler;
504
+ serverInstructions;
503
505
  _roots;
504
506
  requireToolApproval;
505
507
  /** Provides access to resource operations (list, read, subscribe, etc.) */
@@ -746,11 +748,19 @@ var InternalMastraMCPClient = class extends MastraBase {
746
748
  } else {
747
749
  throw new Error("Server configuration must include either a command or a url.");
748
750
  }
751
+ this.refreshServerInstructions();
749
752
  resolve(true);
750
753
  const originalOnClose = this.client.onclose;
751
754
  this.client.onclose = () => {
752
755
  this.log("debug", `MCP server connection closed`);
756
+ const staleTransport = this.transport;
757
+ this.transport = void 0;
753
758
  this.isConnected = null;
759
+ this.serverInstructions = void 0;
760
+ if (staleTransport) {
761
+ void staleTransport.close().catch(() => {
762
+ });
763
+ }
754
764
  if (typeof originalOnClose === "function") {
755
765
  originalOnClose();
756
766
  }
@@ -808,6 +818,18 @@ var InternalMastraMCPClient = class extends MastraBase {
808
818
  }
809
819
  return null;
810
820
  }
821
+ get instructions() {
822
+ return this.serverInstructions;
823
+ }
824
+ get forwardInstructions() {
825
+ return this.serverConfig.forwardInstructions ?? false;
826
+ }
827
+ get instructionsMaxLength() {
828
+ return this.serverConfig.instructionsMaxLength ?? DEFAULT_INSTRUCTIONS_MAX_LENGTH;
829
+ }
830
+ refreshServerInstructions() {
831
+ this.serverInstructions = this.client.getInstructions();
832
+ }
811
833
  async disconnect() {
812
834
  if (!this.transport) {
813
835
  this.log("debug", "Disconnect called but no transport was connected.");
@@ -825,6 +847,7 @@ var InternalMastraMCPClient = class extends MastraBase {
825
847
  } finally {
826
848
  this.transport = void 0;
827
849
  this.isConnected = null;
850
+ this.serverInstructions = void 0;
828
851
  if (this.exitHookUnsubscribe) {
829
852
  this.exitHookUnsubscribe();
830
853
  this.exitHookUnsubscribe = void 0;
@@ -863,6 +886,7 @@ var InternalMastraMCPClient = class extends MastraBase {
863
886
  }
864
887
  this.transport = void 0;
865
888
  this.isConnected = null;
889
+ this.serverInstructions = void 0;
866
890
  await this.connect();
867
891
  this.log("debug", "Successfully reconnected to MCP server");
868
892
  }
@@ -1001,7 +1025,10 @@ var InternalMastraMCPClient = class extends MastraBase {
1001
1025
  requireApproval,
1002
1026
  mcpMetadata: {
1003
1027
  serverName: this.name,
1004
- serverVersion: this.client.getServerVersion()?.version
1028
+ serverVersion: this.client.getServerVersion()?.version,
1029
+ serverInstructions: this.serverInstructions,
1030
+ forwardInstructions: this.forwardInstructions,
1031
+ instructionsMaxLength: this.instructionsMaxLength
1005
1032
  },
1006
1033
  execute: async (input, context) => {
1007
1034
  const operationContext = context?.requestContext ?? null;
@@ -1865,6 +1892,19 @@ To fix this you have three different options:
1865
1892
  await this.getConnectedClientForServer(serverName);
1866
1893
  }
1867
1894
  }
1895
+ /**
1896
+ * Returns instructions advertised by connected MCP servers during initialize.
1897
+ *
1898
+ * Servers that have not connected yet, or did not advertise instructions,
1899
+ * return `undefined`.
1900
+ */
1901
+ getServerInstructions() {
1902
+ const instructions = {};
1903
+ for (const serverName of Object.keys(this.serverConfigs)) {
1904
+ instructions[serverName] = this.mcpClientsById.get(serverName)?.instructions;
1905
+ }
1906
+ return instructions;
1907
+ }
1868
1908
  /**
1869
1909
  * Retrieves all tools from all configured servers with namespaced names.
1870
1910
  *
@@ -2310,7 +2350,7 @@ function createSimpleTokenProvider(accessToken, options) {
2310
2350
  });
2311
2351
  }
2312
2352
 
2313
- // ../../node_modules/.pnpm/hono@4.12.18/node_modules/hono/dist/utils/stream.js
2353
+ // ../../../../../setup-pnpm/node_modules/.bin/store/v11/links/@/hono/4.12.18/a4592d669eb2c33e4f24995614864047f2c1a155accc9d64dd2de0f4e8d608a9/node_modules/hono/dist/utils/stream.js
2314
2354
  var StreamingApi = class {
2315
2355
  writer;
2316
2356
  encoder;
@@ -2387,7 +2427,7 @@ var StreamingApi = class {
2387
2427
  }
2388
2428
  };
2389
2429
 
2390
- // ../../node_modules/.pnpm/hono@4.12.18/node_modules/hono/dist/helper/streaming/utils.js
2430
+ // ../../../../../setup-pnpm/node_modules/.bin/store/v11/links/@/hono/4.12.18/a4592d669eb2c33e4f24995614864047f2c1a155accc9d64dd2de0f4e8d608a9/node_modules/hono/dist/helper/streaming/utils.js
2391
2431
  var isOldBunVersion = () => {
2392
2432
  const version = typeof Bun !== "undefined" ? Bun.version : void 0;
2393
2433
  if (version === void 0) {
@@ -2398,7 +2438,7 @@ var isOldBunVersion = () => {
2398
2438
  return result;
2399
2439
  };
2400
2440
 
2401
- // ../../node_modules/.pnpm/hono@4.12.18/node_modules/hono/dist/utils/html.js
2441
+ // ../../../../../setup-pnpm/node_modules/.bin/store/v11/links/@/hono/4.12.18/a4592d669eb2c33e4f24995614864047f2c1a155accc9d64dd2de0f4e8d608a9/node_modules/hono/dist/utils/html.js
2402
2442
  var HtmlEscapedCallbackPhase = {
2403
2443
  Stringify: 1};
2404
2444
  var resolveCallback = async (str, phase, preserveCallbacks, context, buffer) => {
@@ -2429,7 +2469,7 @@ var resolveCallback = async (str, phase, preserveCallbacks, context, buffer) =>
2429
2469
  }
2430
2470
  };
2431
2471
 
2432
- // ../../node_modules/.pnpm/hono@4.12.18/node_modules/hono/dist/helper/streaming/sse.js
2472
+ // ../../../../../setup-pnpm/node_modules/.bin/store/v11/links/@/hono/4.12.18/a4592d669eb2c33e4f24995614864047f2c1a155accc9d64dd2de0f4e8d608a9/node_modules/hono/dist/helper/streaming/sse.js
2433
2473
  var SSEStreamingApi = class extends StreamingApi {
2434
2474
  constructor(writable, readable) {
2435
2475
  super(writable, readable);
@@ -2736,6 +2776,7 @@ var MCPServer = class extends MCPServerBase {
2736
2776
  definedPrompts;
2737
2777
  promptOptions;
2738
2778
  jsonSchemaValidator;
2779
+ mapAuthInfoToUser;
2739
2780
  subscriptions = /* @__PURE__ */ new Set();
2740
2781
  currentLoggingLevel;
2741
2782
  /**
@@ -2837,6 +2878,7 @@ var MCPServer = class extends MCPServerBase {
2837
2878
  * @param opts.prompts - Optional prompt configuration for exposing reusable templates
2838
2879
  * @param opts.id - Optional unique identifier (generated if not provided)
2839
2880
  * @param opts.description - Optional description of what the server does
2881
+ * @param opts.mapAuthInfoToUser - Optional mapper from MCP `extra.authInfo` to the FGA user context
2840
2882
  *
2841
2883
  * @example
2842
2884
  * ```typescript
@@ -2873,6 +2915,7 @@ var MCPServer = class extends MCPServerBase {
2873
2915
  this.resourceOptions = this.mergeAppResources(opts.resources, opts.appResources);
2874
2916
  this.promptOptions = opts.prompts;
2875
2917
  this.jsonSchemaValidator = opts.jsonSchemaValidator;
2918
+ this.mapAuthInfoToUser = opts.mapAuthInfoToUser;
2876
2919
  const capabilities = {
2877
2920
  tools: {},
2878
2921
  logging: { enabled: true }
@@ -3109,7 +3152,7 @@ var MCPServer = class extends MCPServerBase {
3109
3152
  */
3110
3153
  registerHandlersOnServer(serverInstance) {
3111
3154
  serverInstance.setRequestHandler(ListToolsRequestSchema, async (_request, extra) => {
3112
- const proxiedContext = this.createProxiedRequestContext(extra);
3155
+ const proxiedContext = await this.createProxiedRequestContext(extra);
3113
3156
  const tools = await this.getAuthorizedConvertedToolEntries(proxiedContext);
3114
3157
  return {
3115
3158
  tools: tools.map(([, tool]) => {
@@ -3189,12 +3232,7 @@ Provided arguments: ${JSON.stringify(request.params.arguments, null, 2)}`
3189
3232
  return this.handleElicitationRequest(request2, serverInstance, options);
3190
3233
  }
3191
3234
  };
3192
- const proxiedContext = new RequestContext();
3193
- if (extra) {
3194
- Object.entries(extra).forEach(([key, value]) => {
3195
- proxiedContext.set(key, value);
3196
- });
3197
- }
3235
+ const proxiedContext = await this.createProxiedRequestContext(extra);
3198
3236
  const mcpOptions = {
3199
3237
  messages: [],
3200
3238
  toolCallId: "",
@@ -4473,22 +4511,50 @@ Provided arguments: ${JSON.stringify(request.params.arguments, null, 2)}`
4473
4511
  _meta: withMastraToolStrictMeta(tool.mcp?._meta, tool.strict)
4474
4512
  };
4475
4513
  }
4476
- createProxiedRequestContext(extra) {
4514
+ async createProxiedRequestContext(extra) {
4477
4515
  const proxiedContext = new RequestContext();
4516
+ let extraRecord;
4478
4517
  if (extra && typeof extra === "object") {
4479
- Object.entries(extra).forEach(([key, value]) => {
4518
+ extraRecord = extra;
4519
+ Object.entries(extraRecord).forEach(([key, value]) => {
4480
4520
  proxiedContext.set(key, value);
4481
4521
  });
4482
4522
  }
4523
+ await this.resolveMappedFGAUser(proxiedContext, extraRecord);
4483
4524
  return proxiedContext;
4484
4525
  }
4526
+ async resolveMappedFGAUser(requestContext, extra) {
4527
+ if (!requestContext) {
4528
+ return void 0;
4529
+ }
4530
+ const existingUser = requestContext.get("user");
4531
+ if (existingUser) {
4532
+ return existingUser;
4533
+ }
4534
+ if (!this.mapAuthInfoToUser) {
4535
+ return void 0;
4536
+ }
4537
+ const authInfo = extra && "authInfo" in extra ? extra.authInfo : requestContext.get("authInfo");
4538
+ if (!authInfo) {
4539
+ return void 0;
4540
+ }
4541
+ const user = await this.mapAuthInfoToUser({
4542
+ authInfo,
4543
+ extra: extra ?? { authInfo },
4544
+ requestContext
4545
+ });
4546
+ if (user) {
4547
+ requestContext.set("user", user);
4548
+ }
4549
+ return user;
4550
+ }
4485
4551
  async getAuthorizedConvertedToolEntries(requestContext) {
4486
4552
  const entries = Object.entries(this.convertedTools);
4487
4553
  const fgaProvider = this.mastra?.getServer?.()?.fga;
4488
4554
  if (!fgaProvider) {
4489
4555
  return entries;
4490
4556
  }
4491
- const user = requestContext.get("user");
4557
+ const user = await this.resolveMappedFGAUser(requestContext);
4492
4558
  if (!user) {
4493
4559
  return [];
4494
4560
  }
@@ -4512,17 +4578,26 @@ Provided arguments: ${JSON.stringify(request.params.arguments, null, 2)}`
4512
4578
  if (!fgaProvider) {
4513
4579
  return;
4514
4580
  }
4515
- const { checkFGA, FGADeniedError, MastraFGAPermissions } = await import('@mastra/core/auth/ee');
4516
- const resourceId = JSON.stringify([this.id, toolId]);
4517
- const user = requestContext?.get("user");
4581
+ const { getMCPToolFGAResourceId, requireFGA, FGADeniedError, MastraFGAPermissions } = await import('@mastra/core/auth/ee');
4582
+ const resourceId = getMCPToolFGAResourceId(this.id, toolId);
4583
+ const user = await this.resolveMappedFGAUser(requestContext);
4518
4584
  if (!user) {
4519
4585
  throw new FGADeniedError({ id: "unknown" }, { type: "tool", id: resourceId }, MastraFGAPermissions.TOOLS_EXECUTE);
4520
4586
  }
4521
- await checkFGA({
4587
+ await requireFGA({
4522
4588
  fgaProvider,
4523
4589
  user,
4524
4590
  resource: { type: "tool", id: resourceId },
4525
- permission: MastraFGAPermissions.TOOLS_EXECUTE
4591
+ permission: MastraFGAPermissions.TOOLS_EXECUTE,
4592
+ requestContext,
4593
+ context: {
4594
+ resourceId
4595
+ },
4596
+ metadata: {
4597
+ mcpServerId: this.id,
4598
+ mcpServerName: this.name,
4599
+ toolId
4600
+ }
4526
4601
  });
4527
4602
  }
4528
4603
  /**