@mastra/mcp 0.10.2-alpha.1 → 0.10.3-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 +8 -8
- package/CHANGELOG.md +36 -0
- package/README.md +32 -0
- package/dist/_tsup-dts-rollup.d.cts +179 -14
- package/dist/_tsup-dts-rollup.d.ts +179 -14
- package/dist/index.cjs +258 -3
- package/dist/index.d.cts +3 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.js +260 -5
- package/integration-tests/node_modules/.bin/vitest +2 -2
- package/integration-tests/package.json +3 -3
- package/package.json +10 -10
- package/src/__fixtures__/weather.ts +62 -2
- package/src/client/client.test.ts +46 -0
- package/src/client/client.ts +43 -1
- package/src/client/configuration.test.ts +280 -168
- package/src/client/configuration.ts +27 -1
- package/src/client/index.ts +3 -0
- package/src/client/promptActions.ts +70 -0
- package/src/client/resourceActions.ts +0 -2
- package/src/index.ts +2 -4
- package/src/server/index.ts +2 -0
- package/src/server/promptActions.ts +37 -0
- package/src/server/server.test.ts +224 -1
- package/src/server/server.ts +147 -15
- package/src/server/types.ts +30 -0
- package/src/logger.ts +0 -104
package/dist/index.js
CHANGED
|
@@ -6,13 +6,13 @@ import { SSEClientTransport } from '@modelcontextprotocol/sdk/client/sse.js';
|
|
|
6
6
|
import { StdioClientTransport, getDefaultEnvironment } from '@modelcontextprotocol/sdk/client/stdio.js';
|
|
7
7
|
import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js';
|
|
8
8
|
import { DEFAULT_REQUEST_TIMEOUT_MSEC } from '@modelcontextprotocol/sdk/shared/protocol.js';
|
|
9
|
-
import { ListToolsRequestSchema, CallToolRequestSchema, ListResourcesRequestSchema, ReadResourceRequestSchema, ListResourceTemplatesRequestSchema, SubscribeRequestSchema, UnsubscribeRequestSchema, ListResourcesResultSchema, ReadResourceResultSchema, ListResourceTemplatesResultSchema, ResourceUpdatedNotificationSchema, ResourceListChangedNotificationSchema, CallToolResultSchema, JSONRPCMessageSchema, ErrorCode } from '@modelcontextprotocol/sdk/types.js';
|
|
9
|
+
import { ListToolsRequestSchema, CallToolRequestSchema, ListResourcesRequestSchema, ReadResourceRequestSchema, ListResourceTemplatesRequestSchema, SubscribeRequestSchema, UnsubscribeRequestSchema, ListPromptsRequestSchema, PromptSchema, GetPromptRequestSchema, ListResourcesResultSchema, ReadResourceResultSchema, ListResourceTemplatesResultSchema, ListPromptsResultSchema, GetPromptResultSchema, PromptListChangedNotificationSchema, ResourceUpdatedNotificationSchema, ResourceListChangedNotificationSchema, CallToolResultSchema, JSONRPCMessageSchema, ErrorCode } from '@modelcontextprotocol/sdk/types.js';
|
|
10
10
|
import { asyncExitHook, gracefulExit } from 'exit-hook';
|
|
11
11
|
import { z } from 'zod';
|
|
12
12
|
import { convertJsonSchemaToZod } from 'zod-from-json-schema';
|
|
13
13
|
import equal from 'fast-deep-equal';
|
|
14
14
|
import { v5 } from 'uuid';
|
|
15
|
-
import { randomUUID } from '
|
|
15
|
+
import { randomUUID } from 'crypto';
|
|
16
16
|
import { createTool, makeCoreTool } from '@mastra/core';
|
|
17
17
|
import { Agent } from '@mastra/core/agent';
|
|
18
18
|
import { MCPServerBase } from '@mastra/core/mcp';
|
|
@@ -24,6 +24,58 @@ import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/
|
|
|
24
24
|
import { streamSSE } from 'hono/streaming';
|
|
25
25
|
|
|
26
26
|
// src/client/client.ts
|
|
27
|
+
var PromptClientActions = class {
|
|
28
|
+
client;
|
|
29
|
+
logger;
|
|
30
|
+
constructor({ client, logger }) {
|
|
31
|
+
this.client = client;
|
|
32
|
+
this.logger = logger;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Get all prompts from the connected MCP server.
|
|
36
|
+
* @returns A list of prompts with their versions.
|
|
37
|
+
*/
|
|
38
|
+
async list() {
|
|
39
|
+
try {
|
|
40
|
+
const response = await this.client.listPrompts();
|
|
41
|
+
if (response && response.prompts && Array.isArray(response.prompts)) {
|
|
42
|
+
return response.prompts.map((prompt) => ({ ...prompt, version: prompt.version || "" }));
|
|
43
|
+
} else {
|
|
44
|
+
this.logger.warn(`Prompts response from server ${this.client.name} did not have expected structure.`, {
|
|
45
|
+
response
|
|
46
|
+
});
|
|
47
|
+
return [];
|
|
48
|
+
}
|
|
49
|
+
} catch (e) {
|
|
50
|
+
if (e.code === ErrorCode.MethodNotFound) {
|
|
51
|
+
return [];
|
|
52
|
+
}
|
|
53
|
+
this.logger.error(`Error getting prompts from server ${this.client.name}`, {
|
|
54
|
+
error: e instanceof Error ? e.message : String(e)
|
|
55
|
+
});
|
|
56
|
+
throw new Error(
|
|
57
|
+
`Failed to fetch prompts from server ${this.client.name}: ${e instanceof Error ? e.stack || e.message : String(e)}`
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Get a specific prompt.
|
|
63
|
+
* @param name The name of the prompt to get.
|
|
64
|
+
* @param args Optional arguments for the prompt.
|
|
65
|
+
* @param version Optional version of the prompt to get.
|
|
66
|
+
* @returns The prompt content.
|
|
67
|
+
*/
|
|
68
|
+
async get({ name, args, version }) {
|
|
69
|
+
return this.client.getPrompt({ name, args, version });
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Set a notification handler for when the list of available prompts changes.
|
|
73
|
+
* @param handler The callback function to handle the notification.
|
|
74
|
+
*/
|
|
75
|
+
async onListChanged(handler) {
|
|
76
|
+
this.client.setPromptListChangedNotificationHandler(handler);
|
|
77
|
+
}
|
|
78
|
+
};
|
|
27
79
|
var ResourceClientActions = class {
|
|
28
80
|
client;
|
|
29
81
|
logger;
|
|
@@ -53,7 +105,6 @@ var ResourceClientActions = class {
|
|
|
53
105
|
this.logger.error(`Error getting resources from server ${this.client.name}`, {
|
|
54
106
|
error: e instanceof Error ? e.message : String(e)
|
|
55
107
|
});
|
|
56
|
-
console.log("errorheere", e);
|
|
57
108
|
throw new Error(
|
|
58
109
|
`Failed to fetch resources from server ${this.client.name}: ${e instanceof Error ? e.stack || e.message : String(e)}`
|
|
59
110
|
);
|
|
@@ -76,7 +127,6 @@ var ResourceClientActions = class {
|
|
|
76
127
|
return [];
|
|
77
128
|
}
|
|
78
129
|
} catch (e) {
|
|
79
|
-
console.log({ errorcooode: e.code });
|
|
80
130
|
if (e.code === ErrorCode.MethodNotFound) {
|
|
81
131
|
return [];
|
|
82
132
|
}
|
|
@@ -155,6 +205,7 @@ var InternalMastraMCPClient = class extends MastraBase {
|
|
|
155
205
|
transport;
|
|
156
206
|
currentOperationContext = null;
|
|
157
207
|
resources;
|
|
208
|
+
prompts;
|
|
158
209
|
constructor({
|
|
159
210
|
name,
|
|
160
211
|
version = "1.0.0",
|
|
@@ -179,6 +230,7 @@ var InternalMastraMCPClient = class extends MastraBase {
|
|
|
179
230
|
);
|
|
180
231
|
this.setupLogging();
|
|
181
232
|
this.resources = new ResourceClientActions({ client: this, logger: this.logger });
|
|
233
|
+
this.prompts = new PromptClientActions({ client: this, logger: this.logger });
|
|
182
234
|
}
|
|
183
235
|
/**
|
|
184
236
|
* Log a message at the specified level
|
|
@@ -362,6 +414,39 @@ var InternalMastraMCPClient = class extends MastraBase {
|
|
|
362
414
|
timeout: this.timeout
|
|
363
415
|
});
|
|
364
416
|
}
|
|
417
|
+
/**
|
|
418
|
+
* Fetch the list of available prompts from the MCP server.
|
|
419
|
+
*/
|
|
420
|
+
async listPrompts() {
|
|
421
|
+
this.log("debug", `Requesting prompts from MCP server`);
|
|
422
|
+
return await this.client.request({ method: "prompts/list" }, ListPromptsResultSchema, {
|
|
423
|
+
timeout: this.timeout
|
|
424
|
+
});
|
|
425
|
+
}
|
|
426
|
+
/**
|
|
427
|
+
* Get a prompt and its dynamic messages from the server.
|
|
428
|
+
* @param name The prompt name
|
|
429
|
+
* @param args Arguments for the prompt
|
|
430
|
+
* @param version (optional) The prompt version to retrieve
|
|
431
|
+
*/
|
|
432
|
+
async getPrompt({ name, args, version }) {
|
|
433
|
+
this.log("debug", `Requesting prompt from MCP server: ${name}`);
|
|
434
|
+
return await this.client.request(
|
|
435
|
+
{ method: "prompts/get", params: { name, arguments: args, version } },
|
|
436
|
+
GetPromptResultSchema,
|
|
437
|
+
{ timeout: this.timeout }
|
|
438
|
+
);
|
|
439
|
+
}
|
|
440
|
+
/**
|
|
441
|
+
* Register a handler to be called when the prompt list changes on the server.
|
|
442
|
+
* Use this to refresh cached prompt lists in the client/UI if needed.
|
|
443
|
+
*/
|
|
444
|
+
setPromptListChangedNotificationHandler(handler) {
|
|
445
|
+
this.log("debug", "Setting prompt list changed notification handler");
|
|
446
|
+
this.client.setNotificationHandler(PromptListChangedNotificationSchema, () => {
|
|
447
|
+
handler();
|
|
448
|
+
});
|
|
449
|
+
}
|
|
365
450
|
setResourceUpdatedNotificationHandler(handler) {
|
|
366
451
|
this.log("debug", "Setting resource updated notification handler");
|
|
367
452
|
this.client.setNotificationHandler(ResourceUpdatedNotificationSchema, (notification) => {
|
|
@@ -551,6 +636,31 @@ To fix this you have three different options:
|
|
|
551
636
|
}
|
|
552
637
|
};
|
|
553
638
|
}
|
|
639
|
+
get prompts() {
|
|
640
|
+
this.addToInstanceCache();
|
|
641
|
+
return {
|
|
642
|
+
list: async () => {
|
|
643
|
+
const allPrompts = {};
|
|
644
|
+
for (const serverName of Object.keys(this.serverConfigs)) {
|
|
645
|
+
try {
|
|
646
|
+
const internalClient = await this.getConnectedClientForServer(serverName);
|
|
647
|
+
allPrompts[serverName] = await internalClient.prompts.list();
|
|
648
|
+
} catch (error) {
|
|
649
|
+
this.logger.error(`Failed to list prompts from server ${serverName}`, { error });
|
|
650
|
+
}
|
|
651
|
+
}
|
|
652
|
+
return allPrompts;
|
|
653
|
+
},
|
|
654
|
+
get: async ({ serverName, name, args, version }) => {
|
|
655
|
+
const internalClient = await this.getConnectedClientForServer(serverName);
|
|
656
|
+
return internalClient.prompts.get({ name, args, version });
|
|
657
|
+
},
|
|
658
|
+
onListChanged: async (serverName, handler) => {
|
|
659
|
+
const internalClient = await this.getConnectedClientForServer(serverName);
|
|
660
|
+
return internalClient.prompts.onListChanged(handler);
|
|
661
|
+
}
|
|
662
|
+
};
|
|
663
|
+
}
|
|
554
664
|
addToInstanceCache() {
|
|
555
665
|
if (!mcpClientInstances.has(this.id)) {
|
|
556
666
|
mcpClientInstances.set(this.id, this);
|
|
@@ -759,6 +869,34 @@ var SSETransport = class {
|
|
|
759
869
|
}
|
|
760
870
|
};
|
|
761
871
|
|
|
872
|
+
// src/server/promptActions.ts
|
|
873
|
+
var ServerPromptActions = class {
|
|
874
|
+
getLogger;
|
|
875
|
+
getSdkServer;
|
|
876
|
+
clearDefinedPrompts;
|
|
877
|
+
constructor(dependencies) {
|
|
878
|
+
this.getLogger = dependencies.getLogger;
|
|
879
|
+
this.getSdkServer = dependencies.getSdkServer;
|
|
880
|
+
this.clearDefinedPrompts = dependencies.clearDefinedPrompts;
|
|
881
|
+
}
|
|
882
|
+
/**
|
|
883
|
+
* Notifies the server that the overall list of available prompts has changed.
|
|
884
|
+
* This will clear the internal cache of defined prompts and send a list_changed notification to clients.
|
|
885
|
+
*/
|
|
886
|
+
async notifyListChanged() {
|
|
887
|
+
this.getLogger().info("Prompt list change externally notified. Clearing definedPrompts and sending notification.");
|
|
888
|
+
this.clearDefinedPrompts();
|
|
889
|
+
try {
|
|
890
|
+
await this.getSdkServer().sendPromptListChanged();
|
|
891
|
+
} catch (error) {
|
|
892
|
+
this.getLogger().error("Failed to send prompt list changed notification:", {
|
|
893
|
+
error: error instanceof Error ? error.message : String(error)
|
|
894
|
+
});
|
|
895
|
+
throw error;
|
|
896
|
+
}
|
|
897
|
+
}
|
|
898
|
+
};
|
|
899
|
+
|
|
762
900
|
// src/server/resourceActions.ts
|
|
763
901
|
var ServerResourceActions = class {
|
|
764
902
|
getSubscriptions;
|
|
@@ -823,11 +961,16 @@ var MCPServer = class extends MCPServerBase {
|
|
|
823
961
|
listResourceTemplatesHandlerIsRegistered = false;
|
|
824
962
|
subscribeResourceHandlerIsRegistered = false;
|
|
825
963
|
unsubscribeResourceHandlerIsRegistered = false;
|
|
964
|
+
listPromptsHandlerIsRegistered = false;
|
|
965
|
+
getPromptHandlerIsRegistered = false;
|
|
826
966
|
definedResources;
|
|
827
967
|
definedResourceTemplates;
|
|
828
968
|
resourceOptions;
|
|
969
|
+
definedPrompts;
|
|
970
|
+
promptOptions;
|
|
829
971
|
subscriptions = /* @__PURE__ */ new Set();
|
|
830
972
|
resources;
|
|
973
|
+
prompts;
|
|
831
974
|
/**
|
|
832
975
|
* Get the current stdio transport.
|
|
833
976
|
*/
|
|
@@ -852,6 +995,12 @@ var MCPServer = class extends MCPServerBase {
|
|
|
852
995
|
getStreamableHTTPTransport() {
|
|
853
996
|
return this.streamableHTTPTransport;
|
|
854
997
|
}
|
|
998
|
+
/**
|
|
999
|
+
* Get the current server instance.
|
|
1000
|
+
*/
|
|
1001
|
+
getServer() {
|
|
1002
|
+
return this.server;
|
|
1003
|
+
}
|
|
855
1004
|
/**
|
|
856
1005
|
* Construct a new MCPServer instance.
|
|
857
1006
|
* @param opts - Configuration options for the server, including registry metadata.
|
|
@@ -859,6 +1008,7 @@ var MCPServer = class extends MCPServerBase {
|
|
|
859
1008
|
constructor(opts) {
|
|
860
1009
|
super(opts);
|
|
861
1010
|
this.resourceOptions = opts.resources;
|
|
1011
|
+
this.promptOptions = opts.prompts;
|
|
862
1012
|
const capabilities = {
|
|
863
1013
|
tools: {},
|
|
864
1014
|
logging: { enabled: true }
|
|
@@ -866,6 +1016,9 @@ var MCPServer = class extends MCPServerBase {
|
|
|
866
1016
|
if (opts.resources) {
|
|
867
1017
|
capabilities.resources = { subscribe: true, listChanged: true };
|
|
868
1018
|
}
|
|
1019
|
+
if (opts.prompts) {
|
|
1020
|
+
capabilities.prompts = { listChanged: true };
|
|
1021
|
+
}
|
|
869
1022
|
this.server = new Server({ name: this.name, version: this.version }, { capabilities });
|
|
870
1023
|
this.logger.info(
|
|
871
1024
|
`Initialized MCPServer '${this.name}' v${this.version} (ID: ${this.id}) with tools: ${Object.keys(this.convertedTools).join(", ")} and resources. Capabilities: ${JSON.stringify(capabilities)}`
|
|
@@ -882,6 +1035,12 @@ var MCPServer = class extends MCPServerBase {
|
|
|
882
1035
|
this.registerListResourceTemplatesHandler();
|
|
883
1036
|
}
|
|
884
1037
|
}
|
|
1038
|
+
if (opts.prompts) {
|
|
1039
|
+
this.registerListPromptsHandler();
|
|
1040
|
+
this.registerGetPromptHandler({
|
|
1041
|
+
getPromptMessagesCallback: opts.prompts.getPromptMessages
|
|
1042
|
+
});
|
|
1043
|
+
}
|
|
885
1044
|
this.resources = new ServerResourceActions({
|
|
886
1045
|
getSubscriptions: () => this.subscriptions,
|
|
887
1046
|
getLogger: () => this.logger,
|
|
@@ -893,6 +1052,13 @@ var MCPServer = class extends MCPServerBase {
|
|
|
893
1052
|
this.definedResourceTemplates = void 0;
|
|
894
1053
|
}
|
|
895
1054
|
});
|
|
1055
|
+
this.prompts = new ServerPromptActions({
|
|
1056
|
+
getLogger: () => this.logger,
|
|
1057
|
+
getSdkServer: () => this.server,
|
|
1058
|
+
clearDefinedPrompts: () => {
|
|
1059
|
+
this.definedPrompts = void 0;
|
|
1060
|
+
}
|
|
1061
|
+
});
|
|
896
1062
|
}
|
|
897
1063
|
convertAgentsToTools(agentsConfig, definedConvertedTools) {
|
|
898
1064
|
const agentTools = {};
|
|
@@ -1135,7 +1301,7 @@ var MCPServer = class extends MCPServerBase {
|
|
|
1135
1301
|
content: [
|
|
1136
1302
|
{
|
|
1137
1303
|
type: "text",
|
|
1138
|
-
text: JSON.stringify(result)
|
|
1304
|
+
text: typeof result === "string" ? result : JSON.stringify(result)
|
|
1139
1305
|
}
|
|
1140
1306
|
],
|
|
1141
1307
|
isError: false
|
|
@@ -1319,6 +1485,95 @@ var MCPServer = class extends MCPServerBase {
|
|
|
1319
1485
|
return {};
|
|
1320
1486
|
});
|
|
1321
1487
|
}
|
|
1488
|
+
/**
|
|
1489
|
+
* Register the ListPrompts handler.
|
|
1490
|
+
*/
|
|
1491
|
+
registerListPromptsHandler() {
|
|
1492
|
+
if (this.listPromptsHandlerIsRegistered) {
|
|
1493
|
+
return;
|
|
1494
|
+
}
|
|
1495
|
+
this.listPromptsHandlerIsRegistered = true;
|
|
1496
|
+
const capturedPromptOptions = this.promptOptions;
|
|
1497
|
+
if (!capturedPromptOptions?.listPrompts) {
|
|
1498
|
+
this.logger.warn("ListPrompts capability not supported by server configuration.");
|
|
1499
|
+
return;
|
|
1500
|
+
}
|
|
1501
|
+
this.server.setRequestHandler(ListPromptsRequestSchema, async () => {
|
|
1502
|
+
this.logger.debug("Handling ListPrompts request");
|
|
1503
|
+
if (this.definedPrompts) {
|
|
1504
|
+
return {
|
|
1505
|
+
prompts: this.definedPrompts?.map((p) => ({ ...p, version: p.version ?? void 0 }))
|
|
1506
|
+
};
|
|
1507
|
+
} else {
|
|
1508
|
+
try {
|
|
1509
|
+
const prompts = await capturedPromptOptions.listPrompts();
|
|
1510
|
+
for (const prompt of prompts) {
|
|
1511
|
+
PromptSchema.parse(prompt);
|
|
1512
|
+
}
|
|
1513
|
+
this.definedPrompts = prompts;
|
|
1514
|
+
this.logger.debug(`Fetched and cached ${this.definedPrompts.length} prompts.`);
|
|
1515
|
+
return {
|
|
1516
|
+
prompts: this.definedPrompts?.map((p) => ({ ...p, version: p.version ?? void 0 }))
|
|
1517
|
+
};
|
|
1518
|
+
} catch (error) {
|
|
1519
|
+
this.logger.error("Error fetching prompts via listPrompts():", {
|
|
1520
|
+
error: error instanceof Error ? error.message : String(error)
|
|
1521
|
+
});
|
|
1522
|
+
throw error;
|
|
1523
|
+
}
|
|
1524
|
+
}
|
|
1525
|
+
});
|
|
1526
|
+
}
|
|
1527
|
+
/**
|
|
1528
|
+
* Register the GetPrompt handler.
|
|
1529
|
+
*/
|
|
1530
|
+
registerGetPromptHandler({
|
|
1531
|
+
getPromptMessagesCallback
|
|
1532
|
+
}) {
|
|
1533
|
+
if (this.getPromptHandlerIsRegistered) return;
|
|
1534
|
+
this.getPromptHandlerIsRegistered = true;
|
|
1535
|
+
this.server.setRequestHandler(
|
|
1536
|
+
GetPromptRequestSchema,
|
|
1537
|
+
async (request) => {
|
|
1538
|
+
const startTime = Date.now();
|
|
1539
|
+
const { name, version, arguments: args } = request.params;
|
|
1540
|
+
if (!this.definedPrompts) {
|
|
1541
|
+
const prompts = await this.promptOptions?.listPrompts?.();
|
|
1542
|
+
if (!prompts) throw new Error("Failed to load prompts");
|
|
1543
|
+
this.definedPrompts = prompts;
|
|
1544
|
+
}
|
|
1545
|
+
let prompt;
|
|
1546
|
+
if (version) {
|
|
1547
|
+
prompt = this.definedPrompts?.find((p) => p.name === name && p.version === version);
|
|
1548
|
+
} else {
|
|
1549
|
+
prompt = this.definedPrompts?.find((p) => p.name === name);
|
|
1550
|
+
}
|
|
1551
|
+
if (!prompt) throw new Error(`Prompt "${name}"${version ? ` (version ${version})` : ""} not found`);
|
|
1552
|
+
if (prompt.arguments) {
|
|
1553
|
+
for (const arg of prompt.arguments) {
|
|
1554
|
+
if (arg.required && (args?.[arg.name] === void 0 || args?.[arg.name] === null)) {
|
|
1555
|
+
throw new Error(`Missing required argument: ${arg.name}`);
|
|
1556
|
+
}
|
|
1557
|
+
}
|
|
1558
|
+
}
|
|
1559
|
+
try {
|
|
1560
|
+
let messages = [];
|
|
1561
|
+
if (getPromptMessagesCallback) {
|
|
1562
|
+
messages = await getPromptMessagesCallback({ name, version, args });
|
|
1563
|
+
}
|
|
1564
|
+
const duration = Date.now() - startTime;
|
|
1565
|
+
this.logger.info(
|
|
1566
|
+
`Prompt '${name}'${version ? ` (version ${version})` : ""} retrieved successfully in ${duration}ms.`
|
|
1567
|
+
);
|
|
1568
|
+
return { prompt, messages };
|
|
1569
|
+
} catch (error) {
|
|
1570
|
+
const duration = Date.now() - startTime;
|
|
1571
|
+
this.logger.error(`Failed to get content for prompt '${name}' in ${duration}ms`, { error });
|
|
1572
|
+
throw error;
|
|
1573
|
+
}
|
|
1574
|
+
}
|
|
1575
|
+
);
|
|
1576
|
+
}
|
|
1322
1577
|
/**
|
|
1323
1578
|
* Start the MCP server using stdio transport (for Windsurf integration).
|
|
1324
1579
|
*/
|
|
@@ -10,9 +10,9 @@ case `uname` in
|
|
|
10
10
|
esac
|
|
11
11
|
|
|
12
12
|
if [ -z "$NODE_PATH" ]; then
|
|
13
|
-
export NODE_PATH="/home/runner/work/mastra/mastra/node_modules/.pnpm/vitest@
|
|
13
|
+
export NODE_PATH="/home/runner/work/mastra/mastra/node_modules/.pnpm/vitest@3.2.2_@edge-runtime+vm@3.2.0_@types+debug@4.1.12_@types+node@20.19.0_@vitest+ui@_f2f8c5e73aab614bca86ded968276706/node_modules/vitest/node_modules:/home/runner/work/mastra/mastra/node_modules/.pnpm/vitest@3.2.2_@edge-runtime+vm@3.2.0_@types+debug@4.1.12_@types+node@20.19.0_@vitest+ui@_f2f8c5e73aab614bca86ded968276706/node_modules:/home/runner/work/mastra/mastra/node_modules/.pnpm/node_modules"
|
|
14
14
|
else
|
|
15
|
-
export NODE_PATH="/home/runner/work/mastra/mastra/node_modules/.pnpm/vitest@
|
|
15
|
+
export NODE_PATH="/home/runner/work/mastra/mastra/node_modules/.pnpm/vitest@3.2.2_@edge-runtime+vm@3.2.0_@types+debug@4.1.12_@types+node@20.19.0_@vitest+ui@_f2f8c5e73aab614bca86ded968276706/node_modules/vitest/node_modules:/home/runner/work/mastra/mastra/node_modules/.pnpm/vitest@3.2.2_@edge-runtime+vm@3.2.0_@types+debug@4.1.12_@types+node@20.19.0_@vitest+ui@_f2f8c5e73aab614bca86ded968276706/node_modules:/home/runner/work/mastra/mastra/node_modules/.pnpm/node_modules:$NODE_PATH"
|
|
16
16
|
fi
|
|
17
17
|
if [ -x "$basedir/node" ]; then
|
|
18
18
|
exec "$basedir/node" "$basedir/../vitest/vitest.mjs" "$@"
|
|
@@ -12,14 +12,14 @@
|
|
|
12
12
|
"@mastra/client-js": "workspace:*",
|
|
13
13
|
"@mastra/mcp": "workspace:*",
|
|
14
14
|
"dotenv": "^16.5.0",
|
|
15
|
-
"zod": "^3.
|
|
15
|
+
"zod": "^3.25.56"
|
|
16
16
|
},
|
|
17
17
|
"devDependencies": {
|
|
18
18
|
"@testing-library/react": "^16.2.0",
|
|
19
|
-
"@types/node": "^20.17.
|
|
19
|
+
"@types/node": "^20.17.57",
|
|
20
20
|
"mastra": "workspace:*",
|
|
21
21
|
"typescript": "^5.8.2",
|
|
22
|
-
"vitest": "^
|
|
22
|
+
"vitest": "^3.2.2",
|
|
23
23
|
"@mastra/core": "workspace:*"
|
|
24
24
|
},
|
|
25
25
|
"peerDependencies": {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mastra/mcp",
|
|
3
|
-
"version": "0.10.
|
|
3
|
+
"version": "0.10.3-alpha.0",
|
|
4
4
|
"description": "",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
},
|
|
33
33
|
"peerDependencies": {
|
|
34
34
|
"zod": "^3.0.0",
|
|
35
|
-
"@mastra/core": "^0.10.
|
|
35
|
+
"@mastra/core": "^0.10.2-alpha.0"
|
|
36
36
|
},
|
|
37
37
|
"devDependencies": {
|
|
38
38
|
"@ai-sdk/anthropic": "^1.1.15",
|
|
@@ -40,18 +40,18 @@
|
|
|
40
40
|
"ai": "4.3.16",
|
|
41
41
|
"@hono/node-server": "^1.13.8",
|
|
42
42
|
"@mendable/firecrawl-js": "^1.24.0",
|
|
43
|
-
"@microsoft/api-extractor": "^7.52.
|
|
44
|
-
"@types/node": "^20.17.
|
|
45
|
-
"eslint": "^9.
|
|
43
|
+
"@microsoft/api-extractor": "^7.52.8",
|
|
44
|
+
"@types/node": "^20.17.57",
|
|
45
|
+
"eslint": "^9.28.0",
|
|
46
46
|
"hono-mcp-server-sse-transport": "0.0.6",
|
|
47
|
-
"tsup": "^8.
|
|
47
|
+
"tsup": "^8.5.0",
|
|
48
48
|
"tsx": "^4.19.3",
|
|
49
49
|
"typescript": "^5.8.2",
|
|
50
|
-
"vitest": "^3.
|
|
51
|
-
"zod": "^3.
|
|
50
|
+
"vitest": "^3.2.2",
|
|
51
|
+
"zod": "^3.25.56",
|
|
52
52
|
"zod-to-json-schema": "^3.24.5",
|
|
53
|
-
"@internal/lint": "0.0.
|
|
54
|
-
"@mastra/core": "0.10.
|
|
53
|
+
"@internal/lint": "0.0.10",
|
|
54
|
+
"@mastra/core": "0.10.4-alpha.1"
|
|
55
55
|
},
|
|
56
56
|
"scripts": {
|
|
57
57
|
"build": "tsup src/index.ts --format esm,cjs --experimental-dts --clean --treeshake=smallest --splitting",
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import type { IncomingMessage, ServerResponse } from 'http';
|
|
2
2
|
import { createServer } from 'http';
|
|
3
3
|
import { createTool } from '@mastra/core';
|
|
4
|
-
import type { Resource, ResourceTemplate } from '@modelcontextprotocol/sdk/types.js';
|
|
4
|
+
import type { Prompt, PromptMessage, Resource, ResourceTemplate } from '@modelcontextprotocol/sdk/types.js';
|
|
5
5
|
import { z } from 'zod';
|
|
6
6
|
import { MCPServer } from '../server/server';
|
|
7
|
-
import type { MCPServerResources, MCPServerResourceContent } from '../server/
|
|
7
|
+
import type { MCPServerResources, MCPServerResourceContent, MCPServerPrompts } from '../server/types';
|
|
8
8
|
|
|
9
9
|
const getWeather = async (location: string) => {
|
|
10
10
|
// Return mock data for testing
|
|
@@ -119,6 +119,36 @@ const weatherResourceContents: Record<string, MCPServerResourceContent> = {
|
|
|
119
119
|
},
|
|
120
120
|
};
|
|
121
121
|
|
|
122
|
+
const weatherPrompts: Prompt[] = [
|
|
123
|
+
{
|
|
124
|
+
name: 'current',
|
|
125
|
+
version: 'v1',
|
|
126
|
+
description: 'Get current weather for a location',
|
|
127
|
+
mimeType: 'application/json',
|
|
128
|
+
content: JSON.stringify({
|
|
129
|
+
location: 'Current weather for San Francisco',
|
|
130
|
+
}),
|
|
131
|
+
},
|
|
132
|
+
{
|
|
133
|
+
name: 'forecast',
|
|
134
|
+
version: 'v1',
|
|
135
|
+
description: 'Get weather forecast for a location',
|
|
136
|
+
mimeType: 'application/json',
|
|
137
|
+
content: JSON.stringify({
|
|
138
|
+
location: 'Forecast for San Francisco',
|
|
139
|
+
}),
|
|
140
|
+
},
|
|
141
|
+
{
|
|
142
|
+
name: 'historical',
|
|
143
|
+
version: 'v1',
|
|
144
|
+
description: 'Get historical weather data for a location',
|
|
145
|
+
mimeType: 'application/json',
|
|
146
|
+
content: JSON.stringify({
|
|
147
|
+
location: 'Historical weather for San Francisco',
|
|
148
|
+
}),
|
|
149
|
+
},
|
|
150
|
+
];
|
|
151
|
+
|
|
122
152
|
const mcpServerResources: MCPServerResources = {
|
|
123
153
|
listResources: async () => weatherResourceDefinitions,
|
|
124
154
|
getResourceContent: async ({ uri }: { uri: string }) => {
|
|
@@ -130,6 +160,25 @@ const mcpServerResources: MCPServerResources = {
|
|
|
130
160
|
resourceTemplates: async () => weatherResourceTemplatesDefinitions,
|
|
131
161
|
};
|
|
132
162
|
|
|
163
|
+
const mcpServerPrompts: MCPServerPrompts = {
|
|
164
|
+
listPrompts: async () => weatherPrompts,
|
|
165
|
+
getPromptMessages: async ({ name }: { name: string }): Promise<PromptMessage[]> => {
|
|
166
|
+
const prompt = weatherPrompts.find(p => p.name === name);
|
|
167
|
+
if (!prompt) {
|
|
168
|
+
throw new Error(`Mock prompt not found for ${name}`);
|
|
169
|
+
}
|
|
170
|
+
return [
|
|
171
|
+
{
|
|
172
|
+
role: 'user',
|
|
173
|
+
content: {
|
|
174
|
+
type: 'text',
|
|
175
|
+
text: prompt.content as string,
|
|
176
|
+
},
|
|
177
|
+
},
|
|
178
|
+
];
|
|
179
|
+
},
|
|
180
|
+
};
|
|
181
|
+
|
|
133
182
|
const mcpServer = new MCPServer({
|
|
134
183
|
name: serverId,
|
|
135
184
|
version: '1.0.0',
|
|
@@ -137,6 +186,7 @@ const mcpServer = new MCPServer({
|
|
|
137
186
|
getWeather: weatherToolDefinition,
|
|
138
187
|
},
|
|
139
188
|
resources: mcpServerResources,
|
|
189
|
+
prompts: mcpServerPrompts,
|
|
140
190
|
});
|
|
141
191
|
|
|
142
192
|
const httpServer = createServer(async (req: IncomingMessage, res: ServerResponse) => {
|
|
@@ -193,12 +243,22 @@ const notificationInterval = setInterval(async () => {
|
|
|
193
243
|
}
|
|
194
244
|
}
|
|
195
245
|
}, NOTIFICATION_INTERVAL_MS);
|
|
246
|
+
|
|
247
|
+
const promptNotificationInterval = setInterval(async () => {
|
|
248
|
+
const listChangePrefix = `[${serverId}] IntervalListChange`;
|
|
249
|
+
try {
|
|
250
|
+
await mcpServer.prompts.notifyListChanged();
|
|
251
|
+
} catch (e: any) {
|
|
252
|
+
console.error(`${listChangePrefix} - Error sending promptListChanged via MCPServer: ${e.message}`);
|
|
253
|
+
}
|
|
254
|
+
}, NOTIFICATION_INTERVAL_MS);
|
|
196
255
|
// --- End Interval-based Notifications ---
|
|
197
256
|
|
|
198
257
|
// Handle graceful shutdown
|
|
199
258
|
process.on('SIGINT', async () => {
|
|
200
259
|
console.log('Shutting down weather server...');
|
|
201
260
|
clearInterval(notificationInterval); // Clear the interval
|
|
261
|
+
clearInterval(promptNotificationInterval); // Clear the interval
|
|
202
262
|
await mcpServer.close();
|
|
203
263
|
httpServer.close(() => {
|
|
204
264
|
console.log('Weather server shut down complete');
|
|
@@ -19,6 +19,7 @@ async function setupTestServer(withSessionManagement: boolean) {
|
|
|
19
19
|
logging: {},
|
|
20
20
|
tools: {},
|
|
21
21
|
resources: {},
|
|
22
|
+
prompts: {},
|
|
22
23
|
},
|
|
23
24
|
},
|
|
24
25
|
);
|
|
@@ -47,6 +48,27 @@ async function setupTestServer(withSessionManagement: boolean) {
|
|
|
47
48
|
};
|
|
48
49
|
});
|
|
49
50
|
|
|
51
|
+
mcpServer.prompt(
|
|
52
|
+
'greet',
|
|
53
|
+
'A simple greeting prompt',
|
|
54
|
+
() => {
|
|
55
|
+
return {
|
|
56
|
+
prompt: {
|
|
57
|
+
name: 'greet',
|
|
58
|
+
version: 'v1',
|
|
59
|
+
description: 'A simple greeting prompt',
|
|
60
|
+
mimeType: 'application/json',
|
|
61
|
+
},
|
|
62
|
+
messages: [
|
|
63
|
+
{
|
|
64
|
+
role: 'assistant',
|
|
65
|
+
content: { type: 'text', text: `Hello, World!` }
|
|
66
|
+
}
|
|
67
|
+
]
|
|
68
|
+
};
|
|
69
|
+
},
|
|
70
|
+
);
|
|
71
|
+
|
|
50
72
|
const serverTransport = new StreamableHTTPServerTransport({
|
|
51
73
|
sessionIdGenerator: withSessionManagement ? () => randomUUID() : undefined,
|
|
52
74
|
});
|
|
@@ -121,6 +143,30 @@ describe('MastraMCPClient with Streamable HTTP', () => {
|
|
|
121
143
|
expect(readResult.contents.length).toBe(1);
|
|
122
144
|
expect(readResult.contents[0].text).toBe('Hello, world!');
|
|
123
145
|
});
|
|
146
|
+
|
|
147
|
+
it('should list prompts', async () => {
|
|
148
|
+
const {prompts} = await client.listPrompts();
|
|
149
|
+
expect(prompts).toBeInstanceOf(Array);
|
|
150
|
+
expect(prompts).toHaveLength(1);
|
|
151
|
+
expect(prompts[0]).toHaveProperty('name');
|
|
152
|
+
expect(prompts[0]).toHaveProperty('description');
|
|
153
|
+
expect(prompts[0].description).toBe('A simple greeting prompt');
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
it('should get a specific prompt', async () => {
|
|
157
|
+
const result = await client.getPrompt({name: 'greet'});
|
|
158
|
+
const {prompt, messages} = result;
|
|
159
|
+
expect(prompt).toBeDefined();
|
|
160
|
+
expect(prompt).toMatchObject({
|
|
161
|
+
name: 'greet',
|
|
162
|
+
version: 'v1',
|
|
163
|
+
description: expect.any(String),
|
|
164
|
+
mimeType: 'application/json',
|
|
165
|
+
});
|
|
166
|
+
expect(messages).toBeDefined();
|
|
167
|
+
const messageItem = messages[0];
|
|
168
|
+
expect(messageItem.content.text).toBe('Hello, World!');
|
|
169
|
+
});
|
|
124
170
|
});
|
|
125
171
|
|
|
126
172
|
describe('Stateful Mode', () => {
|