@softeria/ms-365-mcp-server 0.158.0 → 0.158.2
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.
|
@@ -748,6 +748,7 @@ const microsoft_graph_customEmojiFromIdentitySet = z.object({
|
|
|
748
748
|
user: microsoft_graph_identity.optional()
|
|
749
749
|
}).passthrough();
|
|
750
750
|
const microsoft_graph_teamworkCustomEmoji = z.object({
|
|
751
|
+
id: z.string().describe("The unique identifier for an entity. Read-only.").optional(),
|
|
751
752
|
contentBytes: z.string().describe(
|
|
752
753
|
"The base64-encoded image content of the emoji. Supported formats include PNG and GIF."
|
|
753
754
|
).nullish(),
|
package/dist/generated/client.js
CHANGED
|
@@ -2688,7 +2688,16 @@ const microsoft_graph_subscription = z.object({
|
|
|
2688
2688
|
).nullish(),
|
|
2689
2689
|
resource: z.string().describe(
|
|
2690
2690
|
"Required. Specifies the resource that is monitored for changes. Don't include the base URL (https://graph.microsoft.com/v1.0/). See the possible resource path values for each supported resource."
|
|
2691
|
-
).optional()
|
|
2691
|
+
).optional(),
|
|
2692
|
+
vapidPublicKey: z.string().describe(
|
|
2693
|
+
"Optional. The application server's VAPID public key, base64url-encoded (P-256 uncompressed point, 65 bytes pre-encoding). Obtained by calling the getVapidPublicKey function on the subscription collection. The browser passes this value to PushManager.subscribe({ applicationServerKey: vapidPublicKey }) to bind the push subscription to this server identity. Required when notificationUrl targets a known Web Push service origin (for example, *.push.apple.com, fcm.googleapis.com, updates.push.services.mozilla.com); rejected with 400 Bad Request if supplied on a standard webhook subscription. For more information, see RFC 8292."
|
|
2694
|
+
).nullish(),
|
|
2695
|
+
webPushEncryptionP256dhPublicKey: z.string().describe(
|
|
2696
|
+
"Optional. The subscriber's ECDH public key, base64url-encoded (P-256 uncompressed point, 65 bytes pre-encoding). Obtained from the browser via PushSubscription.getKey('p256dh'). Used as the peer public key during ECDH key agreement to derive the per-message content encryption key for RFC 8291 payload encryption. Required when notificationUrl targets a known Web Push service origin; rejected with 400 Bad Request if supplied on a standard webhook subscription. For more information, see RFC 8291 Section 3."
|
|
2697
|
+
).nullish(),
|
|
2698
|
+
webPushEncryptionSecret: z.string().describe(
|
|
2699
|
+
"Optional. The subscriber's auth secret, base64url-encoded (16 bytes pre-encoding). Obtained from the browser via PushSubscription.getKey('auth'). Used as the HMAC-SHA-256 salt for the HKDF combine step that derives key material for RFC 8291 payload encryption. Write-only: this value is never returned in GET responses (returned as null). Treat as a secret. Required when notificationUrl targets a known Web Push service origin; rejected with 400 Bad Request if supplied on a standard webhook subscription. For more information, see RFC 8291 Section 3."
|
|
2700
|
+
).nullish()
|
|
2692
2701
|
}).passthrough();
|
|
2693
2702
|
const microsoft_graph_list = z.lazy(
|
|
2694
2703
|
() => z.object({
|
package/dist/graph-tools.js
CHANGED
|
@@ -18,6 +18,7 @@ import { getAttachmentMinting } from "./lib/attachment-minting.js";
|
|
|
18
18
|
import {
|
|
19
19
|
buildAttachmentUrl,
|
|
20
20
|
isPlainGraphPath,
|
|
21
|
+
isUnalteredGraphPath,
|
|
21
22
|
TicketStoreFullError
|
|
22
23
|
} from "./lib/attachment-tickets.js";
|
|
23
24
|
import { buildBM25Index, scoreQuery, tokenize } from "./lib/bm25.js";
|
|
@@ -685,6 +686,8 @@ const UTILITY_TOOLS = [
|
|
|
685
686
|
isError: true
|
|
686
687
|
};
|
|
687
688
|
}
|
|
689
|
+
const alteredTarget = unalteredTargetError(target);
|
|
690
|
+
if (alteredTarget) return alteredTarget;
|
|
688
691
|
const restrictedTarget = userProfilePassthroughError(target, userFields);
|
|
689
692
|
if (restrictedTarget) return restrictedTarget;
|
|
690
693
|
try {
|
|
@@ -782,6 +785,8 @@ const UTILITY_TOOLS = [
|
|
|
782
785
|
isError: true
|
|
783
786
|
};
|
|
784
787
|
}
|
|
788
|
+
const alteredTarget = unalteredTargetError(target);
|
|
789
|
+
if (alteredTarget) return alteredTarget;
|
|
785
790
|
const restrictedTarget = userProfilePassthroughError(target, userFields);
|
|
786
791
|
if (restrictedTarget) return restrictedTarget;
|
|
787
792
|
if (!path.isAbsolute(outputPath)) {
|
|
@@ -907,6 +912,8 @@ const UTILITY_TOOLS = [
|
|
|
907
912
|
isError: true
|
|
908
913
|
};
|
|
909
914
|
}
|
|
915
|
+
const alteredTarget = unalteredTargetError(target);
|
|
916
|
+
if (alteredTarget) return alteredTarget;
|
|
910
917
|
const restrictedTarget = userProfilePassthroughError(target, userFields);
|
|
911
918
|
if (restrictedTarget) return restrictedTarget;
|
|
912
919
|
const queryIdx = target.indexOf("?");
|
|
@@ -1193,6 +1200,36 @@ function describeParamsForLog(params) {
|
|
|
1193
1200
|
return `[unserializable: ${error instanceof Error ? error.name : "unknown error"}]`;
|
|
1194
1201
|
}
|
|
1195
1202
|
}
|
|
1203
|
+
function escapeIfStringLiteral(template, names, value) {
|
|
1204
|
+
const quoted = names.some((n) => template.includes(`'{${n}}'`) || template.includes(`':${n}'`));
|
|
1205
|
+
return quoted ? String(value).replace(/'/g, "''") : String(value);
|
|
1206
|
+
}
|
|
1207
|
+
function rawValueAddsSegments(template, names, value) {
|
|
1208
|
+
const isTail = names.some((n) => template.endsWith(`/{${n}}`) || template.endsWith(`/:${n}`));
|
|
1209
|
+
return isTail ? value.includes(":") : value.includes("/");
|
|
1210
|
+
}
|
|
1211
|
+
function invalidPathParameter(message) {
|
|
1212
|
+
return {
|
|
1213
|
+
content: [
|
|
1214
|
+
{ type: "text", text: JSON.stringify({ error: "invalid_path_parameter", message }) }
|
|
1215
|
+
],
|
|
1216
|
+
isError: true
|
|
1217
|
+
};
|
|
1218
|
+
}
|
|
1219
|
+
function unalteredTargetError(target) {
|
|
1220
|
+
if (isUnalteredGraphPath(target.split("?")[0])) return void 0;
|
|
1221
|
+
return {
|
|
1222
|
+
content: [
|
|
1223
|
+
{
|
|
1224
|
+
type: "text",
|
|
1225
|
+
text: JSON.stringify({
|
|
1226
|
+
error: 'target must not contain "." or ".." segments, a fragment or a backslash.'
|
|
1227
|
+
})
|
|
1228
|
+
}
|
|
1229
|
+
],
|
|
1230
|
+
isError: true
|
|
1231
|
+
};
|
|
1232
|
+
}
|
|
1196
1233
|
async function executeGraphTool(tool, config, graphClient, params, authManager, userFields) {
|
|
1197
1234
|
logger.info(`Tool ${tool.alias} called with params: ${describeParamsForLog(params)}`);
|
|
1198
1235
|
if (isConfirmGateEnabled() && isDestructiveOperation(tool.method, config) && params.confirm !== true) {
|
|
@@ -1314,8 +1351,17 @@ async function executeGraphTool(tool, config, graphClient, params, authManager,
|
|
|
1314
1351
|
switch (paramDef.type) {
|
|
1315
1352
|
case "Path": {
|
|
1316
1353
|
const shouldSkipEncoding = config?.skipEncoding?.includes(paramName) ?? false;
|
|
1317
|
-
const
|
|
1318
|
-
|
|
1354
|
+
const names = [paramName, camelCaseParamName];
|
|
1355
|
+
if (shouldSkipEncoding && rawValueAddsSegments(tool.path, names, `${paramValue}`)) {
|
|
1356
|
+
return invalidPathParameter(
|
|
1357
|
+
`'${paramName}' cannot add path segments to this endpoint: no "/" in a value, and no ":" in a site path.`
|
|
1358
|
+
);
|
|
1359
|
+
}
|
|
1360
|
+
const encodedValue = shouldSkipEncoding ? paramValue : encodeURIComponent(escapeIfStringLiteral(tool.path, names, paramValue)).replace(
|
|
1361
|
+
/%3D/g,
|
|
1362
|
+
"="
|
|
1363
|
+
);
|
|
1364
|
+
path2 = path2.replace(`{${paramName}}`, () => encodedValue).replace(`:${paramName}`, () => encodedValue).replace(`{${camelCaseParamName}}`, () => encodedValue).replace(`:${camelCaseParamName}`, () => encodedValue);
|
|
1319
1365
|
break;
|
|
1320
1366
|
}
|
|
1321
1367
|
case "Query":
|
|
@@ -1352,8 +1398,10 @@ async function executeGraphTool(tool, config, graphClient, params, authManager,
|
|
|
1352
1398
|
body = paramValue;
|
|
1353
1399
|
logger.info(`Set body param: ${JSON.stringify(body)}`);
|
|
1354
1400
|
} else if (path2.includes(`:${paramName}`) || path2.includes(`{${paramName}}`) || path2.includes(`:${camelCaseParamName}`) || path2.includes(`{${camelCaseParamName}}`)) {
|
|
1355
|
-
const encodedValue = encodeURIComponent(
|
|
1356
|
-
|
|
1401
|
+
const encodedValue = encodeURIComponent(
|
|
1402
|
+
escapeIfStringLiteral(tool.path, [paramName, camelCaseParamName], paramValue)
|
|
1403
|
+
).replace(/%3D/g, "=");
|
|
1404
|
+
path2 = path2.replace(`{${paramName}}`, () => encodedValue).replace(`:${paramName}`, () => encodedValue).replace(`{${camelCaseParamName}}`, () => encodedValue).replace(`:${camelCaseParamName}`, () => encodedValue);
|
|
1357
1405
|
logger.info(`Path param fallback: replaced :${camelCaseParamName} with encoded value`);
|
|
1358
1406
|
} else if (paramName.toLowerCase() === "accept" && config?.acceptType) {
|
|
1359
1407
|
headers["Accept"] = `${paramValue}`;
|
|
@@ -1370,6 +1418,11 @@ async function executeGraphTool(tool, config, graphClient, params, authManager,
|
|
|
1370
1418
|
logger.warn(`Dropping unrecognized parameter '${paramName}' for tool ${tool.alias}`);
|
|
1371
1419
|
}
|
|
1372
1420
|
}
|
|
1421
|
+
if (!isUnalteredGraphPath(path2)) {
|
|
1422
|
+
return invalidPathParameter(
|
|
1423
|
+
'A path parameter would send this request to a different endpoint. Path parameters cannot be "." or "..", or contain "/../", "?", "#" or a backslash. Percent-encode "?" and "#" that are part of a name.'
|
|
1424
|
+
);
|
|
1425
|
+
}
|
|
1373
1426
|
if (isPlainObject(body) && bodyShape != null && hasOwn(bodyShape, "body") && !Object.keys(body).some((k) => k.toLowerCase() === "body")) {
|
|
1374
1427
|
const nestedShape = bodySchemaShape(bodyShape.body);
|
|
1375
1428
|
if (nestedShape != null) {
|
|
@@ -24,6 +24,15 @@ function isPlainGraphPath(target) {
|
|
|
24
24
|
}
|
|
25
25
|
return resolved.origin === PROBE_ORIGIN && resolved.search === "" && resolved.hash === "" && resolved.pathname === PROBE_PREFIX + target;
|
|
26
26
|
}
|
|
27
|
+
function isUnalteredGraphPath(path) {
|
|
28
|
+
const decode = (s) => decodeURIComponent(s.replace(/%(?![0-9a-f]{2})/gi, "%25"));
|
|
29
|
+
try {
|
|
30
|
+
const resolved = new URL(PROBE_ORIGIN + PROBE_PREFIX + path);
|
|
31
|
+
return resolved.origin === PROBE_ORIGIN && resolved.search === "" && resolved.hash === "" && decode(resolved.pathname) === decode(PROBE_PREFIX + path);
|
|
32
|
+
} catch {
|
|
33
|
+
return false;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
27
36
|
const MAX_LIVE_TICKETS = 256;
|
|
28
37
|
const TICKET_BYTES = 32;
|
|
29
38
|
class TicketStoreFullError extends Error {
|
|
@@ -94,5 +103,6 @@ export {
|
|
|
94
103
|
TICKET_PARAM,
|
|
95
104
|
TicketStoreFullError,
|
|
96
105
|
buildAttachmentUrl,
|
|
97
|
-
isPlainGraphPath
|
|
106
|
+
isPlainGraphPath,
|
|
107
|
+
isUnalteredGraphPath
|
|
98
108
|
};
|
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.158.
|
|
4
|
+
"version": "0.158.2",
|
|
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",
|