conversations_add_message
Post messages to Slack channels, threads, or direct messages using markdown or plain text formatting. Requires OAuth authentication and channel targeting for secure message delivery.
Instructions
Posts a message to a channel, thread, or DM. Supports markdown and plain text. NOTE: Disabled by default for safety - set SLACK_MCP_ADD_MESSAGE_TOOL environment variable to enable.
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| accessToken | Yes | Slack OAuth token (xoxp-... or xoxb-...) with chat:write scope | |
| channel_id | Yes | Target channel ID, name (#general), or DM (@username) | |
| thread_ts | No | Thread timestamp; omit to post to channel directly | |
| payload | Yes | Message content to post | |
| content_type | No | Content format (default: text/markdown) |
Implementation Reference
- src/tools.ts:235-288 (handler)Primary execution handler for the conversations_add_message tool. Performs input validation, safety checks via environment variable, channel resolution, permission verification, and posts the message to Slack using chat.postMessage.export async function conversationsAddMessage(args: any): Promise<ToolResponse> { try { // Safety check - require explicit enable const enabledChannels = process.env.SLACK_MCP_ADD_MESSAGE_TOOL; if (!enabledChannels) { return { success: false, error: 'Message posting is disabled by default for safety. Set SLACK_MCP_ADD_MESSAGE_TOOL environment variable to enable.' }; } const validated = ConversationsAddMessageSchema.parse(args); const client = new WebClient(validated.accessToken); console.log('Posting message to channel:', validated.channel_id); // Resolve channel name/username to ID if needed const channelId = await resolveChannelId(client, validated.channel_id); // Check if channel is in enabled list (if not '*') if (enabledChannels !== '*') { const allowedChannels = enabledChannels.split(',').map(c => c.trim()); if (!allowedChannels.includes(channelId) && !allowedChannels.includes(validated.channel_id)) { return { success: false, error: `Message posting not enabled for channel: ${validated.channel_id}` }; } } // Post message const result = await client.chat.postMessage({ channel: channelId, text: validated.payload, thread_ts: validated.thread_ts, mrkdwn: validated.content_type === 'text/markdown' }); return { success: true, data: { ok: result.ok, channel: result.channel, ts: result.ts, message: result.message } }; } catch (error: any) { if (error.name === 'ZodError') { return { success: false, error: `Validation error: ${error.errors.map((e: any) => e.message).join(', ')}` }; } return handleSlackError(error); } }
- src/schemas.ts:29-39 (schema)Zod input schema for the conversations_add_message tool, defining required and optional parameters with descriptions.* Schema for conversations_add_message tool * Posts messages to channels, threads, or DMs * Note: Disabled by default for safety - requires configuration to enable */ export const ConversationsAddMessageSchema = z.object({ accessToken: z.string().describe("Slack OAuth token (xoxp-... or xoxb-...) with chat:write scope"), channel_id: z.string().describe("Target channel ID, name (#general), or DM (@username)"), thread_ts: z.string().optional().describe("Thread timestamp; omit to post to channel directly"), payload: z.string().describe("Message content to post"), content_type: z.enum(['text/markdown', 'text/plain']).optional().describe("Content format (default: text/markdown)") });
- src/index.ts:106-109 (registration)Tool registration in the MCP server's listTools response, defining the tool's name, description, and input schema converted from Zod.name: 'conversations_add_message', description: 'Posts a message to a channel, thread, or DM. Supports markdown and plain text. NOTE: Disabled by default for safety - set SLACK_MCP_ADD_MESSAGE_TOOL environment variable to enable.', inputSchema: zodToMCPSchema(ConversationsAddMessageSchema) },
- src/index.ts:137-139 (registration)Dispatcher case in the callTool handler that invokes the conversationsAddMessage function when the tool is called.case 'conversations_add_message': result = await conversationsAddMessage(args); break;
- src/tools.ts:64-95 (helper)Helper function used by the handler to resolve channel names (#channel) or usernames (@user) to actual channel IDs.async function resolveChannelId(client: WebClient, channelInput: string): Promise<string> { // If already an ID (starts with C, D, or G), return as-is if (/^[CDG][A-Z0-9]+$/.test(channelInput)) { return channelInput; } // Handle #channel format if (channelInput.startsWith('#')) { const channelName = channelInput.slice(1); const result = await client.conversations.list({ types: 'public_channel,private_channel' }); const channel = result.channels?.find((ch: any) => ch.name === channelName); if (channel) { return channel.id!; } throw new Error(`Channel not found: ${channelInput}`); } // Handle @username format for DMs if (channelInput.startsWith('@')) { const username = channelInput.slice(1); const usersResult = await client.users.list({}); const user = usersResult.members?.find((u: any) => u.name === username); if (user) { // Open or get existing DM const dmResult = await client.conversations.open({ users: user.id! }); return dmResult.channel!.id!; } throw new Error(`User not found: ${channelInput}`); } return channelInput; // Return as-is if no special format }