@softeria/ms-365-mcp-server 0.134.1 → 0.134.3
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 +13 -0
- package/dist/generated/hack.js +16 -4
- package/dist/graph-client.js +4 -1
- package/dist/graph-tools.js +2 -1
- package/dist/lib/path-params.js +16 -0
- package/package.json +1 -1
- package/src/generated/hack.ts +23 -4
|
@@ -393,6 +393,19 @@ describe("graph-tools", () => {
|
|
|
393
393
|
expect(schema["expand"].description).toContain("navigation");
|
|
394
394
|
expect(schema["expand"].description).toContain("attachments");
|
|
395
395
|
});
|
|
396
|
+
it("should describe path params it synthesizes itself", async () => {
|
|
397
|
+
const endpoint = makeEndpoint({ path: "/me/messages/:messageId" });
|
|
398
|
+
const config = makeConfig();
|
|
399
|
+
mockEndpoints.push(endpoint);
|
|
400
|
+
mockEndpointsJson = [config];
|
|
401
|
+
const server = createMockServer();
|
|
402
|
+
const { registerGraphTools } = await loadModule();
|
|
403
|
+
registerGraphTools(server, createMockGraphClient());
|
|
404
|
+
const schema = server.tools.get("test-tool").schema;
|
|
405
|
+
expect(schema["messageId"]).toBeDefined();
|
|
406
|
+
expect(schema["messageId"].description).not.toBe("Path parameter: messageId");
|
|
407
|
+
expect(schema["messageId"].description).toContain("not as 'id'");
|
|
408
|
+
});
|
|
396
409
|
});
|
|
397
410
|
describe("MS365_MCP_MAX_TOP", () => {
|
|
398
411
|
const prevMaxTop = process.env.MS365_MCP_MAX_TOP;
|
package/dist/generated/hack.js
CHANGED
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
+
import { describePathParam } from "../lib/path-params.js";
|
|
2
3
|
function makeApi(endpoints) {
|
|
3
4
|
return endpoints;
|
|
4
5
|
}
|
|
6
|
+
function isPathParamStub(param) {
|
|
7
|
+
const description = param.description ?? param.schema?.description;
|
|
8
|
+
return typeof description === "string" && /^Path parameter: /.test(description);
|
|
9
|
+
}
|
|
5
10
|
class Zodios {
|
|
6
11
|
constructor(baseUrlOrEndpoints, endpoints, options) {
|
|
7
12
|
if (typeof baseUrlOrEndpoints === "string") {
|
|
@@ -19,17 +24,24 @@ class Zodios {
|
|
|
19
24
|
pathParams.push(match[1]);
|
|
20
25
|
}
|
|
21
26
|
for (const pathParam of pathParams) {
|
|
22
|
-
const
|
|
27
|
+
const existing = endpoint.parameters.find(
|
|
23
28
|
(param) => param.name === pathParam || param.name === pathParam.replace(/[$_]+/g, "")
|
|
24
29
|
);
|
|
25
|
-
if (!
|
|
30
|
+
if (!existing) {
|
|
31
|
+
const description = describePathParam(pathParam);
|
|
26
32
|
const newParam = {
|
|
27
33
|
name: pathParam,
|
|
28
34
|
type: "Path",
|
|
29
|
-
schema: z.string().describe(
|
|
30
|
-
description
|
|
35
|
+
schema: z.string().describe(description),
|
|
36
|
+
description
|
|
31
37
|
};
|
|
32
38
|
endpoint.parameters.push(newParam);
|
|
39
|
+
continue;
|
|
40
|
+
}
|
|
41
|
+
if (existing.type === "Path" && isPathParamStub(existing)) {
|
|
42
|
+
const description = describePathParam(existing.name);
|
|
43
|
+
existing.description = description;
|
|
44
|
+
existing.schema = z.string().describe(description);
|
|
33
45
|
}
|
|
34
46
|
}
|
|
35
47
|
return endpoint;
|
package/dist/graph-client.js
CHANGED
|
@@ -204,7 +204,10 @@ class GraphClient {
|
|
|
204
204
|
}
|
|
205
205
|
async graphRequest(endpoint, options = {}) {
|
|
206
206
|
try {
|
|
207
|
-
|
|
207
|
+
const { accessToken: _redacted, ...safeOptions } = options;
|
|
208
|
+
logger.info(
|
|
209
|
+
`Calling ${endpoint} with options: ${JSON.stringify(safeOptions)}${_redacted ? " [accessToken=REDACTED]" : ""}`
|
|
210
|
+
);
|
|
208
211
|
const result = await this.makeRequest(endpoint, options);
|
|
209
212
|
const outputFormat = options.forceJsonOutput ? "json" : this.outputFormat;
|
|
210
213
|
return this.formatJsonResponse(
|
package/dist/graph-tools.js
CHANGED
|
@@ -2,6 +2,7 @@ import { randomUUID } from "crypto";
|
|
|
2
2
|
import logger from "./logger.js";
|
|
3
3
|
import { auditLog, getUserIdentityForAudit } from "./audit-log.js";
|
|
4
4
|
import { isDestructiveOperation } from "./lib/destructive-ops.js";
|
|
5
|
+
import { describePathParam } from "./lib/path-params.js";
|
|
5
6
|
import {
|
|
6
7
|
getEndpointScopeGroups,
|
|
7
8
|
getMissingAllowedScopesForGroups,
|
|
@@ -1002,7 +1003,7 @@ function registerGraphTools(server, graphClient, readOnly = false, enabledToolsP
|
|
|
1002
1003
|
for (const match of pathParamMatches) {
|
|
1003
1004
|
const pathParamName = match[1];
|
|
1004
1005
|
if (!(pathParamName in paramSchema)) {
|
|
1005
|
-
paramSchema[pathParamName] = z.string().describe(
|
|
1006
|
+
paramSchema[pathParamName] = z.string().describe(describePathParam(pathParamName));
|
|
1006
1007
|
}
|
|
1007
1008
|
}
|
|
1008
1009
|
if (isFetchAllPagesApplicable(tool)) {
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
function describePathParam(pathParamName) {
|
|
2
|
+
const match = /^(.*?)Id(\d*)$/.exec(pathParamName);
|
|
3
|
+
if (match && match[1]) {
|
|
4
|
+
const base = `Value for the '${pathParamName}' path segment. Pass it under the name '${pathParamName}', not as 'id'. `;
|
|
5
|
+
if (match[2]) {
|
|
6
|
+
return base + `It identifies a different resource from the similarly named parameter without the number; check the tool's path to see which.`;
|
|
7
|
+
}
|
|
8
|
+
const resource = match[1].replace(/([a-z0-9])([A-Z])/g, "$1 $2").toLowerCase();
|
|
9
|
+
const noun = /(^|\s)object$/.test(resource) ? resource : `${resource} object`;
|
|
10
|
+
return base + `Use the 'id' field of the ${noun} as returned by Microsoft Graph.`;
|
|
11
|
+
}
|
|
12
|
+
return `Value for the '${pathParamName}' path segment.`;
|
|
13
|
+
}
|
|
14
|
+
export {
|
|
15
|
+
describePathParam
|
|
16
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@softeria/ms-365-mcp-server",
|
|
3
|
-
"version": "0.134.
|
|
3
|
+
"version": "0.134.3",
|
|
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",
|
package/src/generated/hack.ts
CHANGED
|
@@ -1,10 +1,17 @@
|
|
|
1
1
|
import { Endpoint, Parameter } from './endpoint-types.js';
|
|
2
2
|
import { z } from 'zod';
|
|
3
|
+
import { describePathParam } from '../lib/path-params.js';
|
|
3
4
|
|
|
4
5
|
export function makeApi(endpoints: Endpoint[]) {
|
|
5
6
|
return endpoints;
|
|
6
7
|
}
|
|
7
8
|
|
|
9
|
+
/** True when a path parameter carries only the generated `Path parameter: x` placeholder. */
|
|
10
|
+
function isPathParamStub(param: Parameter): boolean {
|
|
11
|
+
const description = param.description ?? param.schema?.description;
|
|
12
|
+
return typeof description === 'string' && /^Path parameter: /.test(description);
|
|
13
|
+
}
|
|
14
|
+
|
|
8
15
|
export class Zodios {
|
|
9
16
|
endpoints: Endpoint[];
|
|
10
17
|
|
|
@@ -26,18 +33,30 @@ export class Zodios {
|
|
|
26
33
|
}
|
|
27
34
|
|
|
28
35
|
for (const pathParam of pathParams) {
|
|
29
|
-
const
|
|
36
|
+
const existing = endpoint.parameters.find(
|
|
30
37
|
(param) => param.name === pathParam || param.name === pathParam.replace(/[$_]+/g, '')
|
|
31
38
|
);
|
|
32
39
|
|
|
33
|
-
if (!
|
|
40
|
+
if (!existing) {
|
|
41
|
+
const description = describePathParam(pathParam);
|
|
34
42
|
const newParam: Parameter = {
|
|
35
43
|
name: pathParam,
|
|
36
44
|
type: 'Path',
|
|
37
|
-
schema: z.string().describe(
|
|
38
|
-
description
|
|
45
|
+
schema: z.string().describe(description),
|
|
46
|
+
description,
|
|
39
47
|
};
|
|
40
48
|
endpoint.parameters.push(newParam);
|
|
49
|
+
continue;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Some operations arrive with the path parameter already declared, but described
|
|
53
|
+
// only as `Path parameter: drive-id` — no more use to the model than nothing, and
|
|
54
|
+
// spelled in kebab-case so it does not even match the name callers must send.
|
|
55
|
+
// Upgrade those; any real upstream description is left alone.
|
|
56
|
+
if (existing.type === 'Path' && isPathParamStub(existing)) {
|
|
57
|
+
const description = describePathParam(existing.name);
|
|
58
|
+
existing.description = description;
|
|
59
|
+
existing.schema = z.string().describe(description);
|
|
41
60
|
}
|
|
42
61
|
}
|
|
43
62
|
|