Skip to content

Commit 03462b9

Browse files
authored
fix(microsoft-teams): align tools with live Graph API docs, add missing endpoints (#5477)
* fix(microsoft-teams): align tools with live Graph API docs, add missing endpoints - fix list_channel_members/list_team_members falling back to userId when email is missing - add $top=50 pagination to read_channel for parity with read_chat - add list_teams, list_chats, list_channels, list_chat_members tools so agents can discover ids without the UI selectors - all new tools use only existing granted scopes (Team.ReadBasic.All, Chat.ReadBasic, Channel.ReadBasic.All) — no new OAuth scopes requested * fix(microsoft-teams): surface pagination truncation via hasMore flag - add hasMore output (derived from @odata.nextLink presence) to list_teams, list_chats, list_channels, list_chat_members so agents can detect truncated results instead of trusting the count field as a total - do NOT add $top to list_teams' joinedTeams request — Graph docs explicitly state this endpoint does not support OData query parameters, so it would silently no-op
1 parent fe7d043 commit 03462b9

11 files changed

Lines changed: 464 additions & 7 deletions

File tree

apps/sim/blocks/blocks/microsoft_teams.ts

Lines changed: 66 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ export const MicrosoftTeamsBlock: BlockConfig<MicrosoftTeamsResponse> = {
1212
description: 'Manage messages, reactions, and members in Teams',
1313
authMode: AuthMode.OAuth,
1414
longDescription:
15-
'Integrate Microsoft Teams into the workflow. Read, write, update, and delete chat and channel messages. Reply to messages, add reactions, and list team/channel members. Can be used in trigger mode to trigger a workflow when a message is sent to a chat or channel. To mention users in messages, wrap their name in `<at>` tags: `<at>userName</at>`',
15+
'Integrate Microsoft Teams into the workflow. Read, write, update, and delete chat and channel messages. Reply to messages, add reactions, and list teams, chats, channels, and their members. Can be used in trigger mode to trigger a workflow when a message is sent to a chat or channel. To mention users in messages, wrap their name in `<at>` tags: `<at>userName</at>`',
1616
docsLink: 'https://docs.sim.ai/integrations/microsoft_teams',
1717
category: 'tools',
1818
integrationType: IntegrationType.Communication,
@@ -39,6 +39,10 @@ export const MicrosoftTeamsBlock: BlockConfig<MicrosoftTeamsResponse> = {
3939
{ label: 'Remove Reaction', id: 'unset_reaction' },
4040
{ label: 'List Team Members', id: 'list_team_members' },
4141
{ label: 'List Channel Members', id: 'list_channel_members' },
42+
{ label: 'List Chat Members', id: 'list_chat_members' },
43+
{ label: 'List Teams', id: 'list_teams' },
44+
{ label: 'List Chats', id: 'list_chats' },
45+
{ label: 'List Channels', id: 'list_channels' },
4246
],
4347
value: () => 'read_chat',
4448
},
@@ -83,6 +87,7 @@ export const MicrosoftTeamsBlock: BlockConfig<MicrosoftTeamsResponse> = {
8387
'reply_to_message',
8488
'list_team_members',
8589
'list_channel_members',
90+
'list_channels',
8691
],
8792
},
8893
required: true,
@@ -105,6 +110,7 @@ export const MicrosoftTeamsBlock: BlockConfig<MicrosoftTeamsResponse> = {
105110
'reply_to_message',
106111
'list_team_members',
107112
'list_channel_members',
113+
'list_channels',
108114
],
109115
},
110116
required: true,
@@ -122,7 +128,13 @@ export const MicrosoftTeamsBlock: BlockConfig<MicrosoftTeamsResponse> = {
122128
mode: 'basic',
123129
condition: {
124130
field: 'operation',
125-
value: ['read_chat', 'write_chat', 'update_chat_message', 'delete_chat_message'],
131+
value: [
132+
'read_chat',
133+
'write_chat',
134+
'update_chat_message',
135+
'delete_chat_message',
136+
'list_chat_members',
137+
],
126138
},
127139
required: true,
128140
},
@@ -136,7 +148,13 @@ export const MicrosoftTeamsBlock: BlockConfig<MicrosoftTeamsResponse> = {
136148
mode: 'advanced',
137149
condition: {
138150
field: 'operation',
139-
value: ['read_chat', 'write_chat', 'update_chat_message', 'delete_chat_message'],
151+
value: [
152+
'read_chat',
153+
'write_chat',
154+
'update_chat_message',
155+
'delete_chat_message',
156+
'list_chat_members',
157+
],
140158
},
141159
required: true,
142160
},
@@ -281,6 +299,10 @@ export const MicrosoftTeamsBlock: BlockConfig<MicrosoftTeamsResponse> = {
281299
'microsoft_teams_unset_reaction',
282300
'microsoft_teams_list_team_members',
283301
'microsoft_teams_list_channel_members',
302+
'microsoft_teams_list_chat_members',
303+
'microsoft_teams_list_teams',
304+
'microsoft_teams_list_chats',
305+
'microsoft_teams_list_channels',
284306
],
285307
config: {
286308
tool: (params) => {
@@ -313,6 +335,14 @@ export const MicrosoftTeamsBlock: BlockConfig<MicrosoftTeamsResponse> = {
313335
return 'microsoft_teams_list_team_members'
314336
case 'list_channel_members':
315337
return 'microsoft_teams_list_channel_members'
338+
case 'list_chat_members':
339+
return 'microsoft_teams_list_chat_members'
340+
case 'list_teams':
341+
return 'microsoft_teams_list_teams'
342+
case 'list_chats':
343+
return 'microsoft_teams_list_chats'
344+
case 'list_channels':
345+
return 'microsoft_teams_list_channels'
316346
default:
317347
return 'microsoft_teams_read_chat'
318348
}
@@ -393,6 +423,21 @@ export const MicrosoftTeamsBlock: BlockConfig<MicrosoftTeamsResponse> = {
393423
return { ...baseParams, teamId: effectiveTeamId, channelId: effectiveChannelId }
394424
}
395425

426+
// Chat member operations
427+
if (operation === 'list_chat_members') {
428+
return { ...baseParams, chatId: effectiveChatId }
429+
}
430+
431+
// List channels in a team
432+
if (operation === 'list_channels') {
433+
return { ...baseParams, teamId: effectiveTeamId }
434+
}
435+
436+
// List the user's teams / chats — no additional identifiers required
437+
if (operation === 'list_teams' || operation === 'list_chats') {
438+
return baseParams
439+
}
440+
396441
// Operations that work with either chat or channel (get_message, reactions)
397442
// These tools handle the routing internally based on what IDs are provided
398443
if (
@@ -462,8 +507,18 @@ export const MicrosoftTeamsBlock: BlockConfig<MicrosoftTeamsResponse> = {
462507
},
463508
reactionType: { type: 'string', description: 'Emoji reaction that was added/removed' },
464509
success: { type: 'boolean', description: 'Whether the operation was successful' },
465-
members: { type: 'json', description: 'Array of team/channel member objects' },
510+
members: { type: 'json', description: 'Array of team/channel/chat member objects' },
466511
memberCount: { type: 'number', description: 'Total number of members' },
512+
teams: { type: 'json', description: 'Array of teams the user is a member of' },
513+
teamCount: { type: 'number', description: 'Total number of teams' },
514+
chats: { type: 'json', description: 'Array of chats the user is part of' },
515+
chatCount: { type: 'number', description: 'Total number of chats' },
516+
channels: { type: 'json', description: 'Array of channels in the team' },
517+
channelCount: { type: 'number', description: 'Total number of channels' },
518+
hasMore: {
519+
type: 'boolean',
520+
description: 'Whether Graph indicated additional pages beyond this response',
521+
},
467522
type: { type: 'string', description: 'Type of Teams message' },
468523
id: { type: 'string', description: 'Unique message identifier' },
469524
timestamp: { type: 'string', description: 'Message timestamp' },
@@ -580,5 +635,12 @@ export const MicrosoftTeamsBlockMeta = {
580635
content:
581636
'# List Team Members\n\nRetrieve who belongs to a Microsoft Teams team or channel.\n\n## Steps\n1. Decide whether you need team-wide membership or a single channel and pick List Team Members or List Channel Members.\n2. Run the operation with the team id (and channel id when needed).\n3. Normalize the result into a clean roster of names and roles.\n\n## Output\nA roster list with display name and role. Note the total count at the top.',
582637
},
638+
{
639+
name: 'discover-teams-chats-channels',
640+
description:
641+
'Enumerate the teams, chats, and channels available to the connected Microsoft account before acting on them.',
642+
content:
643+
'# Discover Teams, Chats, and Channels\n\nResolve human-friendly names to the ids other Microsoft Teams operations need.\n\n## Steps\n1. Run List Teams to see every team the connected account belongs to, or List Chats to see active chats.\n2. If the target is a channel, run List Channels with the resolved team id to find the channel id.\n3. If the target is a chat, run List Chat Members to confirm the right people are present before writing to it.\n4. Feed the resolved team id, channel id, or chat id into the read/write/reply/reaction operations.\n\n## Output\nReturn the matched team, chat, or channel name alongside its id so subsequent steps can reference it.',
644+
},
583645
],
584646
} as const satisfies BlockMeta

apps/sim/tools/microsoft_teams/index.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,11 @@ import { deleteChannelMessageTool } from '@/tools/microsoft_teams/delete_channel
22
import { deleteChatMessageTool } from '@/tools/microsoft_teams/delete_chat_message'
33
import { getMessageTool } from '@/tools/microsoft_teams/get_message'
44
import { listChannelMembersTool } from '@/tools/microsoft_teams/list_channel_members'
5+
import { listChannelsTool } from '@/tools/microsoft_teams/list_channels'
6+
import { listChatMembersTool } from '@/tools/microsoft_teams/list_chat_members'
7+
import { listChatsTool } from '@/tools/microsoft_teams/list_chats'
58
import { listTeamMembersTool } from '@/tools/microsoft_teams/list_team_members'
9+
import { listTeamsTool } from '@/tools/microsoft_teams/list_teams'
610
import { readChannelTool } from '@/tools/microsoft_teams/read_channel'
711
import { readChatTool } from '@/tools/microsoft_teams/read_chat'
812
import { replyToMessageTool } from '@/tools/microsoft_teams/reply_to_message'
@@ -27,3 +31,7 @@ export const microsoftTeamsSetReactionTool = setReactionTool
2731
export const microsoftTeamsUnsetReactionTool = unsetReactionTool
2832
export const microsoftTeamsListTeamMembersTool = listTeamMembersTool
2933
export const microsoftTeamsListChannelMembersTool = listChannelMembersTool
34+
export const microsoftTeamsListChatMembersTool = listChatMembersTool
35+
export const microsoftTeamsListTeamsTool = listTeamsTool
36+
export const microsoftTeamsListChatsTool = listChatsTool
37+
export const microsoftTeamsListChannelsTool = listChannelsTool

apps/sim/tools/microsoft_teams/list_channel_members.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ export const listChannelMembersTool: ToolConfig<
7272
const members = (data.value || []).map((member: any) => ({
7373
id: member.id || '',
7474
displayName: member.displayName || '',
75-
email: member.email || member.userId || '',
75+
email: member.email || '',
7676
userId: member.userId || '',
7777
roles: member.roles || [],
7878
}))
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
import type {
2+
MicrosoftTeamsListChannelsResponse,
3+
MicrosoftTeamsToolParams,
4+
} from '@/tools/microsoft_teams/types'
5+
import type { ToolConfig } from '@/tools/types'
6+
7+
export const listChannelsTool: ToolConfig<
8+
MicrosoftTeamsToolParams,
9+
MicrosoftTeamsListChannelsResponse
10+
> = {
11+
id: 'microsoft_teams_list_channels',
12+
name: 'List Microsoft Teams Channels',
13+
description: 'List all channels in a Microsoft Teams team',
14+
version: '1.0',
15+
errorExtractor: 'nested-error-object',
16+
oauth: {
17+
required: true,
18+
provider: 'microsoft-teams',
19+
},
20+
params: {
21+
accessToken: {
22+
type: 'string',
23+
required: true,
24+
visibility: 'hidden',
25+
description: 'The access token for the Microsoft Teams API',
26+
},
27+
teamId: {
28+
type: 'string',
29+
required: true,
30+
visibility: 'user-or-llm',
31+
description:
32+
'The ID of the team (e.g., "12345678-abcd-1234-efgh-123456789012" - a GUID from team listings)',
33+
},
34+
},
35+
36+
outputs: {
37+
success: { type: 'boolean', description: 'Whether the listing was successful' },
38+
channels: { type: 'array', description: 'Array of channels in the team' },
39+
channelCount: { type: 'number', description: 'Total number of channels' },
40+
hasMore: {
41+
type: 'boolean',
42+
description: 'Whether Graph indicated additional pages beyond this response',
43+
},
44+
},
45+
46+
request: {
47+
url: (params) => {
48+
const teamId = params.teamId?.trim()
49+
if (!teamId) {
50+
throw new Error('Team ID is required')
51+
}
52+
return `https://graph.microsoft.com/v1.0/teams/${encodeURIComponent(teamId)}/channels`
53+
},
54+
method: 'GET',
55+
headers: (params) => {
56+
if (!params.accessToken) {
57+
throw new Error('Access token is required')
58+
}
59+
return {
60+
Authorization: `Bearer ${params.accessToken}`,
61+
}
62+
},
63+
},
64+
65+
transformResponse: async (response: Response, params?: MicrosoftTeamsToolParams) => {
66+
const data = await response.json()
67+
68+
const channels = (data.value || []).map((channel: any) => ({
69+
id: channel.id || '',
70+
displayName: channel.displayName || '',
71+
description: channel.description ?? '',
72+
membershipType: channel.membershipType || 'standard',
73+
webUrl: channel.webUrl || '',
74+
}))
75+
76+
return {
77+
success: true,
78+
output: {
79+
channels,
80+
channelCount: channels.length,
81+
hasMore: Boolean(data['@odata.nextLink']),
82+
metadata: {
83+
teamId: params?.teamId || '',
84+
},
85+
},
86+
}
87+
},
88+
}
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
import type {
2+
MicrosoftTeamsListMembersResponse,
3+
MicrosoftTeamsToolParams,
4+
} from '@/tools/microsoft_teams/types'
5+
import type { ToolConfig } from '@/tools/types'
6+
7+
export const listChatMembersTool: ToolConfig<
8+
MicrosoftTeamsToolParams,
9+
MicrosoftTeamsListMembersResponse
10+
> = {
11+
id: 'microsoft_teams_list_chat_members',
12+
name: 'List Microsoft Teams Chat Members',
13+
description: 'List all members of a Microsoft Teams chat',
14+
version: '1.0',
15+
errorExtractor: 'nested-error-object',
16+
oauth: {
17+
required: true,
18+
provider: 'microsoft-teams',
19+
},
20+
params: {
21+
accessToken: {
22+
type: 'string',
23+
required: true,
24+
visibility: 'hidden',
25+
description: 'The access token for the Microsoft Teams API',
26+
},
27+
chatId: {
28+
type: 'string',
29+
required: true,
30+
visibility: 'user-or-llm',
31+
description: 'The ID of the chat (e.g., "19:abc123def456@thread.v2" - from chat listings)',
32+
},
33+
},
34+
35+
outputs: {
36+
success: { type: 'boolean', description: 'Whether the listing was successful' },
37+
members: { type: 'array', description: 'Array of chat members' },
38+
memberCount: { type: 'number', description: 'Total number of members' },
39+
hasMore: {
40+
type: 'boolean',
41+
description: 'Whether Graph indicated additional pages beyond this response',
42+
},
43+
},
44+
45+
request: {
46+
url: (params) => {
47+
const chatId = params.chatId?.trim()
48+
if (!chatId) {
49+
throw new Error('Chat ID is required')
50+
}
51+
return `https://graph.microsoft.com/v1.0/chats/${encodeURIComponent(chatId)}/members`
52+
},
53+
method: 'GET',
54+
headers: (params) => {
55+
if (!params.accessToken) {
56+
throw new Error('Access token is required')
57+
}
58+
return {
59+
Authorization: `Bearer ${params.accessToken}`,
60+
}
61+
},
62+
},
63+
64+
transformResponse: async (response: Response, params?: MicrosoftTeamsToolParams) => {
65+
const data = await response.json()
66+
67+
const members = (data.value || []).map((member: any) => ({
68+
id: member.id || '',
69+
displayName: member.displayName || '',
70+
email: member.email || '',
71+
userId: member.userId || '',
72+
roles: member.roles || [],
73+
}))
74+
75+
return {
76+
success: true,
77+
output: {
78+
members,
79+
memberCount: members.length,
80+
hasMore: Boolean(data['@odata.nextLink']),
81+
metadata: {
82+
chatId: params?.chatId || '',
83+
},
84+
},
85+
}
86+
},
87+
}

0 commit comments

Comments
 (0)