@softeria/ms-365-mcp-server 0.131.0 → 0.131.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.
|
@@ -93,6 +93,15 @@ function createMockServer() {
|
|
|
93
93
|
tools.set(name, { description, schema, handler });
|
|
94
94
|
}
|
|
95
95
|
),
|
|
96
|
+
registerTool: vi.fn(
|
|
97
|
+
(name, config, handler) => {
|
|
98
|
+
tools.set(name, {
|
|
99
|
+
description: config.description,
|
|
100
|
+
schema: config.inputSchema?.shape ?? config.inputSchema,
|
|
101
|
+
handler
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
),
|
|
96
105
|
tools
|
|
97
106
|
};
|
|
98
107
|
}
|
package/dist/endpoints.json
CHANGED
|
@@ -1486,7 +1486,7 @@
|
|
|
1486
1486
|
"toolName": "send-chat-message",
|
|
1487
1487
|
"presets": ["teams", "work"],
|
|
1488
1488
|
"workScopes": ["ChatMessage.Send"],
|
|
1489
|
-
"llmTip": "Use contentType 'html' in the body — plain text contentType gets mangled by Graph API."
|
|
1489
|
+
"llmTip": "Use contentType 'html' in the body — plain text contentType gets mangled by Graph API. To @mention someone, put an <at id=\"0\">Name</at> tag in the html content and add a mentions array to the request body, as a sibling of the body property (not a separate top-level tool parameter): { body: { contentType: 'html', content: 'Hi <at id=\"0\">Name</at>' }, mentions: [{ id: 0, mentionText: 'Name', mentioned: { user: { id: '<aad-user-id>', displayName: 'Name', userIdentityType: 'aadUser' } } }] }. Each mention id must match its <at id> value; include userIdentityType: 'aadUser' or Graph returns 400 'value without a type name'."
|
|
1490
1490
|
},
|
|
1491
1491
|
{
|
|
1492
1492
|
"pathPattern": "/me/joinedTeams",
|
|
@@ -1572,7 +1572,7 @@
|
|
|
1572
1572
|
"toolName": "send-channel-message",
|
|
1573
1573
|
"presets": ["teams", "work"],
|
|
1574
1574
|
"workScopes": ["ChannelMessage.Send"],
|
|
1575
|
-
"llmTip": "Use contentType 'html' in the body — plain text contentType gets mangled by Graph API."
|
|
1575
|
+
"llmTip": "Use contentType 'html' in the body — plain text contentType gets mangled by Graph API. To @mention someone, put an <at id=\"0\">Name</at> tag in the html content and add a mentions array to the request body, as a sibling of the body property (not a separate top-level tool parameter): { body: { contentType: 'html', content: 'Hi <at id=\"0\">Name</at>' }, mentions: [{ id: 0, mentionText: 'Name', mentioned: { user: { id: '<aad-user-id>', displayName: 'Name', userIdentityType: 'aadUser' } } }] }. Each mention id must match its <at id> value; include userIdentityType: 'aadUser' or Graph returns 400 'value without a type name'."
|
|
1576
1576
|
},
|
|
1577
1577
|
{
|
|
1578
1578
|
"pathPattern": "/teams/{team-id}/channels/{channel-id}/messages/{chatMessage-id}/replies",
|
|
@@ -1580,7 +1580,7 @@
|
|
|
1580
1580
|
"toolName": "reply-to-channel-message",
|
|
1581
1581
|
"presets": ["teams", "work"],
|
|
1582
1582
|
"workScopes": ["ChannelMessage.Send"],
|
|
1583
|
-
"llmTip": "Use contentType 'html' in the body — plain text contentType gets mangled by Graph API."
|
|
1583
|
+
"llmTip": "Use contentType 'html' in the body — plain text contentType gets mangled by Graph API. To @mention someone, put an <at id=\"0\">Name</at> tag in the html content and add a mentions array to the request body, as a sibling of the body property (not a separate top-level tool parameter): { body: { contentType: 'html', content: 'Hi <at id=\"0\">Name</at>' }, mentions: [{ id: 0, mentionText: 'Name', mentioned: { user: { id: '<aad-user-id>', displayName: 'Name', userIdentityType: 'aadUser' } } }] }. Each mention id must match its <at id> value; include userIdentityType: 'aadUser' or Graph returns 400 'value without a type name'."
|
|
1584
1584
|
},
|
|
1585
1585
|
{
|
|
1586
1586
|
"pathPattern": "/teams/{team-id}/channels/{channel-id}/messages/{chatMessage-id}/replies",
|
|
@@ -1659,7 +1659,7 @@
|
|
|
1659
1659
|
"toolName": "reply-to-chat-message",
|
|
1660
1660
|
"presets": ["teams", "work"],
|
|
1661
1661
|
"workScopes": ["ChatMessage.Send"],
|
|
1662
|
-
"llmTip": "Use contentType 'html' in the body — plain text contentType gets mangled by Graph API."
|
|
1662
|
+
"llmTip": "Use contentType 'html' in the body — plain text contentType gets mangled by Graph API. To @mention someone, put an <at id=\"0\">Name</at> tag in the html content and add a mentions array to the request body, as a sibling of the body property (not a separate top-level tool parameter): { body: { contentType: 'html', content: 'Hi <at id=\"0\">Name</at>' }, mentions: [{ id: 0, mentionText: 'Name', mentioned: { user: { id: '<aad-user-id>', displayName: 'Name', userIdentityType: 'aadUser' } } }] }. Each mention id must match its <at id> value; include userIdentityType: 'aadUser' or Graph returns 400 'value without a type name'."
|
|
1663
1663
|
},
|
|
1664
1664
|
{
|
|
1665
1665
|
"pathPattern": "/chats/{chat-id}/messages/{chatMessage-id}/setReaction",
|
package/dist/graph-tools.js
CHANGED
|
@@ -395,6 +395,44 @@ function registerUtilityToolWithMcp(server, utility, ctx) {
|
|
|
395
395
|
async (params) => utility.execute(params, ctx)
|
|
396
396
|
);
|
|
397
397
|
}
|
|
398
|
+
function bodySchemaShape(schema) {
|
|
399
|
+
let current = schema;
|
|
400
|
+
for (let i = 0; i < 10 && current; i++) {
|
|
401
|
+
if (current instanceof z.ZodObject) {
|
|
402
|
+
return current.shape;
|
|
403
|
+
}
|
|
404
|
+
const def = current._def;
|
|
405
|
+
current = def?.innerType ?? def?.schema ?? def?.getter?.();
|
|
406
|
+
}
|
|
407
|
+
return null;
|
|
408
|
+
}
|
|
409
|
+
function lenientBodySchema(schema) {
|
|
410
|
+
if (schema instanceof z.ZodObject) {
|
|
411
|
+
return schema.passthrough();
|
|
412
|
+
}
|
|
413
|
+
if (schema instanceof z.ZodOptional) {
|
|
414
|
+
return lenientBodySchema(schema.unwrap()).optional();
|
|
415
|
+
}
|
|
416
|
+
if (schema instanceof z.ZodNullable) {
|
|
417
|
+
return lenientBodySchema(schema.unwrap()).nullable();
|
|
418
|
+
}
|
|
419
|
+
if (schema instanceof z.ZodLazy) {
|
|
420
|
+
return z.lazy(() => lenientBodySchema(schema.schema));
|
|
421
|
+
}
|
|
422
|
+
return schema;
|
|
423
|
+
}
|
|
424
|
+
const READ_ONLY_BODY_FIELDS = /* @__PURE__ */ new Set([
|
|
425
|
+
"id",
|
|
426
|
+
"createdDateTime",
|
|
427
|
+
"lastModifiedDateTime",
|
|
428
|
+
"changeKey"
|
|
429
|
+
]);
|
|
430
|
+
function isPlainObject(value) {
|
|
431
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
432
|
+
}
|
|
433
|
+
function hasOwn(obj, key) {
|
|
434
|
+
return Object.prototype.hasOwnProperty.call(obj, key);
|
|
435
|
+
}
|
|
398
436
|
async function executeGraphTool(tool, config, graphClient, params, authManager) {
|
|
399
437
|
logger.info(`Tool ${tool.alias} called with params: ${JSON.stringify(params)}`);
|
|
400
438
|
if (isConfirmGateEnabled() && isDestructiveOperation(tool.method, config) && params.confirm !== true) {
|
|
@@ -451,6 +489,10 @@ async function executeGraphTool(tool, config, graphClient, params, authManager)
|
|
|
451
489
|
const queryParams = {};
|
|
452
490
|
const headers = {};
|
|
453
491
|
let body = null;
|
|
492
|
+
const bodyShape = bodySchemaShape(
|
|
493
|
+
parameterDefinitions.find((p) => p.type === "Body")?.schema
|
|
494
|
+
);
|
|
495
|
+
const strayBodyFields = {};
|
|
454
496
|
for (const [paramName, paramValue] of Object.entries(params)) {
|
|
455
497
|
if ([
|
|
456
498
|
"account",
|
|
@@ -529,6 +571,29 @@ async function executeGraphTool(tool, config, graphClient, params, authManager)
|
|
|
529
571
|
} else if (isOdataParam) {
|
|
530
572
|
queryParams[fixedParamName] = `${paramValue}`;
|
|
531
573
|
logger.info(`OData param fallback: forwarded ${fixedParamName}=${paramValue}`);
|
|
574
|
+
} else if (bodyShape && (hasOwn(bodyShape, paramName) || hasOwn(bodyShape, camelCaseParamName)) && !READ_ONLY_BODY_FIELDS.has(hasOwn(bodyShape, paramName) ? paramName : camelCaseParamName)) {
|
|
575
|
+
const fieldName = hasOwn(bodyShape, paramName) ? paramName : camelCaseParamName;
|
|
576
|
+
strayBodyFields[fieldName] = paramValue;
|
|
577
|
+
logger.info(
|
|
578
|
+
`Body field fallback: merging top-level param '${fieldName}' into request body`
|
|
579
|
+
);
|
|
580
|
+
} else {
|
|
581
|
+
logger.warn(`Dropping unrecognized parameter '${paramName}' for tool ${tool.alias}`);
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
if (Object.keys(strayBodyFields).length > 0) {
|
|
585
|
+
if (isPlainObject(body)) {
|
|
586
|
+
const keys = Object.keys(body);
|
|
587
|
+
const bodyIsNestedField = bodyShape != null && hasOwn(bodyShape, "body") && keys.length > 0 && keys.every((k) => !hasOwn(bodyShape, k));
|
|
588
|
+
body = bodyIsNestedField ? { ...strayBodyFields, body } : { ...strayBodyFields, ...body };
|
|
589
|
+
logger.info(`Merged flattened body fields: ${Object.keys(strayBodyFields).join(", ")}`);
|
|
590
|
+
} else if (body == null) {
|
|
591
|
+
body = strayBodyFields;
|
|
592
|
+
logger.info(`Merged flattened body fields: ${Object.keys(strayBodyFields).join(", ")}`);
|
|
593
|
+
} else {
|
|
594
|
+
logger.warn(
|
|
595
|
+
`Cannot merge flattened body fields (${Object.keys(strayBodyFields).join(", ")}) into non-object request body; dropping them`
|
|
596
|
+
);
|
|
532
597
|
}
|
|
533
598
|
}
|
|
534
599
|
if (TOP_UNSUPPORTED_DELTA_TOOLS.has(tool.alias)) {
|
|
@@ -792,7 +857,7 @@ function registerGraphTools(server, graphClient, readOnly = false, enabledToolsP
|
|
|
792
857
|
const paramSchema = {};
|
|
793
858
|
if (tool.parameters && tool.parameters.length > 0) {
|
|
794
859
|
for (const param of tool.parameters) {
|
|
795
|
-
paramSchema[param.name] = param.schema || z.any();
|
|
860
|
+
paramSchema[param.name] = param.type === "Body" && param.schema ? lenientBodySchema(param.schema) : param.schema || z.any();
|
|
796
861
|
}
|
|
797
862
|
}
|
|
798
863
|
const pathParamMatches = tool.path.matchAll(/:([a-zA-Z]+)/g);
|
|
@@ -880,16 +945,19 @@ function registerGraphTools(server, graphClient, readOnly = false, enabledToolsP
|
|
|
880
945
|
}
|
|
881
946
|
const isReadOnlyTool = tool.method.toUpperCase() === "GET" || endpointConfig?.readOnly === true;
|
|
882
947
|
try {
|
|
883
|
-
server.
|
|
948
|
+
server.registerTool(
|
|
884
949
|
tool.alias,
|
|
885
|
-
toolDescription,
|
|
886
|
-
paramSchema,
|
|
887
950
|
{
|
|
888
951
|
title: tool.alias,
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
952
|
+
description: toolDescription,
|
|
953
|
+
inputSchema: z.object(paramSchema).passthrough(),
|
|
954
|
+
annotations: {
|
|
955
|
+
title: tool.alias,
|
|
956
|
+
readOnlyHint: isReadOnlyTool,
|
|
957
|
+
destructiveHint: destructive,
|
|
958
|
+
openWorldHint: true
|
|
959
|
+
// All tools call Microsoft Graph API
|
|
960
|
+
}
|
|
893
961
|
},
|
|
894
962
|
async (params) => executeGraphTool(tool, endpointConfig, graphClient, params, authManager)
|
|
895
963
|
);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@softeria/ms-365-mcp-server",
|
|
3
|
-
"version": "0.131.
|
|
3
|
+
"version": "0.131.2",
|
|
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/endpoints.json
CHANGED
|
@@ -1486,7 +1486,7 @@
|
|
|
1486
1486
|
"toolName": "send-chat-message",
|
|
1487
1487
|
"presets": ["teams", "work"],
|
|
1488
1488
|
"workScopes": ["ChatMessage.Send"],
|
|
1489
|
-
"llmTip": "Use contentType 'html' in the body — plain text contentType gets mangled by Graph API."
|
|
1489
|
+
"llmTip": "Use contentType 'html' in the body — plain text contentType gets mangled by Graph API. To @mention someone, put an <at id=\"0\">Name</at> tag in the html content and add a mentions array to the request body, as a sibling of the body property (not a separate top-level tool parameter): { body: { contentType: 'html', content: 'Hi <at id=\"0\">Name</at>' }, mentions: [{ id: 0, mentionText: 'Name', mentioned: { user: { id: '<aad-user-id>', displayName: 'Name', userIdentityType: 'aadUser' } } }] }. Each mention id must match its <at id> value; include userIdentityType: 'aadUser' or Graph returns 400 'value without a type name'."
|
|
1490
1490
|
},
|
|
1491
1491
|
{
|
|
1492
1492
|
"pathPattern": "/me/joinedTeams",
|
|
@@ -1572,7 +1572,7 @@
|
|
|
1572
1572
|
"toolName": "send-channel-message",
|
|
1573
1573
|
"presets": ["teams", "work"],
|
|
1574
1574
|
"workScopes": ["ChannelMessage.Send"],
|
|
1575
|
-
"llmTip": "Use contentType 'html' in the body — plain text contentType gets mangled by Graph API."
|
|
1575
|
+
"llmTip": "Use contentType 'html' in the body — plain text contentType gets mangled by Graph API. To @mention someone, put an <at id=\"0\">Name</at> tag in the html content and add a mentions array to the request body, as a sibling of the body property (not a separate top-level tool parameter): { body: { contentType: 'html', content: 'Hi <at id=\"0\">Name</at>' }, mentions: [{ id: 0, mentionText: 'Name', mentioned: { user: { id: '<aad-user-id>', displayName: 'Name', userIdentityType: 'aadUser' } } }] }. Each mention id must match its <at id> value; include userIdentityType: 'aadUser' or Graph returns 400 'value without a type name'."
|
|
1576
1576
|
},
|
|
1577
1577
|
{
|
|
1578
1578
|
"pathPattern": "/teams/{team-id}/channels/{channel-id}/messages/{chatMessage-id}/replies",
|
|
@@ -1580,7 +1580,7 @@
|
|
|
1580
1580
|
"toolName": "reply-to-channel-message",
|
|
1581
1581
|
"presets": ["teams", "work"],
|
|
1582
1582
|
"workScopes": ["ChannelMessage.Send"],
|
|
1583
|
-
"llmTip": "Use contentType 'html' in the body — plain text contentType gets mangled by Graph API."
|
|
1583
|
+
"llmTip": "Use contentType 'html' in the body — plain text contentType gets mangled by Graph API. To @mention someone, put an <at id=\"0\">Name</at> tag in the html content and add a mentions array to the request body, as a sibling of the body property (not a separate top-level tool parameter): { body: { contentType: 'html', content: 'Hi <at id=\"0\">Name</at>' }, mentions: [{ id: 0, mentionText: 'Name', mentioned: { user: { id: '<aad-user-id>', displayName: 'Name', userIdentityType: 'aadUser' } } }] }. Each mention id must match its <at id> value; include userIdentityType: 'aadUser' or Graph returns 400 'value without a type name'."
|
|
1584
1584
|
},
|
|
1585
1585
|
{
|
|
1586
1586
|
"pathPattern": "/teams/{team-id}/channels/{channel-id}/messages/{chatMessage-id}/replies",
|
|
@@ -1659,7 +1659,7 @@
|
|
|
1659
1659
|
"toolName": "reply-to-chat-message",
|
|
1660
1660
|
"presets": ["teams", "work"],
|
|
1661
1661
|
"workScopes": ["ChatMessage.Send"],
|
|
1662
|
-
"llmTip": "Use contentType 'html' in the body — plain text contentType gets mangled by Graph API."
|
|
1662
|
+
"llmTip": "Use contentType 'html' in the body — plain text contentType gets mangled by Graph API. To @mention someone, put an <at id=\"0\">Name</at> tag in the html content and add a mentions array to the request body, as a sibling of the body property (not a separate top-level tool parameter): { body: { contentType: 'html', content: 'Hi <at id=\"0\">Name</at>' }, mentions: [{ id: 0, mentionText: 'Name', mentioned: { user: { id: '<aad-user-id>', displayName: 'Name', userIdentityType: 'aadUser' } } }] }. Each mention id must match its <at id> value; include userIdentityType: 'aadUser' or Graph returns 400 'value without a type name'."
|
|
1663
1663
|
},
|
|
1664
1664
|
{
|
|
1665
1665
|
"pathPattern": "/chats/{chat-id}/messages/{chatMessage-id}/setReaction",
|