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