@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.
- package/dist/__tests__/graph-tools.test.js +82 -0
- package/dist/graph-tools.js +23 -4
- package/dist/server.js +2 -1
- package/package.json +1 -1
|
@@ -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
|
});
|
package/dist/graph-tools.js
CHANGED
|
@@ -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
|
-
|
|
636
|
-
|
|
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
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@softeria/ms-365-mcp-server",
|
|
3
|
-
"version": "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",
|