@mastra/mcp 0.10.0 → 0.10.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/.turbo/turbo-build.log +7 -7
- package/CHANGELOG.md +34 -0
- package/dist/_tsup-dts-rollup.d.cts +332 -40
- package/dist/_tsup-dts-rollup.d.ts +332 -40
- package/dist/index.cjs +599 -45
- package/dist/index.d.cts +14 -9
- package/dist/index.d.ts +14 -9
- package/dist/index.js +604 -50
- package/package.json +4 -3
- package/src/__fixtures__/fire-crawl-complex-schema.ts +1 -1
- package/src/__fixtures__/server-weather.ts +1 -1
- package/src/__fixtures__/weather.ts +122 -190
- package/src/{client.test.ts → client/client.test.ts} +27 -0
- package/src/{client.ts → client/client.ts} +58 -5
- package/src/{configuration.test.ts → client/configuration.test.ts} +133 -21
- package/src/{configuration.ts → client/configuration.ts} +64 -48
- package/src/client/resourceActions.ts +121 -0
- package/src/index.ts +4 -4
- package/src/server/resourceActions.ts +62 -0
- package/src/{server-logging.test.ts → server/server-logging.test.ts} +9 -7
- package/src/server/server.test.ts +1126 -0
- package/src/{server.ts → server/server.ts} +444 -16
- package/src/server.test.ts +0 -467
package/dist/index.cjs
CHANGED
|
@@ -16,6 +16,7 @@ var equal = require('fast-deep-equal');
|
|
|
16
16
|
var uuid = require('uuid');
|
|
17
17
|
var crypto$1 = require('crypto');
|
|
18
18
|
var core = require('@mastra/core');
|
|
19
|
+
var agent = require('@mastra/core/agent');
|
|
19
20
|
var mcp = require('@mastra/core/mcp');
|
|
20
21
|
var runtimeContext = require('@mastra/core/runtime-context');
|
|
21
22
|
var index_js = require('@modelcontextprotocol/sdk/server/index.js');
|
|
@@ -28,7 +29,110 @@ function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
|
28
29
|
|
|
29
30
|
var equal__default = /*#__PURE__*/_interopDefault(equal);
|
|
30
31
|
|
|
31
|
-
// src/client.ts
|
|
32
|
+
// src/client/client.ts
|
|
33
|
+
var ResourceClientActions = class {
|
|
34
|
+
client;
|
|
35
|
+
logger;
|
|
36
|
+
constructor({ client, logger }) {
|
|
37
|
+
this.client = client;
|
|
38
|
+
this.logger = logger;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Get all resources from the connected MCP server.
|
|
42
|
+
* @returns A list of resources.
|
|
43
|
+
*/
|
|
44
|
+
async list() {
|
|
45
|
+
try {
|
|
46
|
+
const response = await this.client.listResources();
|
|
47
|
+
if (response && response.resources && Array.isArray(response.resources)) {
|
|
48
|
+
return response.resources;
|
|
49
|
+
} else {
|
|
50
|
+
this.logger.warn(`Resources response from server ${this.client.name} did not have expected structure.`, {
|
|
51
|
+
response
|
|
52
|
+
});
|
|
53
|
+
return [];
|
|
54
|
+
}
|
|
55
|
+
} catch (e) {
|
|
56
|
+
if (e.code === types_js.ErrorCode.MethodNotFound) {
|
|
57
|
+
return [];
|
|
58
|
+
}
|
|
59
|
+
this.logger.error(`Error getting resources from server ${this.client.name}`, {
|
|
60
|
+
error: e instanceof Error ? e.message : String(e)
|
|
61
|
+
});
|
|
62
|
+
console.log("errorheere", e);
|
|
63
|
+
throw new Error(
|
|
64
|
+
`Failed to fetch resources from server ${this.client.name}: ${e instanceof Error ? e.stack || e.message : String(e)}`
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Get all resource templates from the connected MCP server.
|
|
70
|
+
* @returns A list of resource templates.
|
|
71
|
+
*/
|
|
72
|
+
async templates() {
|
|
73
|
+
try {
|
|
74
|
+
const response = await this.client.listResourceTemplates();
|
|
75
|
+
if (response && response.resourceTemplates && Array.isArray(response.resourceTemplates)) {
|
|
76
|
+
return response.resourceTemplates;
|
|
77
|
+
} else {
|
|
78
|
+
this.logger.warn(
|
|
79
|
+
`Resource templates response from server ${this.client.name} did not have expected structure.`,
|
|
80
|
+
{ response }
|
|
81
|
+
);
|
|
82
|
+
return [];
|
|
83
|
+
}
|
|
84
|
+
} catch (e) {
|
|
85
|
+
console.log({ errorcooode: e.code });
|
|
86
|
+
if (e.code === types_js.ErrorCode.MethodNotFound) {
|
|
87
|
+
return [];
|
|
88
|
+
}
|
|
89
|
+
this.logger.error(`Error getting resource templates from server ${this.client.name}`, {
|
|
90
|
+
error: e instanceof Error ? e.message : String(e)
|
|
91
|
+
});
|
|
92
|
+
throw new Error(
|
|
93
|
+
`Failed to fetch resource templates from server ${this.client.name}: ${e instanceof Error ? e.stack || e.message : String(e)}`
|
|
94
|
+
);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Read a specific resource.
|
|
99
|
+
* @param uri The URI of the resource to read.
|
|
100
|
+
* @returns The resource content.
|
|
101
|
+
*/
|
|
102
|
+
async read(uri) {
|
|
103
|
+
return this.client.readResource(uri);
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Subscribe to a specific resource.
|
|
107
|
+
* @param uri The URI of the resource to subscribe to.
|
|
108
|
+
*/
|
|
109
|
+
async subscribe(uri) {
|
|
110
|
+
return this.client.subscribeResource(uri);
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Unsubscribe from a specific resource.
|
|
114
|
+
* @param uri The URI of the resource to unsubscribe from.
|
|
115
|
+
*/
|
|
116
|
+
async unsubscribe(uri) {
|
|
117
|
+
return this.client.unsubscribeResource(uri);
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Set a notification handler for when a specific resource is updated.
|
|
121
|
+
* @param handler The callback function to handle the notification.
|
|
122
|
+
*/
|
|
123
|
+
async onUpdated(handler) {
|
|
124
|
+
this.client.setResourceUpdatedNotificationHandler(handler);
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Set a notification handler for when the list of available resources changes.
|
|
128
|
+
* @param handler The callback function to handle the notification.
|
|
129
|
+
*/
|
|
130
|
+
async onListChanged(handler) {
|
|
131
|
+
this.client.setResourceListChangedNotificationHandler(handler);
|
|
132
|
+
}
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
// src/client/client.ts
|
|
32
136
|
function convertLogLevelToLoggerMethod(level) {
|
|
33
137
|
switch (level) {
|
|
34
138
|
case "debug":
|
|
@@ -56,6 +160,7 @@ var InternalMastraMCPClient = class extends base.MastraBase {
|
|
|
56
160
|
serverConfig;
|
|
57
161
|
transport;
|
|
58
162
|
currentOperationContext = null;
|
|
163
|
+
resources;
|
|
59
164
|
constructor({
|
|
60
165
|
name,
|
|
61
166
|
version = "1.0.0",
|
|
@@ -79,6 +184,7 @@ var InternalMastraMCPClient = class extends base.MastraBase {
|
|
|
79
184
|
}
|
|
80
185
|
);
|
|
81
186
|
this.setupLogging();
|
|
187
|
+
this.resources = new ResourceClientActions({ client: this, logger: this.logger });
|
|
82
188
|
}
|
|
83
189
|
/**
|
|
84
190
|
* Log a message at the specified level
|
|
@@ -232,13 +338,48 @@ var InternalMastraMCPClient = class extends base.MastraBase {
|
|
|
232
338
|
this.isConnected = false;
|
|
233
339
|
}
|
|
234
340
|
}
|
|
235
|
-
|
|
236
|
-
async resources() {
|
|
341
|
+
async listResources() {
|
|
237
342
|
this.log("debug", `Requesting resources from MCP server`);
|
|
238
343
|
return await this.client.request({ method: "resources/list" }, types_js.ListResourcesResultSchema, {
|
|
239
344
|
timeout: this.timeout
|
|
240
345
|
});
|
|
241
346
|
}
|
|
347
|
+
async readResource(uri) {
|
|
348
|
+
this.log("debug", `Reading resource from MCP server: ${uri}`);
|
|
349
|
+
return await this.client.request({ method: "resources/read", params: { uri } }, types_js.ReadResourceResultSchema, {
|
|
350
|
+
timeout: this.timeout
|
|
351
|
+
});
|
|
352
|
+
}
|
|
353
|
+
async subscribeResource(uri) {
|
|
354
|
+
this.log("debug", `Subscribing to resource on MCP server: ${uri}`);
|
|
355
|
+
return await this.client.request({ method: "resources/subscribe", params: { uri } }, zod.z.object({}), {
|
|
356
|
+
timeout: this.timeout
|
|
357
|
+
});
|
|
358
|
+
}
|
|
359
|
+
async unsubscribeResource(uri) {
|
|
360
|
+
this.log("debug", `Unsubscribing from resource on MCP server: ${uri}`);
|
|
361
|
+
return await this.client.request({ method: "resources/unsubscribe", params: { uri } }, zod.z.object({}), {
|
|
362
|
+
timeout: this.timeout
|
|
363
|
+
});
|
|
364
|
+
}
|
|
365
|
+
async listResourceTemplates() {
|
|
366
|
+
this.log("debug", `Requesting resource templates from MCP server`);
|
|
367
|
+
return await this.client.request({ method: "resources/templates/list" }, types_js.ListResourceTemplatesResultSchema, {
|
|
368
|
+
timeout: this.timeout
|
|
369
|
+
});
|
|
370
|
+
}
|
|
371
|
+
setResourceUpdatedNotificationHandler(handler) {
|
|
372
|
+
this.log("debug", "Setting resource updated notification handler");
|
|
373
|
+
this.client.setNotificationHandler(types_js.ResourceUpdatedNotificationSchema, (notification) => {
|
|
374
|
+
handler(notification.params);
|
|
375
|
+
});
|
|
376
|
+
}
|
|
377
|
+
setResourceListChangedNotificationHandler(handler) {
|
|
378
|
+
this.log("debug", "Setting resource list changed notification handler");
|
|
379
|
+
this.client.setNotificationHandler(types_js.ResourceListChangedNotificationSchema, () => {
|
|
380
|
+
handler();
|
|
381
|
+
});
|
|
382
|
+
}
|
|
242
383
|
convertInputSchema(inputSchema) {
|
|
243
384
|
if (utils.isZodType(inputSchema)) {
|
|
244
385
|
return inputSchema;
|
|
@@ -342,6 +483,7 @@ var MCPClient = class extends base.MastraBase {
|
|
|
342
483
|
const existingInstance2 = mcpClientInstances.get(this.id);
|
|
343
484
|
if (existingInstance2) {
|
|
344
485
|
void existingInstance2.disconnect();
|
|
486
|
+
mcpClientInstances.delete(this.id);
|
|
345
487
|
}
|
|
346
488
|
}
|
|
347
489
|
} else {
|
|
@@ -366,6 +508,55 @@ To fix this you have three different options:
|
|
|
366
508
|
this.addToInstanceCache();
|
|
367
509
|
return this;
|
|
368
510
|
}
|
|
511
|
+
get resources() {
|
|
512
|
+
this.addToInstanceCache();
|
|
513
|
+
return {
|
|
514
|
+
list: async () => {
|
|
515
|
+
const allResources = {};
|
|
516
|
+
for (const serverName of Object.keys(this.serverConfigs)) {
|
|
517
|
+
try {
|
|
518
|
+
const internalClient = await this.getConnectedClientForServer(serverName);
|
|
519
|
+
allResources[serverName] = await internalClient.resources.list();
|
|
520
|
+
} catch (error) {
|
|
521
|
+
this.logger.error(`Failed to list resources from server ${serverName}`, { error });
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
return allResources;
|
|
525
|
+
},
|
|
526
|
+
templates: async () => {
|
|
527
|
+
const allTemplates = {};
|
|
528
|
+
for (const serverName of Object.keys(this.serverConfigs)) {
|
|
529
|
+
try {
|
|
530
|
+
const internalClient = await this.getConnectedClientForServer(serverName);
|
|
531
|
+
allTemplates[serverName] = await internalClient.resources.templates();
|
|
532
|
+
} catch (error) {
|
|
533
|
+
this.logger.error(`Failed to list resource templates from server ${serverName}`, { error });
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
return allTemplates;
|
|
537
|
+
},
|
|
538
|
+
read: async (serverName, uri) => {
|
|
539
|
+
const internalClient = await this.getConnectedClientForServer(serverName);
|
|
540
|
+
return internalClient.resources.read(uri);
|
|
541
|
+
},
|
|
542
|
+
subscribe: async (serverName, uri) => {
|
|
543
|
+
const internalClient = await this.getConnectedClientForServer(serverName);
|
|
544
|
+
return internalClient.resources.subscribe(uri);
|
|
545
|
+
},
|
|
546
|
+
unsubscribe: async (serverName, uri) => {
|
|
547
|
+
const internalClient = await this.getConnectedClientForServer(serverName);
|
|
548
|
+
return internalClient.resources.unsubscribe(uri);
|
|
549
|
+
},
|
|
550
|
+
onUpdated: async (serverName, handler) => {
|
|
551
|
+
const internalClient = await this.getConnectedClientForServer(serverName);
|
|
552
|
+
return internalClient.resources.onUpdated(handler);
|
|
553
|
+
},
|
|
554
|
+
onListChanged: async (serverName, handler) => {
|
|
555
|
+
const internalClient = await this.getConnectedClientForServer(serverName);
|
|
556
|
+
return internalClient.resources.onListChanged(handler);
|
|
557
|
+
}
|
|
558
|
+
};
|
|
559
|
+
}
|
|
369
560
|
addToInstanceCache() {
|
|
370
561
|
if (!mcpClientInstances.has(this.id)) {
|
|
371
562
|
mcpClientInstances.set(this.id, this);
|
|
@@ -412,18 +603,10 @@ To fix this you have three different options:
|
|
|
412
603
|
return connectedToolsets;
|
|
413
604
|
}
|
|
414
605
|
/**
|
|
415
|
-
*
|
|
416
|
-
* @returns A record of server names to their resources
|
|
606
|
+
* @deprecated all resource actions have been moved to the this.resources object. Use this.resources.list() instead.
|
|
417
607
|
*/
|
|
418
608
|
async getResources() {
|
|
419
|
-
this.
|
|
420
|
-
const connectedResources = {};
|
|
421
|
-
await this.eachClientResources(async ({ serverName, resources }) => {
|
|
422
|
-
if (resources && Array.isArray(resources)) {
|
|
423
|
-
connectedResources[serverName] = resources;
|
|
424
|
-
}
|
|
425
|
-
});
|
|
426
|
-
return connectedResources;
|
|
609
|
+
return this.resources.list();
|
|
427
610
|
}
|
|
428
611
|
/**
|
|
429
612
|
* Get the current session IDs for all connected MCP clients using the Streamable HTTP transport.
|
|
@@ -472,6 +655,13 @@ To fix this you have three different options:
|
|
|
472
655
|
this.logger.debug(`Connected to ${name} MCP server`);
|
|
473
656
|
return mcpClient;
|
|
474
657
|
}
|
|
658
|
+
async getConnectedClientForServer(serverName) {
|
|
659
|
+
const serverConfig = this.serverConfigs[serverName];
|
|
660
|
+
if (!serverConfig) {
|
|
661
|
+
throw new Error(`Server configuration not found for name: ${serverName}`);
|
|
662
|
+
}
|
|
663
|
+
return this.getConnectedClient(serverName, serverConfig);
|
|
664
|
+
}
|
|
475
665
|
async eachClientTools(cb) {
|
|
476
666
|
await Promise.all(
|
|
477
667
|
Object.entries(this.serverConfigs).map(async ([serverName, serverConfig]) => {
|
|
@@ -481,27 +671,6 @@ To fix this you have three different options:
|
|
|
481
671
|
})
|
|
482
672
|
);
|
|
483
673
|
}
|
|
484
|
-
/**
|
|
485
|
-
* Helper method to iterate through each connected MCP client and retrieve resources
|
|
486
|
-
* @param cb Callback function to process resources from each server
|
|
487
|
-
*/
|
|
488
|
-
async eachClientResources(cb) {
|
|
489
|
-
await Promise.all(
|
|
490
|
-
Object.entries(this.serverConfigs).map(async ([serverName, serverConfig]) => {
|
|
491
|
-
try {
|
|
492
|
-
const client = await this.getConnectedClient(serverName, serverConfig);
|
|
493
|
-
const response = await client.resources();
|
|
494
|
-
if (response && "resources" in response && Array.isArray(response.resources)) {
|
|
495
|
-
await cb({ serverName, resources: response.resources, client });
|
|
496
|
-
}
|
|
497
|
-
} catch (e) {
|
|
498
|
-
this.logger.error(`Error getting resources from server ${serverName}`, {
|
|
499
|
-
error: e instanceof Error ? e.message : String(e)
|
|
500
|
-
});
|
|
501
|
-
}
|
|
502
|
-
})
|
|
503
|
-
);
|
|
504
|
-
}
|
|
505
674
|
};
|
|
506
675
|
var MCPConfiguration = class extends MCPClient {
|
|
507
676
|
constructor(args) {
|
|
@@ -595,6 +764,58 @@ var SSETransport = class {
|
|
|
595
764
|
});
|
|
596
765
|
}
|
|
597
766
|
};
|
|
767
|
+
|
|
768
|
+
// src/server/resourceActions.ts
|
|
769
|
+
var ServerResourceActions = class {
|
|
770
|
+
getSubscriptions;
|
|
771
|
+
getLogger;
|
|
772
|
+
getSdkServer;
|
|
773
|
+
clearDefinedResources;
|
|
774
|
+
clearDefinedResourceTemplates;
|
|
775
|
+
constructor(dependencies) {
|
|
776
|
+
this.getSubscriptions = dependencies.getSubscriptions;
|
|
777
|
+
this.getLogger = dependencies.getLogger;
|
|
778
|
+
this.getSdkServer = dependencies.getSdkServer;
|
|
779
|
+
this.clearDefinedResources = dependencies.clearDefinedResources;
|
|
780
|
+
this.clearDefinedResourceTemplates = dependencies.clearDefinedResourceTemplates;
|
|
781
|
+
}
|
|
782
|
+
/**
|
|
783
|
+
* Checks if any resources have been updated.
|
|
784
|
+
* If the resource is subscribed to by clients, an update notification will be sent.
|
|
785
|
+
*/
|
|
786
|
+
async notifyUpdated({ uri }) {
|
|
787
|
+
if (this.getSubscriptions().has(uri)) {
|
|
788
|
+
this.getLogger().info(`Sending notifications/resources/updated for externally notified resource: ${uri}`);
|
|
789
|
+
try {
|
|
790
|
+
await this.getSdkServer().sendResourceUpdated({ uri });
|
|
791
|
+
} catch (error) {
|
|
792
|
+
this.getLogger().error("Failed to send resource updated notification:", { error });
|
|
793
|
+
throw error;
|
|
794
|
+
}
|
|
795
|
+
} else {
|
|
796
|
+
this.getLogger().debug(`Resource ${uri} was updated, but no active subscriptions for it.`);
|
|
797
|
+
}
|
|
798
|
+
}
|
|
799
|
+
/**
|
|
800
|
+
* Notifies the server that the overall list of available resources has changed.
|
|
801
|
+
* This will clear the internal cache of defined resources and send a list_changed notification to clients.
|
|
802
|
+
*/
|
|
803
|
+
async notifyListChanged() {
|
|
804
|
+
this.getLogger().info(
|
|
805
|
+
"Resource list change externally notified. Clearing definedResources and sending notification."
|
|
806
|
+
);
|
|
807
|
+
this.clearDefinedResources();
|
|
808
|
+
this.clearDefinedResourceTemplates();
|
|
809
|
+
try {
|
|
810
|
+
await this.getSdkServer().sendResourceListChanged();
|
|
811
|
+
} catch (error) {
|
|
812
|
+
this.getLogger().error("Failed to send resource list changed notification:", { error });
|
|
813
|
+
throw error;
|
|
814
|
+
}
|
|
815
|
+
}
|
|
816
|
+
};
|
|
817
|
+
|
|
818
|
+
// src/server/server.ts
|
|
598
819
|
var MCPServer = class extends mcp.MCPServerBase {
|
|
599
820
|
server;
|
|
600
821
|
stdioTransport;
|
|
@@ -603,6 +824,16 @@ var MCPServer = class extends mcp.MCPServerBase {
|
|
|
603
824
|
streamableHTTPTransport;
|
|
604
825
|
listToolsHandlerIsRegistered = false;
|
|
605
826
|
callToolHandlerIsRegistered = false;
|
|
827
|
+
listResourcesHandlerIsRegistered = false;
|
|
828
|
+
readResourceHandlerIsRegistered = false;
|
|
829
|
+
listResourceTemplatesHandlerIsRegistered = false;
|
|
830
|
+
subscribeResourceHandlerIsRegistered = false;
|
|
831
|
+
unsubscribeResourceHandlerIsRegistered = false;
|
|
832
|
+
definedResources;
|
|
833
|
+
definedResourceTemplates;
|
|
834
|
+
resourceOptions;
|
|
835
|
+
subscriptions = /* @__PURE__ */ new Set();
|
|
836
|
+
resources;
|
|
606
837
|
/**
|
|
607
838
|
* Get the current stdio transport.
|
|
608
839
|
*/
|
|
@@ -633,24 +864,179 @@ var MCPServer = class extends mcp.MCPServerBase {
|
|
|
633
864
|
*/
|
|
634
865
|
constructor(opts) {
|
|
635
866
|
super(opts);
|
|
636
|
-
this.
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
867
|
+
this.resourceOptions = opts.resources;
|
|
868
|
+
const capabilities = {
|
|
869
|
+
tools: {},
|
|
870
|
+
logging: { enabled: true }
|
|
871
|
+
};
|
|
872
|
+
if (opts.resources) {
|
|
873
|
+
capabilities.resources = { subscribe: true, listChanged: true };
|
|
874
|
+
}
|
|
875
|
+
this.server = new index_js.Server({ name: this.name, version: this.version }, { capabilities });
|
|
640
876
|
this.logger.info(
|
|
641
|
-
`Initialized MCPServer '${this.name}' v${this.version} (ID: ${this.id}) with tools: ${Object.keys(this.convertedTools).join(", ")}`
|
|
877
|
+
`Initialized MCPServer '${this.name}' v${this.version} (ID: ${this.id}) with tools: ${Object.keys(this.convertedTools).join(", ")} and resources. Capabilities: ${JSON.stringify(capabilities)}`
|
|
642
878
|
);
|
|
643
879
|
this.sseHonoTransports = /* @__PURE__ */ new Map();
|
|
644
880
|
this.registerListToolsHandler();
|
|
645
881
|
this.registerCallToolHandler();
|
|
882
|
+
if (opts.resources) {
|
|
883
|
+
this.registerListResourcesHandler();
|
|
884
|
+
this.registerReadResourceHandler({ getResourcesCallback: opts.resources.getResourceContent });
|
|
885
|
+
this.registerSubscribeResourceHandler();
|
|
886
|
+
this.registerUnsubscribeResourceHandler();
|
|
887
|
+
if (opts.resources.resourceTemplates) {
|
|
888
|
+
this.registerListResourceTemplatesHandler();
|
|
889
|
+
}
|
|
890
|
+
}
|
|
891
|
+
this.resources = new ServerResourceActions({
|
|
892
|
+
getSubscriptions: () => this.subscriptions,
|
|
893
|
+
getLogger: () => this.logger,
|
|
894
|
+
getSdkServer: () => this.server,
|
|
895
|
+
clearDefinedResources: () => {
|
|
896
|
+
this.definedResources = void 0;
|
|
897
|
+
},
|
|
898
|
+
clearDefinedResourceTemplates: () => {
|
|
899
|
+
this.definedResourceTemplates = void 0;
|
|
900
|
+
}
|
|
901
|
+
});
|
|
902
|
+
}
|
|
903
|
+
convertAgentsToTools(agentsConfig, definedConvertedTools) {
|
|
904
|
+
const agentTools = {};
|
|
905
|
+
if (!agentsConfig) {
|
|
906
|
+
return agentTools;
|
|
907
|
+
}
|
|
908
|
+
for (const agentKey in agentsConfig) {
|
|
909
|
+
const agent$1 = agentsConfig[agentKey];
|
|
910
|
+
if (!agent$1 || !(agent$1 instanceof agent.Agent)) {
|
|
911
|
+
this.logger.warn(`Agent instance for '${agentKey}' is invalid or missing a generate function. Skipping.`);
|
|
912
|
+
continue;
|
|
913
|
+
}
|
|
914
|
+
const agentDescription = agent$1.getDescription();
|
|
915
|
+
if (!agentDescription) {
|
|
916
|
+
throw new Error(
|
|
917
|
+
`Agent '${agent$1.name}' (key: '${agentKey}') must have a non-empty description to be used in an MCPServer.`
|
|
918
|
+
);
|
|
919
|
+
}
|
|
920
|
+
const agentToolName = `ask_${agentKey}`;
|
|
921
|
+
if (definedConvertedTools?.[agentToolName] || agentTools[agentToolName]) {
|
|
922
|
+
this.logger.warn(
|
|
923
|
+
`Tool with name '${agentToolName}' already exists. Agent '${agentKey}' will not be added as a duplicate tool.`
|
|
924
|
+
);
|
|
925
|
+
continue;
|
|
926
|
+
}
|
|
927
|
+
const agentToolDefinition = core.createTool({
|
|
928
|
+
id: agentToolName,
|
|
929
|
+
description: `Ask agent '${agent$1.name}' a question. Agent description: ${agentDescription}`,
|
|
930
|
+
inputSchema: zod.z.object({
|
|
931
|
+
message: zod.z.string().describe("The question or input for the agent.")
|
|
932
|
+
}),
|
|
933
|
+
execute: async ({ context, runtimeContext }) => {
|
|
934
|
+
this.logger.debug(
|
|
935
|
+
`Executing agent tool '${agentToolName}' for agent '${agent$1.name}' with message: "${context.message}"`
|
|
936
|
+
);
|
|
937
|
+
try {
|
|
938
|
+
const response = await agent$1.generate(context.message, { runtimeContext });
|
|
939
|
+
return response;
|
|
940
|
+
} catch (error) {
|
|
941
|
+
this.logger.error(`Error executing agent tool '${agentToolName}' for agent '${agent$1.name}':`, error);
|
|
942
|
+
throw error;
|
|
943
|
+
}
|
|
944
|
+
}
|
|
945
|
+
});
|
|
946
|
+
const options = {
|
|
947
|
+
name: agentToolName,
|
|
948
|
+
logger: this.logger,
|
|
949
|
+
mastra: this.mastra,
|
|
950
|
+
runtimeContext: new runtimeContext.RuntimeContext(),
|
|
951
|
+
description: agentToolDefinition.description
|
|
952
|
+
};
|
|
953
|
+
const coreTool = core.makeCoreTool(agentToolDefinition, options);
|
|
954
|
+
agentTools[agentToolName] = {
|
|
955
|
+
name: agentToolName,
|
|
956
|
+
description: coreTool.description,
|
|
957
|
+
parameters: coreTool.parameters,
|
|
958
|
+
execute: coreTool.execute
|
|
959
|
+
};
|
|
960
|
+
this.logger.info(`Registered agent '${agent$1.name}' (key: '${agentKey}') as tool: '${agentToolName}'`);
|
|
961
|
+
}
|
|
962
|
+
return agentTools;
|
|
963
|
+
}
|
|
964
|
+
convertWorkflowsToTools(workflowsConfig, definedConvertedTools) {
|
|
965
|
+
const workflowTools = {};
|
|
966
|
+
if (!workflowsConfig) {
|
|
967
|
+
return workflowTools;
|
|
968
|
+
}
|
|
969
|
+
for (const workflowKey in workflowsConfig) {
|
|
970
|
+
const workflow = workflowsConfig[workflowKey];
|
|
971
|
+
if (!workflow || typeof workflow.createRun !== "function") {
|
|
972
|
+
this.logger.warn(
|
|
973
|
+
`Workflow instance for '${workflowKey}' is invalid or missing a createRun function. Skipping.`
|
|
974
|
+
);
|
|
975
|
+
continue;
|
|
976
|
+
}
|
|
977
|
+
const workflowDescription = workflow.description;
|
|
978
|
+
if (!workflowDescription) {
|
|
979
|
+
throw new Error(
|
|
980
|
+
`Workflow '${workflow.id}' (key: '${workflowKey}') must have a non-empty description to be used in an MCPServer.`
|
|
981
|
+
);
|
|
982
|
+
}
|
|
983
|
+
const workflowToolName = `run_${workflowKey}`;
|
|
984
|
+
if (definedConvertedTools?.[workflowToolName] || workflowTools[workflowToolName]) {
|
|
985
|
+
this.logger.warn(
|
|
986
|
+
`Tool with name '${workflowToolName}' already exists. Workflow '${workflowKey}' will not be added as a duplicate tool.`
|
|
987
|
+
);
|
|
988
|
+
continue;
|
|
989
|
+
}
|
|
990
|
+
const workflowToolDefinition = core.createTool({
|
|
991
|
+
id: workflowToolName,
|
|
992
|
+
description: `Run workflow '${workflowKey}'. Workflow description: ${workflowDescription}`,
|
|
993
|
+
inputSchema: workflow.inputSchema,
|
|
994
|
+
execute: async ({ context, runtimeContext }) => {
|
|
995
|
+
this.logger.debug(
|
|
996
|
+
`Executing workflow tool '${workflowToolName}' for workflow '${workflow.id}' with input:`,
|
|
997
|
+
context
|
|
998
|
+
);
|
|
999
|
+
try {
|
|
1000
|
+
const run = workflow.createRun({ runId: runtimeContext?.get("runId") });
|
|
1001
|
+
const response = await run.start({ inputData: context, runtimeContext });
|
|
1002
|
+
return response;
|
|
1003
|
+
} catch (error) {
|
|
1004
|
+
this.logger.error(
|
|
1005
|
+
`Error executing workflow tool '${workflowToolName}' for workflow '${workflow.id}':`,
|
|
1006
|
+
error
|
|
1007
|
+
);
|
|
1008
|
+
throw error;
|
|
1009
|
+
}
|
|
1010
|
+
}
|
|
1011
|
+
});
|
|
1012
|
+
const options = {
|
|
1013
|
+
name: workflowToolName,
|
|
1014
|
+
logger: this.logger,
|
|
1015
|
+
mastra: this.mastra,
|
|
1016
|
+
runtimeContext: new runtimeContext.RuntimeContext(),
|
|
1017
|
+
description: workflowToolDefinition.description
|
|
1018
|
+
};
|
|
1019
|
+
const coreTool = core.makeCoreTool(workflowToolDefinition, options);
|
|
1020
|
+
workflowTools[workflowToolName] = {
|
|
1021
|
+
name: workflowToolName,
|
|
1022
|
+
description: coreTool.description,
|
|
1023
|
+
parameters: coreTool.parameters,
|
|
1024
|
+
execute: coreTool.execute
|
|
1025
|
+
};
|
|
1026
|
+
this.logger.info(`Registered workflow '${workflow.id}' (key: '${workflowKey}') as tool: '${workflowToolName}'`);
|
|
1027
|
+
}
|
|
1028
|
+
return workflowTools;
|
|
646
1029
|
}
|
|
647
1030
|
/**
|
|
648
1031
|
* Convert and validate all provided tools, logging registration status.
|
|
1032
|
+
* Also converts agents and workflows into tools.
|
|
649
1033
|
* @param tools Tool definitions
|
|
1034
|
+
* @param agentsConfig Agent definitions to be converted to tools, expected from MCPServerConfig
|
|
1035
|
+
* @param workflowsConfig Workflow definitions to be converted to tools, expected from MCPServerConfig
|
|
650
1036
|
* @returns Converted tools registry
|
|
651
1037
|
*/
|
|
652
|
-
convertTools(tools) {
|
|
653
|
-
const
|
|
1038
|
+
convertTools(tools, agentsConfig, workflowsConfig) {
|
|
1039
|
+
const definedConvertedTools = {};
|
|
654
1040
|
for (const toolName of Object.keys(tools)) {
|
|
655
1041
|
const toolInstance = tools[toolName];
|
|
656
1042
|
if (!toolInstance) {
|
|
@@ -669,16 +1055,26 @@ var MCPServer = class extends mcp.MCPServerBase {
|
|
|
669
1055
|
description: toolInstance?.description
|
|
670
1056
|
};
|
|
671
1057
|
const coreTool = core.makeCoreTool(toolInstance, options);
|
|
672
|
-
|
|
1058
|
+
definedConvertedTools[toolName] = {
|
|
673
1059
|
name: toolName,
|
|
674
1060
|
description: coreTool.description,
|
|
675
1061
|
parameters: coreTool.parameters,
|
|
676
1062
|
execute: coreTool.execute
|
|
677
1063
|
};
|
|
678
|
-
this.logger.info(`Registered tool: '${toolName}'
|
|
1064
|
+
this.logger.info(`Registered explicit tool: '${toolName}'`);
|
|
679
1065
|
}
|
|
680
|
-
this.logger.info(`Total tools registered: ${Object.keys(
|
|
681
|
-
|
|
1066
|
+
this.logger.info(`Total defined tools registered: ${Object.keys(definedConvertedTools).length}`);
|
|
1067
|
+
const agentDerivedTools = this.convertAgentsToTools(agentsConfig, definedConvertedTools);
|
|
1068
|
+
const workflowDerivedTools = this.convertWorkflowsToTools(workflowsConfig, definedConvertedTools);
|
|
1069
|
+
const allConvertedTools = { ...definedConvertedTools, ...agentDerivedTools, ...workflowDerivedTools };
|
|
1070
|
+
const finalToolCount = Object.keys(allConvertedTools).length;
|
|
1071
|
+
const definedCount = Object.keys(definedConvertedTools).length;
|
|
1072
|
+
const fromAgentsCount = Object.keys(agentDerivedTools).length;
|
|
1073
|
+
const fromWorkflowsCount = Object.keys(workflowDerivedTools).length;
|
|
1074
|
+
this.logger.info(
|
|
1075
|
+
`${finalToolCount} total tools registered (${definedCount} defined + ${fromAgentsCount} agents + ${fromWorkflowsCount} workflows)`
|
|
1076
|
+
);
|
|
1077
|
+
return allConvertedTools;
|
|
682
1078
|
}
|
|
683
1079
|
/**
|
|
684
1080
|
* Register the ListTools handler for listing all available tools.
|
|
@@ -774,6 +1170,159 @@ var MCPServer = class extends mcp.MCPServerBase {
|
|
|
774
1170
|
}
|
|
775
1171
|
});
|
|
776
1172
|
}
|
|
1173
|
+
/**
|
|
1174
|
+
* Register the ListResources handler for listing all available resources.
|
|
1175
|
+
*/
|
|
1176
|
+
registerListResourcesHandler() {
|
|
1177
|
+
if (this.listResourcesHandlerIsRegistered) {
|
|
1178
|
+
return;
|
|
1179
|
+
}
|
|
1180
|
+
this.listResourcesHandlerIsRegistered = true;
|
|
1181
|
+
const capturedResourceOptions = this.resourceOptions;
|
|
1182
|
+
if (!capturedResourceOptions?.listResources) {
|
|
1183
|
+
this.logger.warn("ListResources capability not supported by server configuration.");
|
|
1184
|
+
return;
|
|
1185
|
+
}
|
|
1186
|
+
this.server.setRequestHandler(types_js.ListResourcesRequestSchema, async () => {
|
|
1187
|
+
this.logger.debug("Handling ListResources request");
|
|
1188
|
+
if (this.definedResources) {
|
|
1189
|
+
return { resources: this.definedResources };
|
|
1190
|
+
} else {
|
|
1191
|
+
try {
|
|
1192
|
+
const resources = await capturedResourceOptions.listResources();
|
|
1193
|
+
this.definedResources = resources;
|
|
1194
|
+
this.logger.debug(`Fetched and cached ${this.definedResources.length} resources.`);
|
|
1195
|
+
return { resources: this.definedResources };
|
|
1196
|
+
} catch (error) {
|
|
1197
|
+
this.logger.error("Error fetching resources via listResources():", { error });
|
|
1198
|
+
throw error;
|
|
1199
|
+
}
|
|
1200
|
+
}
|
|
1201
|
+
});
|
|
1202
|
+
}
|
|
1203
|
+
/**
|
|
1204
|
+
* Register the ReadResource handler for reading a resource by URI.
|
|
1205
|
+
*/
|
|
1206
|
+
registerReadResourceHandler({
|
|
1207
|
+
getResourcesCallback
|
|
1208
|
+
}) {
|
|
1209
|
+
if (this.readResourceHandlerIsRegistered) {
|
|
1210
|
+
return;
|
|
1211
|
+
}
|
|
1212
|
+
this.readResourceHandlerIsRegistered = true;
|
|
1213
|
+
this.server.setRequestHandler(types_js.ReadResourceRequestSchema, async (request) => {
|
|
1214
|
+
const startTime = Date.now();
|
|
1215
|
+
const uri = request.params.uri;
|
|
1216
|
+
this.logger.debug(`Handling ReadResource request for URI: ${uri}`);
|
|
1217
|
+
if (!this.definedResources) {
|
|
1218
|
+
const resources = await this.resourceOptions?.listResources?.();
|
|
1219
|
+
if (!resources) throw new Error("Failed to load resources");
|
|
1220
|
+
this.definedResources = resources;
|
|
1221
|
+
}
|
|
1222
|
+
const resource = this.definedResources?.find((r) => r.uri === uri);
|
|
1223
|
+
if (!resource) {
|
|
1224
|
+
this.logger.warn(`ReadResource: Unknown resource URI '${uri}' requested.`);
|
|
1225
|
+
throw new Error(`Resource not found: ${uri}`);
|
|
1226
|
+
}
|
|
1227
|
+
try {
|
|
1228
|
+
const resourcesOrResourceContent = await getResourcesCallback({ uri });
|
|
1229
|
+
const resourcesContent = Array.isArray(resourcesOrResourceContent) ? resourcesOrResourceContent : [resourcesOrResourceContent];
|
|
1230
|
+
const contents = resourcesContent.map((resourceContent) => {
|
|
1231
|
+
const contentItem = {
|
|
1232
|
+
uri: resource.uri,
|
|
1233
|
+
mimeType: resource.mimeType
|
|
1234
|
+
};
|
|
1235
|
+
if ("text" in resourceContent) {
|
|
1236
|
+
contentItem.text = resourceContent.text;
|
|
1237
|
+
}
|
|
1238
|
+
if ("blob" in resourceContent) {
|
|
1239
|
+
contentItem.blob = resourceContent.blob;
|
|
1240
|
+
}
|
|
1241
|
+
return contentItem;
|
|
1242
|
+
});
|
|
1243
|
+
const duration = Date.now() - startTime;
|
|
1244
|
+
this.logger.info(`Resource '${uri}' read successfully in ${duration}ms.`);
|
|
1245
|
+
return {
|
|
1246
|
+
contents
|
|
1247
|
+
};
|
|
1248
|
+
} catch (error) {
|
|
1249
|
+
const duration = Date.now() - startTime;
|
|
1250
|
+
this.logger.error(`Failed to get content for resource URI '${uri}' in ${duration}ms`, { error });
|
|
1251
|
+
throw error;
|
|
1252
|
+
}
|
|
1253
|
+
});
|
|
1254
|
+
}
|
|
1255
|
+
/**
|
|
1256
|
+
* Register the ListResourceTemplates handler.
|
|
1257
|
+
*/
|
|
1258
|
+
registerListResourceTemplatesHandler() {
|
|
1259
|
+
if (this.listResourceTemplatesHandlerIsRegistered) {
|
|
1260
|
+
return;
|
|
1261
|
+
}
|
|
1262
|
+
if (!this.resourceOptions || typeof this.resourceOptions.resourceTemplates !== "function") {
|
|
1263
|
+
this.logger.warn(
|
|
1264
|
+
"ListResourceTemplates handler called, but resourceTemplates function is not available on resourceOptions or not a function."
|
|
1265
|
+
);
|
|
1266
|
+
this.server.setRequestHandler(types_js.ListResourceTemplatesRequestSchema, async () => {
|
|
1267
|
+
this.logger.debug("Handling ListResourceTemplates request (no templates configured or resourceOptions issue)");
|
|
1268
|
+
return { resourceTemplates: [] };
|
|
1269
|
+
});
|
|
1270
|
+
this.listResourceTemplatesHandlerIsRegistered = true;
|
|
1271
|
+
return;
|
|
1272
|
+
}
|
|
1273
|
+
const resourceTemplatesFn = this.resourceOptions.resourceTemplates;
|
|
1274
|
+
this.listResourceTemplatesHandlerIsRegistered = true;
|
|
1275
|
+
this.server.setRequestHandler(types_js.ListResourceTemplatesRequestSchema, async () => {
|
|
1276
|
+
this.logger.debug("Handling ListResourceTemplates request");
|
|
1277
|
+
if (this.definedResourceTemplates) {
|
|
1278
|
+
return { resourceTemplates: this.definedResourceTemplates };
|
|
1279
|
+
} else {
|
|
1280
|
+
try {
|
|
1281
|
+
const templates = await resourceTemplatesFn();
|
|
1282
|
+
this.definedResourceTemplates = templates;
|
|
1283
|
+
this.logger.debug(`Fetched and cached ${this.definedResourceTemplates.length} resource templates.`);
|
|
1284
|
+
return { resourceTemplates: this.definedResourceTemplates };
|
|
1285
|
+
} catch (error) {
|
|
1286
|
+
this.logger.error("Error fetching resource templates via resourceTemplates():", { error });
|
|
1287
|
+
throw error;
|
|
1288
|
+
}
|
|
1289
|
+
}
|
|
1290
|
+
});
|
|
1291
|
+
}
|
|
1292
|
+
/**
|
|
1293
|
+
* Register the SubscribeResource handler.
|
|
1294
|
+
*/
|
|
1295
|
+
registerSubscribeResourceHandler() {
|
|
1296
|
+
if (this.subscribeResourceHandlerIsRegistered) {
|
|
1297
|
+
return;
|
|
1298
|
+
}
|
|
1299
|
+
if (!types_js.SubscribeRequestSchema) {
|
|
1300
|
+
this.logger.warn("SubscribeRequestSchema not available, cannot register SubscribeResource handler.");
|
|
1301
|
+
return;
|
|
1302
|
+
}
|
|
1303
|
+
this.subscribeResourceHandlerIsRegistered = true;
|
|
1304
|
+
this.server.setRequestHandler(types_js.SubscribeRequestSchema, async (request) => {
|
|
1305
|
+
const uri = request.params.uri;
|
|
1306
|
+
this.logger.info(`Received resources/subscribe request for URI: ${uri}`);
|
|
1307
|
+
this.subscriptions.add(uri);
|
|
1308
|
+
return {};
|
|
1309
|
+
});
|
|
1310
|
+
}
|
|
1311
|
+
/**
|
|
1312
|
+
* Register the UnsubscribeResource handler.
|
|
1313
|
+
*/
|
|
1314
|
+
registerUnsubscribeResourceHandler() {
|
|
1315
|
+
if (this.unsubscribeResourceHandlerIsRegistered) {
|
|
1316
|
+
return;
|
|
1317
|
+
}
|
|
1318
|
+
this.unsubscribeResourceHandlerIsRegistered = true;
|
|
1319
|
+
this.server.setRequestHandler(types_js.UnsubscribeRequestSchema, async (request) => {
|
|
1320
|
+
const uri = request.params.uri;
|
|
1321
|
+
this.logger.info(`Received resources/unsubscribe request for URI: ${uri}`);
|
|
1322
|
+
this.subscriptions.delete(uri);
|
|
1323
|
+
return {};
|
|
1324
|
+
});
|
|
1325
|
+
}
|
|
777
1326
|
/**
|
|
778
1327
|
* Start the MCP server using stdio transport (for Windsurf integration).
|
|
779
1328
|
*/
|
|
@@ -940,6 +1489,11 @@ var MCPServer = class extends mcp.MCPServerBase {
|
|
|
940
1489
|
async close() {
|
|
941
1490
|
this.callToolHandlerIsRegistered = false;
|
|
942
1491
|
this.listToolsHandlerIsRegistered = false;
|
|
1492
|
+
this.listResourcesHandlerIsRegistered = false;
|
|
1493
|
+
this.readResourceHandlerIsRegistered = false;
|
|
1494
|
+
this.listResourceTemplatesHandlerIsRegistered = false;
|
|
1495
|
+
this.subscribeResourceHandlerIsRegistered = false;
|
|
1496
|
+
this.unsubscribeResourceHandlerIsRegistered = false;
|
|
943
1497
|
try {
|
|
944
1498
|
if (this.stdioTransport) {
|
|
945
1499
|
await this.stdioTransport.close?.();
|