@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.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
- // 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"
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
- * Get all resources from connected MCP servers
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.addToInstanceCache();
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.server = new index_js.Server(
637
- { name: this.name, version: this.version },
638
- { capabilities: { tools: {}, logging: { enabled: true } } }
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 convertedTools = {};
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
- convertedTools[toolName] = {
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}' [${toolInstance?.description || "No description"}]`);
1064
+ this.logger.info(`Registered explicit tool: '${toolName}'`);
679
1065
  }
680
- this.logger.info(`Total tools registered: ${Object.keys(convertedTools).length}`);
681
- return convertedTools;
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?.();