@mastra/mcp 1.9.1 → 1.10.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 +45 -0
- package/dist/docs/SKILL.md +1 -1
- package/dist/docs/assets/SOURCE_MAP.json +1 -1
- package/dist/docs/references/docs-mcp-mcp-apps.md +4 -4
- package/dist/docs/references/docs-server-auth-fga.md +15 -15
- package/dist/docs/references/reference-tools-mcp-client.md +5 -5
- package/dist/docs/references/reference-tools-mcp-server.md +45 -0
- package/dist/index.cjs +64 -64
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +64 -64
- package/dist/index.js.map +1 -1
- package/dist/server/resourceActions.d.ts +3 -6
- package/dist/server/resourceActions.d.ts.map +1 -1
- package/dist/server/server.d.ts +3 -2
- package/dist/server/server.d.ts.map +1 -1
- package/package.json +10 -10
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,50 @@
|
|
|
1
1
|
# @mastra/mcp
|
|
2
2
|
|
|
3
|
+
## 1.10.0-alpha.1
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- Added MCP server Fine-Grained Authorization mapping overrides for tool authorization. ([#17529](https://github.com/mastra-ai/mastra/pull/17529))
|
|
8
|
+
|
|
9
|
+
Use the new `fga` option on `MCPServer` to customize the resource and permission mappings used for `tools/list` and `tools/call` checks without changing the Mastra instance-level `tool` mapping used by internal agent and workflow tool execution.
|
|
10
|
+
|
|
11
|
+
```ts
|
|
12
|
+
const server = new MCPServer({
|
|
13
|
+
name: 'My Server',
|
|
14
|
+
version: '1.0.0',
|
|
15
|
+
tools: { getData },
|
|
16
|
+
fga: {
|
|
17
|
+
resourceMapping: {
|
|
18
|
+
tool: {
|
|
19
|
+
fgaResourceType: 'user',
|
|
20
|
+
deriveId: ({ user }) => user.id,
|
|
21
|
+
},
|
|
22
|
+
},
|
|
23
|
+
permissionMapping: {
|
|
24
|
+
'tools:execute': 'read',
|
|
25
|
+
},
|
|
26
|
+
},
|
|
27
|
+
});
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
### Patch Changes
|
|
31
|
+
|
|
32
|
+
- Updated dependencies [[`575f815`](https://github.com/mastra-ai/mastra/commit/575f815c5c3567b71c0b83cbb7fa98c8253a9d9c), [`306909a`](https://github.com/mastra-ai/mastra/commit/306909a693de77d709b38706e2673c9547d24a28), [`5191af8`](https://github.com/mastra-ai/mastra/commit/5191af80c799eea25357c545fc05d91b3883531d), [`43bd3d4`](https://github.com/mastra-ai/mastra/commit/43bd3d421987463fdf35386a45199c49499ed069), [`e6fa79e`](https://github.com/mastra-ai/mastra/commit/e6fa79ec72a2ddffdd25e85270398951e9d552a4), [`904bcdf`](https://github.com/mastra-ai/mastra/commit/904bcdf7b8004aa7be823f9f70ca63580e47e470), [`7f5ee1d`](https://github.com/mastra-ai/mastra/commit/7f5ee1dca46daee8d2817f2ebe49e6335da81956), [`1e9aab5`](https://github.com/mastra-ai/mastra/commit/1e9aab50ff11e6e88fde4d7cbf512c44a9fe8d61), [`bf8eb6d`](https://github.com/mastra-ai/mastra/commit/bf8eb6d0ec213a403eb9265a594ad283c44ab3dc), [`493a328`](https://github.com/mastra-ai/mastra/commit/493a328f4346a1deeb9f1e2e44c8f2a3a4d7591b), [`029a414`](https://github.com/mastra-ai/mastra/commit/029a4141719793bd3e898a39eb5a0466a55f5f3a), [`b147b29`](https://github.com/mastra-ai/mastra/commit/b147b2907f0cd1aa812efe6d6e3f58d22e66fc88), [`d371ac1`](https://github.com/mastra-ai/mastra/commit/d371ac1d9820afaaf7cfdbc380a475946a994d8f), [`cf182b7`](https://github.com/mastra-ai/mastra/commit/cf182b7fb495767946d9840ef29f19cfa906f31f), [`a049c2a`](https://github.com/mastra-ai/mastra/commit/a049c2a9dfb41d0ee2e7a28874a88cd64fd5669f), [`b147b29`](https://github.com/mastra-ai/mastra/commit/b147b2907f0cd1aa812efe6d6e3f58d22e66fc88), [`2a96528`](https://github.com/mastra-ai/mastra/commit/2a9652848dfa3c5a2426f952e9d93554c26fd90f), [`2656d9c`](https://github.com/mastra-ai/mastra/commit/2656d9c2976d4f3354253bfbbbf9b88a1b2bbf34), [`63e3fe1`](https://github.com/mastra-ai/mastra/commit/63e3fe13cc1ea96f91d7c68aea92f400faf9e4da), [`1d4ce8d`](https://github.com/mastra-ai/mastra/commit/1d4ce8daaa54511f325c1b609d31b8e54009d677), [`8c68372`](https://github.com/mastra-ai/mastra/commit/8c68372e85fe0b066ec12c58bd29ffb93e54c552)]:
|
|
33
|
+
- @mastra/core@1.42.0-alpha.4
|
|
34
|
+
|
|
35
|
+
## 1.9.2-alpha.0
|
|
36
|
+
|
|
37
|
+
### Patch Changes
|
|
38
|
+
|
|
39
|
+
- Fixed `MCPServer` leaking one caller's resources to other callers. The result of the first `resources/list` request was cached on the shared, long-lived server instance and replayed to everyone, so a dynamic resource provider that scopes resources per user or tenant (resolved from `extra.authInfo`) served the first caller's resource index — names and URIs — to subsequent callers. The same stale cache also backed `resources/read` URI resolution and the public `listResources()` method. The `resources/templates/list` handler had the same defect for dynamic resource template providers. ([#17610](https://github.com/mastra-ai/mastra/pull/17610))
|
|
40
|
+
|
|
41
|
+
Resource and resource template providers are now invoked on every request with the current caller's context, so each caller only sees their own resources. See https://github.com/mastra-ai/mastra/issues/17609
|
|
42
|
+
|
|
43
|
+
- Fixed flaky MCP server tests by replacing real weather API calls with deterministic mock tool ([#17572](https://github.com/mastra-ai/mastra/pull/17572))
|
|
44
|
+
|
|
45
|
+
- Updated dependencies [[`d468acb`](https://github.com/mastra-ai/mastra/commit/d468acb07aec1bb19a2cb0ada8042b05b46746b2), [`e9be4e7`](https://github.com/mastra-ai/mastra/commit/e9be4e747ec3d8b65548bff92f9377db06105376), [`d53cfc2`](https://github.com/mastra-ai/mastra/commit/d53cfc2c7f8d78343a4aa84ec4e129ba25f3325e), [`65799d4`](https://github.com/mastra-ai/mastra/commit/65799d4d549e5ebb9c848fbe3f51ac090f64becf), [`c268c89`](https://github.com/mastra-ai/mastra/commit/c268c89f4c63a93ee474d3cffdf3ea60bf00d4f2), [`d468acb`](https://github.com/mastra-ai/mastra/commit/d468acb07aec1bb19a2cb0ada8042b05b46746b2), [`0c72f03`](https://github.com/mastra-ai/mastra/commit/0c72f032abb13254df5a7856d64be2f207b8006d), [`3b45ea9`](https://github.com/mastra-ai/mastra/commit/3b45ea95015557a6cb9d70dc5252af54ab1b78ac), [`f084be1`](https://github.com/mastra-ai/mastra/commit/f084be1fcbe33ad7480913e44d6130c421c0976f)]:
|
|
46
|
+
- @mastra/core@1.42.0-alpha.0
|
|
47
|
+
|
|
3
48
|
## 1.9.1
|
|
4
49
|
|
|
5
50
|
### Patch Changes
|
package/dist/docs/SKILL.md
CHANGED
|
@@ -83,7 +83,7 @@ calculatorTool._meta = {
|
|
|
83
83
|
|
|
84
84
|
## Connecting MCP Apps to agents
|
|
85
85
|
|
|
86
|
-
Agents consume tools — they
|
|
86
|
+
Agents consume tools — they don't need to know about MCP servers. Pass tools to the agent's `tools` config, and register the MCP server at the Mastra level so Studio can resolve app resources.
|
|
87
87
|
|
|
88
88
|
```typescript
|
|
89
89
|
import { Agent } from '@mastra/core/agent'
|
|
@@ -158,8 +158,8 @@ Agent calls tool → Tool returns brief content + structuredContent
|
|
|
158
158
|
|
|
159
159
|
Tools with app resources should return two fields:
|
|
160
160
|
|
|
161
|
-
- `content`: A brief text summary for the model. Keep this short so the agent
|
|
162
|
-
- `structuredContent`: The data payload that hydrates the UI. The model
|
|
161
|
+
- `content`: A brief text summary for the model. Keep this short so the agent doesn't parrot the full result.
|
|
162
|
+
- `structuredContent`: The data payload that hydrates the UI. The model doesn't see this field.
|
|
163
163
|
|
|
164
164
|
```typescript
|
|
165
165
|
execute: async ({ num1, num2, operation }) => {
|
|
@@ -294,7 +294,7 @@ App iframes are sandboxed with the following permissions:
|
|
|
294
294
|
- `allow-forms`: Allows form submission
|
|
295
295
|
- `allow-popups`: Permits `window.open()` and link targets
|
|
296
296
|
|
|
297
|
-
The iframe
|
|
297
|
+
The iframe doesn't have access to the parent page's DOM, cookies, or storage. All communication happens through the JSON-RPC postMessage protocol managed by `@mcp-ui/client`'s `AppRenderer` on the host side and `@modelcontextprotocol/ext-apps`'s `App` class on the guest side.
|
|
298
298
|
|
|
299
299
|
## Related
|
|
300
300
|
|
|
@@ -53,7 +53,7 @@ When using `MastraFGAWorkos`, set `fetchMemberships: true` on `MastraAuthWorkos`
|
|
|
53
53
|
|
|
54
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
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`
|
|
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` isn't configured, these FGA checks are skipped and Mastra keeps the previous behavior.
|
|
57
57
|
|
|
58
58
|
### Resource mapping
|
|
59
59
|
|
|
@@ -100,7 +100,7 @@ Use `validatePermissions()` to validate the full set of permissions Mastra may e
|
|
|
100
100
|
|
|
101
101
|
### Stored resource scoping
|
|
102
102
|
|
|
103
|
-
FGA authorizes access to a resource. It
|
|
103
|
+
FGA authorizes access to a resource. It doesn't 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
104
|
|
|
105
105
|
```typescript
|
|
106
106
|
const mastra = new Mastra({
|
|
@@ -136,7 +136,7 @@ If `requireScope` is `true` or omitted, scoped stored resource routes fail when
|
|
|
136
136
|
|
|
137
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
138
|
|
|
139
|
-
To deny protected routes that
|
|
139
|
+
To deny protected routes that don't resolve FGA metadata, configure route policy coverage on the FGA provider:
|
|
140
140
|
|
|
141
141
|
```typescript
|
|
142
142
|
const fga = new MastraFGAWorkos({
|
|
@@ -202,20 +202,20 @@ const fga = new MastraFGAWorkos({
|
|
|
202
202
|
|
|
203
203
|
When an FGA provider is configured, Mastra automatically checks authorization at these lifecycle points:
|
|
204
204
|
|
|
205
|
-
| Lifecycle point | Permission checked | Resource type
|
|
206
|
-
| ---------------------------------------------------------------- | ----------------------------------------------- |
|
|
207
|
-
| Agent execution (`generate`, `stream`) | `agents:execute` | `agent`
|
|
208
|
-
| Built-in workflow HTTP execution routes and `Workflow.execute()` | `workflows:execute` | `workflow`
|
|
209
|
-
| Standalone tool execution | `tools:execute` | `tool`
|
|
210
|
-
| Agent tool execution | `tools:execute` | `tool`
|
|
211
|
-
| MCP tool execution | `tools:execute` | `tool`
|
|
212
|
-
| Thread and memory access | `memory:read`, `memory:write`, `memory:delete` | `thread`
|
|
213
|
-
| Stored resource routes | Stored resource permission for the route action | Stored resource type
|
|
214
|
-
| HTTP resource routes | Configured per route | Configured per route
|
|
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` by default, or the server-level `fga.resourceMapping` override | `JSON.stringify([serverName, toolName])` by default, or the server-level derived ID |
|
|
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
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).
|
|
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`. Use the server-level `fga` option when MCP tool checks need different resource or permission mapping than internal agent and workflow tool checks. See [MCPServer authentication context](https://mastra.ai/reference/tools/mcp-server).
|
|
217
217
|
|
|
218
|
-
Direct SDK calls to `createRun().start()`, `resume()`, or `restart()`
|
|
218
|
+
Direct SDK calls to `createRun().start()`, `resume()`, or `restart()` aren't 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
219
|
|
|
220
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
221
|
|
|
@@ -153,7 +153,7 @@ const agent = new Agent({
|
|
|
153
153
|
})
|
|
154
154
|
```
|
|
155
155
|
|
|
156
|
-
When `forwardInstructions` is omitted (the default), instructions are still cached and can be inspected via [`getServerInstructions()`](#getserverinstructions), but
|
|
156
|
+
When `forwardInstructions` is omitted (the default), instructions are still cached and can be inspected via [`getServerInstructions()`](#getserverinstructions), but aren't added to any agent's system prompt.
|
|
157
157
|
|
|
158
158
|
> **Security note:** server instructions are forwarded verbatim (subject only to length truncation) into the agent's system prompt. A malicious or compromised MCP server can use them to inject instructions the agent will treat as trusted system guidance. Only enable `forwardInstructions` for servers you trust, and prefer reviewing instructions with `getServerInstructions()` before forwarding instructions from third-party servers.
|
|
159
159
|
|
|
@@ -179,7 +179,7 @@ const res = await agent.stream(prompt, {
|
|
|
179
179
|
|
|
180
180
|
### `getServerInstructions()`
|
|
181
181
|
|
|
182
|
-
Returns the instructions currently known for each configured MCP server. Servers that
|
|
182
|
+
Returns the instructions currently known for each configured MCP server. Servers that haven't connected yet, or don't advertise instructions, return `undefined`.
|
|
183
183
|
|
|
184
184
|
```typescript
|
|
185
185
|
getServerInstructions(): Record<string, string | undefined>
|
|
@@ -1065,9 +1065,9 @@ await agent.generate('Hello!', {
|
|
|
1065
1065
|
|
|
1066
1066
|
## Handling auth failures inside custom fetch
|
|
1067
1067
|
|
|
1068
|
-
A custom `fetch`
|
|
1068
|
+
A custom `fetch` shouldn't `throw` when authentication is unavailable. The Streamable HTTP transport in the MCP SDK opens a long-lived `GET /mcp` "standalone listener" stream in the background to receive server-pushed notifications. Errors on that stream are retried with exponential backoff, and a thrown `fetch` or a cleanly-closed stream can produce an indefinite reconnect loop at roughly one attempt per second.
|
|
1069
1069
|
|
|
1070
|
-
Return a synthetic `Response` instead. The [MCP Streamable HTTP specification](https://modelcontextprotocol.io/specification/2025-03-26/basic/transports) defines `405 Method Not Allowed` as the signal a server returns when it
|
|
1070
|
+
Return a synthetic `Response` instead. The [MCP Streamable HTTP specification](https://modelcontextprotocol.io/specification/2025-03-26/basic/transports) defines `405 Method Not Allowed` as the signal a server returns when it doesn't offer the GET SSE stream, and the SDK honors it as a terminal status that stops the listener cleanly. Use this to disable the listener when your server doesn't push notifications.
|
|
1071
1071
|
|
|
1072
1072
|
The following pattern waits for an auth token on POST requests, attaches it to outgoing headers, and short-circuits the GET listener with a synthetic 405:
|
|
1073
1073
|
|
|
@@ -1108,7 +1108,7 @@ const mcpClient = new MCPClient({
|
|
|
1108
1108
|
})
|
|
1109
1109
|
```
|
|
1110
1110
|
|
|
1111
|
-
Return `405` for the GET listener only when your server
|
|
1111
|
+
Return `405` for the GET listener only when your server doesn't push notifications back to the client. If your server uses the standalone GET stream, attach the auth token on `GET` requests as well and let the request through.
|
|
1112
1112
|
|
|
1113
1113
|
## Using SSE request headers
|
|
1114
1114
|
|
|
@@ -69,6 +69,8 @@ The constructor accepts an `MCPServerConfig` object with the following propertie
|
|
|
69
69
|
|
|
70
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
71
|
|
|
72
|
+
**fga** (`{ resourceMapping?: Partial<Record<'tool' | 'tools', { fgaResourceType: string; deriveId?: ({ user, resourceId, requestContext }) => string | undefined }>>; permissionMapping?: Record<string, string> }`): Overrides resource and permission mappings for this MCP server's \`tools/list\` and \`tools/call\` FGA checks. Use this when MCP authorization should be scoped differently from internal agent or workflow tool execution.
|
|
73
|
+
|
|
72
74
|
**repository** (`Repository`): Optional repository information for the server's source code.
|
|
73
75
|
|
|
74
76
|
**releaseDate** (`string`): Optional release date of this server version (ISO 8601 string). Defaults to the time of instantiation if not provided.
|
|
@@ -1129,6 +1131,49 @@ const server = new MCPServer({
|
|
|
1129
1131
|
})
|
|
1130
1132
|
```
|
|
1131
1133
|
|
|
1134
|
+
### Scope MCP tool FGA separately
|
|
1135
|
+
|
|
1136
|
+
Use `fga.resourceMapping` and `fga.permissionMapping` when MCP clients need a different authorization scope than internal agent or workflow tool execution. The override applies only to `tools/list` and `tools/call` checks for this MCP server.
|
|
1137
|
+
|
|
1138
|
+
```typescript
|
|
1139
|
+
import { MastraFGAPermissions } from '@mastra/core/auth/ee'
|
|
1140
|
+
|
|
1141
|
+
const server = new MCPServer({
|
|
1142
|
+
id: 'my-server',
|
|
1143
|
+
name: 'My Server',
|
|
1144
|
+
version: '1.0.0',
|
|
1145
|
+
tools: { getUserData },
|
|
1146
|
+
mapAuthInfoToUser: ({ authInfo }) => {
|
|
1147
|
+
const user = authInfo as {
|
|
1148
|
+
extra?: {
|
|
1149
|
+
userId?: string
|
|
1150
|
+
organizationMembershipId?: string
|
|
1151
|
+
}
|
|
1152
|
+
}
|
|
1153
|
+
|
|
1154
|
+
if (!user.extra?.userId) {
|
|
1155
|
+
return null
|
|
1156
|
+
}
|
|
1157
|
+
|
|
1158
|
+
return {
|
|
1159
|
+
id: user.extra.userId,
|
|
1160
|
+
organizationMembershipId: user.extra.organizationMembershipId,
|
|
1161
|
+
}
|
|
1162
|
+
},
|
|
1163
|
+
fga: {
|
|
1164
|
+
resourceMapping: {
|
|
1165
|
+
tool: {
|
|
1166
|
+
fgaResourceType: 'user',
|
|
1167
|
+
deriveId: ({ user }) => (user as { id: string }).id,
|
|
1168
|
+
},
|
|
1169
|
+
},
|
|
1170
|
+
permissionMapping: {
|
|
1171
|
+
[MastraFGAPermissions.TOOLS_EXECUTE]: 'read',
|
|
1172
|
+
},
|
|
1173
|
+
},
|
|
1174
|
+
})
|
|
1175
|
+
```
|
|
1176
|
+
|
|
1132
1177
|
### Setting Up Authentication Middleware
|
|
1133
1178
|
|
|
1134
1179
|
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
|
@@ -2671,8 +2671,6 @@ var ServerResourceActions = class {
|
|
|
2671
2671
|
getSubscriptions;
|
|
2672
2672
|
getLogger;
|
|
2673
2673
|
getSdkServer;
|
|
2674
|
-
clearDefinedResources;
|
|
2675
|
-
clearDefinedResourceTemplates;
|
|
2676
2674
|
/**
|
|
2677
2675
|
* @internal
|
|
2678
2676
|
*/
|
|
@@ -2680,8 +2678,6 @@ var ServerResourceActions = class {
|
|
|
2680
2678
|
this.getSubscriptions = dependencies.getSubscriptions;
|
|
2681
2679
|
this.getLogger = dependencies.getLogger;
|
|
2682
2680
|
this.getSdkServer = dependencies.getSdkServer;
|
|
2683
|
-
this.clearDefinedResources = dependencies.clearDefinedResources;
|
|
2684
|
-
this.clearDefinedResourceTemplates = dependencies.clearDefinedResourceTemplates;
|
|
2685
2681
|
}
|
|
2686
2682
|
/**
|
|
2687
2683
|
* Notifies subscribed clients that a specific resource has been updated.
|
|
@@ -2730,8 +2726,9 @@ var ServerResourceActions = class {
|
|
|
2730
2726
|
/**
|
|
2731
2727
|
* Notifies clients that the overall list of available resources has changed.
|
|
2732
2728
|
*
|
|
2733
|
-
* This
|
|
2734
|
-
*
|
|
2729
|
+
* This sends a `notifications/resources/list_changed` message to all clients, prompting
|
|
2730
|
+
* them to re-fetch the resource list. Resource lists and templates are always evaluated
|
|
2731
|
+
* per request, so there is no server-side cache to clear.
|
|
2735
2732
|
*
|
|
2736
2733
|
* @throws {MastraError} If sending the notification fails
|
|
2737
2734
|
*
|
|
@@ -2742,11 +2739,7 @@ var ServerResourceActions = class {
|
|
|
2742
2739
|
* ```
|
|
2743
2740
|
*/
|
|
2744
2741
|
async notifyListChanged() {
|
|
2745
|
-
this.getLogger().info(
|
|
2746
|
-
"Resource list change externally notified. Clearing definedResources and sending notification."
|
|
2747
|
-
);
|
|
2748
|
-
this.clearDefinedResources();
|
|
2749
|
-
this.clearDefinedResourceTemplates();
|
|
2742
|
+
this.getLogger().info("Resource list change externally notified. Sending notification.");
|
|
2750
2743
|
try {
|
|
2751
2744
|
await this.getSdkServer().sendResourceListChanged();
|
|
2752
2745
|
} catch (error$1) {
|
|
@@ -2777,13 +2770,16 @@ var MCPServer = class extends mcp.MCPServerBase {
|
|
|
2777
2770
|
streamableHTTPTransports = /* @__PURE__ */ new Map();
|
|
2778
2771
|
// Track server instances for each HTTP session
|
|
2779
2772
|
httpServerInstances = /* @__PURE__ */ new Map();
|
|
2780
|
-
definedResources;
|
|
2781
|
-
definedResourceTemplates;
|
|
2782
2773
|
resourceOptions;
|
|
2774
|
+
// Whether any UI (`ui://`) app resources are configured. Captured at construction so
|
|
2775
|
+
// per-request server instances can advertise the MCP Apps extension without relying on
|
|
2776
|
+
// a shared, per-caller resource cache.
|
|
2777
|
+
hasUiResources = false;
|
|
2783
2778
|
definedPrompts;
|
|
2784
2779
|
promptOptions;
|
|
2785
2780
|
jsonSchemaValidator;
|
|
2786
2781
|
mapAuthInfoToUser;
|
|
2782
|
+
fga;
|
|
2787
2783
|
subscriptions = /* @__PURE__ */ new Set();
|
|
2788
2784
|
currentLoggingLevel;
|
|
2789
2785
|
/**
|
|
@@ -2920,9 +2916,11 @@ var MCPServer = class extends mcp.MCPServerBase {
|
|
|
2920
2916
|
constructor(opts) {
|
|
2921
2917
|
super(opts);
|
|
2922
2918
|
this.resourceOptions = this.mergeAppResources(opts.resources, opts.appResources);
|
|
2919
|
+
this.hasUiResources = !!opts.appResources && Object.keys(opts.appResources).length > 0;
|
|
2923
2920
|
this.promptOptions = opts.prompts;
|
|
2924
2921
|
this.jsonSchemaValidator = opts.jsonSchemaValidator;
|
|
2925
2922
|
this.mapAuthInfoToUser = opts.mapAuthInfoToUser;
|
|
2923
|
+
this.fga = opts.fga;
|
|
2926
2924
|
const capabilities = {
|
|
2927
2925
|
tools: {},
|
|
2928
2926
|
logging: { enabled: true }
|
|
@@ -2965,13 +2963,7 @@ var MCPServer = class extends mcp.MCPServerBase {
|
|
|
2965
2963
|
this.resources = new ServerResourceActions({
|
|
2966
2964
|
getSubscriptions: () => this.subscriptions,
|
|
2967
2965
|
getLogger: () => this.logger,
|
|
2968
|
-
getSdkServer: () => this.server
|
|
2969
|
-
clearDefinedResources: () => {
|
|
2970
|
-
this.definedResources = void 0;
|
|
2971
|
-
},
|
|
2972
|
-
clearDefinedResourceTemplates: () => {
|
|
2973
|
-
this.definedResourceTemplates = void 0;
|
|
2974
|
-
}
|
|
2966
|
+
getSdkServer: () => this.server
|
|
2975
2967
|
});
|
|
2976
2968
|
this.prompts = new ServerPromptActions({
|
|
2977
2969
|
getLogger: () => this.logger,
|
|
@@ -3130,14 +3122,11 @@ var MCPServer = class extends mcp.MCPServerBase {
|
|
|
3130
3122
|
const hasUiTools = Object.values(this.convertedTools).some(
|
|
3131
3123
|
(tool) => tool.mcp?._meta?.ui?.resourceUri
|
|
3132
3124
|
);
|
|
3133
|
-
if (hasUiTools || this.
|
|
3134
|
-
|
|
3135
|
-
|
|
3136
|
-
|
|
3137
|
-
|
|
3138
|
-
"io.modelcontextprotocol/ui": {}
|
|
3139
|
-
};
|
|
3140
|
-
}
|
|
3125
|
+
if (hasUiTools || this.hasUiResources) {
|
|
3126
|
+
capabilities.extensions = {
|
|
3127
|
+
...capabilities.extensions,
|
|
3128
|
+
"io.modelcontextprotocol/ui": {}
|
|
3129
|
+
};
|
|
3141
3130
|
}
|
|
3142
3131
|
const serverInstance = new index_js$1.Server(
|
|
3143
3132
|
{
|
|
@@ -3354,18 +3343,13 @@ Provided arguments: ${JSON.stringify(request.params.arguments, null, 2)}`
|
|
|
3354
3343
|
if (!capturedResourceOptions) return;
|
|
3355
3344
|
if (capturedResourceOptions.listResources) {
|
|
3356
3345
|
serverInstance.setRequestHandler(types_js.ListResourcesRequestSchema, async (_request, extra) => {
|
|
3357
|
-
|
|
3358
|
-
|
|
3359
|
-
|
|
3360
|
-
|
|
3361
|
-
|
|
3362
|
-
|
|
3363
|
-
|
|
3364
|
-
return { resources: this.definedResources };
|
|
3365
|
-
} catch (error) {
|
|
3366
|
-
this.logger.error("Error fetching resources", { error });
|
|
3367
|
-
throw error;
|
|
3368
|
-
}
|
|
3346
|
+
try {
|
|
3347
|
+
const resources = await capturedResourceOptions.listResources({ extra });
|
|
3348
|
+
this.logger.debug("Fetched resources", { count: resources.length });
|
|
3349
|
+
return { resources };
|
|
3350
|
+
} catch (error) {
|
|
3351
|
+
this.logger.error("Error fetching resources", { error });
|
|
3352
|
+
throw error;
|
|
3369
3353
|
}
|
|
3370
3354
|
});
|
|
3371
3355
|
}
|
|
@@ -3374,12 +3358,9 @@ Provided arguments: ${JSON.stringify(request.params.arguments, null, 2)}`
|
|
|
3374
3358
|
const startTime = Date.now();
|
|
3375
3359
|
const uri = request.params.uri;
|
|
3376
3360
|
this.logger.debug("Handling ReadResource request", { uri });
|
|
3377
|
-
|
|
3378
|
-
|
|
3379
|
-
|
|
3380
|
-
this.definedResources = resources;
|
|
3381
|
-
}
|
|
3382
|
-
const resource = this.definedResources?.find((r) => r.uri === uri);
|
|
3361
|
+
const resources = await capturedResourceOptions.listResources?.({ extra });
|
|
3362
|
+
if (!resources) throw new Error("Failed to load resources");
|
|
3363
|
+
const resource = resources.find((r) => r.uri === uri);
|
|
3383
3364
|
if (!resource) {
|
|
3384
3365
|
this.logger.warn("Unknown resource URI requested", { uri });
|
|
3385
3366
|
throw new Error(`Resource not found: ${uri}`);
|
|
@@ -3419,18 +3400,13 @@ Provided arguments: ${JSON.stringify(request.params.arguments, null, 2)}`
|
|
|
3419
3400
|
}
|
|
3420
3401
|
if (capturedResourceOptions.resourceTemplates) {
|
|
3421
3402
|
serverInstance.setRequestHandler(types_js.ListResourceTemplatesRequestSchema, async (_request, extra) => {
|
|
3422
|
-
|
|
3423
|
-
|
|
3424
|
-
|
|
3425
|
-
|
|
3426
|
-
|
|
3427
|
-
|
|
3428
|
-
|
|
3429
|
-
return { resourceTemplates: this.definedResourceTemplates };
|
|
3430
|
-
} catch (error) {
|
|
3431
|
-
this.logger.error("Error fetching resource templates via resourceTemplates():", { error });
|
|
3432
|
-
throw error;
|
|
3433
|
-
}
|
|
3403
|
+
try {
|
|
3404
|
+
const templates = await capturedResourceOptions.resourceTemplates({ extra });
|
|
3405
|
+
this.logger.debug("Fetched resource templates", { count: templates.length });
|
|
3406
|
+
return { resourceTemplates: templates };
|
|
3407
|
+
} catch (error) {
|
|
3408
|
+
this.logger.error("Error fetching resource templates via resourceTemplates():", { error });
|
|
3409
|
+
throw error;
|
|
3434
3410
|
}
|
|
3435
3411
|
});
|
|
3436
3412
|
}
|
|
@@ -4592,11 +4568,17 @@ Provided arguments: ${JSON.stringify(request.params.arguments, null, 2)}`
|
|
|
4592
4568
|
if (!user) {
|
|
4593
4569
|
throw new FGADeniedError({ id: "unknown" }, { type: "tool", id: resourceId }, MastraFGAPermissions.TOOLS_EXECUTE);
|
|
4594
4570
|
}
|
|
4571
|
+
const { resource, permission } = this.resolveToolFGAParams({
|
|
4572
|
+
user,
|
|
4573
|
+
resourceId,
|
|
4574
|
+
requestContext,
|
|
4575
|
+
permission: MastraFGAPermissions.TOOLS_EXECUTE
|
|
4576
|
+
});
|
|
4595
4577
|
await requireFGA({
|
|
4596
4578
|
fgaProvider,
|
|
4597
4579
|
user,
|
|
4598
|
-
resource
|
|
4599
|
-
permission
|
|
4580
|
+
resource,
|
|
4581
|
+
permission,
|
|
4600
4582
|
requestContext,
|
|
4601
4583
|
context: {
|
|
4602
4584
|
resourceId
|
|
@@ -4608,6 +4590,28 @@ Provided arguments: ${JSON.stringify(request.params.arguments, null, 2)}`
|
|
|
4608
4590
|
}
|
|
4609
4591
|
});
|
|
4610
4592
|
}
|
|
4593
|
+
resolveToolFGAParams({
|
|
4594
|
+
user,
|
|
4595
|
+
resourceId,
|
|
4596
|
+
requestContext,
|
|
4597
|
+
permission
|
|
4598
|
+
}) {
|
|
4599
|
+
const mappedPermission = this.fga?.permissionMapping?.[permission] ?? permission;
|
|
4600
|
+
const resourceMapping = this.fga?.resourceMapping?.tool ?? this.fga?.resourceMapping?.tools;
|
|
4601
|
+
if (!resourceMapping) {
|
|
4602
|
+
return {
|
|
4603
|
+
resource: { type: "tool", id: resourceId },
|
|
4604
|
+
permission: mappedPermission
|
|
4605
|
+
};
|
|
4606
|
+
}
|
|
4607
|
+
return {
|
|
4608
|
+
resource: {
|
|
4609
|
+
type: resourceMapping.fgaResourceType,
|
|
4610
|
+
id: resourceMapping.deriveId?.({ user, resourceId, requestContext }) ?? resourceId
|
|
4611
|
+
},
|
|
4612
|
+
permission: mappedPermission
|
|
4613
|
+
};
|
|
4614
|
+
}
|
|
4611
4615
|
/**
|
|
4612
4616
|
* Executes a specific tool provided by this MCP server.
|
|
4613
4617
|
*
|
|
@@ -4754,11 +4758,7 @@ Provided arguments: ${JSON.stringify(args, null, 2)}`,
|
|
|
4754
4758
|
return { resources: [] };
|
|
4755
4759
|
}
|
|
4756
4760
|
const extra = {};
|
|
4757
|
-
if (this.definedResources) {
|
|
4758
|
-
return { resources: this.definedResources };
|
|
4759
|
-
}
|
|
4760
4761
|
const resources = await this.resourceOptions.listResources({ extra });
|
|
4761
|
-
this.definedResources = resources;
|
|
4762
4762
|
return { resources };
|
|
4763
4763
|
}
|
|
4764
4764
|
};
|