Slack Bot MCP Server Recipe

An MCP server that connects AI assistants to Slack — send messages, list channels, read history, and manage conversations.

IntermediatePrep: 10 minCook: 25 minServes: Team communication automation via AI

title: "Slack Bot MCP Server Recipe" description: "An MCP server that connects AI assistants to Slack — send messages, list channels, read history, and manage conversations." order: 6 keywords:

  • mcp slack bot
  • mcp-framework slack
  • slack api mcp
  • ai slack integration date: "2026-04-01" difficulty: "Intermediate" prepTime: "10 min" cookTime: "25 min" serves: "Team communication automation via AI"

Prep Time

10 minutes — scaffold project, create a Slack app, and get a Bot Token.

Cook Time

25 minutes — implement channel listing, message sending, and history tools.

Serves

Any AI assistant that needs to interact with Slack workspaces. Ideal for automated notifications, channel summaries, and team communication.

Ingredients

Instructions

Step 1: Create a Slack App

  1. Go to api.slack.com/apps and create a new app
  2. Under "OAuth & Permissions", add scopes: channels:read, chat:write, channels:history
  3. Install the app to your workspace and copy the Bot User OAuth Token

Step 2: Scaffold and Configure

npx mcp-framework create slack-server
cd slack-server

Step 3: Create the List Channels Tool

Create src/tools/ListChannelsTool.ts:

import { MCPTool } from "mcp-framework";
import { z } from "zod";

const SLACK_TOKEN = process.env.SLACK_BOT_TOKEN || "";

class ListChannelsTool extends MCPTool<typeof inputSchema> {
  name = "list_channels";
  description = "List public Slack channels in the workspace";

  schema = {
    limit: {
      type: z.number().min(1).max(100).default(20),
      description: "Max number of channels to return",
    },
  };

  async execute(input: z.infer<typeof inputSchema>): Promise<string> {
    try {
      const response = await fetch(
        `https://slack.com/api/conversations.list?limit=${input.limit}&types=public_channel`,
        {
          headers: { Authorization: `Bearer ${SLACK_TOKEN}` },
        }
      );

      const data = await response.json();
      if (!data.ok) throw new Error(data.error);

      const channels = data.channels.map(
        (ch: Record<string, unknown>) => ({
          id: ch.id,
          name: ch.name,
          topic: (ch.topic as Record<string, unknown>)?.value || "",
          memberCount: ch.num_members,
        })
      );

      return JSON.stringify(channels, null, 2);
    } catch (error) {
      const msg = error instanceof Error ? error.message : "Unknown error";
      return JSON.stringify({ error: msg });
    }
  }
}

const inputSchema = z.object({
  limit: z.number().min(1).max(100).default(20),
});
export default ListChannelsTool;

Step 4: Create the Send Message Tool

Create src/tools/SendMessageTool.ts:

import { MCPTool } from "mcp-framework";
import { z } from "zod";

const SLACK_TOKEN = process.env.SLACK_BOT_TOKEN || "";

class SendMessageTool extends MCPTool<typeof inputSchema> {
  name = "send_message";
  description = "Send a message to a Slack channel";

  schema = {
    channel: {
      type: z.string(),
      description: "Channel ID or name (e.g., C01234567 or #general)",
    },
    text: {
      type: z.string(),
      description: "Message text to send",
    },
  };

  async execute(input: z.infer<typeof inputSchema>): Promise<string> {
    try {
      const response = await fetch("https://slack.com/api/chat.postMessage", {
        method: "POST",
        headers: {
          Authorization: `Bearer ${SLACK_TOKEN}`,
          "Content-Type": "application/json",
        },
        body: JSON.stringify({
          channel: input.channel,
          text: input.text,
        }),
      });

      const data = await response.json();
      if (!data.ok) throw new Error(data.error);

      return JSON.stringify({
        success: true,
        channel: data.channel,
        timestamp: data.ts,
      }, null, 2);
    } catch (error) {
      const msg = error instanceof Error ? error.message : "Unknown error";
      return JSON.stringify({ error: msg });
    }
  }
}

const inputSchema = z.object({
  channel: z.string(),
  text: z.string(),
});
export default SendMessageTool;

Step 5: Create the Channel History Tool

Create src/tools/ChannelHistoryTool.ts:

import { MCPTool } from "mcp-framework";
import { z } from "zod";

const SLACK_TOKEN = process.env.SLACK_BOT_TOKEN || "";

class ChannelHistoryTool extends MCPTool<typeof inputSchema> {
  name = "channel_history";
  description = "Get recent messages from a Slack channel";

  schema = {
    channel: {
      type: z.string(),
      description: "Channel ID",
    },
    limit: {
      type: z.number().min(1).max(50).default(10),
      description: "Number of messages to fetch",
    },
  };

  async execute(input: z.infer<typeof inputSchema>): Promise<string> {
    try {
      const response = await fetch(
        `https://slack.com/api/conversations.history?channel=${input.channel}&limit=${input.limit}`,
        {
          headers: { Authorization: `Bearer ${SLACK_TOKEN}` },
        }
      );

      const data = await response.json();
      if (!data.ok) throw new Error(data.error);

      const messages = data.messages.map(
        (msg: Record<string, unknown>) => ({
          user: msg.user,
          text: msg.text,
          timestamp: msg.ts,
        })
      );

      return JSON.stringify({ channel: input.channel, messages }, null, 2);
    } catch (error) {
      const msg = error instanceof Error ? error.message : "Unknown error";
      return JSON.stringify({ error: msg });
    }
  }
}

const inputSchema = z.object({
  channel: z.string(),
  limit: z.number().min(1).max(50).default(10),
});
export default ChannelHistoryTool;

Step 6: Build and Connect

npm run build
{
  "mcpServers": {
    "slack": {
      "command": "node",
      "args": ["/absolute/path/to/slack-server/dist/index.js"],
      "env": {
        "SLACK_BOT_TOKEN": "xoxb-your-token-here"
      }
    }
  }
}

Chef's Notes

  • Always use Bot tokens (xoxb-), not User tokens, for MCP servers.
  • The bot must be invited to channels before it can read history or post messages.
  • Consider adding a confirmation step before sending messages in production.
  • Slack rate limits: ~1 request per second for most APIs.
  • Extend with thread replies, reactions, and user lookup tools.

More recipes: Web Scraper Server | Calculator Tool | All Recipes

Built with mcp-framework by @QuantGeekDev — 3.3M+ downloads, validated by Anthropic.

Built with mcp-framework (3.3M+ downloads) — created by @QuantGeekDev and validated by Anthropic.