Skip to main content
Glama

send_channel_message

Send messages to Microsoft Teams channels with text, markdown formatting, mentions, attachments, and importance levels for team communication.

Instructions

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

Input Schema

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

Implementation Reference

  • The main handler function that executes the tool: processes input (text/markdown, mentions, images), uses Microsoft Graph API to post the message to the channel, handles errors and returns success/error response.
    async ({
      teamId,
      channelId,
      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`)
          .post(messagePayload)) as ChatMessage;
    
        // Build success message
        const successText = `✅ Message sent successfully. Message 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 message: ${error.message}`,
            },
          ],
          isError: true,
        };
      }
    }
  • Zod input schema validating parameters like teamId, channelId, message, optional importance, format, mentions array, and image attachment fields.
    {
      teamId: z.string().describe("Team ID"),
      channelId: z.string().describe("Channel ID"),
      message: z.string().describe("Message 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 message"),
      imageUrl: z.string().optional().describe("URL of an image to attach to the message"),
      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"),
    },
  • MCP server.tool registration call that defines the tool name, description, input schema, and handler function.
    server.tool(
      "send_channel_message",
      "Send a message to a specific channel in a Microsoft Team. Supports text and markdown formatting, mentions, and importance levels.",
      {
        teamId: z.string().describe("Team ID"),
        channelId: z.string().describe("Channel ID"),
        message: z.string().describe("Message 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 message"),
        imageUrl: z.string().optional().describe("URL of an image to attach to the message"),
        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,
        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`)
            .post(messagePayload)) as ChatMessage;
    
          // Build success message
          const successText = `✅ Message sent successfully. Message 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 message: ${error.message}`,
              },
            ],
            isError: true,
          };
        }
      }
    );

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