← Back to Blog
tutorialslackintegrationproject

Building a Slack Integration MCP Tool

Create an MCP tool that sends messages, creates channels, and manages Slack workspaces from AI agents.

·4 min read·xapable

Building a Slack Integration MCP Tool

Let AI agents interact with Slack — send messages, create channels, search conversations. Full walkthrough.

What You'll Build

  • send_message — send a message to a channel or user
  • list_channels — list all channels the bot can see
  • search_messages — search conversation history
  • create_channel — create a new channel

Step 1: Setup Slack App

  1. Go to https://api.slack.com/apps
  2. Create a new app → "From scratch"
  3. Name it (e.g., "MCP Slack Bot")
  4. Under OAuth & Permissions, add scopes:
    • chat:write — send messages
    • channels:read — list channels
    • channels:history — search messages
    • channels:manage — create channels
    • search:read — search messages
  5. Install to workspace → copy the Bot User OAuth Token (starts with xoxb-)

Step 2: Initialize

mkdir slack-mcp && cd slack-mcp
npm init -y
npm install @modelcontextprotocol/sdk @slack/web-api

Step 3: Build the Server

import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { WebClient } from "@slack/web-api";

const TOKEN = process.env.SLACK_BOT_TOKEN;
if (!TOKEN) {
  console.error("SLACK_BOT_TOKEN is required");
  process.exit(1);
}

const slack = new WebClient(TOKEN);

const server = new Server({
  name: "slack-mcp",
  version: "1.0.0",
}, { capabilities: { tools: {} } });

server.setRequestHandler("tools/list", async () => ({
  tools: [
    {
      name: "send_message",
      description: "Send a message to a Slack channel or user",
      inputSchema: {
        type: "object",
        properties: {
          channel: {
            type: "string",
            description: "Channel name (e.g., '#general') or channel ID"
          },
          text: {
            type: "string",
            description: "Message text (supports Slack mrkdwn)"
          }
        },
        required: ["channel", "text"]
      }
    },
    {
      name: "list_channels",
      description: "List all public channels in the workspace",
      inputSchema: {
        type: "object",
        properties: {
          include_archived: {
            type: "boolean",
            description: "Include archived channels (default: false)"
          }
        }
      }
    },
    {
      name: "search_messages",
      description: "Search for messages matching a query",
      inputSchema: {
        type: "object",
        properties: {
          query: {
            type: "string",
            description: "Search query"
          },
          channel: {
            type: "string",
            description: "Limit search to a specific channel"
          },
          count: {
            type: "number",
            description: "Number of results (default: 10, max: 50)"
          }
        },
        required: ["query"]
      }
    },
    {
      name: "create_channel",
      description: "Create a new public channel",
      inputSchema: {
        type: "object",
        properties: {
          name: {
            type: "string",
            description: "Channel name (lowercase, no spaces, max 80 chars)"
          },
          is_private: {
            type: "boolean",
            description: "Create a private channel (default: false)"
          }
        },
        required: ["name"]
      }
    }
  ]
}));

server.setRequestHandler("tools/call", async (request) => {
  const { name, arguments: args } = request.params;

  try {
    switch (name) {
      case "send_message": {
        const result = await slack.chat.postMessage({
          channel: args.channel,
          text: args.text
        });

        return {
          content: [{
            type: "text",
            text: `✅ Message sent to ${args.channel} (${result.ts})`
          }]
        };
      }

      case "list_channels": {
        const result = await slack.conversations.list({
          exclude_archived: !args.include_archived,
          types: "public_channel"
        });

        const channels = result.channels
          .map(c => `   #${c.name} ${c.is_archived ? "(archived)" : ""}`)
          .join("\n");

        return {
          content: [{
            type: "text",
            text: `📋 Channels (${result.channels.length}):
${channels}`
          }]
        };
      }

      case "search_messages": {
        const result = await slack.search.messages({
          query: args.query,
          count: Math.min(args.count || 10, 50)
        });

        const messages = result.messages.matches
          .slice(0, args.count || 10)
          .map((m, i) =>
            `${i + 1}. [${m.channel.name}] ${m.username}: ${m.text.slice(0, 100)}`
          ).join("\n");

        return {
          content: [{
            type: "text",
            text: `🔍 Results for "${args.query}":
${messages || "No messages found."}`
          }]
        };
      }

      case "create_channel": {
        const result = await slack.conversations.create({
          name: args.name.toLowerCase().replace(/[^a-z0-9-_]/g, "").slice(0, 80),
          is_private: args.is_private || false
        });

        return {
          content: [{
            type: "text",
            text: `✅ Channel #${result.channel.name} created!`
          }]
        };
      }

      default:
        return {
          content: [{ type: "text", text: `❌ Unknown tool: ${name}` }],
          isError: true
        };
    }
  } catch (err) {
    return {
      content: [{
        type: "text",
        text: `❌ Slack error: ${err.message}`
      }],
      isError: true
    };
  }
});

const transport = new StdioServerTransport();
await server.connect(transport);

Step 4: Test and Publish

SLACK_BOT_TOKEN=xoxb-your-token node index.js
mcpm-dev publish

Security Notes

  • Bot tokens have limited scope — configure only what you need
  • Never expose tokens — use environment variables
  • Be careful with create_channel — can create channel spam
  • Add approval for sensitive actions if needed

Your AI agent now has full Slack superpowers. 🎉

#MCP #Tutorial #Slack #Integration #mcpm