@mastra/mcp 1.9.0-alpha.0 → 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/CHANGELOG.md CHANGED
@@ -1,5 +1,14 @@
1
1
  # @mastra/mcp
2
2
 
3
+ ## 1.9.0-alpha.1
4
+
5
+ ### Patch Changes
6
+
7
+ - Fixed FGA-enabled MCP servers so OAuth authInfo can be mapped to a Mastra user before tools/list and tools/call authorization. ([#17475](https://github.com/mastra-ai/mastra/pull/17475))
8
+
9
+ - Updated dependencies [[`0c1ed1d`](https://github.com/mastra-ai/mastra/commit/0c1ed1d00c7d87b5ac99ca95896211a2fa9189fa), [`849efb9`](https://github.com/mastra-ai/mastra/commit/849efb9fca6dc976589c1f90a303fea618769109)]:
10
+ - @mastra/core@1.38.0-alpha.8
11
+
3
12
  ## 1.9.0-alpha.0
4
13
 
5
14
  ### Minor 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.9.0-alpha.0"
6
+ version: "1.9.0-alpha.1"
7
7
  ---
8
8
 
9
9
  ## When to use
@@ -18,6 +18,7 @@ Read the individual reference documents for detailed explanations and code examp
18
18
 
19
19
  - [MCP Apps](references/docs-mcp-mcp-apps.md) - Serve interactive HTML UIs from MCP tools using the MCP Apps extension.
20
20
  - [MCP overview](references/docs-mcp-overview.md) - Learn about the Model Context Protocol (MCP), how to use third-party tools via MCPClient, connect to registries, and share your own tools using MCPServer.
21
+ - [Fine-Grained Authorization (FGA)](references/docs-server-auth-fga.md) - Add resource-level authorization to your Mastra application with FGA providers.
21
22
 
22
23
  ### Reference
23
24
 
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "1.9.0-alpha.0",
2
+ "version": "1.9.0-alpha.1",
3
3
  "package": "@mastra/mcp",
4
4
  "exports": {
5
5
  "UnauthorizedError": {
@@ -93,7 +93,7 @@ export const myAgent = new Agent({
93
93
  id: 'my-agent',
94
94
  name: 'My Agent',
95
95
  instructions: 'You have access to interactive UI tools.',
96
- model: '__OPENAI_MODEL_MINI__',
96
+ model: 'openai/gpt-5-mini',
97
97
  tools: { calculatorTool },
98
98
  })
99
99
  ```
@@ -125,7 +125,7 @@ const mcpClient = new MCPClient({
125
125
  const myAgent = new Agent({
126
126
  id: 'my-agent',
127
127
  name: 'My Agent',
128
- model: '__OPENAI_MODEL_MINI__',
128
+ model: 'openai/gpt-5-mini',
129
129
  tools: await mcpClient.listTools(),
130
130
  })
131
131
 
@@ -270,7 +270,7 @@ const mcpClient = new MCPClient({
270
270
  const myAgent = new Agent({
271
271
  id: 'my-agent',
272
272
  name: 'My Agent',
273
- model: '__OPENAI_MODEL_MINI__',
273
+ model: 'openai/gpt-5-mini',
274
274
  tools: await mcpClient.listTools(),
275
275
  })
276
276
 
@@ -82,7 +82,7 @@ export const testAgent = new Agent({
82
82
  - US National Weather Service
83
83
 
84
84
  Answer questions using the information you find using the MCP Servers.`,
85
- model: 'openai/gpt-5.4',
85
+ model: 'openai/gpt-5.5',
86
86
  tools: await testMcpClient.listTools(),
87
87
  })
88
88
  ```
@@ -0,0 +1,258 @@
1
+ # Fine-Grained Authorization (FGA)
2
+
3
+ > **Note:** Fine-Grained Authorization is part of the Mastra Enterprise Edition. Production deployments require a valid EE license. [Contact sales](https://mastra.ai/contact) for more information.
4
+
5
+ Fine-Grained Authorization (FGA) adds resource-level permission checks to your Mastra application. While RBAC answers "can this role do this action?", FGA answers **"can this user do this action on this specific resource?"**
6
+
7
+ ## When to use FGA
8
+
9
+ FGA is designed for multi-tenant B2B products where permissions are contextual:
10
+
11
+ - A user might be an **admin** of Team A but only a **member** of Team B
12
+ - Thread access should be limited to the user's own organization
13
+ - Workflow execution should be scoped to a specific team or project
14
+ - Tool access depends on the user's relationship to a resource
15
+
16
+ ## Configuration
17
+
18
+ Configure FGA in your Mastra server config alongside authentication and RBAC:
19
+
20
+ ```typescript
21
+ import { Mastra } from '@mastra/core/mastra';
22
+ import { MastraFGAPermissions } from '@mastra/core/auth/ee';
23
+ import { MastraAuthWorkos, MastraFGAWorkos } from '@mastra/auth-workos';
24
+
25
+ const mastra = new Mastra({
26
+ server: {
27
+ auth: new MastraAuthWorkos({
28
+ /* ... */
29
+ fetchMemberships: true,
30
+ mapUserToResourceId: user => user.teamId,
31
+ }),
32
+ fga: new MastraFGAWorkos({
33
+ resourceMapping: {
34
+ agent: { fgaResourceType: 'team', deriveId: (ctx) => ctx.user.teamId },
35
+ workflow: { fgaResourceType: 'team', deriveId: (ctx) => ctx.user.teamId },
36
+ thread: { fgaResourceType: 'workspace-thread', deriveId: ({ resourceId }) => resourceId },
37
+ },
38
+ permissionMapping: {
39
+ [MastraFGAPermissions.AGENTS_EXECUTE]: 'manage-workflows',
40
+ [MastraFGAPermissions.WORKFLOWS_EXECUTE]: 'manage-workflows',
41
+ [MastraFGAPermissions.MEMORY_READ]: 'read',
42
+ [MastraFGAPermissions.MEMORY_WRITE]: 'update',
43
+ },
44
+ }),
45
+ storedResources: {
46
+ scope: true,
47
+ },
48
+ },
49
+ });
50
+ ```
51
+
52
+ When using `MastraFGAWorkos`, set `fetchMemberships: true` on `MastraAuthWorkos`. WorkOS FGA checks need the user's organization memberships to resolve the correct membership ID for authorization.
53
+
54
+ Use `thread` as the resource-mapping key for memory authorization. `MastraFGAWorkos` still accepts the legacy alias `memory`, but new configs should prefer `thread`.
55
+
56
+ When `server.fga` is configured, Mastra enforces FGA on protected actions. If a protected action has no authenticated user, Mastra denies it. If `server.fga` is not configured, these FGA checks are skipped and Mastra keeps the previous behavior.
57
+
58
+ ### Resource mapping
59
+
60
+ The `resourceMapping` tells Mastra how to resolve FGA resource types and IDs from request context. Keys are Mastra resource types, values define the FGA resource type and how to derive the ID:
61
+
62
+ ```typescript
63
+ resourceMapping: {
64
+ // When checking "can user execute agent X?", resolve the FGA resource
65
+ // as the user's team (type: 'team', id: user.teamId)
66
+ agent: {
67
+ fgaResourceType: 'team',
68
+ deriveId: (ctx) => ctx.user.teamId,
69
+ },
70
+ }
71
+ ```
72
+
73
+ `deriveId()` receives:
74
+
75
+ - `user` — the authenticated user
76
+ - `resourceId` — the owning Mastra resource ID when available (for example, a thread's `resourceId`)
77
+ - `requestContext` — the current request context for advanced tenant resolution
78
+ - `metadata` — provider-specific metadata for the attempted action
79
+
80
+ Return `undefined` from `deriveId()` to fall back to the original Mastra resource ID.
81
+
82
+ For thread and memory checks, Mastra still passes the raw `threadId` as the resource being checked, but it also forwards the thread's owning `resourceId` into `deriveId()`. This lets you map thread permissions to composite tenant IDs such as `userId-teamId-orgId`.
83
+
84
+ ### Permission mapping
85
+
86
+ The `permissionMapping` translates Mastra's internal permission strings to your FGA provider's permission slugs:
87
+
88
+ ```typescript
89
+ import { MastraFGAPermissions } from '@mastra/core/auth/ee';
90
+
91
+ permissionMapping: {
92
+ [MastraFGAPermissions.AGENTS_EXECUTE]: 'manage-workflows', // Mastra permission -> WorkOS permission slug
93
+ [MastraFGAPermissions.MEMORY_READ]: 'read',
94
+ }
95
+ ```
96
+
97
+ If no mapping exists for a permission, the original string is passed through.
98
+
99
+ Use `validatePermissions()` to validate the full set of permissions Mastra may emit at startup. Use this when a provider requires every Mastra permission to have an explicit provider permission slug.
100
+
101
+ ### Stored resource scoping
102
+
103
+ FGA authorizes access to a resource. It does not automatically filter stored records that live in shared storage. Enable stored resource scoping when the built-in stored resource APIs are used in a multi-tenant app.
104
+
105
+ ```typescript
106
+ const mastra = new Mastra({
107
+ server: {
108
+ auth: new MastraAuthWorkos({
109
+ /* ... */
110
+ mapUserToResourceId: user => user.teamId,
111
+ }),
112
+ storedResources: {
113
+ scope: true,
114
+ },
115
+ },
116
+ });
117
+ ```
118
+
119
+ With `scope: true`, Mastra reads `MASTRA_RESOURCE_ID_KEY` from the request context. `mapUserToResourceId()` sets this value after authentication. Stored resource handlers persist the scope in record metadata and filter list, read, update, publish, and delete operations by that scope.
120
+
121
+ Use an object when the scope needs custom request logic:
122
+
123
+ ```typescript
124
+ storedResources: {
125
+ scope: {
126
+ metadataKey: 'teamId',
127
+ resolve: ({ user }) => user.teamId,
128
+ requireScope: true,
129
+ },
130
+ },
131
+ ```
132
+
133
+ If `requireScope` is `true` or omitted, scoped stored resource routes fail when no scope can be resolved.
134
+
135
+ ### Route policy coverage
136
+
137
+ Mastra includes route-level FGA metadata for built-in resource routes, including agents, workflows, tools, MCP tools, memory threads, responses, conversations, and stored resources. Stored resource route coverage includes `/stored/agents`, `/stored/mcp-clients`, `/stored/prompt-blocks`, `/stored/scorers`, `/stored/skills`, and `/stored/workspaces`. A route is checked when it has route-level `fga` metadata, when Mastra can derive built-in metadata for that route, or when the provider supplies metadata with `resolveRouteFGA()`.
138
+
139
+ To deny protected routes that do not resolve FGA metadata, configure route policy coverage on the FGA provider:
140
+
141
+ ```typescript
142
+ const fga = new MastraFGAWorkos({
143
+ resourceMapping: {
144
+ project: { fgaResourceType: 'project' },
145
+ },
146
+ permissionMapping: {
147
+ 'projects:read': 'read',
148
+ },
149
+ requireForProtectedRoutes: true,
150
+ auditProtectedRoutes: 'warn',
151
+ validatePermissions: async permissions => {
152
+ // Throw if a Mastra permission is missing from permissionMapping.
153
+ },
154
+ });
155
+ ```
156
+
157
+ Set `auditProtectedRoutes: 'error'` to fail startup when protected routes are missing built-in FGA metadata. If `requireForProtectedRoutes` is enabled, Mastra logs this audit as a warning by default.
158
+
159
+ For custom routes, prefer route-level `fga` metadata. This keeps authorization policy next to the route:
160
+
161
+ ```typescript
162
+ import { createRoute } from '@mastra/server/server-adapter';
163
+
164
+ export const getProjectRoute = createRoute({
165
+ method: 'GET',
166
+ path: '/projects/:projectId',
167
+ responseType: 'json',
168
+ requiresAuth: true,
169
+ fga: {
170
+ resourceType: 'project',
171
+ resourceIdParam: 'projectId',
172
+ permission: 'projects:read',
173
+ },
174
+ handler: async () => {
175
+ return { project: null };
176
+ },
177
+ });
178
+ ```
179
+
180
+ Use `resolveRouteFGA()` only when route metadata must be derived centrally from route, params, or request context. A route map scales better than string-prefix checks:
181
+
182
+ ```typescript
183
+ import type { FGARouteConfig, FGARouteResolver } from '@mastra/core/auth/ee';
184
+
185
+ const routeFGA = {
186
+ 'GET /billing/:accountId': {
187
+ resourceType: 'account',
188
+ resourceIdParam: 'accountId',
189
+ permission: 'billing:read',
190
+ },
191
+ } satisfies Record<string, FGARouteConfig>;
192
+
193
+ const resolveRouteFGA: FGARouteResolver = ({ route }) => routeFGA[`${route.method} ${route.path}`];
194
+
195
+ const fga = new MastraFGAWorkos({
196
+ /* ... */
197
+ resolveRouteFGA,
198
+ });
199
+ ```
200
+
201
+ ## Enforcement points
202
+
203
+ When an FGA provider is configured, Mastra automatically checks authorization at these lifecycle points:
204
+
205
+ | Lifecycle point | Permission checked | Resource type | Resource ID |
206
+ | ---------------------------------------------------------------- | ----------------------------------------------- | -------------------- | ------------------------------------------------------------------- |
207
+ | Agent execution (`generate`, `stream`) | `agents:execute` | `agent` | `agentId` |
208
+ | Built-in workflow HTTP execution routes and `Workflow.execute()` | `workflows:execute` | `workflow` | `workflowId` |
209
+ | Standalone tool execution | `tools:execute` | `tool` | `toolName` |
210
+ | Agent tool execution | `tools:execute` | `tool` | `${agentId}:${toolName}` |
211
+ | MCP tool execution | `tools:execute` | `tool` | `JSON.stringify([serverName, toolName])` |
212
+ | Thread and memory access | `memory:read`, `memory:write`, `memory:delete` | `thread` | `threadId` |
213
+ | Stored resource routes | Stored resource permission for the route action | Stored resource type | Route record ID, or the stored-resource scope for collection routes |
214
+ | HTTP resource routes | Configured per route | Configured per route | Configured per route |
215
+
216
+ For OAuth-protected MCP servers, HTTP MCP transports pass authenticated data as `extra.authInfo`. If an `MCPServer` is registered on an FGA-enabled Mastra instance, configure `mapAuthInfoToUser` so Mastra can set `requestContext.get('user')` before checking `tools/list` and `tools/call`. See [MCPServer authentication context](https://mastra.ai/reference/tools/mcp-server).
217
+
218
+ Direct SDK calls to `createRun().start()`, `resume()`, or `restart()` are not independently checked by core FGA in this release. Make those calls from a protected route or guard them in application code. Pass a `requestContext` with an authenticated user when invoking protected entry points directly.
219
+
220
+ Core agent, internal workflow, tool, and memory checks also pass `requestContext` and action metadata to the FGA provider. Route checks pass `requestContext`. Thread checks pass the owning `resourceId` when available.
221
+
222
+ ## Custom FGA provider
223
+
224
+ Implement `IFGAProvider` to use any FGA backend:
225
+
226
+ ```typescript
227
+ import { FGADeniedError } from '@mastra/core/auth/ee'
228
+ import type { FGACheckParams, IFGAProvider, MastraFGAPermissionInput } from '@mastra/core/auth/ee'
229
+
230
+ class MyFGAProvider implements IFGAProvider {
231
+ async check(user: any, params: FGACheckParams): Promise<boolean> {
232
+ // Your authorization logic
233
+ return true
234
+ }
235
+
236
+ async require(user: any, params: FGACheckParams): Promise<void> {
237
+ const allowed = await this.check(user, params)
238
+ if (!allowed) {
239
+ throw new FGADeniedError(user, params.resource, params.permission)
240
+ }
241
+ }
242
+
243
+ async filterAccessible<T extends { id: string }>(
244
+ user: any,
245
+ resources: T[],
246
+ resourceType: string,
247
+ permission: MastraFGAPermissionInput,
248
+ ): Promise<T[]> {
249
+ // Filter resources the user can access
250
+ return resources
251
+ }
252
+ }
253
+ ```
254
+
255
+ ## Related
256
+
257
+ - [Authentication overview](https://mastra.ai/docs/server/auth)
258
+ - [WorkOS authentication](https://mastra.ai/docs/server/auth/workos)
@@ -890,7 +890,7 @@ const agent = new Agent({
890
890
  id: 'multi-tool-agent',
891
891
  name: 'Multi-tool Agent',
892
892
  instructions: 'You have access to multiple tool servers.',
893
- model: 'openai/gpt-5.4',
893
+ model: 'openai/gpt-5.5',
894
894
  tools: await mcp.listTools(),
895
895
  })
896
896
 
@@ -941,7 +941,7 @@ const agent = new Agent({
941
941
  id: 'multi-tool-agent',
942
942
  name: 'Multi-tool Agent',
943
943
  instructions: 'You help users check stocks and weather.',
944
- model: 'openai/gpt-5.4',
944
+ model: 'openai/gpt-5.5',
945
945
  })
946
946
 
947
947
  // Later, configure MCP with user-specific settings
@@ -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
@@ -2783,6 +2783,7 @@ var MCPServer = class extends mcp.MCPServerBase {
2783
2783
  definedPrompts;
2784
2784
  promptOptions;
2785
2785
  jsonSchemaValidator;
2786
+ mapAuthInfoToUser;
2786
2787
  subscriptions = /* @__PURE__ */ new Set();
2787
2788
  currentLoggingLevel;
2788
2789
  /**
@@ -2884,6 +2885,7 @@ var MCPServer = class extends mcp.MCPServerBase {
2884
2885
  * @param opts.prompts - Optional prompt configuration for exposing reusable templates
2885
2886
  * @param opts.id - Optional unique identifier (generated if not provided)
2886
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
2887
2889
  *
2888
2890
  * @example
2889
2891
  * ```typescript
@@ -2920,6 +2922,7 @@ var MCPServer = class extends mcp.MCPServerBase {
2920
2922
  this.resourceOptions = this.mergeAppResources(opts.resources, opts.appResources);
2921
2923
  this.promptOptions = opts.prompts;
2922
2924
  this.jsonSchemaValidator = opts.jsonSchemaValidator;
2925
+ this.mapAuthInfoToUser = opts.mapAuthInfoToUser;
2923
2926
  const capabilities = {
2924
2927
  tools: {},
2925
2928
  logging: { enabled: true }
@@ -3156,7 +3159,7 @@ var MCPServer = class extends mcp.MCPServerBase {
3156
3159
  */
3157
3160
  registerHandlersOnServer(serverInstance) {
3158
3161
  serverInstance.setRequestHandler(types_js.ListToolsRequestSchema, async (_request, extra) => {
3159
- const proxiedContext = this.createProxiedRequestContext(extra);
3162
+ const proxiedContext = await this.createProxiedRequestContext(extra);
3160
3163
  const tools = await this.getAuthorizedConvertedToolEntries(proxiedContext);
3161
3164
  return {
3162
3165
  tools: tools.map(([, tool]) => {
@@ -3236,12 +3239,7 @@ Provided arguments: ${JSON.stringify(request.params.arguments, null, 2)}`
3236
3239
  return this.handleElicitationRequest(request2, serverInstance, options);
3237
3240
  }
3238
3241
  };
3239
- const proxiedContext = new requestContext.RequestContext();
3240
- if (extra) {
3241
- Object.entries(extra).forEach(([key, value]) => {
3242
- proxiedContext.set(key, value);
3243
- });
3244
- }
3242
+ const proxiedContext = await this.createProxiedRequestContext(extra);
3245
3243
  const mcpOptions = {
3246
3244
  messages: [],
3247
3245
  toolCallId: "",
@@ -4520,22 +4518,50 @@ Provided arguments: ${JSON.stringify(request.params.arguments, null, 2)}`
4520
4518
  _meta: withMastraToolStrictMeta(tool.mcp?._meta, tool.strict)
4521
4519
  };
4522
4520
  }
4523
- createProxiedRequestContext(extra) {
4521
+ async createProxiedRequestContext(extra) {
4524
4522
  const proxiedContext = new requestContext.RequestContext();
4523
+ let extraRecord;
4525
4524
  if (extra && typeof extra === "object") {
4526
- Object.entries(extra).forEach(([key, value]) => {
4525
+ extraRecord = extra;
4526
+ Object.entries(extraRecord).forEach(([key, value]) => {
4527
4527
  proxiedContext.set(key, value);
4528
4528
  });
4529
4529
  }
4530
+ await this.resolveMappedFGAUser(proxiedContext, extraRecord);
4530
4531
  return proxiedContext;
4531
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
+ }
4532
4558
  async getAuthorizedConvertedToolEntries(requestContext) {
4533
4559
  const entries = Object.entries(this.convertedTools);
4534
4560
  const fgaProvider = this.mastra?.getServer?.()?.fga;
4535
4561
  if (!fgaProvider) {
4536
4562
  return entries;
4537
4563
  }
4538
- const user = requestContext.get("user");
4564
+ const user = await this.resolveMappedFGAUser(requestContext);
4539
4565
  if (!user) {
4540
4566
  return [];
4541
4567
  }
@@ -4559,17 +4585,26 @@ Provided arguments: ${JSON.stringify(request.params.arguments, null, 2)}`
4559
4585
  if (!fgaProvider) {
4560
4586
  return;
4561
4587
  }
4562
- const { checkFGA, FGADeniedError, MastraFGAPermissions } = await import('@mastra/core/auth/ee');
4563
- const resourceId = JSON.stringify([this.id, toolId]);
4564
- 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);
4565
4591
  if (!user) {
4566
4592
  throw new FGADeniedError({ id: "unknown" }, { type: "tool", id: resourceId }, MastraFGAPermissions.TOOLS_EXECUTE);
4567
4593
  }
4568
- await checkFGA({
4594
+ await requireFGA({
4569
4595
  fgaProvider,
4570
4596
  user,
4571
4597
  resource: { type: "tool", id: resourceId },
4572
- 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
+ }
4573
4608
  });
4574
4609
  }
4575
4610
  /**