Skip to main content
Glama

reply_to_channel_message

Respond to a specific message in a Microsoft Teams channel with formatted text, mentions, importance levels, and optional image attachments. Enables targeted and structured replies for effective communication.

Instructions

Reply to a specific message in a channel. Supports text and markdown formatting, mentions, and importance levels.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
channelIdYesChannel ID
formatNoMessage format (text or markdown)
imageContentTypeNoMIME type of the image (e.g., 'image/jpeg', 'image/png')
imageDataNoBase64 encoded image data to attach
imageFileNameNoName for the attached image file
imageUrlNoURL of an image to attach to the reply
importanceNoMessage importance
mentionsNoArray of @mentions to include in the reply
messageYesReply content
messageIdYesMessage ID to reply to
teamIdYesTeam ID

Implementation Reference

  • Full registration of the reply_to_channel_message tool including the Zod input schema and the complete async handler function. The handler processes message formatting (markdown/HTML), handles user mentions by fetching display names and injecting HTML, uploads image attachments as hosted content, and posts the reply to the Microsoft Graph endpoint `/teams/${teamId}/channels/${channelId}/messages/${messageId}/replies`.
    server.tool( "reply_to_channel_message", "Reply to a specific message in a channel. Supports text and markdown formatting, mentions, and importance levels.", { teamId: z.string().describe("Team ID"), channelId: z.string().describe("Channel ID"), messageId: z.string().describe("Message ID to reply to"), message: z.string().describe("Reply content"), importance: z.enum(["normal", "high", "urgent"]).optional().describe("Message importance"), format: z.enum(["text", "markdown"]).optional().describe("Message format (text or markdown)"), mentions: z .array( z.object({ mention: z .string() .describe("The @mention text (e.g., 'john.doe' or 'john.doe@company.com')"), userId: z.string().describe("Azure AD User ID of the mentioned user"), }) ) .optional() .describe("Array of @mentions to include in the reply"), imageUrl: z.string().optional().describe("URL of an image to attach to the reply"), imageData: z.string().optional().describe("Base64 encoded image data to attach"), imageContentType: z .string() .optional() .describe("MIME type of the image (e.g., 'image/jpeg', 'image/png')"), imageFileName: z.string().optional().describe("Name for the attached image file"), }, async ({ teamId, channelId, messageId, message, importance = "normal", format = "text", mentions, imageUrl, imageData, imageContentType, imageFileName, }) => { try { const client = await graphService.getClient(); // Process message content based on format let content: string; let contentType: "text" | "html"; if (format === "markdown") { content = await markdownToHtml(message); contentType = "html"; } else { content = message; contentType = "text"; } // Process @mentions if provided const mentionMappings: Array<{ mention: string; userId: string; displayName: string }> = []; if (mentions && mentions.length > 0) { // Convert provided mentions to mappings with display names for (const mention of mentions) { try { // Get user info to get display name const userResponse = await client .api(`/users/${mention.userId}`) .select("displayName") .get(); mentionMappings.push({ mention: mention.mention, userId: mention.userId, displayName: userResponse.displayName || mention.mention, }); } catch (_error) { console.warn( `Could not resolve user ${mention.userId}, using mention text as display name` ); mentionMappings.push({ mention: mention.mention, userId: mention.userId, displayName: mention.mention, }); } } } // Process mentions in HTML content let finalMentions: Array<{ id: number; mentionText: string; mentioned: { user: { id: string } }; }> = []; if (mentionMappings.length > 0) { const result = processMentionsInHtml(content, mentionMappings); content = result.content; finalMentions = result.mentions; // Ensure we're using HTML content type when mentions are present contentType = "html"; } // Handle image attachment const attachments: ImageAttachment[] = []; if (imageUrl || imageData) { let imageInfo: { data: string; contentType: string } | null = null; if (imageUrl) { imageInfo = await imageUrlToBase64(imageUrl); if (!imageInfo) { return { content: [ { type: "text" as const, text: `❌ Failed to download image from URL: ${imageUrl}`, }, ], isError: true, }; } } else if (imageData && imageContentType) { if (!isValidImageType(imageContentType)) { return { content: [ { type: "text" as const, text: `❌ Unsupported image type: ${imageContentType}`, }, ], isError: true, }; } imageInfo = { data: imageData, contentType: imageContentType }; } if (imageInfo) { const uploadResult = await uploadImageAsHostedContent( graphService, teamId, channelId, imageInfo.data, imageInfo.contentType, imageFileName ); if (uploadResult) { attachments.push(uploadResult.attachment); } else { return { content: [ { type: "text" as const, text: "❌ Failed to upload image attachment", }, ], isError: true, }; } } } // Build message payload const messagePayload: any = { body: { content, contentType, }, importance, }; if (finalMentions.length > 0) { messagePayload.mentions = finalMentions; } if (attachments.length > 0) { messagePayload.attachments = attachments; } const result = (await client .api(`/teams/${teamId}/channels/${channelId}/messages/${messageId}/replies`) .post(messagePayload)) as ChatMessage; // Build success message const successText = `✅ Reply sent successfully. Reply ID: ${result.id}${ finalMentions.length > 0 ? `\n📱 Mentions: ${finalMentions.map((m) => m.mentionText).join(", ")}` : "" }${attachments.length > 0 ? `\n🖼️ Image attached: ${attachments[0].name}` : ""}`; return { content: [ { type: "text" as const, text: successText, }, ], }; } catch (error: any) { return { content: [ { type: "text" as const, text: `❌ Failed to send reply: ${error.message}`, }, ], isError: true, }; } } );
  • The core handler function that executes the tool. It authenticates with GraphService, converts markdown to HTML if needed, processes @mentions by resolving user display names and injecting HTML tags, handles image uploads via uploadImageAsHostedContent, constructs the message payload, and sends the POST request to create the reply.
    async ({ teamId, channelId, messageId, message, importance = "normal", format = "text", mentions, imageUrl, imageData, imageContentType, imageFileName, }) => { try { const client = await graphService.getClient(); // Process message content based on format let content: string; let contentType: "text" | "html"; if (format === "markdown") { content = await markdownToHtml(message); contentType = "html"; } else { content = message; contentType = "text"; } // Process @mentions if provided const mentionMappings: Array<{ mention: string; userId: string; displayName: string }> = []; if (mentions && mentions.length > 0) { // Convert provided mentions to mappings with display names for (const mention of mentions) { try { // Get user info to get display name const userResponse = await client .api(`/users/${mention.userId}`) .select("displayName") .get(); mentionMappings.push({ mention: mention.mention, userId: mention.userId, displayName: userResponse.displayName || mention.mention, }); } catch (_error) { console.warn( `Could not resolve user ${mention.userId}, using mention text as display name` ); mentionMappings.push({ mention: mention.mention, userId: mention.userId, displayName: mention.mention, }); } } } // Process mentions in HTML content let finalMentions: Array<{ id: number; mentionText: string; mentioned: { user: { id: string } }; }> = []; if (mentionMappings.length > 0) { const result = processMentionsInHtml(content, mentionMappings); content = result.content; finalMentions = result.mentions; // Ensure we're using HTML content type when mentions are present contentType = "html"; } // Handle image attachment const attachments: ImageAttachment[] = []; if (imageUrl || imageData) { let imageInfo: { data: string; contentType: string } | null = null; if (imageUrl) { imageInfo = await imageUrlToBase64(imageUrl); if (!imageInfo) { return { content: [ { type: "text" as const, text: `❌ Failed to download image from URL: ${imageUrl}`, }, ], isError: true, }; } } else if (imageData && imageContentType) { if (!isValidImageType(imageContentType)) { return { content: [ { type: "text" as const, text: `❌ Unsupported image type: ${imageContentType}`, }, ], isError: true, }; } imageInfo = { data: imageData, contentType: imageContentType }; } if (imageInfo) { const uploadResult = await uploadImageAsHostedContent( graphService, teamId, channelId, imageInfo.data, imageInfo.contentType, imageFileName ); if (uploadResult) { attachments.push(uploadResult.attachment); } else { return { content: [ { type: "text" as const, text: "❌ Failed to upload image attachment", }, ], isError: true, }; } } } // Build message payload const messagePayload: any = { body: { content, contentType, }, importance, }; if (finalMentions.length > 0) { messagePayload.mentions = finalMentions; } if (attachments.length > 0) { messagePayload.attachments = attachments; } const result = (await client .api(`/teams/${teamId}/channels/${channelId}/messages/${messageId}/replies`) .post(messagePayload)) as ChatMessage; // Build success message const successText = `✅ Reply sent successfully. Reply ID: ${result.id}${ finalMentions.length > 0 ? `\n📱 Mentions: ${finalMentions.map((m) => m.mentionText).join(", ")}` : "" }${attachments.length > 0 ? `\n🖼️ Image attached: ${attachments[0].name}` : ""}`; return { content: [ { type: "text" as const, text: successText, }, ], }; } catch (error: any) { return { content: [ { type: "text" as const, text: `❌ Failed to send reply: ${error.message}`, }, ], isError: true, }; } }
  • Zod schema defining the input parameters for the tool, including required teamId, channelId, messageId, message, and optional fields for importance, format, mentions array, and image attachment details.
    teamId: z.string().describe("Team ID"), channelId: z.string().describe("Channel ID"), messageId: z.string().describe("Message ID to reply to"), message: z.string().describe("Reply content"), importance: z.enum(["normal", "high", "urgent"]).optional().describe("Message importance"), format: z.enum(["text", "markdown"]).optional().describe("Message format (text or markdown)"), mentions: z .array( z.object({ mention: z .string() .describe("The @mention text (e.g., 'john.doe' or 'john.doe@company.com')"), userId: z.string().describe("Azure AD User ID of the mentioned user"), }) ) .optional() .describe("Array of @mentions to include in the reply"), imageUrl: z.string().optional().describe("URL of an image to attach to the reply"), imageData: z.string().optional().describe("Base64 encoded image data to attach"), imageContentType: z .string() .optional() .describe("MIME type of the image (e.g., 'image/jpeg', 'image/png')"), imageFileName: z.string().optional().describe("Name for the attached image file"), },
  • src/index.ts:134-136 (registration)
    Top-level call to registerTeamsTools during MCP server initialization, which in turn registers the reply_to_channel_message tool along with other Teams-related tools.
    registerTeamsTools(server, graphService); registerChatTools(server, graphService); registerSearchTools(server, graphService);

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/floriscornel/teams-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server