@mastra/mcp 0.10.4 → 0.10.5-alpha.0
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/.turbo/turbo-build.log +7 -7
- package/CHANGELOG.md +13 -0
- package/dist/index.cjs +461 -143
- package/dist/index.js +451 -133
- package/package.json +2 -2
- package/src/client/configuration.ts +166 -35
- package/src/server/promptActions.ts +13 -2
- package/src/server/resourceActions.ts +32 -4
- package/src/server/server.ts +251 -98
- package/integration-tests/node_modules/.bin/mastra +0 -21
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mastra/mcp",
|
|
3
|
-
"version": "0.10.
|
|
3
|
+
"version": "0.10.5-alpha.0",
|
|
4
4
|
"description": "",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -51,7 +51,7 @@
|
|
|
51
51
|
"zod": "^3.25.57",
|
|
52
52
|
"zod-to-json-schema": "^3.24.5",
|
|
53
53
|
"@internal/lint": "0.0.13",
|
|
54
|
-
"@mastra/core": "0.10.
|
|
54
|
+
"@mastra/core": "0.10.7-alpha.1"
|
|
55
55
|
},
|
|
56
56
|
"scripts": {
|
|
57
57
|
"build": "tsup src/index.ts --format esm,cjs --experimental-dts --clean --treeshake=smallest --splitting",
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { MastraBase } from '@mastra/core/base';
|
|
2
|
+
import { ErrorCategory, ErrorDomain, MastraError } from '@mastra/core/error';
|
|
2
3
|
import { DEFAULT_REQUEST_TIMEOUT_MSEC } from '@modelcontextprotocol/sdk/shared/protocol.js';
|
|
3
4
|
import type { Prompt, Resource, ResourceTemplate } from '@modelcontextprotocol/sdk/types.js';
|
|
4
5
|
import equal from 'fast-deep-equal';
|
|
@@ -74,7 +75,16 @@ To fix this you have three different options:
|
|
|
74
75
|
const internalClient = await this.getConnectedClientForServer(serverName);
|
|
75
76
|
allResources[serverName] = await internalClient.resources.list();
|
|
76
77
|
} catch (error) {
|
|
77
|
-
|
|
78
|
+
const mastraError = new MastraError({
|
|
79
|
+
id: 'MCP_CLIENT_LIST_RESOURCES_FAILED',
|
|
80
|
+
domain: ErrorDomain.MCP,
|
|
81
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
82
|
+
details: {
|
|
83
|
+
serverName,
|
|
84
|
+
}
|
|
85
|
+
}, error);
|
|
86
|
+
this.logger.trackException(mastraError);
|
|
87
|
+
this.logger.error('Failed to list resources from server:', { error: mastraError.toString() });
|
|
78
88
|
}
|
|
79
89
|
}
|
|
80
90
|
return allResources;
|
|
@@ -86,30 +96,97 @@ To fix this you have three different options:
|
|
|
86
96
|
const internalClient = await this.getConnectedClientForServer(serverName);
|
|
87
97
|
allTemplates[serverName] = await internalClient.resources.templates();
|
|
88
98
|
} catch (error) {
|
|
89
|
-
|
|
99
|
+
const mastraError = new MastraError({
|
|
100
|
+
id: 'MCP_CLIENT_LIST_RESOURCE_TEMPLATES_FAILED',
|
|
101
|
+
domain: ErrorDomain.MCP,
|
|
102
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
103
|
+
details: {
|
|
104
|
+
serverName,
|
|
105
|
+
}
|
|
106
|
+
}, error);
|
|
107
|
+
this.logger.trackException(mastraError);
|
|
108
|
+
this.logger.error('Failed to list resource templates from server:', { error: mastraError.toString() });
|
|
90
109
|
}
|
|
91
110
|
}
|
|
92
111
|
return allTemplates;
|
|
93
112
|
},
|
|
94
113
|
read: async (serverName: string, uri: string) => {
|
|
95
|
-
|
|
96
|
-
|
|
114
|
+
try {
|
|
115
|
+
const internalClient = await this.getConnectedClientForServer(serverName);
|
|
116
|
+
return internalClient.resources.read(uri);
|
|
117
|
+
} catch (error) {
|
|
118
|
+
throw new MastraError({
|
|
119
|
+
id: 'MCP_CLIENT_READ_RESOURCE_FAILED',
|
|
120
|
+
domain: ErrorDomain.MCP,
|
|
121
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
122
|
+
details: {
|
|
123
|
+
serverName,
|
|
124
|
+
uri,
|
|
125
|
+
}
|
|
126
|
+
}, error);
|
|
127
|
+
}
|
|
97
128
|
},
|
|
98
129
|
subscribe: async (serverName: string, uri: string) => {
|
|
99
|
-
|
|
100
|
-
|
|
130
|
+
try {
|
|
131
|
+
const internalClient = await this.getConnectedClientForServer(serverName);
|
|
132
|
+
return internalClient.resources.subscribe(uri);
|
|
133
|
+
} catch (error) {
|
|
134
|
+
throw new MastraError({
|
|
135
|
+
id: 'MCP_CLIENT_SUBSCRIBE_RESOURCE_FAILED',
|
|
136
|
+
domain: ErrorDomain.MCP,
|
|
137
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
138
|
+
details: {
|
|
139
|
+
serverName,
|
|
140
|
+
uri,
|
|
141
|
+
}
|
|
142
|
+
}, error);
|
|
143
|
+
}
|
|
101
144
|
},
|
|
102
145
|
unsubscribe: async (serverName: string, uri: string) => {
|
|
103
|
-
|
|
104
|
-
|
|
146
|
+
try {
|
|
147
|
+
const internalClient = await this.getConnectedClientForServer(serverName);
|
|
148
|
+
return internalClient.resources.unsubscribe(uri);
|
|
149
|
+
} catch (err) {
|
|
150
|
+
throw new MastraError({
|
|
151
|
+
id: 'MCP_CLIENT_UNSUBSCRIBE_RESOURCE_FAILED',
|
|
152
|
+
domain: ErrorDomain.MCP,
|
|
153
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
154
|
+
details: {
|
|
155
|
+
serverName,
|
|
156
|
+
uri,
|
|
157
|
+
}
|
|
158
|
+
}, err);
|
|
159
|
+
}
|
|
105
160
|
},
|
|
106
161
|
onUpdated: async (serverName: string, handler: (params: { uri: string }) => void) => {
|
|
107
|
-
|
|
108
|
-
|
|
162
|
+
try {
|
|
163
|
+
const internalClient = await this.getConnectedClientForServer(serverName);
|
|
164
|
+
return internalClient.resources.onUpdated(handler);
|
|
165
|
+
} catch (err) {
|
|
166
|
+
throw new MastraError({
|
|
167
|
+
id: 'MCP_CLIENT_ON_UPDATED_RESOURCE_FAILED',
|
|
168
|
+
domain: ErrorDomain.MCP,
|
|
169
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
170
|
+
details: {
|
|
171
|
+
serverName,
|
|
172
|
+
}
|
|
173
|
+
}, err);
|
|
174
|
+
}
|
|
109
175
|
},
|
|
110
176
|
onListChanged: async (serverName: string, handler: () => void) => {
|
|
111
|
-
|
|
112
|
-
|
|
177
|
+
try {
|
|
178
|
+
const internalClient = await this.getConnectedClientForServer(serverName);
|
|
179
|
+
return internalClient.resources.onListChanged(handler);
|
|
180
|
+
} catch (err) {
|
|
181
|
+
throw new MastraError({
|
|
182
|
+
id: 'MCP_CLIENT_ON_LIST_CHANGED_RESOURCE_FAILED',
|
|
183
|
+
domain: ErrorDomain.MCP,
|
|
184
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
185
|
+
details: {
|
|
186
|
+
serverName,
|
|
187
|
+
}
|
|
188
|
+
}, err);
|
|
189
|
+
}
|
|
113
190
|
},
|
|
114
191
|
};
|
|
115
192
|
}
|
|
@@ -124,18 +201,50 @@ To fix this you have three different options:
|
|
|
124
201
|
const internalClient = await this.getConnectedClientForServer(serverName);
|
|
125
202
|
allPrompts[serverName] = await internalClient.prompts.list();
|
|
126
203
|
} catch (error) {
|
|
127
|
-
|
|
204
|
+
const mastraError = new MastraError({
|
|
205
|
+
id: 'MCP_CLIENT_LIST_PROMPTS_FAILED',
|
|
206
|
+
domain: ErrorDomain.MCP,
|
|
207
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
208
|
+
details: {
|
|
209
|
+
serverName,
|
|
210
|
+
}
|
|
211
|
+
}, error);
|
|
212
|
+
this.logger.trackException(mastraError);
|
|
213
|
+
this.logger.error('Failed to list prompts from server:', { error: mastraError.toString() });
|
|
128
214
|
}
|
|
129
215
|
}
|
|
130
216
|
return allPrompts;
|
|
131
217
|
},
|
|
132
|
-
get: async ({serverName, name, args, version}: {serverName: string, name: string, args?: Record<string, any>, version?: string}) => {
|
|
133
|
-
|
|
134
|
-
|
|
218
|
+
get: async ({ serverName, name, args, version }: { serverName: string, name: string, args?: Record<string, any>, version?: string }) => {
|
|
219
|
+
try {
|
|
220
|
+
const internalClient = await this.getConnectedClientForServer(serverName);
|
|
221
|
+
return internalClient.prompts.get({ name, args, version });
|
|
222
|
+
} catch (error) {
|
|
223
|
+
throw new MastraError({
|
|
224
|
+
id: 'MCP_CLIENT_GET_PROMPT_FAILED',
|
|
225
|
+
domain: ErrorDomain.MCP,
|
|
226
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
227
|
+
details: {
|
|
228
|
+
serverName,
|
|
229
|
+
name,
|
|
230
|
+
}
|
|
231
|
+
}, error);
|
|
232
|
+
}
|
|
135
233
|
},
|
|
136
234
|
onListChanged: async (serverName: string, handler: () => void) => {
|
|
137
|
-
|
|
138
|
-
|
|
235
|
+
try {
|
|
236
|
+
const internalClient = await this.getConnectedClientForServer(serverName);
|
|
237
|
+
return internalClient.prompts.onListChanged(handler);
|
|
238
|
+
} catch (error) {
|
|
239
|
+
throw new MastraError({
|
|
240
|
+
id: 'MCP_CLIENT_ON_LIST_CHANGED_PROMPT_FAILED',
|
|
241
|
+
domain: ErrorDomain.MCP,
|
|
242
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
243
|
+
details: {
|
|
244
|
+
serverName,
|
|
245
|
+
}
|
|
246
|
+
}, error);
|
|
247
|
+
}
|
|
139
248
|
},
|
|
140
249
|
};
|
|
141
250
|
}
|
|
@@ -163,7 +272,7 @@ To fix this you have three different options:
|
|
|
163
272
|
this.disconnectPromise = (async () => {
|
|
164
273
|
try {
|
|
165
274
|
mcpClientInstances.delete(this.id);
|
|
166
|
-
|
|
275
|
+
|
|
167
276
|
// Disconnect all clients in the cache
|
|
168
277
|
await Promise.all(Array.from(this.mcpClientsById.values()).map(client => client.disconnect()));
|
|
169
278
|
this.mcpClientsById.clear();
|
|
@@ -179,11 +288,19 @@ To fix this you have three different options:
|
|
|
179
288
|
this.addToInstanceCache();
|
|
180
289
|
const connectedTools: Record<string, any> = {}; // <- any because we don't have proper tool schemas
|
|
181
290
|
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
291
|
+
try {
|
|
292
|
+
await this.eachClientTools(async ({ serverName, tools }) => {
|
|
293
|
+
for (const [toolName, toolConfig] of Object.entries(tools)) {
|
|
294
|
+
connectedTools[`${serverName}_${toolName}`] = toolConfig; // namespace tool to prevent tool name conflicts between servers
|
|
295
|
+
}
|
|
296
|
+
});
|
|
297
|
+
} catch (error) {
|
|
298
|
+
throw new MastraError({
|
|
299
|
+
id: 'MCP_CLIENT_GET_TOOLS_FAILED',
|
|
300
|
+
domain: ErrorDomain.MCP,
|
|
301
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
302
|
+
}, error);
|
|
303
|
+
}
|
|
187
304
|
|
|
188
305
|
return connectedTools;
|
|
189
306
|
}
|
|
@@ -192,11 +309,19 @@ To fix this you have three different options:
|
|
|
192
309
|
this.addToInstanceCache();
|
|
193
310
|
const connectedToolsets: Record<string, Record<string, any>> = {}; // <- any because we don't have proper tool schemas
|
|
194
311
|
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
312
|
+
try {
|
|
313
|
+
await this.eachClientTools(async ({ serverName, tools }) => {
|
|
314
|
+
if (tools) {
|
|
315
|
+
connectedToolsets[serverName] = tools;
|
|
316
|
+
}
|
|
317
|
+
});
|
|
318
|
+
} catch (error) {
|
|
319
|
+
throw new MastraError({
|
|
320
|
+
id: 'MCP_CLIENT_GET_TOOLSETS_FAILED',
|
|
321
|
+
domain: ErrorDomain.MCP,
|
|
322
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
323
|
+
}, error);
|
|
324
|
+
}
|
|
200
325
|
|
|
201
326
|
return connectedToolsets;
|
|
202
327
|
}
|
|
@@ -258,13 +383,19 @@ To fix this you have three different options:
|
|
|
258
383
|
try {
|
|
259
384
|
await mcpClient.connect();
|
|
260
385
|
} catch (e) {
|
|
386
|
+
const mastraError = new MastraError({
|
|
387
|
+
id: 'MCP_CLIENT_CONNECT_FAILED',
|
|
388
|
+
domain: ErrorDomain.MCP,
|
|
389
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
390
|
+
text: `Failed to connect to MCP server ${name}: ${e instanceof Error ? e.stack || e.message : String(e)}`,
|
|
391
|
+
details: {
|
|
392
|
+
name,
|
|
393
|
+
}
|
|
394
|
+
}, e);
|
|
395
|
+
this.logger.trackException(mastraError);
|
|
396
|
+
this.logger.error('MCPClient errored connecting to MCP server:', { error: mastraError.toString() });
|
|
261
397
|
this.mcpClientsById.delete(name);
|
|
262
|
-
|
|
263
|
-
error: e instanceof Error ? e.message : String(e),
|
|
264
|
-
});
|
|
265
|
-
throw new Error(
|
|
266
|
-
`Failed to connect to MCP server ${name}: ${e instanceof Error ? e.stack || e.message : String(e)}`,
|
|
267
|
-
);
|
|
398
|
+
throw mastraError;
|
|
268
399
|
}
|
|
269
400
|
this.logger.debug(`Connected to ${name} MCP server`);
|
|
270
401
|
return mcpClient;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { ErrorCategory, ErrorDomain, MastraError } from '@mastra/core/error';
|
|
1
2
|
import type { IMastraLogger } from '@mastra/core/logger';
|
|
2
3
|
import type { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
3
4
|
|
|
@@ -28,10 +29,20 @@ export class ServerPromptActions {
|
|
|
28
29
|
try {
|
|
29
30
|
await this.getSdkServer().sendPromptListChanged();
|
|
30
31
|
} catch (error) {
|
|
32
|
+
const mastraError = new MastraError(
|
|
33
|
+
{
|
|
34
|
+
id: 'MCP_SERVER_PROMPT_LIST_CHANGED_NOTIFICATION_FAILED',
|
|
35
|
+
domain: ErrorDomain.MCP,
|
|
36
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
37
|
+
text: 'Failed to send prompt list changed notification',
|
|
38
|
+
},
|
|
39
|
+
error,
|
|
40
|
+
);
|
|
31
41
|
this.getLogger().error('Failed to send prompt list changed notification:', {
|
|
32
|
-
error:
|
|
42
|
+
error: mastraError.toString(),
|
|
33
43
|
});
|
|
34
|
-
|
|
44
|
+
this.getLogger().trackException(mastraError);
|
|
45
|
+
throw mastraError;
|
|
35
46
|
}
|
|
36
47
|
}
|
|
37
48
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { ErrorCategory, ErrorDomain, MastraError } from '@mastra/core/error';
|
|
1
2
|
import type { IMastraLogger } from '@mastra/core/logger';
|
|
2
3
|
import type { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
3
4
|
|
|
@@ -34,8 +35,23 @@ export class ServerResourceActions {
|
|
|
34
35
|
try {
|
|
35
36
|
await this.getSdkServer().sendResourceUpdated({ uri });
|
|
36
37
|
} catch (error) {
|
|
37
|
-
|
|
38
|
-
|
|
38
|
+
const mastraError = new MastraError(
|
|
39
|
+
{
|
|
40
|
+
id: 'MCP_SERVER_RESOURCE_UPDATED_NOTIFICATION_FAILED',
|
|
41
|
+
domain: ErrorDomain.MCP,
|
|
42
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
43
|
+
text: 'Failed to send resource updated notification',
|
|
44
|
+
details: {
|
|
45
|
+
uri,
|
|
46
|
+
},
|
|
47
|
+
},
|
|
48
|
+
error,
|
|
49
|
+
);
|
|
50
|
+
this.getLogger().trackException(mastraError);
|
|
51
|
+
this.getLogger().error('Failed to send resource updated notification:', {
|
|
52
|
+
error: mastraError.toString(),
|
|
53
|
+
});
|
|
54
|
+
throw mastraError;
|
|
39
55
|
}
|
|
40
56
|
} else {
|
|
41
57
|
this.getLogger().debug(`Resource ${uri} was updated, but no active subscriptions for it.`);
|
|
@@ -55,8 +71,20 @@ export class ServerResourceActions {
|
|
|
55
71
|
try {
|
|
56
72
|
await this.getSdkServer().sendResourceListChanged();
|
|
57
73
|
} catch (error) {
|
|
58
|
-
|
|
59
|
-
|
|
74
|
+
const mastraError = new MastraError(
|
|
75
|
+
{
|
|
76
|
+
id: 'MCP_SERVER_RESOURCE_LIST_CHANGED_NOTIFICATION_FAILED',
|
|
77
|
+
domain: ErrorDomain.MCP,
|
|
78
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
79
|
+
text: 'Failed to send resource list changed notification',
|
|
80
|
+
},
|
|
81
|
+
error,
|
|
82
|
+
);
|
|
83
|
+
this.getLogger().trackException(mastraError);
|
|
84
|
+
this.getLogger().error('Failed to send resource list changed notification:', {
|
|
85
|
+
error: mastraError.toString(),
|
|
86
|
+
});
|
|
87
|
+
throw mastraError;
|
|
60
88
|
}
|
|
61
89
|
}
|
|
62
90
|
}
|