# Slack MCP Server
A Model Context Protocol (MCP) server for Slack API integration. This server allows AI assistants to interact with Slack workspaces through OAuth 2.0 authenticated user tokens.
## Features
- **Channel Operations**: List channels, get channel info, get channel members
- **Message Operations**: Read messages, send messages, reply to threads, search messages
- **User Operations**: List users, get user info, get user profiles
- **File Operations**: List files, get file info, upload files
- **Reaction Operations**: Add/remove reactions, get message reactions
## Prerequisites
- Node.js 18+
- A Slack App with OAuth 2.0 configured
- User token (xoxp-...) with appropriate scopes
## Installation
```bash
npm install
npm run build
```
## Slack App Setup
### 1. Create a Slack App
1. Go to [api.slack.com/apps](https://api.slack.com/apps)
2. Click "Create New App" → "From scratch"
3. Enter app name and select workspace
### 2. Configure OAuth Scopes
Add these **User Token Scopes** under "OAuth & Permissions":
```
channels:read # List channels
channels:history # Read channel messages
groups:read # List private channels
groups:history # Read private channel messages
im:read # List direct messages
im:history # Read direct messages
mpim:read # List group DMs
mpim:history # Read group DMs
chat:write # Send messages
users:read # List users
users.profile:read # Read user profiles
files:read # List files
files:write # Upload files
reactions:read # Read reactions
reactions:write # Add/remove reactions
search:read # Search messages
```
### 3. Configure Redirect URI
Add your platform's callback URL under "OAuth & Permissions" → "Redirect URLs":
```
https://your-platform.com/oauth/slack/callback
```
### 4. Get Client Credentials
Note down your:
- Client ID
- Client Secret
## Environment Variables
The MCP server reads credentials from environment variables:
```bash
# Required
SLACK_ACCESS_TOKEN=xoxp-your-user-token
# Optional
SLACK_TEAM_ID=T0123456789
```
## OAuth 2.0 Flow (Platform Implementation)
### Step 1: Redirect User to Slack Authorization
```typescript
const SLACK_CLIENT_ID = 'your-client-id';
const REDIRECT_URI = 'https://your-platform.com/oauth/slack/callback';
const SCOPES = 'channels:read,channels:history,chat:write,users:read,files:read,files:write,reactions:read,reactions:write,search:read,groups:read,groups:history,im:read,im:history,mpim:read,mpim:history,users.profile:read';
const authUrl = `https://slack.com/oauth/v2/authorize?client_id=${SLACK_CLIENT_ID}&user_scope=${SCOPES}&redirect_uri=${encodeURIComponent(REDIRECT_URI)}&state=${generateRandomState()}`;
// Redirect user to authUrl
```
### Step 2: Handle OAuth Callback
```typescript
// In your callback handler
app.get('/oauth/slack/callback', async (req, res) => {
const { code, state } = req.query;
// Verify state to prevent CSRF
if (!verifyState(state)) {
return res.status(400).send('Invalid state');
}
// Exchange code for token
const response = await fetch('https://slack.com/api/oauth.v2.access', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: new URLSearchParams({
client_id: SLACK_CLIENT_ID,
client_secret: SLACK_CLIENT_SECRET,
code,
redirect_uri: REDIRECT_URI,
}),
});
const data = await response.json();
if (data.ok) {
// Save user token to your database
const userToken = data.authed_user.access_token; // xoxp-...
const userId = data.authed_user.id;
const teamId = data.team.id;
await db.saveSlackToken(currentUserId, {
token: userToken,
slackUserId: userId,
teamId: teamId,
});
res.redirect('/success');
} else {
res.status(400).send(`OAuth error: ${data.error}`);
}
});
```
### Step 3: Start MCP Server with User Token
```typescript
// When starting the MCP server for a user, inject their token as env var
const userSlackToken = await db.getSlackToken(currentUserId);
const mcpProcess = spawn('node', ['/path/to/slack-mcp/dist/index.js'], {
env: {
...process.env,
SLACK_ACCESS_TOKEN: userSlackToken,
},
});
```
## MCP Configuration
Add to your Claude Code configuration (`~/.claude/claude_desktop_config.json`):
```json
{
"mcpServers": {
"slack": {
"command": "node",
"args": ["/path/to/slack-mcp/dist/index.js"],
"env": {
"SLACK_ACCESS_TOKEN": "xoxp-your-token"
}
}
}
}
```
## Available Tools
### Channel Tools
| Tool | Description |
|------|-------------|
| `slack_list_channels` | List all accessible channels |
| `slack_get_channel_info` | Get channel details |
| `slack_get_channel_members` | Get channel member list |
### Message Tools
| Tool | Description |
|------|-------------|
| `slack_get_messages` | Get messages from a channel |
| `slack_get_thread_replies` | Get replies in a thread |
| `slack_send_message` | Send a message to a channel |
| `slack_reply_to_thread` | Reply to a thread |
| `slack_search_messages` | Search messages (requires user token) |
### User Tools
| Tool | Description |
|------|-------------|
| `slack_list_users` | List workspace users |
| `slack_get_user_info` | Get user details |
| `slack_get_user_profile` | Get user profile |
### File Tools
| Tool | Description |
|------|-------------|
| `slack_list_files` | List shared files |
| `slack_get_file_info` | Get file details |
| `slack_upload_file` | Upload a file |
### Reaction Tools
| Tool | Description |
|------|-------------|
| `slack_add_reaction` | Add emoji reaction |
| `slack_remove_reaction` | Remove emoji reaction |
| `slack_get_reactions` | Get message reactions |
## Example Usage
### List Channels
```json
{
"tool": "slack_list_channels",
"arguments": {
"types": "public_channel,private_channel",
"limit": 50
}
}
```
### Send Message
```json
{
"tool": "slack_send_message",
"arguments": {
"channel_id": "C1234567890",
"text": "Hello from MCP!"
}
}
```
### Search Messages
```json
{
"tool": "slack_search_messages",
"arguments": {
"query": "from:@user in:#channel important",
"count": 20
}
}
```
## Error Handling
The server returns structured error responses:
```json
{
"error": "Slack API Error (invalid_auth): Invalid authentication token."
}
```
Common error codes:
- `invalid_auth`: Token is invalid
- `token_revoked`: Token has been revoked
- `missing_scope`: Token lacks required scope
- `channel_not_found`: Channel doesn't exist
- `ratelimited`: Rate limit exceeded
## Security Considerations
- Never expose user tokens in client-side code
- Store tokens securely in your database (encrypted)
- Use HTTPS for all OAuth redirects
- Validate state parameter to prevent CSRF
- Rotate tokens periodically using refresh tokens
- Request only necessary scopes
## License
MIT