Skip to main content
Glama
coreyepstein

Slack MCP Server

by coreyepstein
README.md
# Slack MCP Server

A Slack [MCP server](https://modelcontextprotocol.io) for [Claude Code](https://docs.anthropic.com/en/docs/claude-code) that provides full messaging, channels, search, and threads across multiple Slack workspaces.

## Features

- **12 tools** — channels, messages, threads, DMs, reactions, search, file upload
- **Multi-workspace** — connect as many Slack workspaces as you need
- **Dual token support** — bot tokens and user tokens, with automatic preference
- **Post as yourself** — user tokens make messages appear from you, not a bot
- **Full search** — `search_messages` works with user tokens (no limitations)
- **Env-var tokens** — no secrets in config files or the repo
- **Channel name resolution** — use `#general` or `general`, not just IDs

## Quick Start

### 1. Clone and install

```bash
git clone https://github.com/coreyepstein/slack-mcp.git
cd slack-mcp
npm install
```

### 2. Create a Slack App

1. Go to [api.slack.com/apps](https://api.slack.com/apps) and click **Create New App** > **From scratch**
2. Name it (e.g. "Claude MCP") and select your workspace
3. Go to **OAuth & Permissions** and add scopes (see [Scopes](#scopes) below)
4. Click **Install to Workspace**
5. Copy the **Bot User OAuth Token** (`xoxb-...`)
6. Optionally, copy the **User OAuth Token** (`xoxp-...`) — see [Token Types](#token-types)

### 3. Configure workspaces

```bash
cp workspaces.example.json workspaces.json
```

Edit `workspaces.json` with your workspace name and team ID. Tokens are **not** stored here:

```json
{
  "workspaces": [
    {
      "name": "my-company",
      "team_id": "T_YOUR_TEAM_ID"
    }
  ],
  "default": "my-company"
}
```

Find your team ID by running `npm run auth -- --setup` or from your Slack workspace URL settings.

### 4. Set token environment variables

Tokens are passed via environment variables, never stored in files. The naming convention is:

```
SLACK_TOKEN_{NAME}_BOT   →  Bot token (xoxb-...)
SLACK_TOKEN_{NAME}_USER  →  User token (xoxp-...)
```

Where `{NAME}` is the workspace name uppercased with non-alphanumeric characters replaced by underscores.

Examples:
| Workspace name | Bot env var | User env var |
|----------------|-------------|--------------|
| `my-company` | `SLACK_TOKEN_MY_COMPANY_BOT` | `SLACK_TOKEN_MY_COMPANY_USER` |
| `voyage` | `SLACK_TOKEN_VOYAGE_BOT` | `SLACK_TOKEN_VOYAGE_USER` |
| `acme-corp` | `SLACK_TOKEN_ACME_CORP_BOT` | `SLACK_TOKEN_ACME_CORP_USER` |

### 5. Add to Claude Code

Add to your project `.mcp.json` or `~/.claude.json`:

```json
{
  "mcpServers": {
    "slack": {
      "type": "stdio",
      "command": "npx",
      "args": ["tsx", "/absolute/path/to/slack-mcp/src/server.ts"],
      "env": {
        "SLACK_TOKEN_MY_COMPANY_BOT": "xoxb-your-bot-token",
        "SLACK_TOKEN_MY_COMPANY_USER": "xoxp-your-user-token"
      }
    }
  }
}
```

For multiple workspaces, add all tokens to the same `env` block:

```json
{
  "mcpServers": {
    "slack": {
      "type": "stdio",
      "command": "npx",
      "args": ["tsx", "/absolute/path/to/slack-mcp/src/server.ts"],
      "env": {
        "SLACK_TOKEN_VOYAGE_BOT": "xoxb-...",
        "SLACK_TOKEN_VOYAGE_USER": "xoxp-...",
        "SLACK_TOKEN_INDIGO_BOT": "xoxb-...",
        "SLACK_TOKEN_INDIGO_USER": "xoxp-..."
      }
    }
  }
}
```

> **Important:** Use the absolute path to `src/server.ts`. Never commit tokens to any tracked file.

### 6. Verify setup

```bash
npm run auth:check
```

This validates all configured tokens against the Slack API and reports their status.

### 7. Invite the bot

In each Slack channel you want to access, invite the bot:

```
/invite @Claude MCP
```

This is required for the bot token to read/write in channels. User tokens can access any channel the token owner is already a member of.

## Token Types

This server supports two types of Slack tokens per workspace:

### Bot Token (`xoxb-...`)

- Created when you install a Slack App to a workspace
- Messages appear as the app (e.g. "Claude MCP"), not as a person
- Must be explicitly invited to channels with `/invite`
- Cannot use `search:read` (Slack API limitation)
- Good for: automated notifications, bot-style interactions

### User Token (`xoxp-...`)

- Generated from the **User OAuth Token** on the OAuth & Permissions page
- Messages appear as the token owner (e.g. "Corey Epstein") — your real name and avatar
- Has access to any channel the token owner is a member of
- Required for `search_messages` (`search:read` scope)
- Good for: acting as yourself, search, accessing channels without `/invite`

### How tokens are selected

The server automatically selects the best token for each operation:

| Scenario | Token used |
|----------|-----------|
| User token available | User token (preferred for all operations) |
| User token missing, bot token available | Bot token (automatic fallback) |
| Neither token available | Error with setup instructions |
| `search_messages` | User token only (hard requirement — no fallback) |

You can start with just a bot token and add a user token later. All operations except `search_messages` work with either token type.

## Post as User

When a user token (`xoxp-...`) is configured, all messages sent through the server appear as if **you** sent them — your name, your profile picture, your identity. This applies to:

- `send_message` — channel messages show your name
- `reply_thread` — thread replies show your name
- `send_dm` — DMs come from you, not a bot

This happens automatically. The Slack API posts as the token owner when using a user token. No special configuration or `as_user` parameter is needed.

If only a bot token is available, messages appear as the Slack App instead.

## Scopes

### Bot Token Scopes

Add these under **OAuth & Permissions > Bot Token Scopes** in your Slack App:

| Scope | Description |
|-------|-------------|
| `channels:read` | List public channels |
| `channels:history` | Read messages in public channels |
| `chat:write` | Send messages |
| `groups:read` | List private channels |
| `groups:history` | Read messages in private channels |
| `im:write` | Send direct messages |
| `users:read` | List workspace members |
| `users:read.email` | Access user email addresses |
| `files:write` | Upload files |
| `reactions:write` | Add emoji reactions |

### User Token Scopes

Add these under **OAuth & Permissions > User Token Scopes** for full functionality:

| Scope | Description |
|-------|-------------|
| `chat:write` | Send messages as yourself |
| `channels:read` | List public channels |
| `channels:history` | Read messages in public channels |
| `groups:read` | List private channels |
| `groups:history` | Read messages in private channels |
| `im:read` | Read direct message history |
| `im:write` | Send direct messages |
| `mpim:read` | Read group DM history |
| `mpim:write` | Send group DMs |
| `users:read` | List workspace members |
| `users:read.email` | Access user email addresses |
| `files:write` | Upload files |
| `reactions:write` | Add emoji reactions |
| `search:read` | Search messages and files |

After adding user token scopes, click **Reinstall to Workspace** to generate the user token.

## Tools

| Tool | Description | Token requirement |
|------|-------------|-------------------|
| `list_channels` | List channels in a workspace (public and optionally private) | Bot or user |
| `read_channel` | Read recent messages from a channel | Bot or user |
| `read_thread` | Read all replies in a thread | Bot or user |
| `get_channel_info` | Get channel details (topic, purpose, member count) | Bot or user |
| `send_message` | Send a message to a channel | Bot or user |
| `reply_thread` | Reply to a specific thread | Bot or user |
| `send_dm` | Send a direct message to a user (by name, email, or ID) | Bot or user |
| `list_users` | List all workspace members | Bot or user |
| `find_user` | Find a user by name, display name, email, or ID | Bot or user |
| `add_reaction` | Add an emoji reaction to a message | Bot or user |
| `search_messages` | Search messages across the workspace | **User token required** |
| `upload_file` | Upload a file to a channel | Bot or user |

All tools accept an optional `workspace` parameter (name or team ID). If omitted, uses the default workspace from `workspaces.json`.

## Multi-Workspace

Configure multiple workspaces in `workspaces.json` and set tokens for each:

```json
{
  "workspaces": [
    { "name": "voyage", "team_id": "T7V83HY5B" },
    { "name": "indigo", "team_id": "T_INDIGO_ID" }
  ],
  "default": "voyage"
}
```

Then use the `workspace` parameter in any tool:

```
send_message(channel: "general", text: "hello", workspace: "voyage")
send_message(channel: "team", text: "hello", workspace: "indigo")
```

If `workspace` is omitted, uses the `default` from `workspaces.json`.

## Auth CLI

The auth CLI helps with setup and health checks:

```bash
# Interactive setup — walks through token configuration
npm run auth -- --setup

# Health check — validates all tokens for all workspaces
npm run auth:check
```

The `--check` command tests each token against the Slack API and reports:

```
Workspace Health Check:
──────────────────────────────────────────────────────────────────────────
  Workspace: voyage (default)
    Bot token:  + connected as @claude-mcp in Voyage
    User token: + connected as @corey in Voyage

  Workspace: indigo
    Bot token:  + connected as @claude-mcp in Indigo
    User token: - missing (optional — needed for search:read)
                  Set env: SLACK_TOKEN_INDIGO_USER=xoxp-...
```

## E2E Verification

An end-to-end verification script is included at `scripts/verify-e2e.ts`:

```bash
npx tsx scripts/verify-e2e.ts

# Or with a specific test channel:
TEST_CHANNEL=bot-testing npx tsx scripts/verify-e2e.ts
```

This verifies token connectivity, message posting identity, search functionality, and bot-token fallback across all configured workspaces.

## Troubleshooting

| Error | Cause | Fix |
|-------|-------|-----|
| `workspaces.json not found` | Config file missing | Run `cp workspaces.example.json workspaces.json` and edit it |
| `No token available for workspace "X"` | No env vars set for this workspace | Set `SLACK_TOKEN_X_BOT` and/or `SLACK_TOKEN_X_USER` env vars |
| `invalid_auth` | Token is expired or incorrect | Regenerate the token from your Slack App's OAuth & Permissions page |
| `channel_not_found` | Bot is not in the channel | Invite the bot: `/invite @Claude MCP` |
| `missing_scope` | Token lacks a required OAuth scope | Add the scope in your Slack App's OAuth & Permissions settings, then reinstall |
| `not_allowed_token_type` | Using a bot token for a user-token-only operation | `search_messages` requires a user token (`xoxp-`). Set `SLACK_TOKEN_X_USER` |
| `search.messages requires a user token` | No user token configured for this workspace | Obtain a user token (see [Token Types](#token-types)) and set the `_USER` env var |
| `not_in_channel` | Bot token cannot access this channel | Invite the bot to the channel, or use a user token (which can access any channel the owner is in) |
| `Unknown workspace: "X"` | Workspace name not in `workspaces.json` | Add a workspace entry to `workspaces.json` with the correct name and team_id |
| Messages appear as the bot, not as me | Using bot token instead of user token | Set `SLACK_TOKEN_X_USER` with your user OAuth token (`xoxp-...`) |

## Security

> **Token in git history:** An earlier version of this repo stored a Slack bot token in `workspaces.json`. That token has been rotated and is no longer valid, but it may still appear in git commit history. If you forked or cloned this repo before the token was removed, the old token in your local history is already invalidated.
>
> **Best practice:** Always pass tokens via environment variables in the `.mcp.json` `env` block. Never commit tokens to `workspaces.json` or any other tracked file. The `.gitignore` excludes `workspaces.json` to prevent accidental commits.

## Architecture

```
src/
  server.ts           — MCP server entry point
  config.ts           — workspace + token resolution from env vars
  auth.ts             — setup and health check CLI
  slack/
    client.ts         — Slack Web API wrapper with dual-token client caching
    types.ts          — shared TypeScript types
  tools/
    *.ts              — one file per MCP tool (12 tools)
scripts/
  verify-e2e.ts       — end-to-end verification script
```

## License

MIT