@softeria/ms-365-mcp-server 0.167.0 → 0.168.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 +169 -0
- package/dist/endpoints.json +10 -7
- package/dist/graph-tools.js +68 -0
- package/package.json +1 -1
- package/src/endpoints.json +10 -7
|
@@ -4190,6 +4190,175 @@ describe("graph-tools", () => {
|
|
|
4190
4190
|
expect(isDestructiveOperation("DELETE", { readOnly: true })).toBe(true);
|
|
4191
4191
|
});
|
|
4192
4192
|
});
|
|
4193
|
+
describe("rewriteMentionShorthand", () => {
|
|
4194
|
+
const U1 = "11111111-2222-3333-4444-555555555555";
|
|
4195
|
+
const U2 = "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee";
|
|
4196
|
+
const html = (content, extra = {}) => ({
|
|
4197
|
+
body: { contentType: "html", content },
|
|
4198
|
+
...extra
|
|
4199
|
+
});
|
|
4200
|
+
const mention = (id, userId, name) => ({
|
|
4201
|
+
id,
|
|
4202
|
+
mentionText: name,
|
|
4203
|
+
mentioned: { user: { id: userId, displayName: name, userIdentityType: "aadUser" } }
|
|
4204
|
+
});
|
|
4205
|
+
it("rewrites one tag and builds the mentions array", async () => {
|
|
4206
|
+
const { rewriteMentionShorthand } = await loadModule();
|
|
4207
|
+
expect(rewriteMentionShorthand(html(`Hi <at user-id="${U1}">Jane Doe</at>!`)).body).toEqual(
|
|
4208
|
+
html('Hi <at id="0">Jane Doe</at>!', { mentions: [mention(0, U1, "Jane Doe")] })
|
|
4209
|
+
);
|
|
4210
|
+
});
|
|
4211
|
+
it("numbers several tags in order, including a repeated user", async () => {
|
|
4212
|
+
const { rewriteMentionShorthand } = await loadModule();
|
|
4213
|
+
const out = rewriteMentionShorthand(
|
|
4214
|
+
html(
|
|
4215
|
+
`<at user-id="${U1}">Jane</at> <at user-id="${U2}">Bob</at> <at user-id="${U1}">Jane</at>`
|
|
4216
|
+
)
|
|
4217
|
+
).body;
|
|
4218
|
+
expect(out.body.content).toBe(
|
|
4219
|
+
'<at id="0">Jane</at> <at id="1">Bob</at> <at id="2">Jane</at>'
|
|
4220
|
+
);
|
|
4221
|
+
expect(out.mentions.map((m) => m.id)).toEqual([0, 1, 2]);
|
|
4222
|
+
});
|
|
4223
|
+
it("starts above ids already in the content and mentions", async () => {
|
|
4224
|
+
const { rewriteMentionShorthand } = await loadModule();
|
|
4225
|
+
const input = html(`<at id="0">Old</at> <at user-id="${U1}">Jane</at>`, {
|
|
4226
|
+
mentions: [mention(0, U2, "Old")]
|
|
4227
|
+
});
|
|
4228
|
+
const out = rewriteMentionShorthand(input).body;
|
|
4229
|
+
expect(out.body.content).toBe('<at id="0">Old</at> <at id="1">Jane</at>');
|
|
4230
|
+
expect(out.mentions).toEqual([mention(0, U2, "Old"), mention(1, U1, "Jane")]);
|
|
4231
|
+
});
|
|
4232
|
+
it("passes a correct hand-built body through unchanged", async () => {
|
|
4233
|
+
const { rewriteMentionShorthand } = await loadModule();
|
|
4234
|
+
const input = html('<at id="0">Jane</at>', { mentions: [mention(0, U1, "Jane")] });
|
|
4235
|
+
expect(rewriteMentionShorthand(input).body).toBe(input);
|
|
4236
|
+
});
|
|
4237
|
+
it("returns an error for a user-id that is not a GUID", async () => {
|
|
4238
|
+
const { rewriteMentionShorthand } = await loadModule();
|
|
4239
|
+
const input = html('<at user-id="jane@contoso.com">Jane</at>');
|
|
4240
|
+
const out = rewriteMentionShorthand(input);
|
|
4241
|
+
expect(out.error).toContain("jane@contoso.com");
|
|
4242
|
+
expect(out.body).toBe(input);
|
|
4243
|
+
});
|
|
4244
|
+
it("leaves non-html content without the shorthand alone", async () => {
|
|
4245
|
+
const { rewriteMentionShorthand } = await loadModule();
|
|
4246
|
+
const text = { body: { contentType: "text", content: "plain words" } };
|
|
4247
|
+
const bare = { body: { content: "plain words" } };
|
|
4248
|
+
expect(rewriteMentionShorthand(text).body).toBe(text);
|
|
4249
|
+
expect(rewriteMentionShorthand(bare).body).toBe(bare);
|
|
4250
|
+
});
|
|
4251
|
+
it.each([
|
|
4252
|
+
["text", { contentType: "text" }],
|
|
4253
|
+
["missing", {}]
|
|
4254
|
+
])("returns an error for the shorthand when contentType is %s", async (_label, type) => {
|
|
4255
|
+
const { rewriteMentionShorthand } = await loadModule();
|
|
4256
|
+
const input = { body: { ...type, content: `<at user-id="${U1}">Jane</at>` } };
|
|
4257
|
+
const out = rewriteMentionShorthand(input);
|
|
4258
|
+
expect(out.error).toContain("contentType");
|
|
4259
|
+
expect(out.body).toBe(input);
|
|
4260
|
+
});
|
|
4261
|
+
it.each([
|
|
4262
|
+
["single-quoted user-id", `<at user-id='${U1}'>Jane</at>`],
|
|
4263
|
+
["spaces around =", `<at user-id = "${U1}">Jane</at>`],
|
|
4264
|
+
["an extra attribute", `<at user-id="${U1}" class="x">Jane</at>`],
|
|
4265
|
+
["nested markup in the name", `<at user-id="${U1}"><b>Jane</b></at>`]
|
|
4266
|
+
])("returns an error for an unsupported tag form: %s", async (_label, tag) => {
|
|
4267
|
+
const { rewriteMentionShorthand } = await loadModule();
|
|
4268
|
+
const input = html(`Hi ${tag}`);
|
|
4269
|
+
const out = rewriteMentionShorthand(input);
|
|
4270
|
+
expect(out.error).toContain("Unsupported mention tag form");
|
|
4271
|
+
expect(out.body).toBe(input);
|
|
4272
|
+
});
|
|
4273
|
+
it("leaves a raw string body alone", async () => {
|
|
4274
|
+
const { rewriteMentionShorthand } = await loadModule();
|
|
4275
|
+
expect(rewriteMentionShorthand('{"body":{}}').body).toBe('{"body":{}}');
|
|
4276
|
+
expect(rewriteMentionShorthand(null).body).toBeNull();
|
|
4277
|
+
});
|
|
4278
|
+
it("keeps the display name verbatim", async () => {
|
|
4279
|
+
const { rewriteMentionShorthand } = await loadModule();
|
|
4280
|
+
const out = rewriteMentionShorthand(html(`<at user-id="${U1}">Smith & Co</at>`)).body;
|
|
4281
|
+
expect(out.body.content).toBe('<at id="0">Smith & Co</at>');
|
|
4282
|
+
expect(out.mentions[0].mentionText).toBe("Smith & Co");
|
|
4283
|
+
});
|
|
4284
|
+
});
|
|
4285
|
+
describe("mention shorthand (tool wiring)", () => {
|
|
4286
|
+
const U1 = "11111111-2222-3333-4444-555555555555";
|
|
4287
|
+
const aliases = [
|
|
4288
|
+
["send-channel-message", "post"],
|
|
4289
|
+
["reply-to-channel-message", "post"],
|
|
4290
|
+
["update-channel-message", "patch"],
|
|
4291
|
+
["update-channel-message-reply", "patch"],
|
|
4292
|
+
["send-chat-message", "post"],
|
|
4293
|
+
["reply-to-chat-message", "post"],
|
|
4294
|
+
["update-chat-message", "patch"]
|
|
4295
|
+
];
|
|
4296
|
+
async function call(alias, method, body) {
|
|
4297
|
+
mockEndpoints.push(
|
|
4298
|
+
makeEndpoint({
|
|
4299
|
+
alias,
|
|
4300
|
+
method,
|
|
4301
|
+
path: "/chats/{chat-id}/messages",
|
|
4302
|
+
parameters: [{ name: "body", type: "Body", schema: z.object({}).passthrough() }]
|
|
4303
|
+
})
|
|
4304
|
+
);
|
|
4305
|
+
mockEndpointsJson = [
|
|
4306
|
+
makeConfig({ toolName: alias, method, pathPattern: "/chats/{chat-id}/messages" })
|
|
4307
|
+
];
|
|
4308
|
+
const graphClient = createMockGraphClient([
|
|
4309
|
+
{ content: [{ type: "text", text: JSON.stringify({ id: "m1" }) }] }
|
|
4310
|
+
]);
|
|
4311
|
+
const server = createMockServer();
|
|
4312
|
+
const { registerGraphTools } = await loadModule();
|
|
4313
|
+
registerGraphTools(server, graphClient);
|
|
4314
|
+
const result = await server.tools.get(alias).handler({ "chat-id": "c1", body, confirm: true });
|
|
4315
|
+
return { result, graphClient };
|
|
4316
|
+
}
|
|
4317
|
+
const sent = (graphClient) => JSON.parse(graphClient.graphRequest.mock.calls[0][1].body);
|
|
4318
|
+
it.each(aliases)("rewrites the shorthand on %s", async (alias, method) => {
|
|
4319
|
+
const { graphClient } = await call(alias, method, {
|
|
4320
|
+
body: { contentType: "html", content: `Hi <at user-id="${U1}">Jane</at>` }
|
|
4321
|
+
});
|
|
4322
|
+
const out = sent(graphClient);
|
|
4323
|
+
expect(out.body.content).toBe('Hi <at id="0">Jane</at>');
|
|
4324
|
+
expect(out.mentions).toEqual([
|
|
4325
|
+
{
|
|
4326
|
+
id: 0,
|
|
4327
|
+
mentionText: "Jane",
|
|
4328
|
+
mentioned: { user: { id: U1, displayName: "Jane", userIdentityType: "aadUser" } }
|
|
4329
|
+
}
|
|
4330
|
+
]);
|
|
4331
|
+
});
|
|
4332
|
+
it("returns a tool error and sends nothing for a bad user-id", async () => {
|
|
4333
|
+
const { result, graphClient } = await call("send-chat-message", "post", {
|
|
4334
|
+
body: { contentType: "html", content: '<at user-id="jane">Jane</at>' }
|
|
4335
|
+
});
|
|
4336
|
+
expect(result.isError).toBe(true);
|
|
4337
|
+
expect(result.content[0].text).toContain("not an Entra object id");
|
|
4338
|
+
expect(graphClient.graphRequest).not.toHaveBeenCalled();
|
|
4339
|
+
});
|
|
4340
|
+
it("does not rewrite on a tool outside the seven", async () => {
|
|
4341
|
+
const { graphClient } = await call("create-draft-email", "post", {
|
|
4342
|
+
body: { contentType: "html", content: `<at user-id="${U1}">Jane</at>` }
|
|
4343
|
+
});
|
|
4344
|
+
expect(sent(graphClient).body.content).toBe(`<at user-id="${U1}">Jane</at>`);
|
|
4345
|
+
});
|
|
4346
|
+
it("lists only tools that exist in endpoints.json and documents the shorthand", async () => {
|
|
4347
|
+
const actualFs = await vi.importActual("fs");
|
|
4348
|
+
const endpoints = JSON.parse(
|
|
4349
|
+
actualFs.readFileSync(join(__dirname, "..", "endpoints.json"), "utf8")
|
|
4350
|
+
);
|
|
4351
|
+
for (const [alias] of aliases) {
|
|
4352
|
+
const entry = endpoints.find((e) => e.toolName === alias);
|
|
4353
|
+
expect(entry, alias).toBeDefined();
|
|
4354
|
+
const tip = entry.llmTip;
|
|
4355
|
+
expect(tip, alias).toContain("user-id=");
|
|
4356
|
+
expect(tip, alias).not.toContain("name param");
|
|
4357
|
+
expect(tip, alias).toContain("userId");
|
|
4358
|
+
expect(tip.split("To @mention someone").length - 1, alias).toBe(1);
|
|
4359
|
+
}
|
|
4360
|
+
});
|
|
4361
|
+
});
|
|
4193
4362
|
describe("filterMembersByName", () => {
|
|
4194
4363
|
const body = {
|
|
4195
4364
|
value: [
|
package/dist/endpoints.json
CHANGED
|
@@ -1759,7 +1759,8 @@
|
|
|
1759
1759
|
"method": "patch",
|
|
1760
1760
|
"toolName": "update-chat-message",
|
|
1761
1761
|
"presets": ["teams", "work"],
|
|
1762
|
-
"workScopes": ["Chat.ReadWrite"]
|
|
1762
|
+
"workScopes": ["Chat.ReadWrite"],
|
|
1763
|
+
"llmTip": "To @mention someone, put <at user-id=\"<aad-user-id>\">Display Name</at> in the html content; the server numbers the tag and builds the mentions array. Use the userId field (not id) from list-team-members or list-chat-members. Use contentType html in the body."
|
|
1763
1764
|
},
|
|
1764
1765
|
{
|
|
1765
1766
|
"pathPattern": "/chats/{chat-id}/messages/{chatMessage-id}/hostedContents",
|
|
@@ -1775,7 +1776,7 @@
|
|
|
1775
1776
|
"toolName": "send-chat-message",
|
|
1776
1777
|
"presets": ["teams", "teams-write", "work"],
|
|
1777
1778
|
"workScopes": ["ChatMessage.Send"],
|
|
1778
|
-
"llmTip": "Use contentType 'html' in the body — plain text contentType gets mangled by Graph API.
|
|
1779
|
+
"llmTip": "To @mention someone, put <at user-id=\"<aad-user-id>\">Display Name</at> in the html content; the server numbers the tag and builds the mentions array. Use the userId field (not id) from list-team-members or list-chat-members. Use contentType 'html' in the body — plain text contentType gets mangled by Graph API. Alternatively, hand-build the raw form: 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'."
|
|
1779
1780
|
},
|
|
1780
1781
|
{
|
|
1781
1782
|
"pathPattern": "/me/joinedTeams",
|
|
@@ -1878,7 +1879,8 @@
|
|
|
1878
1879
|
"method": "patch",
|
|
1879
1880
|
"toolName": "update-channel-message",
|
|
1880
1881
|
"presets": ["teams", "work"],
|
|
1881
|
-
"workScopes": ["ChannelMessage.ReadWrite"]
|
|
1882
|
+
"workScopes": ["ChannelMessage.ReadWrite"],
|
|
1883
|
+
"llmTip": "To @mention someone, put <at user-id=\"<aad-user-id>\">Display Name</at> in the html content; the server numbers the tag and builds the mentions array. Use the userId field (not id) from list-team-members or list-chat-members. Use contentType html in the body."
|
|
1882
1884
|
},
|
|
1883
1885
|
{
|
|
1884
1886
|
"pathPattern": "/teams/{team-id}/channels/{channel-id}/messages/{chatMessage-id}/hostedContents",
|
|
@@ -1894,7 +1896,7 @@
|
|
|
1894
1896
|
"toolName": "send-channel-message",
|
|
1895
1897
|
"presets": ["teams", "teams-write", "work"],
|
|
1896
1898
|
"workScopes": ["ChannelMessage.Send"],
|
|
1897
|
-
"llmTip": "Use contentType 'html' in the body — plain text contentType gets mangled by Graph API.
|
|
1899
|
+
"llmTip": "To @mention someone, put <at user-id=\"<aad-user-id>\">Display Name</at> in the html content; the server numbers the tag and builds the mentions array. Use the userId field (not id) from list-team-members or list-chat-members. Use contentType 'html' in the body — plain text contentType gets mangled by Graph API. Alternatively, hand-build the raw form: 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'."
|
|
1898
1900
|
},
|
|
1899
1901
|
{
|
|
1900
1902
|
"pathPattern": "/teams/{team-id}/channels/{channel-id}/messages/{chatMessage-id}/replies",
|
|
@@ -1902,7 +1904,7 @@
|
|
|
1902
1904
|
"toolName": "reply-to-channel-message",
|
|
1903
1905
|
"presets": ["teams", "teams-write", "work"],
|
|
1904
1906
|
"workScopes": ["ChannelMessage.Send"],
|
|
1905
|
-
"llmTip": "Use contentType 'html' in the body — plain text contentType gets mangled by Graph API.
|
|
1907
|
+
"llmTip": "To @mention someone, put <at user-id=\"<aad-user-id>\">Display Name</at> in the html content; the server numbers the tag and builds the mentions array. Use the userId field (not id) from list-team-members or list-chat-members. Use contentType 'html' in the body — plain text contentType gets mangled by Graph API. Alternatively, hand-build the raw form: 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'."
|
|
1906
1908
|
},
|
|
1907
1909
|
{
|
|
1908
1910
|
"pathPattern": "/teams/{team-id}/channels/{channel-id}/messages/{chatMessage-id}/replies",
|
|
@@ -1916,7 +1918,8 @@
|
|
|
1916
1918
|
"method": "patch",
|
|
1917
1919
|
"toolName": "update-channel-message-reply",
|
|
1918
1920
|
"presets": ["teams", "work"],
|
|
1919
|
-
"workScopes": ["ChannelMessage.ReadWrite"]
|
|
1921
|
+
"workScopes": ["ChannelMessage.ReadWrite"],
|
|
1922
|
+
"llmTip": "To @mention someone, put <at user-id=\"<aad-user-id>\">Display Name</at> in the html content; the server numbers the tag and builds the mentions array. Use the userId field (not id) from list-team-members or list-chat-members. Use contentType html in the body."
|
|
1920
1923
|
},
|
|
1921
1924
|
{
|
|
1922
1925
|
"pathPattern": "/teams/{team-id}/members",
|
|
@@ -1982,7 +1985,7 @@
|
|
|
1982
1985
|
"toolName": "reply-to-chat-message",
|
|
1983
1986
|
"presets": ["teams", "work"],
|
|
1984
1987
|
"workScopes": ["ChatMessage.Send"],
|
|
1985
|
-
"llmTip": "Use contentType 'html' in the body — plain text contentType gets mangled by Graph API.
|
|
1988
|
+
"llmTip": "To @mention someone, put <at user-id=\"<aad-user-id>\">Display Name</at> in the html content; the server numbers the tag and builds the mentions array. Use the userId field (not id) from list-team-members or list-chat-members. Use contentType 'html' in the body — plain text contentType gets mangled by Graph API. Alternatively, hand-build the raw form: 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'."
|
|
1986
1989
|
},
|
|
1987
1990
|
{
|
|
1988
1991
|
"pathPattern": "/chats/{chat-id}/messages/{chatMessage-id}/setReaction",
|
package/dist/graph-tools.js
CHANGED
|
@@ -397,6 +397,63 @@ function collectRecipients(node, domains, counter, depth = 0) {
|
|
|
397
397
|
collectRecipients(value, domains, counter, depth + 1);
|
|
398
398
|
}
|
|
399
399
|
}
|
|
400
|
+
const MENTION_SHORTHAND_TOOLS = /* @__PURE__ */ new Set([
|
|
401
|
+
"send-channel-message",
|
|
402
|
+
"reply-to-channel-message",
|
|
403
|
+
"update-channel-message",
|
|
404
|
+
"update-channel-message-reply",
|
|
405
|
+
"send-chat-message",
|
|
406
|
+
"reply-to-chat-message",
|
|
407
|
+
"update-chat-message"
|
|
408
|
+
]);
|
|
409
|
+
const AAD_GUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
410
|
+
const SHORTHAND_AT = /<at\s+user-id="([^"]*)">([^<]*)<\/at>/gi;
|
|
411
|
+
const HAS_SHORTHAND = /<at\b[^>]*\buser-id\b/i;
|
|
412
|
+
function rewriteMentionShorthand(body) {
|
|
413
|
+
if (!isPlainObject(body) || !isPlainObject(body.body)) return { body };
|
|
414
|
+
const itemBody = body.body;
|
|
415
|
+
const content = itemBody.content;
|
|
416
|
+
if (typeof content !== "string" || !HAS_SHORTHAND.test(content)) return { body };
|
|
417
|
+
if (String(itemBody.contentType).toLowerCase() !== "html") {
|
|
418
|
+
return {
|
|
419
|
+
body,
|
|
420
|
+
error: 'The <at user-id> mention shorthand needs contentType "html" on the message body. Set body.contentType to "html".'
|
|
421
|
+
};
|
|
422
|
+
}
|
|
423
|
+
const existing = Array.isArray(body.mentions) ? body.mentions : [];
|
|
424
|
+
let nextId = 0;
|
|
425
|
+
for (const m of existing) {
|
|
426
|
+
if (isPlainObject(m) && typeof m.id === "number") nextId = Math.max(nextId, m.id + 1);
|
|
427
|
+
}
|
|
428
|
+
for (const m of content.matchAll(/<at\s+id="(\d+)"/gi)) {
|
|
429
|
+
nextId = Math.max(nextId, Number(m[1]) + 1);
|
|
430
|
+
}
|
|
431
|
+
const added = [];
|
|
432
|
+
let error;
|
|
433
|
+
const rewritten = content.replace(SHORTHAND_AT, (tag, userId, name) => {
|
|
434
|
+
if (!AAD_GUID.test(userId)) {
|
|
435
|
+
error ?? (error = `Mention user-id "${userId}" is not an Entra object id (GUID). Use the userId from list-team-members or list-chat-members.`);
|
|
436
|
+
return tag;
|
|
437
|
+
}
|
|
438
|
+
const id = nextId++;
|
|
439
|
+
added.push({
|
|
440
|
+
id,
|
|
441
|
+
mentionText: name,
|
|
442
|
+
mentioned: { user: { id: userId, displayName: name, userIdentityType: "aadUser" } }
|
|
443
|
+
});
|
|
444
|
+
return `<at id="${id}">${name}</at>`;
|
|
445
|
+
});
|
|
446
|
+
if (error) return { body, error };
|
|
447
|
+
if (HAS_SHORTHAND.test(rewritten)) {
|
|
448
|
+
return {
|
|
449
|
+
body,
|
|
450
|
+
error: 'Unsupported mention tag form. Write <at user-id="<guid>">Name</at> with double quotes, no other attributes and plain text inside.'
|
|
451
|
+
};
|
|
452
|
+
}
|
|
453
|
+
return {
|
|
454
|
+
body: { ...body, body: { ...itemBody, content: rewritten }, mentions: [...existing, ...added] }
|
|
455
|
+
};
|
|
456
|
+
}
|
|
400
457
|
function bodyForRecipientWalk(body) {
|
|
401
458
|
if (typeof body !== "string") return body;
|
|
402
459
|
if (!/^\s*[{[]/.test(body)) return void 0;
|
|
@@ -1730,6 +1787,16 @@ async function executeGraphTool(tool, config, graphClient, params, authManager,
|
|
|
1730
1787
|
);
|
|
1731
1788
|
}
|
|
1732
1789
|
}
|
|
1790
|
+
if (MENTION_SHORTHAND_TOOLS.has(tool.alias)) {
|
|
1791
|
+
const rewritten = rewriteMentionShorthand(body);
|
|
1792
|
+
if (rewritten.error) {
|
|
1793
|
+
return {
|
|
1794
|
+
content: [{ type: "text", text: JSON.stringify({ error: rewritten.error }) }],
|
|
1795
|
+
isError: true
|
|
1796
|
+
};
|
|
1797
|
+
}
|
|
1798
|
+
body = rewritten.body;
|
|
1799
|
+
}
|
|
1733
1800
|
if (TOP_UNSUPPORTED_DELTA_TOOLS.has(tool.alias)) {
|
|
1734
1801
|
delete queryParams["$top"];
|
|
1735
1802
|
}
|
|
@@ -2588,5 +2655,6 @@ export {
|
|
|
2588
2655
|
isDestructiveOperation,
|
|
2589
2656
|
registerDiscoveryTools,
|
|
2590
2657
|
registerGraphTools,
|
|
2658
|
+
rewriteMentionShorthand,
|
|
2591
2659
|
scoreDiscoveryQuery
|
|
2592
2660
|
};
|
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.
|
|
4
|
+
"version": "0.168.0",
|
|
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",
|
package/src/endpoints.json
CHANGED
|
@@ -1759,7 +1759,8 @@
|
|
|
1759
1759
|
"method": "patch",
|
|
1760
1760
|
"toolName": "update-chat-message",
|
|
1761
1761
|
"presets": ["teams", "work"],
|
|
1762
|
-
"workScopes": ["Chat.ReadWrite"]
|
|
1762
|
+
"workScopes": ["Chat.ReadWrite"],
|
|
1763
|
+
"llmTip": "To @mention someone, put <at user-id=\"<aad-user-id>\">Display Name</at> in the html content; the server numbers the tag and builds the mentions array. Use the userId field (not id) from list-team-members or list-chat-members. Use contentType html in the body."
|
|
1763
1764
|
},
|
|
1764
1765
|
{
|
|
1765
1766
|
"pathPattern": "/chats/{chat-id}/messages/{chatMessage-id}/hostedContents",
|
|
@@ -1775,7 +1776,7 @@
|
|
|
1775
1776
|
"toolName": "send-chat-message",
|
|
1776
1777
|
"presets": ["teams", "teams-write", "work"],
|
|
1777
1778
|
"workScopes": ["ChatMessage.Send"],
|
|
1778
|
-
"llmTip": "Use contentType 'html' in the body — plain text contentType gets mangled by Graph API.
|
|
1779
|
+
"llmTip": "To @mention someone, put <at user-id=\"<aad-user-id>\">Display Name</at> in the html content; the server numbers the tag and builds the mentions array. Use the userId field (not id) from list-team-members or list-chat-members. Use contentType 'html' in the body — plain text contentType gets mangled by Graph API. Alternatively, hand-build the raw form: 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'."
|
|
1779
1780
|
},
|
|
1780
1781
|
{
|
|
1781
1782
|
"pathPattern": "/me/joinedTeams",
|
|
@@ -1878,7 +1879,8 @@
|
|
|
1878
1879
|
"method": "patch",
|
|
1879
1880
|
"toolName": "update-channel-message",
|
|
1880
1881
|
"presets": ["teams", "work"],
|
|
1881
|
-
"workScopes": ["ChannelMessage.ReadWrite"]
|
|
1882
|
+
"workScopes": ["ChannelMessage.ReadWrite"],
|
|
1883
|
+
"llmTip": "To @mention someone, put <at user-id=\"<aad-user-id>\">Display Name</at> in the html content; the server numbers the tag and builds the mentions array. Use the userId field (not id) from list-team-members or list-chat-members. Use contentType html in the body."
|
|
1882
1884
|
},
|
|
1883
1885
|
{
|
|
1884
1886
|
"pathPattern": "/teams/{team-id}/channels/{channel-id}/messages/{chatMessage-id}/hostedContents",
|
|
@@ -1894,7 +1896,7 @@
|
|
|
1894
1896
|
"toolName": "send-channel-message",
|
|
1895
1897
|
"presets": ["teams", "teams-write", "work"],
|
|
1896
1898
|
"workScopes": ["ChannelMessage.Send"],
|
|
1897
|
-
"llmTip": "Use contentType 'html' in the body — plain text contentType gets mangled by Graph API.
|
|
1899
|
+
"llmTip": "To @mention someone, put <at user-id=\"<aad-user-id>\">Display Name</at> in the html content; the server numbers the tag and builds the mentions array. Use the userId field (not id) from list-team-members or list-chat-members. Use contentType 'html' in the body — plain text contentType gets mangled by Graph API. Alternatively, hand-build the raw form: 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'."
|
|
1898
1900
|
},
|
|
1899
1901
|
{
|
|
1900
1902
|
"pathPattern": "/teams/{team-id}/channels/{channel-id}/messages/{chatMessage-id}/replies",
|
|
@@ -1902,7 +1904,7 @@
|
|
|
1902
1904
|
"toolName": "reply-to-channel-message",
|
|
1903
1905
|
"presets": ["teams", "teams-write", "work"],
|
|
1904
1906
|
"workScopes": ["ChannelMessage.Send"],
|
|
1905
|
-
"llmTip": "Use contentType 'html' in the body — plain text contentType gets mangled by Graph API.
|
|
1907
|
+
"llmTip": "To @mention someone, put <at user-id=\"<aad-user-id>\">Display Name</at> in the html content; the server numbers the tag and builds the mentions array. Use the userId field (not id) from list-team-members or list-chat-members. Use contentType 'html' in the body — plain text contentType gets mangled by Graph API. Alternatively, hand-build the raw form: 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'."
|
|
1906
1908
|
},
|
|
1907
1909
|
{
|
|
1908
1910
|
"pathPattern": "/teams/{team-id}/channels/{channel-id}/messages/{chatMessage-id}/replies",
|
|
@@ -1916,7 +1918,8 @@
|
|
|
1916
1918
|
"method": "patch",
|
|
1917
1919
|
"toolName": "update-channel-message-reply",
|
|
1918
1920
|
"presets": ["teams", "work"],
|
|
1919
|
-
"workScopes": ["ChannelMessage.ReadWrite"]
|
|
1921
|
+
"workScopes": ["ChannelMessage.ReadWrite"],
|
|
1922
|
+
"llmTip": "To @mention someone, put <at user-id=\"<aad-user-id>\">Display Name</at> in the html content; the server numbers the tag and builds the mentions array. Use the userId field (not id) from list-team-members or list-chat-members. Use contentType html in the body."
|
|
1920
1923
|
},
|
|
1921
1924
|
{
|
|
1922
1925
|
"pathPattern": "/teams/{team-id}/members",
|
|
@@ -1982,7 +1985,7 @@
|
|
|
1982
1985
|
"toolName": "reply-to-chat-message",
|
|
1983
1986
|
"presets": ["teams", "work"],
|
|
1984
1987
|
"workScopes": ["ChatMessage.Send"],
|
|
1985
|
-
"llmTip": "Use contentType 'html' in the body — plain text contentType gets mangled by Graph API.
|
|
1988
|
+
"llmTip": "To @mention someone, put <at user-id=\"<aad-user-id>\">Display Name</at> in the html content; the server numbers the tag and builds the mentions array. Use the userId field (not id) from list-team-members or list-chat-members. Use contentType 'html' in the body — plain text contentType gets mangled by Graph API. Alternatively, hand-build the raw form: 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'."
|
|
1986
1989
|
},
|
|
1987
1990
|
{
|
|
1988
1991
|
"pathPattern": "/chats/{chat-id}/messages/{chatMessage-id}/setReaction",
|