@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.
@@ -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 &amp; Co</at>`)).body;
4281
+ expect(out.body.content).toBe('<at id="0">Smith &amp; Co</at>');
4282
+ expect(out.mentions[0].mentionText).toBe("Smith &amp; 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: [
@@ -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. 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'."
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. 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'."
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. 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'."
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. 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'."
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",
@@ -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.167.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",
@@ -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. 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'."
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. 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'."
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. 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'."
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. 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'."
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",