@mastra/mcp 1.6.1-alpha.0 → 1.6.1-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/CHANGELOG.md CHANGED
@@ -1,5 +1,14 @@
1
1
  # @mastra/mcp
2
2
 
3
+ ## 1.6.1-alpha.1
4
+
5
+ ### Patch Changes
6
+
7
+ - Added Fine-Grained Authorization (FGA) enforcement to MCP tool execution. Both transport-driven calls and direct `executeTool()` calls now run the same authorization checks when a request user is present, and typed FGA permission constants are accepted in MCP server authorization config. ([#15410](https://github.com/mastra-ai/mastra/pull/15410))
8
+
9
+ - Updated dependencies [[`86c0298`](https://github.com/mastra-ai/mastra/commit/86c0298e647306423c842f9d5ac827bd616bd13d), [`7fce309`](https://github.com/mastra-ai/mastra/commit/7fce30912b14170bfc41f0ac736cca0f39fe0cd4), [`7997c2e`](https://github.com/mastra-ai/mastra/commit/7997c2e55ddd121562a4098cd8d2b89c68433bf1), [`e97ccb9`](https://github.com/mastra-ai/mastra/commit/e97ccb900f8b7a390ce82c9f8eb8d6eb2c5e3777), [`c5daf48`](https://github.com/mastra-ai/mastra/commit/c5daf48556e98c46ae06caf00f92c249912007e9), [`cd96779`](https://github.com/mastra-ai/mastra/commit/cd9677937f113b2856dc8b9f3d4bdabcee58bb2e)]:
10
+ - @mastra/core@1.32.0-alpha.2
11
+
3
12
  ## 1.6.1-alpha.0
4
13
 
5
14
  ### Patch Changes
@@ -3,7 +3,7 @@ name: mastra-mcp
3
3
  description: Documentation for @mastra/mcp. Use when working with @mastra/mcp APIs, configuration, or implementation.
4
4
  metadata:
5
5
  package: "@mastra/mcp"
6
- version: "1.6.1-alpha.0"
6
+ version: "1.6.1-alpha.1"
7
7
  ---
8
8
 
9
9
  ## When to use
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "1.6.1-alpha.0",
2
+ "version": "1.6.1-alpha.1",
3
3
  "package": "@mastra/mcp",
4
4
  "exports": {
5
5
  "UnauthorizedError": {
@@ -332,6 +332,31 @@ const mcp = new MCPClient({
332
332
  })
333
333
  ```
334
334
 
335
+ **Apify**:
336
+
337
+ [Apify](https://apify.com) is the largest marketplace of tools for AI, with thousands of ready-made [Actors](https://apify.com/store) to extract real-time data from any website, track competitors, generate leads, analyze sentiment, or orchestrate your apps. Connect your agent through the [Apify MCP Server](https://mcp.apify.com).
338
+
339
+ ```typescript
340
+ import { MCPClient } from '@mastra/mcp'
341
+
342
+ export const mcp = new MCPClient({
343
+ servers: {
344
+ apify: {
345
+ url: new URL('https://mcp.apify.com'),
346
+ requestInit: {
347
+ headers: {
348
+ Authorization: `Bearer ${process.env.APIFY_TOKEN}`,
349
+ },
350
+ },
351
+ },
352
+ },
353
+ })
354
+ ```
355
+
356
+ Get your API token from the [Apify Console](https://console.apify.com/settings/integrations) and store it as `APIFY_TOKEN` in your environment.
357
+
358
+ To pick specific Actors and tools, use the [Apify MCP server configurator](https://mcp.apify.com) and copy the generated URL.
359
+
335
360
  **Ampersand**:
336
361
 
337
362
  [Ampersand](https://withampersand.com?utm_source=mastra-docs) offers an [MCP Server](https://docs.withampersand.com/mcp) that allows you to connect your agent to 150+ integrations with SaaS products like Salesforce, Hubspot, and Zendesk.
package/dist/index.cjs CHANGED
@@ -2826,9 +2826,11 @@ var MCPServer = class extends mcp.MCPServerBase {
2826
2826
  * This allows us to create multiple server instances with identical functionality.
2827
2827
  */
2828
2828
  registerHandlersOnServer(serverInstance) {
2829
- serverInstance.setRequestHandler(types_js.ListToolsRequestSchema, async () => {
2829
+ serverInstance.setRequestHandler(types_js.ListToolsRequestSchema, async (_request, extra) => {
2830
+ const proxiedContext = this.createProxiedRequestContext(extra);
2831
+ const tools = await this.getAuthorizedConvertedToolEntries(proxiedContext);
2830
2832
  return {
2831
- tools: Object.values(this.convertedTools).map((tool) => {
2833
+ tools: tools.map(([, tool]) => {
2832
2834
  const toolSpec = {
2833
2835
  name: tool.id || "unknown",
2834
2836
  description: tool.description,
@@ -2920,6 +2922,7 @@ Provided arguments: ${JSON.stringify(request.params.arguments, null, 2)}`
2920
2922
  throw new Error(`The "extra" key is now nested under "mcp.extra" in tool arguments`);
2921
2923
  }
2922
2924
  };
2925
+ await this.enforceToolExecutionFGA(request.params.name, proxiedContext);
2923
2926
  const result = await tool.execute(validation?.value ?? request.params.arguments ?? {}, mcpOptions);
2924
2927
  const duration = Date.now() - startTime;
2925
2928
  if (tools.isValidationError(result)) {
@@ -4111,7 +4114,24 @@ Provided arguments: ${JSON.stringify(request.params.arguments, null, 2)}`
4111
4114
  * });
4112
4115
  * ```
4113
4116
  */
4114
- getToolListInfo() {
4117
+ getToolListInfo(requestContext) {
4118
+ const fgaProvider = this.mastra?.getServer?.()?.fga;
4119
+ if (fgaProvider && requestContext) {
4120
+ return this.getAuthorizedConvertedToolEntries(requestContext).then((tools) => ({
4121
+ tools: tools.map(([toolId, tool]) => ({
4122
+ id: toolId,
4123
+ name: tool.id || toolId,
4124
+ description: tool.description,
4125
+ inputSchema: this.convertSchema(tool.parameters),
4126
+ outputSchema: this.convertSchema(tool.outputSchema),
4127
+ toolType: tool.mcp?.toolType,
4128
+ _meta: withMastraToolStrictMeta(tool.mcp?._meta, tool.strict)
4129
+ }))
4130
+ }));
4131
+ }
4132
+ if (fgaProvider && !requestContext) {
4133
+ return { tools: [] };
4134
+ }
4115
4135
  this.logger.debug("Getting tool list", { server: this.name });
4116
4136
  return {
4117
4137
  tools: Object.entries(this.convertedTools).map(([toolId, tool]) => ({
@@ -4119,7 +4139,7 @@ Provided arguments: ${JSON.stringify(request.params.arguments, null, 2)}`
4119
4139
  name: tool.id || toolId,
4120
4140
  description: tool.description,
4121
4141
  inputSchema: this.convertSchema(tool.parameters),
4122
- outputSchema: this.convertSchema(tool.parameters),
4142
+ outputSchema: this.convertSchema(tool.outputSchema),
4123
4143
  toolType: tool.mcp?.toolType,
4124
4144
  _meta: withMastraToolStrictMeta(tool.mcp?._meta, tool.strict)
4125
4145
  }))
@@ -4159,6 +4179,58 @@ Provided arguments: ${JSON.stringify(request.params.arguments, null, 2)}`
4159
4179
  _meta: withMastraToolStrictMeta(tool.mcp?._meta, tool.strict)
4160
4180
  };
4161
4181
  }
4182
+ createProxiedRequestContext(extra) {
4183
+ const proxiedContext = new requestContext.RequestContext();
4184
+ if (extra && typeof extra === "object") {
4185
+ Object.entries(extra).forEach(([key, value]) => {
4186
+ proxiedContext.set(key, value);
4187
+ });
4188
+ }
4189
+ return proxiedContext;
4190
+ }
4191
+ async getAuthorizedConvertedToolEntries(requestContext) {
4192
+ const entries = Object.entries(this.convertedTools);
4193
+ const fgaProvider = this.mastra?.getServer?.()?.fga;
4194
+ if (!fgaProvider) {
4195
+ return entries;
4196
+ }
4197
+ const user = requestContext.get("user");
4198
+ if (!user) {
4199
+ return [];
4200
+ }
4201
+ const accessible = await Promise.all(
4202
+ entries.map(async ([toolId, tool]) => {
4203
+ try {
4204
+ await this.enforceToolExecutionFGA(toolId, requestContext);
4205
+ return [toolId, tool];
4206
+ } catch (error) {
4207
+ if (error instanceof Error && error.name === "FGADeniedError") {
4208
+ return null;
4209
+ }
4210
+ throw error;
4211
+ }
4212
+ })
4213
+ );
4214
+ return accessible.filter((entry) => entry !== null);
4215
+ }
4216
+ async enforceToolExecutionFGA(toolId, requestContext) {
4217
+ const fgaProvider = this.mastra?.getServer?.()?.fga;
4218
+ if (!fgaProvider) {
4219
+ return;
4220
+ }
4221
+ const { checkFGA, FGADeniedError, MastraFGAPermissions } = await import('@mastra/core/auth/ee');
4222
+ const resourceId = JSON.stringify([this.id, toolId]);
4223
+ const user = requestContext?.get("user");
4224
+ if (!user) {
4225
+ throw new FGADeniedError({ id: "unknown" }, { type: "tool", id: resourceId }, MastraFGAPermissions.TOOLS_EXECUTE);
4226
+ }
4227
+ await checkFGA({
4228
+ fgaProvider,
4229
+ user,
4230
+ resource: { type: "tool", id: resourceId },
4231
+ permission: MastraFGAPermissions.TOOLS_EXECUTE
4232
+ });
4233
+ }
4162
4234
  /**
4163
4235
  * Executes a specific tool provided by this MCP server.
4164
4236
  *
@@ -4241,8 +4313,10 @@ Provided arguments: ${JSON.stringify(args, null, 2)}`,
4241
4313
  try {
4242
4314
  const finalExecutionContext = {
4243
4315
  messages: executionContext?.messages || [],
4244
- toolCallId: executionContext?.toolCallId || crypto$1.randomUUID()
4316
+ toolCallId: executionContext?.toolCallId || crypto$1.randomUUID(),
4317
+ requestContext: executionContext?.requestContext
4245
4318
  };
4319
+ await this.enforceToolExecutionFGA(toolId, finalExecutionContext.requestContext);
4246
4320
  const result = await tool.execute(validatedArgs, finalExecutionContext);
4247
4321
  this.logger.info("Tool executed successfully", { tool: toolId });
4248
4322
  return result;