@mastra/mcp 0.10.2 → 0.10.3
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 +31 -0
- package/README.md +32 -0
- package/dist/_tsup-dts-rollup.d.cts +175 -1
- package/dist/_tsup-dts-rollup.d.ts +175 -1
- package/dist/index.cjs +251 -2
- package/dist/index.d.cts +3 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.js +253 -4
- package/integration-tests/node_modules/.bin/vitest +2 -2
- package/integration-tests/package.json +3 -3
- package/package.json +9 -9
- 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 +139 -14
- package/src/server/types.ts +30 -0
package/dist/index.d.cts
CHANGED
|
@@ -7,9 +7,11 @@ export { MCPClientOptions } from './_tsup-dts-rollup.cjs';
|
|
|
7
7
|
export { MCPClient } from './_tsup-dts-rollup.cjs';
|
|
8
8
|
export { MCPConfigurationOptions } from './_tsup-dts-rollup.cjs';
|
|
9
9
|
export { MCPConfiguration } from './_tsup-dts-rollup.cjs';
|
|
10
|
+
export { MCPServer } from './_tsup-dts-rollup.cjs';
|
|
10
11
|
export { MCPServerResourceContentCallback } from './_tsup-dts-rollup.cjs';
|
|
11
12
|
export { MCPServerResourceContent } from './_tsup-dts-rollup.cjs';
|
|
12
13
|
export { MCPServerResources } from './_tsup-dts-rollup.cjs';
|
|
14
|
+
export { MCPServerPromptMessagesCallback } from './_tsup-dts-rollup.cjs';
|
|
15
|
+
export { MCPServerPrompts } from './_tsup-dts-rollup.cjs';
|
|
13
16
|
export { Resource } from './_tsup-dts-rollup.cjs';
|
|
14
17
|
export { ResourceTemplate } from './_tsup-dts-rollup.cjs';
|
|
15
|
-
export { MCPServer } from './_tsup-dts-rollup.cjs';
|
package/dist/index.d.ts
CHANGED
|
@@ -7,9 +7,11 @@ export { MCPClientOptions } from './_tsup-dts-rollup.js';
|
|
|
7
7
|
export { MCPClient } from './_tsup-dts-rollup.js';
|
|
8
8
|
export { MCPConfigurationOptions } from './_tsup-dts-rollup.js';
|
|
9
9
|
export { MCPConfiguration } from './_tsup-dts-rollup.js';
|
|
10
|
+
export { MCPServer } from './_tsup-dts-rollup.js';
|
|
10
11
|
export { MCPServerResourceContentCallback } from './_tsup-dts-rollup.js';
|
|
11
12
|
export { MCPServerResourceContent } from './_tsup-dts-rollup.js';
|
|
12
13
|
export { MCPServerResources } from './_tsup-dts-rollup.js';
|
|
14
|
+
export { MCPServerPromptMessagesCallback } from './_tsup-dts-rollup.js';
|
|
15
|
+
export { MCPServerPrompts } from './_tsup-dts-rollup.js';
|
|
13
16
|
export { Resource } from './_tsup-dts-rollup.js';
|
|
14
17
|
export { ResourceTemplate } from './_tsup-dts-rollup.js';
|
|
15
|
-
export { MCPServer } from './_tsup-dts-rollup.js';
|
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
|
*/
|
|
@@ -865,6 +1008,7 @@ var MCPServer = class extends MCPServerBase {
|
|
|
865
1008
|
constructor(opts) {
|
|
866
1009
|
super(opts);
|
|
867
1010
|
this.resourceOptions = opts.resources;
|
|
1011
|
+
this.promptOptions = opts.prompts;
|
|
868
1012
|
const capabilities = {
|
|
869
1013
|
tools: {},
|
|
870
1014
|
logging: { enabled: true }
|
|
@@ -872,6 +1016,9 @@ var MCPServer = class extends MCPServerBase {
|
|
|
872
1016
|
if (opts.resources) {
|
|
873
1017
|
capabilities.resources = { subscribe: true, listChanged: true };
|
|
874
1018
|
}
|
|
1019
|
+
if (opts.prompts) {
|
|
1020
|
+
capabilities.prompts = { listChanged: true };
|
|
1021
|
+
}
|
|
875
1022
|
this.server = new Server({ name: this.name, version: this.version }, { capabilities });
|
|
876
1023
|
this.logger.info(
|
|
877
1024
|
`Initialized MCPServer '${this.name}' v${this.version} (ID: ${this.id}) with tools: ${Object.keys(this.convertedTools).join(", ")} and resources. Capabilities: ${JSON.stringify(capabilities)}`
|
|
@@ -888,6 +1035,12 @@ var MCPServer = class extends MCPServerBase {
|
|
|
888
1035
|
this.registerListResourceTemplatesHandler();
|
|
889
1036
|
}
|
|
890
1037
|
}
|
|
1038
|
+
if (opts.prompts) {
|
|
1039
|
+
this.registerListPromptsHandler();
|
|
1040
|
+
this.registerGetPromptHandler({
|
|
1041
|
+
getPromptMessagesCallback: opts.prompts.getPromptMessages
|
|
1042
|
+
});
|
|
1043
|
+
}
|
|
891
1044
|
this.resources = new ServerResourceActions({
|
|
892
1045
|
getSubscriptions: () => this.subscriptions,
|
|
893
1046
|
getLogger: () => this.logger,
|
|
@@ -899,6 +1052,13 @@ var MCPServer = class extends MCPServerBase {
|
|
|
899
1052
|
this.definedResourceTemplates = void 0;
|
|
900
1053
|
}
|
|
901
1054
|
});
|
|
1055
|
+
this.prompts = new ServerPromptActions({
|
|
1056
|
+
getLogger: () => this.logger,
|
|
1057
|
+
getSdkServer: () => this.server,
|
|
1058
|
+
clearDefinedPrompts: () => {
|
|
1059
|
+
this.definedPrompts = void 0;
|
|
1060
|
+
}
|
|
1061
|
+
});
|
|
902
1062
|
}
|
|
903
1063
|
convertAgentsToTools(agentsConfig, definedConvertedTools) {
|
|
904
1064
|
const agentTools = {};
|
|
@@ -1325,6 +1485,95 @@ var MCPServer = class extends MCPServerBase {
|
|
|
1325
1485
|
return {};
|
|
1326
1486
|
});
|
|
1327
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
|
+
}
|
|
1328
1577
|
/**
|
|
1329
1578
|
* Start the MCP server using stdio transport (for Windsurf integration).
|
|
1330
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@_eb265836689cb09b255052147598b424/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@_eb265836689cb09b255052147598b424/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@_eb265836689cb09b255052147598b424/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@_eb265836689cb09b255052147598b424/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",
|
|
4
4
|
"description": "",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -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.11",
|
|
54
|
+
"@mastra/core": "0.10.4"
|
|
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', () => {
|