@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.
@@ -22,7 +22,7 @@ const myAgent = new Agent({
22
22
  name: 'MyExampleAgent',
23
23
  description: 'A generalist to help with basic questions.',
24
24
  instructions: 'You are a helpful assistant.',
25
- model: 'openai/gpt-5.4',
25
+ model: 'openai/gpt-5.5',
26
26
  })
27
27
 
28
28
  const weatherTool = createTool({
@@ -67,6 +67,8 @@ The constructor accepts an `MCPServerConfig` object with the following propertie
67
67
 
68
68
  **instructions** (`string`): Optional instructions describing how to use the server and its features.
69
69
 
70
+ **mapAuthInfoToUser** (`({ authInfo, extra, requestContext }) => unknown | null | undefined | Promise<unknown | null | undefined>`): Maps MCP transport auth data from \`extra.authInfo\` into the \`user\` value used by Mastra FGA checks. Use this when an OAuth-protected MCP server is registered on a Mastra instance with an FGA provider.
71
+
70
72
  **repository** (`Repository`): Optional repository information for the server's source code.
71
73
 
72
74
  **releaseDate** (`string`): Optional release date of this server version (ISO 8601 string). Defaults to the time of instantiation if not provided.
@@ -1097,6 +1099,36 @@ Whatever you set on `req.auth` in your HTTP middleware becomes available as `con
1097
1099
  req.auth = { ... } → context?.mcp?.extra?.authInfo.extra = { ... }
1098
1100
  ```
1099
1101
 
1102
+ ### Map auth data for FGA
1103
+
1104
+ When an `MCPServer` is registered on a Mastra instance with a fine-grained authorization (FGA) provider, Mastra checks `requestContext.get('user')` before listing or calling tools. HTTP MCP transports pass authenticated data as `extra.authInfo`, so use `mapAuthInfoToUser` to set the user shape expected by your FGA provider.
1105
+
1106
+ ```typescript
1107
+ const server = new MCPServer({
1108
+ id: 'my-server',
1109
+ name: 'My Server',
1110
+ version: '1.0.0',
1111
+ tools: { getUserData },
1112
+ mapAuthInfoToUser: ({ authInfo }) => {
1113
+ const user = authInfo as {
1114
+ extra?: {
1115
+ userId?: string
1116
+ organizationMembershipId?: string
1117
+ }
1118
+ }
1119
+
1120
+ if (!user.extra?.userId) {
1121
+ return null
1122
+ }
1123
+
1124
+ return {
1125
+ id: user.extra.userId,
1126
+ organizationMembershipId: user.extra.organizationMembershipId,
1127
+ }
1128
+ },
1129
+ })
1130
+ ```
1131
+
1100
1132
  ### Setting Up Authentication Middleware
1101
1133
 
1102
1134
  To pass data to your tools, populate `req.auth` on the Node.js request object in your HTTP server middleware before calling `server.startHTTP()`.
package/dist/index.cjs CHANGED
@@ -425,6 +425,7 @@ function isReconnectableMCPError(error) {
425
425
 
426
426
  // src/client/client.ts
427
427
  var DEFAULT_SERVER_CONNECT_TIMEOUT_MSEC = 3e3;
428
+ var DEFAULT_INSTRUCTIONS_MAX_LENGTH = 512;
428
429
  var require2 = module$1.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href)));
429
430
  var SSE_FALLBACK_STATUS_CODES = [400, 404, 405];
430
431
  var DATADOG_TRACER_TEST_SYMBOL = /* @__PURE__ */ Symbol.for("mastra.mcp.dd-trace-test-tracer");
@@ -507,6 +508,7 @@ var InternalMastraMCPClient = class extends base.MastraBase {
507
508
  exitHookUnsubscribe;
508
509
  sigTermHandler;
509
510
  sigHupHandler;
511
+ serverInstructions;
510
512
  _roots;
511
513
  requireToolApproval;
512
514
  /** Provides access to resource operations (list, read, subscribe, etc.) */
@@ -753,11 +755,19 @@ var InternalMastraMCPClient = class extends base.MastraBase {
753
755
  } else {
754
756
  throw new Error("Server configuration must include either a command or a url.");
755
757
  }
758
+ this.refreshServerInstructions();
756
759
  resolve(true);
757
760
  const originalOnClose = this.client.onclose;
758
761
  this.client.onclose = () => {
759
762
  this.log("debug", `MCP server connection closed`);
763
+ const staleTransport = this.transport;
764
+ this.transport = void 0;
760
765
  this.isConnected = null;
766
+ this.serverInstructions = void 0;
767
+ if (staleTransport) {
768
+ void staleTransport.close().catch(() => {
769
+ });
770
+ }
761
771
  if (typeof originalOnClose === "function") {
762
772
  originalOnClose();
763
773
  }
@@ -815,6 +825,18 @@ var InternalMastraMCPClient = class extends base.MastraBase {
815
825
  }
816
826
  return null;
817
827
  }
828
+ get instructions() {
829
+ return this.serverInstructions;
830
+ }
831
+ get forwardInstructions() {
832
+ return this.serverConfig.forwardInstructions ?? false;
833
+ }
834
+ get instructionsMaxLength() {
835
+ return this.serverConfig.instructionsMaxLength ?? DEFAULT_INSTRUCTIONS_MAX_LENGTH;
836
+ }
837
+ refreshServerInstructions() {
838
+ this.serverInstructions = this.client.getInstructions();
839
+ }
818
840
  async disconnect() {
819
841
  if (!this.transport) {
820
842
  this.log("debug", "Disconnect called but no transport was connected.");
@@ -832,6 +854,7 @@ var InternalMastraMCPClient = class extends base.MastraBase {
832
854
  } finally {
833
855
  this.transport = void 0;
834
856
  this.isConnected = null;
857
+ this.serverInstructions = void 0;
835
858
  if (this.exitHookUnsubscribe) {
836
859
  this.exitHookUnsubscribe();
837
860
  this.exitHookUnsubscribe = void 0;
@@ -870,6 +893,7 @@ var InternalMastraMCPClient = class extends base.MastraBase {
870
893
  }
871
894
  this.transport = void 0;
872
895
  this.isConnected = null;
896
+ this.serverInstructions = void 0;
873
897
  await this.connect();
874
898
  this.log("debug", "Successfully reconnected to MCP server");
875
899
  }
@@ -1008,7 +1032,10 @@ var InternalMastraMCPClient = class extends base.MastraBase {
1008
1032
  requireApproval,
1009
1033
  mcpMetadata: {
1010
1034
  serverName: this.name,
1011
- serverVersion: this.client.getServerVersion()?.version
1035
+ serverVersion: this.client.getServerVersion()?.version,
1036
+ serverInstructions: this.serverInstructions,
1037
+ forwardInstructions: this.forwardInstructions,
1038
+ instructionsMaxLength: this.instructionsMaxLength
1012
1039
  },
1013
1040
  execute: async (input, context) => {
1014
1041
  const operationContext = context?.requestContext ?? null;
@@ -1872,6 +1899,19 @@ To fix this you have three different options:
1872
1899
  await this.getConnectedClientForServer(serverName);
1873
1900
  }
1874
1901
  }
1902
+ /**
1903
+ * Returns instructions advertised by connected MCP servers during initialize.
1904
+ *
1905
+ * Servers that have not connected yet, or did not advertise instructions,
1906
+ * return `undefined`.
1907
+ */
1908
+ getServerInstructions() {
1909
+ const instructions = {};
1910
+ for (const serverName of Object.keys(this.serverConfigs)) {
1911
+ instructions[serverName] = this.mcpClientsById.get(serverName)?.instructions;
1912
+ }
1913
+ return instructions;
1914
+ }
1875
1915
  /**
1876
1916
  * Retrieves all tools from all configured servers with namespaced names.
1877
1917
  *
@@ -2317,7 +2357,7 @@ function createSimpleTokenProvider(accessToken, options) {
2317
2357
  });
2318
2358
  }
2319
2359
 
2320
- // ../../node_modules/.pnpm/hono@4.12.18/node_modules/hono/dist/utils/stream.js
2360
+ // ../../../../../setup-pnpm/node_modules/.bin/store/v11/links/@/hono/4.12.18/a4592d669eb2c33e4f24995614864047f2c1a155accc9d64dd2de0f4e8d608a9/node_modules/hono/dist/utils/stream.js
2321
2361
  var StreamingApi = class {
2322
2362
  writer;
2323
2363
  encoder;
@@ -2394,7 +2434,7 @@ var StreamingApi = class {
2394
2434
  }
2395
2435
  };
2396
2436
 
2397
- // ../../node_modules/.pnpm/hono@4.12.18/node_modules/hono/dist/helper/streaming/utils.js
2437
+ // ../../../../../setup-pnpm/node_modules/.bin/store/v11/links/@/hono/4.12.18/a4592d669eb2c33e4f24995614864047f2c1a155accc9d64dd2de0f4e8d608a9/node_modules/hono/dist/helper/streaming/utils.js
2398
2438
  var isOldBunVersion = () => {
2399
2439
  const version = typeof Bun !== "undefined" ? Bun.version : void 0;
2400
2440
  if (version === void 0) {
@@ -2405,7 +2445,7 @@ var isOldBunVersion = () => {
2405
2445
  return result;
2406
2446
  };
2407
2447
 
2408
- // ../../node_modules/.pnpm/hono@4.12.18/node_modules/hono/dist/utils/html.js
2448
+ // ../../../../../setup-pnpm/node_modules/.bin/store/v11/links/@/hono/4.12.18/a4592d669eb2c33e4f24995614864047f2c1a155accc9d64dd2de0f4e8d608a9/node_modules/hono/dist/utils/html.js
2409
2449
  var HtmlEscapedCallbackPhase = {
2410
2450
  Stringify: 1};
2411
2451
  var resolveCallback = async (str, phase, preserveCallbacks, context, buffer) => {
@@ -2436,7 +2476,7 @@ var resolveCallback = async (str, phase, preserveCallbacks, context, buffer) =>
2436
2476
  }
2437
2477
  };
2438
2478
 
2439
- // ../../node_modules/.pnpm/hono@4.12.18/node_modules/hono/dist/helper/streaming/sse.js
2479
+ // ../../../../../setup-pnpm/node_modules/.bin/store/v11/links/@/hono/4.12.18/a4592d669eb2c33e4f24995614864047f2c1a155accc9d64dd2de0f4e8d608a9/node_modules/hono/dist/helper/streaming/sse.js
2440
2480
  var SSEStreamingApi = class extends StreamingApi {
2441
2481
  constructor(writable, readable) {
2442
2482
  super(writable, readable);
@@ -2743,6 +2783,7 @@ var MCPServer = class extends mcp.MCPServerBase {
2743
2783
  definedPrompts;
2744
2784
  promptOptions;
2745
2785
  jsonSchemaValidator;
2786
+ mapAuthInfoToUser;
2746
2787
  subscriptions = /* @__PURE__ */ new Set();
2747
2788
  currentLoggingLevel;
2748
2789
  /**
@@ -2844,6 +2885,7 @@ var MCPServer = class extends mcp.MCPServerBase {
2844
2885
  * @param opts.prompts - Optional prompt configuration for exposing reusable templates
2845
2886
  * @param opts.id - Optional unique identifier (generated if not provided)
2846
2887
  * @param opts.description - Optional description of what the server does
2888
+ * @param opts.mapAuthInfoToUser - Optional mapper from MCP `extra.authInfo` to the FGA user context
2847
2889
  *
2848
2890
  * @example
2849
2891
  * ```typescript
@@ -2880,6 +2922,7 @@ var MCPServer = class extends mcp.MCPServerBase {
2880
2922
  this.resourceOptions = this.mergeAppResources(opts.resources, opts.appResources);
2881
2923
  this.promptOptions = opts.prompts;
2882
2924
  this.jsonSchemaValidator = opts.jsonSchemaValidator;
2925
+ this.mapAuthInfoToUser = opts.mapAuthInfoToUser;
2883
2926
  const capabilities = {
2884
2927
  tools: {},
2885
2928
  logging: { enabled: true }
@@ -3116,7 +3159,7 @@ var MCPServer = class extends mcp.MCPServerBase {
3116
3159
  */
3117
3160
  registerHandlersOnServer(serverInstance) {
3118
3161
  serverInstance.setRequestHandler(types_js.ListToolsRequestSchema, async (_request, extra) => {
3119
- const proxiedContext = this.createProxiedRequestContext(extra);
3162
+ const proxiedContext = await this.createProxiedRequestContext(extra);
3120
3163
  const tools = await this.getAuthorizedConvertedToolEntries(proxiedContext);
3121
3164
  return {
3122
3165
  tools: tools.map(([, tool]) => {
@@ -3196,12 +3239,7 @@ Provided arguments: ${JSON.stringify(request.params.arguments, null, 2)}`
3196
3239
  return this.handleElicitationRequest(request2, serverInstance, options);
3197
3240
  }
3198
3241
  };
3199
- const proxiedContext = new requestContext.RequestContext();
3200
- if (extra) {
3201
- Object.entries(extra).forEach(([key, value]) => {
3202
- proxiedContext.set(key, value);
3203
- });
3204
- }
3242
+ const proxiedContext = await this.createProxiedRequestContext(extra);
3205
3243
  const mcpOptions = {
3206
3244
  messages: [],
3207
3245
  toolCallId: "",
@@ -4480,22 +4518,50 @@ Provided arguments: ${JSON.stringify(request.params.arguments, null, 2)}`
4480
4518
  _meta: withMastraToolStrictMeta(tool.mcp?._meta, tool.strict)
4481
4519
  };
4482
4520
  }
4483
- createProxiedRequestContext(extra) {
4521
+ async createProxiedRequestContext(extra) {
4484
4522
  const proxiedContext = new requestContext.RequestContext();
4523
+ let extraRecord;
4485
4524
  if (extra && typeof extra === "object") {
4486
- Object.entries(extra).forEach(([key, value]) => {
4525
+ extraRecord = extra;
4526
+ Object.entries(extraRecord).forEach(([key, value]) => {
4487
4527
  proxiedContext.set(key, value);
4488
4528
  });
4489
4529
  }
4530
+ await this.resolveMappedFGAUser(proxiedContext, extraRecord);
4490
4531
  return proxiedContext;
4491
4532
  }
4533
+ async resolveMappedFGAUser(requestContext, extra) {
4534
+ if (!requestContext) {
4535
+ return void 0;
4536
+ }
4537
+ const existingUser = requestContext.get("user");
4538
+ if (existingUser) {
4539
+ return existingUser;
4540
+ }
4541
+ if (!this.mapAuthInfoToUser) {
4542
+ return void 0;
4543
+ }
4544
+ const authInfo = extra && "authInfo" in extra ? extra.authInfo : requestContext.get("authInfo");
4545
+ if (!authInfo) {
4546
+ return void 0;
4547
+ }
4548
+ const user = await this.mapAuthInfoToUser({
4549
+ authInfo,
4550
+ extra: extra ?? { authInfo },
4551
+ requestContext
4552
+ });
4553
+ if (user) {
4554
+ requestContext.set("user", user);
4555
+ }
4556
+ return user;
4557
+ }
4492
4558
  async getAuthorizedConvertedToolEntries(requestContext) {
4493
4559
  const entries = Object.entries(this.convertedTools);
4494
4560
  const fgaProvider = this.mastra?.getServer?.()?.fga;
4495
4561
  if (!fgaProvider) {
4496
4562
  return entries;
4497
4563
  }
4498
- const user = requestContext.get("user");
4564
+ const user = await this.resolveMappedFGAUser(requestContext);
4499
4565
  if (!user) {
4500
4566
  return [];
4501
4567
  }
@@ -4519,17 +4585,26 @@ Provided arguments: ${JSON.stringify(request.params.arguments, null, 2)}`
4519
4585
  if (!fgaProvider) {
4520
4586
  return;
4521
4587
  }
4522
- const { checkFGA, FGADeniedError, MastraFGAPermissions } = await import('@mastra/core/auth/ee');
4523
- const resourceId = JSON.stringify([this.id, toolId]);
4524
- const user = requestContext?.get("user");
4588
+ const { getMCPToolFGAResourceId, requireFGA, FGADeniedError, MastraFGAPermissions } = await import('@mastra/core/auth/ee');
4589
+ const resourceId = getMCPToolFGAResourceId(this.id, toolId);
4590
+ const user = await this.resolveMappedFGAUser(requestContext);
4525
4591
  if (!user) {
4526
4592
  throw new FGADeniedError({ id: "unknown" }, { type: "tool", id: resourceId }, MastraFGAPermissions.TOOLS_EXECUTE);
4527
4593
  }
4528
- await checkFGA({
4594
+ await requireFGA({
4529
4595
  fgaProvider,
4530
4596
  user,
4531
4597
  resource: { type: "tool", id: resourceId },
4532
- permission: MastraFGAPermissions.TOOLS_EXECUTE
4598
+ permission: MastraFGAPermissions.TOOLS_EXECUTE,
4599
+ requestContext,
4600
+ context: {
4601
+ resourceId
4602
+ },
4603
+ metadata: {
4604
+ mcpServerId: this.id,
4605
+ mcpServerName: this.name,
4606
+ toolId
4607
+ }
4533
4608
  });
4534
4609
  }
4535
4610
  /**