@softeria/ms-365-mcp-server 0.97.0 → 0.98.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -643,4 +643,86 @@ describe("graph-tools", () => {
643
643
  expect(payload.error).toMatch(/not found/i);
644
644
  });
645
645
  });
646
+ describe("discovery mode: --enabled-tools filter", () => {
647
+ it("search-tools only surfaces Graph tools matching the regex", async () => {
648
+ mockEndpoints.push(
649
+ {
650
+ alias: "list-mail-messages",
651
+ method: "get",
652
+ path: "/me/messages",
653
+ description: "List mail",
654
+ parameters: []
655
+ },
656
+ {
657
+ alias: "list-calendar-events",
658
+ method: "get",
659
+ path: "/me/events",
660
+ description: "List events",
661
+ parameters: []
662
+ }
663
+ );
664
+ mockEndpointsJson = [
665
+ { toolName: "list-mail-messages", method: "get", pathPattern: "/me/messages" },
666
+ { toolName: "list-calendar-events", method: "get", pathPattern: "/me/events" }
667
+ ];
668
+ const server = createMockServer();
669
+ const { registerDiscoveryTools } = await loadModule();
670
+ registerDiscoveryTools(server, {}, false, false, void 0, false, [], "mail");
671
+ const result = await server.tools.get("search-tools").handler({ limit: 50 });
672
+ const found = JSON.parse(result.content[0].text).tools.map((t) => t.name);
673
+ expect(found).toContain("list-mail-messages");
674
+ expect(found).not.toContain("list-calendar-events");
675
+ });
676
+ it("utility tools obey the regex too", async () => {
677
+ mockEndpoints.length = 0;
678
+ mockEndpointsJson = [];
679
+ const server = createMockServer();
680
+ const { registerDiscoveryTools } = await loadModule();
681
+ registerDiscoveryTools(
682
+ server,
683
+ {},
684
+ false,
685
+ false,
686
+ void 0,
687
+ false,
688
+ [],
689
+ "^download-bytes$"
690
+ );
691
+ const result = await server.tools.get("search-tools").handler({ limit: 50 });
692
+ const found = JSON.parse(result.content[0].text).tools.map((t) => t.name);
693
+ expect(found).toContain("download-bytes");
694
+ expect(found).not.toContain("parse-teams-url");
695
+ });
696
+ it("invalid regex pattern is ignored, all tools surface", async () => {
697
+ mockEndpoints.length = 0;
698
+ mockEndpointsJson = [];
699
+ const server = createMockServer();
700
+ const { registerDiscoveryTools } = await loadModule();
701
+ registerDiscoveryTools(
702
+ server,
703
+ {},
704
+ false,
705
+ false,
706
+ void 0,
707
+ false,
708
+ [],
709
+ "[invalid"
710
+ );
711
+ const result = await server.tools.get("search-tools").handler({ limit: 50 });
712
+ const found = JSON.parse(result.content[0].text).tools.map((t) => t.name);
713
+ expect(found).toContain("download-bytes");
714
+ expect(found).toContain("parse-teams-url");
715
+ });
716
+ });
717
+ describe("utility tools in read-only mode", () => {
718
+ it("skips utility tools whose readOnlyHint is not true", async () => {
719
+ mockEndpoints.length = 0;
720
+ mockEndpointsJson = [];
721
+ const server = createMockServer();
722
+ const { registerGraphTools } = await loadModule();
723
+ registerGraphTools(server, {}, true);
724
+ expect(server.tools.has("download-bytes")).toBe(true);
725
+ expect(server.tools.has("parse-teams-url")).toBe(true);
726
+ });
727
+ });
646
728
  });
@@ -543,6 +543,7 @@ function registerGraphTools(server, graphClient, readOnly = false, enabledToolsP
543
543
  accountNames
544
544
  };
545
545
  for (const utility of UTILITY_TOOLS) {
546
+ if (readOnly && !utility.readOnlyHint) continue;
546
547
  if (enabledToolsRegex && !enabledToolsRegex.test(utility.name)) continue;
547
548
  try {
548
549
  registerUtilityToolWithMcp(server, utility, utilityCtx);
@@ -557,7 +558,7 @@ function registerGraphTools(server, graphClient, readOnly = false, enabledToolsP
557
558
  );
558
559
  return registeredCount;
559
560
  }
560
- function buildToolsRegistry(readOnly, orgMode) {
561
+ function buildToolsRegistry(readOnly, orgMode, enabledToolsRegex) {
561
562
  const toolsMap = /* @__PURE__ */ new Map();
562
563
  for (const tool of api.endpoints) {
563
564
  const endpointConfig = endpointsData.find((e) => e.toolName === tool.alias);
@@ -570,6 +571,9 @@ function buildToolsRegistry(readOnly, orgMode) {
570
571
  continue;
571
572
  }
572
573
  }
574
+ if (enabledToolsRegex && !enabledToolsRegex.test(tool.alias)) {
575
+ continue;
576
+ }
573
577
  toolsMap.set(tool.alias, { tool, config: endpointConfig });
574
578
  }
575
579
  return toolsMap;
@@ -631,9 +635,24 @@ function scoreDiscoveryQuery(query, index) {
631
635
  ranked.sort((a, b) => b.score - a.score);
632
636
  return ranked;
633
637
  }
634
- function registerDiscoveryTools(server, graphClient, readOnly = false, orgMode = false, authManager, multiAccount = false, accountNames = []) {
635
- const toolsRegistry = buildToolsRegistry(readOnly, orgMode);
636
- const utilityTools = UTILITY_TOOLS;
638
+ function registerDiscoveryTools(server, graphClient, readOnly = false, orgMode = false, authManager, multiAccount = false, accountNames = [], enabledTools) {
639
+ let enabledToolsRegex;
640
+ if (enabledTools) {
641
+ try {
642
+ enabledToolsRegex = new RegExp(enabledTools, "i");
643
+ logger.info(`Discovery mode: filtering tools with pattern ${enabledTools}`);
644
+ } catch (error) {
645
+ logger.error(
646
+ `Invalid --enabled-tools regex ${JSON.stringify(enabledTools)} \u2014 ignoring filter: ${error.message}`
647
+ );
648
+ }
649
+ }
650
+ const toolsRegistry = buildToolsRegistry(readOnly, orgMode, enabledToolsRegex);
651
+ const utilityTools = UTILITY_TOOLS.filter((u) => {
652
+ if (readOnly && !u.readOnlyHint) return false;
653
+ if (enabledToolsRegex && !enabledToolsRegex.test(u.name)) return false;
654
+ return true;
655
+ });
637
656
  const searchIndex = buildDiscoverySearchIndex(toolsRegistry, utilityTools);
638
657
  const totalCount = toolsRegistry.size + utilityTools.length;
639
658
  logger.info(
package/dist/server.js CHANGED
@@ -75,7 +75,8 @@ class MicrosoftGraphServer {
75
75
  this.options.orgMode,
76
76
  this.authManager,
77
77
  this.multiAccount,
78
- this.accountNames
78
+ this.accountNames,
79
+ this.options.enabledTools
79
80
  );
80
81
  } else {
81
82
  registerGraphTools(
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@softeria/ms-365-mcp-server",
3
- "version": "0.97.0",
3
+ "version": "0.98.0",
4
4
  "description": " A Model Context Protocol (MCP) server for interacting with Microsoft 365 and Office services through the Graph API",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",