@softeria/ms-365-mcp-server 0.154.0 → 0.154.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/__tests__/graph-tools.test.js +89 -0
- package/dist/generated/client.js +16 -16
- package/dist/graph-tools.js +35 -47
- package/dist/lib/query-parameter-schema.js +33 -0
- package/dist/lib/tool-schema.js +14 -16
- package/package.json +1 -1
|
@@ -1068,6 +1068,95 @@ describe("graph-tools", () => {
|
|
|
1068
1068
|
expect(payload).not.toHaveProperty("target_resource");
|
|
1069
1069
|
});
|
|
1070
1070
|
});
|
|
1071
|
+
describe("shared query contract at execution", () => {
|
|
1072
|
+
it.each([{ skiptoken: 123 }, { $SKIPTOKEN: 123 }, { COUNT: "true" }, { $Count: "true" }])(
|
|
1073
|
+
"rejects invalid cursor and mixed-case query values: %j",
|
|
1074
|
+
async (params) => {
|
|
1075
|
+
mockEndpoints.push(makeEndpoint());
|
|
1076
|
+
mockEndpointsJson = [makeConfig()];
|
|
1077
|
+
const graphClient = createMockGraphClient();
|
|
1078
|
+
const server = createMockServer();
|
|
1079
|
+
const { registerGraphTools } = await loadModule();
|
|
1080
|
+
registerGraphTools(server, graphClient);
|
|
1081
|
+
const result = await server.tools.get("test-tool").handler(params);
|
|
1082
|
+
expect(result.isError).toBe(true);
|
|
1083
|
+
expect(graphClient.graphRequest).not.toHaveBeenCalled();
|
|
1084
|
+
}
|
|
1085
|
+
);
|
|
1086
|
+
it("accepts valid mixed-case query values and string cursors", async () => {
|
|
1087
|
+
mockEndpoints.push(makeEndpoint());
|
|
1088
|
+
mockEndpointsJson = [makeConfig()];
|
|
1089
|
+
const graphClient = createMockGraphClient();
|
|
1090
|
+
const server = createMockServer();
|
|
1091
|
+
const { registerGraphTools } = await loadModule();
|
|
1092
|
+
registerGraphTools(server, graphClient);
|
|
1093
|
+
await server.tools.get("test-tool").handler({ COUNT: true, $SKIPTOKEN: "next-token" });
|
|
1094
|
+
expect(graphClient.graphRequest).toHaveBeenCalledTimes(1);
|
|
1095
|
+
const request = JSON.stringify(graphClient.graphRequest.mock.calls[0]);
|
|
1096
|
+
expect(request).toContain("$count=true");
|
|
1097
|
+
expect(request).toContain("$skiptoken=next-token");
|
|
1098
|
+
});
|
|
1099
|
+
it.each(["list-calendar-events-delta", "list-calendar-view-delta"])(
|
|
1100
|
+
"ignores stale mixed-case top values for %s",
|
|
1101
|
+
async (alias) => {
|
|
1102
|
+
mockEndpoints.push(makeEndpoint({ alias }));
|
|
1103
|
+
mockEndpointsJson = [makeConfig({ toolName: alias })];
|
|
1104
|
+
const graphClient = createMockGraphClient();
|
|
1105
|
+
const server = createMockServer();
|
|
1106
|
+
const { registerGraphTools } = await loadModule();
|
|
1107
|
+
registerGraphTools(server, graphClient);
|
|
1108
|
+
for (const key of ["top", "$top", "TOP", "$TOP", "$Top"]) {
|
|
1109
|
+
await server.tools.get(alias).handler({ [key]: 10 });
|
|
1110
|
+
}
|
|
1111
|
+
expect(graphClient.graphRequest).toHaveBeenCalledTimes(5);
|
|
1112
|
+
for (const call of graphClient.graphRequest.mock.calls) {
|
|
1113
|
+
expect(JSON.stringify(call)).not.toContain("$top");
|
|
1114
|
+
}
|
|
1115
|
+
}
|
|
1116
|
+
);
|
|
1117
|
+
it.each(["list-joined-teams", "list-my-associated-teams"])(
|
|
1118
|
+
"rejects unsupported query options before Graph dispatch for %s",
|
|
1119
|
+
async (alias) => {
|
|
1120
|
+
mockEndpoints.push(makeEndpoint({ alias }));
|
|
1121
|
+
mockEndpointsJson = [makeConfig({ toolName: alias })];
|
|
1122
|
+
const graphClient = createMockGraphClient();
|
|
1123
|
+
const server = createMockServer();
|
|
1124
|
+
const { registerGraphTools } = await loadModule();
|
|
1125
|
+
registerGraphTools(server, graphClient);
|
|
1126
|
+
for (const key of ["top", "$top", "skiptoken", "$skiptoken", "select", "$select"]) {
|
|
1127
|
+
const value = key.replace("$", "") === "top" ? 100 : "next-token";
|
|
1128
|
+
const result = await server.tools.get(alias).handler({ [key]: value });
|
|
1129
|
+
expect(result.isError).toBe(true);
|
|
1130
|
+
}
|
|
1131
|
+
expect(graphClient.graphRequest).not.toHaveBeenCalled();
|
|
1132
|
+
}
|
|
1133
|
+
);
|
|
1134
|
+
it("rejects oversized chat pages before Graph dispatch, including dollar-prefixed input", async () => {
|
|
1135
|
+
mockEndpoints.push(makeEndpoint({ alias: "list-chats" }));
|
|
1136
|
+
mockEndpointsJson = [makeConfig({ toolName: "list-chats" })];
|
|
1137
|
+
const graphClient = createMockGraphClient();
|
|
1138
|
+
const server = createMockServer();
|
|
1139
|
+
const { registerGraphTools } = await loadModule();
|
|
1140
|
+
registerGraphTools(server, graphClient);
|
|
1141
|
+
for (const key of ["top", "$top"]) {
|
|
1142
|
+
const result = await server.tools.get("list-chats").handler({ [key]: 100 });
|
|
1143
|
+
expect(result.isError).toBe(true);
|
|
1144
|
+
}
|
|
1145
|
+
expect(graphClient.graphRequest).not.toHaveBeenCalled();
|
|
1146
|
+
});
|
|
1147
|
+
it("sends a calendar field array as comma-separated text in one Graph request", async () => {
|
|
1148
|
+
const alias = "list-calendar-events-delta";
|
|
1149
|
+
mockEndpoints.push(makeEndpoint({ alias }));
|
|
1150
|
+
mockEndpointsJson = [makeConfig({ toolName: alias })];
|
|
1151
|
+
const graphClient = createMockGraphClient();
|
|
1152
|
+
const server = createMockServer();
|
|
1153
|
+
const { registerGraphTools } = await loadModule();
|
|
1154
|
+
registerGraphTools(server, graphClient);
|
|
1155
|
+
await server.tools.get(alias).handler({ select: ["id", "subject"] });
|
|
1156
|
+
expect(graphClient.graphRequest).toHaveBeenCalledTimes(1);
|
|
1157
|
+
expect(JSON.stringify(graphClient.graphRequest.mock.calls[0])).toContain("id,subject");
|
|
1158
|
+
});
|
|
1159
|
+
});
|
|
1071
1160
|
describe("skiptoken cursor", () => {
|
|
1072
1161
|
const prevAllowPagination = process.env.MS365_MCP_ALLOW_PAGINATION;
|
|
1073
1162
|
afterEach(() => {
|
package/dist/generated/client.js
CHANGED
|
@@ -846,8 +846,8 @@ const microsoft_graph_driveItem = z.object({
|
|
|
846
846
|
webUrl: z.string().describe(
|
|
847
847
|
"URL that either displays the resource in the browser (for Office file formats), or is a direct link to the file (for other formats). Read-only."
|
|
848
848
|
).nullish(),
|
|
849
|
-
createdByUser: microsoft_graph_user.describe("[Note: Simplified from
|
|
850
|
-
lastModifiedByUser: microsoft_graph_user.describe("[Note: Simplified from
|
|
849
|
+
createdByUser: microsoft_graph_user.describe("[Note: Simplified from 138 properties to 25 most common ones]").optional(),
|
|
850
|
+
lastModifiedByUser: microsoft_graph_user.describe("[Note: Simplified from 138 properties to 25 most common ones]").optional(),
|
|
851
851
|
audio: microsoft_graph_audio.optional(),
|
|
852
852
|
bundle: microsoft_graph_bundle.optional(),
|
|
853
853
|
cTag: z.string().describe(
|
|
@@ -2586,8 +2586,8 @@ const microsoft_graph_listItem = z.object({
|
|
|
2586
2586
|
webUrl: z.string().describe(
|
|
2587
2587
|
"URL that either displays the resource in the browser (for Office file formats), or is a direct link to the file (for other formats). Read-only."
|
|
2588
2588
|
).nullish(),
|
|
2589
|
-
createdByUser: microsoft_graph_user.describe("[Note: Simplified from
|
|
2590
|
-
lastModifiedByUser: microsoft_graph_user.describe("[Note: Simplified from
|
|
2589
|
+
createdByUser: microsoft_graph_user.describe("[Note: Simplified from 138 properties to 25 most common ones]").optional(),
|
|
2590
|
+
lastModifiedByUser: microsoft_graph_user.describe("[Note: Simplified from 138 properties to 25 most common ones]").optional(),
|
|
2591
2591
|
contentType: microsoft_graph_contentTypeInfo.optional(),
|
|
2592
2592
|
deleted: microsoft_graph_deleted.optional(),
|
|
2593
2593
|
sharepointIds: microsoft_graph_sharepointIds.optional(),
|
|
@@ -2708,8 +2708,8 @@ const microsoft_graph_list = z.lazy(
|
|
|
2708
2708
|
webUrl: z.string().describe(
|
|
2709
2709
|
"URL that either displays the resource in the browser (for Office file formats), or is a direct link to the file (for other formats). Read-only."
|
|
2710
2710
|
).nullish(),
|
|
2711
|
-
createdByUser: microsoft_graph_user.describe("[Note: Simplified from
|
|
2712
|
-
lastModifiedByUser: microsoft_graph_user.describe("[Note: Simplified from
|
|
2711
|
+
createdByUser: microsoft_graph_user.describe("[Note: Simplified from 138 properties to 25 most common ones]").optional(),
|
|
2712
|
+
lastModifiedByUser: microsoft_graph_user.describe("[Note: Simplified from 138 properties to 25 most common ones]").optional(),
|
|
2713
2713
|
displayName: z.string().describe("The displayable title of the list.").nullish(),
|
|
2714
2714
|
list: microsoft_graph_listInfo.optional(),
|
|
2715
2715
|
sharepointIds: microsoft_graph_sharepointIds.optional(),
|
|
@@ -2741,8 +2741,8 @@ const microsoft_graph_drive = z.lazy(
|
|
|
2741
2741
|
webUrl: z.string().describe(
|
|
2742
2742
|
"URL that either displays the resource in the browser (for Office file formats), or is a direct link to the file (for other formats). Read-only."
|
|
2743
2743
|
).nullish(),
|
|
2744
|
-
createdByUser: microsoft_graph_user.describe("[Note: Simplified from
|
|
2745
|
-
lastModifiedByUser: microsoft_graph_user.describe("[Note: Simplified from
|
|
2744
|
+
createdByUser: microsoft_graph_user.describe("[Note: Simplified from 138 properties to 25 most common ones]").optional(),
|
|
2745
|
+
lastModifiedByUser: microsoft_graph_user.describe("[Note: Simplified from 138 properties to 25 most common ones]").optional(),
|
|
2746
2746
|
driveType: z.string().describe(
|
|
2747
2747
|
"Describes the type of drive represented by this resource. OneDrive personal drives return personal. OneDrive for Business returns business. SharePoint document libraries return documentLibrary. Read-only."
|
|
2748
2748
|
).nullish(),
|
|
@@ -4558,8 +4558,8 @@ const microsoft_graph_baseItem = z.object({
|
|
|
4558
4558
|
webUrl: z.string().describe(
|
|
4559
4559
|
"URL that either displays the resource in the browser (for Office file formats), or is a direct link to the file (for other formats). Read-only."
|
|
4560
4560
|
).nullish(),
|
|
4561
|
-
createdByUser: microsoft_graph_user.describe("[Note: Simplified from
|
|
4562
|
-
lastModifiedByUser: microsoft_graph_user.describe("[Note: Simplified from
|
|
4561
|
+
createdByUser: microsoft_graph_user.describe("[Note: Simplified from 138 properties to 25 most common ones]").optional(),
|
|
4562
|
+
lastModifiedByUser: microsoft_graph_user.describe("[Note: Simplified from 138 properties to 25 most common ones]").optional()
|
|
4563
4563
|
}).passthrough();
|
|
4564
4564
|
const microsoft_graph_site = z.object({
|
|
4565
4565
|
id: z.string().describe("The unique identifier for an entity. Read-only.").optional(),
|
|
@@ -4580,8 +4580,8 @@ const microsoft_graph_site = z.object({
|
|
|
4580
4580
|
webUrl: z.string().describe(
|
|
4581
4581
|
"URL that either displays the resource in the browser (for Office file formats), or is a direct link to the file (for other formats). Read-only."
|
|
4582
4582
|
).nullish(),
|
|
4583
|
-
createdByUser: microsoft_graph_user.describe("[Note: Simplified from
|
|
4584
|
-
lastModifiedByUser: microsoft_graph_user.describe("[Note: Simplified from
|
|
4583
|
+
createdByUser: microsoft_graph_user.describe("[Note: Simplified from 138 properties to 25 most common ones]").optional(),
|
|
4584
|
+
lastModifiedByUser: microsoft_graph_user.describe("[Note: Simplified from 138 properties to 25 most common ones]").optional(),
|
|
4585
4585
|
isPersonalSite: z.boolean().describe("Identifies whether the site is personal or not. Read-only.").nullish(),
|
|
4586
4586
|
root: microsoft_graph_root.optional(),
|
|
4587
4587
|
sharepointIds: microsoft_graph_sharepointIds.optional(),
|
|
@@ -5880,8 +5880,8 @@ const endpoints = makeApi([
|
|
|
5880
5880
|
webUrl: z.string().describe(
|
|
5881
5881
|
"URL that either displays the resource in the browser (for Office file formats), or is a direct link to the file (for other formats). Read-only."
|
|
5882
5882
|
).nullish(),
|
|
5883
|
-
createdByUser: microsoft_graph_user.describe("[Note: Simplified from
|
|
5884
|
-
lastModifiedByUser: microsoft_graph_user.describe("[Note: Simplified from
|
|
5883
|
+
createdByUser: microsoft_graph_user.describe("[Note: Simplified from 138 properties to 25 most common ones]").optional(),
|
|
5884
|
+
lastModifiedByUser: microsoft_graph_user.describe("[Note: Simplified from 138 properties to 25 most common ones]").optional(),
|
|
5885
5885
|
audio: microsoft_graph_audio.optional(),
|
|
5886
5886
|
bundle: microsoft_graph_bundle.optional(),
|
|
5887
5887
|
cTag: z.string().describe(
|
|
@@ -5995,8 +5995,8 @@ const endpoints = makeApi([
|
|
|
5995
5995
|
webUrl: z.string().describe(
|
|
5996
5996
|
"URL that either displays the resource in the browser (for Office file formats), or is a direct link to the file (for other formats). Read-only."
|
|
5997
5997
|
).nullish(),
|
|
5998
|
-
createdByUser: microsoft_graph_user.describe("[Note: Simplified from
|
|
5999
|
-
lastModifiedByUser: microsoft_graph_user.describe("[Note: Simplified from
|
|
5998
|
+
createdByUser: microsoft_graph_user.describe("[Note: Simplified from 138 properties to 25 most common ones]").optional(),
|
|
5999
|
+
lastModifiedByUser: microsoft_graph_user.describe("[Note: Simplified from 138 properties to 25 most common ones]").optional(),
|
|
6000
6000
|
audio: microsoft_graph_audio.optional(),
|
|
6001
6001
|
bundle: microsoft_graph_bundle.optional(),
|
|
6002
6002
|
cTag: z.string().describe(
|
package/dist/graph-tools.js
CHANGED
|
@@ -35,6 +35,7 @@ import { parseTeamsUrl } from "./lib/teams-url-parser.js";
|
|
|
35
35
|
import { buildBM25Index, scoreQuery, tokenize } from "./lib/bm25.js";
|
|
36
36
|
import { deriveTargetResource } from "./audit-target-resource.js";
|
|
37
37
|
import { describeToolSchema, describeUtilityToolSchema } from "./lib/tool-schema.js";
|
|
38
|
+
import { queryParameterSchema } from "./lib/query-parameter-schema.js";
|
|
38
39
|
import {
|
|
39
40
|
TOP_UNSUPPORTED_DELTA_TOOLS,
|
|
40
41
|
shouldOmitTopParam,
|
|
@@ -43,14 +44,6 @@ import {
|
|
|
43
44
|
DEFAULT_MAX_PAGES,
|
|
44
45
|
getMaxPages,
|
|
45
46
|
isFetchAllPagesApplicable,
|
|
46
|
-
FILTER_PARAM_DESCRIPTION,
|
|
47
|
-
SEARCH_PARAM_DESCRIPTION,
|
|
48
|
-
SELECT_PARAM_DESCRIPTION,
|
|
49
|
-
EXPAND_PARAM_DESCRIPTION,
|
|
50
|
-
ORDERBY_PARAM_DESCRIPTION,
|
|
51
|
-
TOP_PARAM_DESCRIPTION,
|
|
52
|
-
SKIP_PARAM_DESCRIPTION,
|
|
53
|
-
COUNT_PARAM_DESCRIPTION,
|
|
54
47
|
CONFIRM_PARAM_DESCRIPTION,
|
|
55
48
|
TIMEZONE_PARAM_DESCRIPTION,
|
|
56
49
|
EXPAND_EXTENDED_PROPERTIES_PARAM_DESCRIPTION,
|
|
@@ -1195,13 +1188,38 @@ async function executeGraphTool(tool, config, graphClient, params, authManager)
|
|
|
1195
1188
|
"search",
|
|
1196
1189
|
"format"
|
|
1197
1190
|
];
|
|
1198
|
-
const
|
|
1199
|
-
const isOdataParam = odataParams.includes(
|
|
1200
|
-
const
|
|
1191
|
+
const bareParamName = paramName.startsWith("$") ? paramName.slice(1) : paramName;
|
|
1192
|
+
const isOdataParam = odataParams.includes(bareParamName.toLowerCase());
|
|
1193
|
+
const normalizedParamName = isOdataParam ? bareParamName.toLowerCase() : bareParamName;
|
|
1194
|
+
const fixedParamName = isOdataParam ? `$${normalizedParamName}` : paramName;
|
|
1201
1195
|
const camelCaseParamName = paramName.replace(/-([a-z])/g, (_, c) => c.toUpperCase());
|
|
1202
1196
|
const paramDef = parameterDefinitions.find(
|
|
1203
|
-
(p) => p.name === paramName || p.name === camelCaseParamName || isOdataParam && p.name === normalizedParamName
|
|
1197
|
+
(p) => p.name === paramName || p.name === camelCaseParamName || isOdataParam && p.name.replace(/^\$/, "").toLowerCase() === normalizedParamName
|
|
1204
1198
|
);
|
|
1199
|
+
const isQuery = paramDef?.type === "Query" || isOdataParam;
|
|
1200
|
+
const ignoredDeltaTop = shouldOmitTopParam(tool.alias) && normalizedParamName === "top";
|
|
1201
|
+
if (isQuery && !ignoredDeltaTop && paramValue != null && paramValue !== "") {
|
|
1202
|
+
const schema = queryParameterSchema(
|
|
1203
|
+
tool.alias,
|
|
1204
|
+
normalizedParamName,
|
|
1205
|
+
paramDef?.schema ?? z.any()
|
|
1206
|
+
);
|
|
1207
|
+
if (!schema || !schema.safeParse(paramValue).success) {
|
|
1208
|
+
return {
|
|
1209
|
+
content: [
|
|
1210
|
+
{
|
|
1211
|
+
type: "text",
|
|
1212
|
+
text: JSON.stringify({
|
|
1213
|
+
error: "invalid_query_parameter",
|
|
1214
|
+
parameter: fixedParamName,
|
|
1215
|
+
message: schema ? "Value does not match the query contract. Check get-tool-schema." : "This endpoint does not support this query parameter."
|
|
1216
|
+
})
|
|
1217
|
+
}
|
|
1218
|
+
],
|
|
1219
|
+
isError: true
|
|
1220
|
+
};
|
|
1221
|
+
}
|
|
1222
|
+
}
|
|
1205
1223
|
if (paramDef) {
|
|
1206
1224
|
switch (paramDef.type) {
|
|
1207
1225
|
case "Path": {
|
|
@@ -1632,6 +1650,11 @@ function registerGraphTools(server, graphClient, readOnly = false, enabledToolsP
|
|
|
1632
1650
|
const paramSchema = {};
|
|
1633
1651
|
if (tool.parameters && tool.parameters.length > 0) {
|
|
1634
1652
|
for (const param of tool.parameters) {
|
|
1653
|
+
if (param.type === "Query") {
|
|
1654
|
+
const schema = queryParameterSchema(tool.alias, param.name, param.schema || z.any());
|
|
1655
|
+
if (schema) paramSchema[param.name] = schema;
|
|
1656
|
+
continue;
|
|
1657
|
+
}
|
|
1635
1658
|
paramSchema[param.name] = param.type === "Body" && param.schema ? lenientBodySchema(param.schema) : param.schema || z.any();
|
|
1636
1659
|
}
|
|
1637
1660
|
}
|
|
@@ -1652,41 +1675,6 @@ function registerGraphTools(server, graphClient, readOnly = false, enabledToolsP
|
|
|
1652
1675
|
if (isSkiptokenApplicable(tool, Object.keys(paramSchema))) {
|
|
1653
1676
|
paramSchema["skiptoken"] = z.string().describe(SKIPTOKEN_PARAM_DESCRIPTION).optional();
|
|
1654
1677
|
}
|
|
1655
|
-
if (paramSchema["filter"] !== void 0 || paramSchema["$filter"] !== void 0) {
|
|
1656
|
-
const key = paramSchema["$filter"] !== void 0 ? "$filter" : "filter";
|
|
1657
|
-
paramSchema[key] = z.string().describe(FILTER_PARAM_DESCRIPTION).optional();
|
|
1658
|
-
}
|
|
1659
|
-
if (paramSchema["search"] !== void 0 || paramSchema["$search"] !== void 0) {
|
|
1660
|
-
const key = paramSchema["$search"] !== void 0 ? "$search" : "search";
|
|
1661
|
-
paramSchema[key] = z.string().describe(SEARCH_PARAM_DESCRIPTION).optional();
|
|
1662
|
-
}
|
|
1663
|
-
if (paramSchema["select"] !== void 0 || paramSchema["$select"] !== void 0) {
|
|
1664
|
-
const key = paramSchema["$select"] !== void 0 ? "$select" : "select";
|
|
1665
|
-
paramSchema[key] = z.string().describe(SELECT_PARAM_DESCRIPTION).optional();
|
|
1666
|
-
}
|
|
1667
|
-
if (paramSchema["expand"] !== void 0 || paramSchema["$expand"] !== void 0) {
|
|
1668
|
-
const key = paramSchema["$expand"] !== void 0 ? "$expand" : "expand";
|
|
1669
|
-
paramSchema[key] = z.array(z.string()).describe(EXPAND_PARAM_DESCRIPTION).optional();
|
|
1670
|
-
}
|
|
1671
|
-
if (paramSchema["orderby"] !== void 0 || paramSchema["$orderby"] !== void 0) {
|
|
1672
|
-
const key = paramSchema["$orderby"] !== void 0 ? "$orderby" : "orderby";
|
|
1673
|
-
paramSchema[key] = z.string().describe(ORDERBY_PARAM_DESCRIPTION).optional();
|
|
1674
|
-
}
|
|
1675
|
-
if (shouldOmitTopParam(tool.alias)) {
|
|
1676
|
-
delete paramSchema["top"];
|
|
1677
|
-
delete paramSchema["$top"];
|
|
1678
|
-
} else if (paramSchema["top"] !== void 0 || paramSchema["$top"] !== void 0) {
|
|
1679
|
-
const key = paramSchema["$top"] !== void 0 ? "$top" : "top";
|
|
1680
|
-
paramSchema[key] = z.number().describe(TOP_PARAM_DESCRIPTION).optional();
|
|
1681
|
-
}
|
|
1682
|
-
if (paramSchema["skip"] !== void 0 || paramSchema["$skip"] !== void 0) {
|
|
1683
|
-
const key = paramSchema["$skip"] !== void 0 ? "$skip" : "skip";
|
|
1684
|
-
paramSchema[key] = z.number().describe(SKIP_PARAM_DESCRIPTION).optional();
|
|
1685
|
-
}
|
|
1686
|
-
if (paramSchema["count"] !== void 0 || paramSchema["$count"] !== void 0) {
|
|
1687
|
-
const countKey = paramSchema["$count"] !== void 0 ? "$count" : "count";
|
|
1688
|
-
paramSchema[countKey] = z.boolean().describe(COUNT_PARAM_DESCRIPTION).optional();
|
|
1689
|
-
}
|
|
1690
1678
|
if (multiAccount) {
|
|
1691
1679
|
paramSchema["account"] = z.string().describe(getAccountParamDescription(accountNames)).optional();
|
|
1692
1680
|
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { getODataParamDescription, shouldOmitTopParam } from "./param-descriptions.js";
|
|
3
|
+
const TEAM_LIST_TOOLS = /* @__PURE__ */ new Set(["list-joined-teams", "list-my-associated-teams"]);
|
|
4
|
+
function queryParameterSchema(toolName, name, providerSchema) {
|
|
5
|
+
const bareName = name.replace(/^\$/, "").toLowerCase();
|
|
6
|
+
if (TEAM_LIST_TOOLS.has(toolName)) return void 0;
|
|
7
|
+
if (bareName === "top" && shouldOmitTopParam(toolName)) return void 0;
|
|
8
|
+
const source = providerSchema instanceof z.ZodOptional ? providerSchema.unwrap() : providerSchema;
|
|
9
|
+
let schema = source;
|
|
10
|
+
switch (bareName) {
|
|
11
|
+
case "select":
|
|
12
|
+
case "expand":
|
|
13
|
+
case "orderby":
|
|
14
|
+
schema = z.union([z.string(), z.array(z.string())]);
|
|
15
|
+
break;
|
|
16
|
+
case "filter":
|
|
17
|
+
case "search":
|
|
18
|
+
schema = z.string();
|
|
19
|
+
break;
|
|
20
|
+
case "skiptoken":
|
|
21
|
+
schema = z.string();
|
|
22
|
+
break;
|
|
23
|
+
case "top":
|
|
24
|
+
if (toolName === "list-chats") schema = source.and(z.number().max(50));
|
|
25
|
+
break;
|
|
26
|
+
}
|
|
27
|
+
if (providerSchema.isOptional()) schema = schema.optional();
|
|
28
|
+
const description = getODataParamDescription(bareName);
|
|
29
|
+
return description ? schema.describe(description) : schema;
|
|
30
|
+
}
|
|
31
|
+
export {
|
|
32
|
+
queryParameterSchema
|
|
33
|
+
};
|
package/dist/lib/tool-schema.js
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import { zodToJsonSchema } from "zod-to-json-schema";
|
|
2
|
+
import { queryParameterSchema } from "./query-parameter-schema.js";
|
|
2
3
|
import { isDestructiveOperation } from "./destructive-ops.js";
|
|
3
4
|
import {
|
|
4
|
-
getODataParamDescription,
|
|
5
|
-
shouldOmitTopParam,
|
|
6
5
|
isFetchAllPagesApplicable,
|
|
7
6
|
isSkiptokenApplicable,
|
|
8
7
|
SKIPTOKEN_PARAM_DESCRIPTION,
|
|
@@ -22,24 +21,23 @@ function unwrapOptional(schema) {
|
|
|
22
21
|
}
|
|
23
22
|
return { inner: schema, optional: false };
|
|
24
23
|
}
|
|
25
|
-
function bareParamName(name) {
|
|
26
|
-
return name.startsWith("$") ? name.slice(1) : name;
|
|
27
|
-
}
|
|
28
24
|
function describeToolSchema(tool, config, ctx = {}) {
|
|
29
|
-
const
|
|
30
|
-
|
|
31
|
-
|
|
25
|
+
const params = (tool.parameters ?? []).flatMap((p) => {
|
|
26
|
+
const effectiveSchema = p.type === "Query" ? queryParameterSchema(tool.alias, p.name, p.schema) : p.schema;
|
|
27
|
+
if (!effectiveSchema) return [];
|
|
28
|
+
const { inner, optional } = unwrapOptional(effectiveSchema);
|
|
32
29
|
const isPath = p.type === "Path";
|
|
33
30
|
const jsonSchema = zodToJsonSchema(inner, { target: "jsonSchema7", $refStrategy: "none" });
|
|
34
31
|
const { $schema: _s, ...schema } = jsonSchema;
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
32
|
+
return [
|
|
33
|
+
{
|
|
34
|
+
name: p.name,
|
|
35
|
+
in: p.type,
|
|
36
|
+
required: isPath || !optional,
|
|
37
|
+
description: effectiveSchema.description ?? p.description,
|
|
38
|
+
schema
|
|
39
|
+
}
|
|
40
|
+
];
|
|
43
41
|
});
|
|
44
42
|
if (isDestructiveOperation(tool.method, config)) {
|
|
45
43
|
params.push({
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@softeria/ms-365-mcp-server",
|
|
3
3
|
"mcpName": "io.github.Softeria/ms-365-mcp-server",
|
|
4
|
-
"version": "0.154.
|
|
4
|
+
"version": "0.154.1",
|
|
5
5
|
"description": " A Model Context Protocol (MCP) server for interacting with Microsoft 365 and Office services through the Graph API",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"main": "dist/index.js",
|