# Implementation Guide for Slack MCP Server
This guide walks you through setting up and deploying the Slack MCP server in production.
## Table of Contents
1. [Slack App Configuration](#slack-app-configuration)
2. [OAuth Setup](#oauth-setup)
3. [Server Deployment](#server-deployment)
4. [Storage Implementation](#storage-implementation)
5. [Security Hardening](#security-hardening)
6. [Testing the Integration](#testing-the-integration)
---
## 1. Slack App Configuration
### Step 1: Create a Slack App
1. Go to [api.slack.com/apps](https://api.slack.com/apps)
2. Click **"Create New App"**
3. Choose **"From scratch"**
4. Enter an app name (e.g., "AffinityBots MCP")
5. Select a development workspace
6. Click **"Create App"**
### Step 2: Configure Bot Scopes
1. In your app settings, go to **"OAuth & Permissions"**
2. Scroll to **"Scopes"** → **"Bot Token Scopes"**
3. Add the following scopes (minimum):
- `chat:write` - Send messages
- `channels:history` - Read public channel messages
- `channels:read` - View public channels
4. **Recommended additional scopes:**
- `groups:history` - Read private channel messages
- `groups:read` - View private channels
- `im:history` - Read direct messages
- `im:read` - View direct messages
- `mpim:history` - Read group DM messages
- `mpim:read` - View group DMs
- `users:read` - View users in workspace
- `team:read` - View workspace info
### Step 3: Enable Token Rotation (Recommended)
1. Go to **"OAuth & Permissions"**
2. Scroll to **"Token Rotation"**
3. Click **"Opt-In to Token Rotation"**
4. Confirm the opt-in
**Benefits:**
- Enhanced security with short-lived tokens
- Automatic token refresh handled by the server
- Reduces impact of token leakage
**Note:** The server automatically handles token refresh when rotation is enabled.
### Step 4: Get OAuth Credentials
1. Go to **"Basic Information"**
2. Scroll to **"App Credentials"**
3. Copy the **"Client ID"** - you'll need this for `SLACK_CLIENT_ID`
4. Click **"Show"** next to **"Client Secret"** and copy it - you'll need this for `SLACK_CLIENT_SECRET`
**⚠️ Security Warning:** Never commit these credentials to version control!
---
## 2. OAuth Setup
### Understanding the OAuth Flow
The Slack MCP server implements the OAuth 2.0 authorization code flow:
```
User → /slack/install → Slack Authorization → /slack/oauth/callback → Success
```
### Step 1: Configure Redirect URL
Your redirect URI must be publicly accessible and use HTTPS in production.
**Development:**
```
http://localhost:8080/slack/oauth/callback
```
**Production:**
```
https://yourdomain.com/slack/oauth/callback
```
### Step 2: Add Redirect URL to Slack App
1. In your Slack app, go to **"OAuth & Permissions"**
2. Scroll to **"Redirect URLs"**
3. Click **"Add New Redirect URL"**
4. Enter your callback URL (e.g., `https://yourdomain.com/slack/oauth/callback`)
5. Click **"Add"**
6. Click **"Save URLs"**
**Important:** The redirect URI in your Slack app settings must **exactly match** the `SLACK_REDIRECT_URI` environment variable.
### Step 3: Set Environment Variables
Create a `.env` file (or configure your hosting platform):
```bash
# Required
SLACK_CLIENT_ID=123456789012.1234567890123
SLACK_CLIENT_SECRET=abc123def456ghi789jkl012mno345pq
SLACK_REDIRECT_URI=https://yourdomain.com/slack/oauth/callback
SLACK_SCOPES=chat:write,channels:history,channels:read,groups:history,groups:read
# Security
AFFINITYBOTS_MCP_API_KEY=your-strong-random-key-here
# Optional
PUBLIC_BASE_URL=https://yourdomain.com
ALLOWED_ORIGINS=https://yourdomain.com,https://app.yourdomain.com
PORT=8080
HOST=0.0.0.0
SESSION_TTL_MS=900000
LOG_LEVEL=info
```
### Step 4: Generate API Key
Generate a strong random API key for `AFFINITYBOTS_MCP_API_KEY`:
```bash
# Using OpenSSL
openssl rand -base64 32
# Using Node.js
node -e "console.log(require('crypto').randomBytes(32).toString('base64'))"
# Using Python
python3 -c "import secrets; print(secrets.token_urlsafe(32))"
```
---
## 3. Server Deployment
### Option A: Docker Deployment (Recommended)
#### Step 1: Build the Image
```bash
docker build -t slack-mcp-server .
```
#### Step 2: Run with Environment Variables
```bash
docker run -d \
--name slack-mcp \
-p 8080:8080 \
-e SLACK_CLIENT_ID=your_client_id \
-e SLACK_CLIENT_SECRET=your_client_secret \
-e SLACK_REDIRECT_URI=https://yourdomain.com/slack/oauth/callback \
-e SLACK_SCOPES=chat:write,channels:history,channels:read \
-e AFFINITYBOTS_MCP_API_KEY=your_api_key \
-e ALLOWED_ORIGINS=https://yourdomain.com \
-e PUBLIC_BASE_URL=https://yourdomain.com \
--restart unless-stopped \
slack-mcp-server
```
#### Step 3: Use Docker Compose (Alternative)
Create `docker-compose.yml`:
```yaml
version: '3.8'
services:
slack-mcp:
build: .
ports:
- "8080:8080"
environment:
- SLACK_CLIENT_ID=${SLACK_CLIENT_ID}
- SLACK_CLIENT_SECRET=${SLACK_CLIENT_SECRET}
- SLACK_REDIRECT_URI=${SLACK_REDIRECT_URI}
- SLACK_SCOPES=${SLACK_SCOPES}
- AFFINITYBOTS_MCP_API_KEY=${AFFINITYBOTS_MCP_API_KEY}
- ALLOWED_ORIGINS=${ALLOWED_ORIGINS}
- PUBLIC_BASE_URL=${PUBLIC_BASE_URL}
- LOG_LEVEL=info
restart: unless-stopped
healthcheck:
test: ["CMD", "wget", "--quiet", "--tries=1", "--spider", "http://localhost:8080/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
```
Run with:
```bash
docker-compose up -d
```
### Option B: Direct Node.js Deployment
#### Step 1: Install Dependencies
```bash
pnpm install --prod
```
#### Step 2: Build TypeScript
```bash
pnpm build
```
#### Step 3: Run with PM2 (Recommended)
Install PM2:
```bash
pnpm add -g pm2
```
Create `ecosystem.config.js`:
```javascript
module.exports = {
apps: [{
name: 'slack-mcp-server',
script: './build/index.js',
instances: 1,
exec_mode: 'cluster',
env: {
NODE_ENV: 'production',
PORT: 8080,
HOST: '0.0.0.0'
}
}]
};
```
Start the server:
```bash
pm2 start ecosystem.config.js
pm2 save
pm2 startup
```
### Option C: Cloud Platform Deployment
#### Heroku
1. Create `Procfile`:
```
web: node build/index.js
```
2. Deploy:
```bash
heroku create your-app-name
heroku config:set SLACK_CLIENT_ID=your_client_id
heroku config:set SLACK_CLIENT_SECRET=your_client_secret
# ... set other env vars
git push heroku main
```
#### Railway
1. Connect your GitHub repository
2. Add environment variables in the Railway dashboard
3. Deploy automatically on push
#### Render
1. Create a new Web Service
2. Connect your repository
3. Set environment variables
4. Deploy
---
## 4. Storage Implementation
### Why Replace InMemoryInstallStore?
The default `InMemoryInstallStore` stores OAuth tokens in memory, which means:
- ❌ Data is lost on server restart
- ❌ Doesn't work with multiple server instances
- ❌ Not suitable for production
### Implementing Persistent Storage
#### Interface to Implement
```typescript
export interface InstallStore {
upsert(install: SlackInstallation): Promise<void>;
getByTeamId(teamId: string): Promise<SlackInstallation | null>;
}
```
#### Example: PostgreSQL Implementation
Create `src/postgresInstallStore.ts`:
```typescript
import { Pool } from 'pg';
import type { InstallStore, SlackInstallation } from './installStore.js';
export class PostgresInstallStore implements InstallStore {
private pool: Pool;
constructor(connectionString: string) {
this.pool = new Pool({ connectionString });
}
async upsert(install: SlackInstallation): Promise<void> {
await this.pool.query(
`INSERT INTO slack_installations (
team_id, enterprise_id, bot_access_token, bot_user_id,
authed_user_id, refresh_token, expires_at, scope, installed_at
) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)
ON CONFLICT (team_id)
DO UPDATE SET
bot_access_token = $3,
refresh_token = $6,
expires_at = $7,
scope = $8`,
[
install.team_id,
install.enterprise_id,
install.bot_access_token,
install.bot_user_id,
install.authed_user_id,
install.refresh_token,
install.expires_at,
install.scope,
install.installed_at
]
);
}
async getByTeamId(teamId: string): Promise<SlackInstallation | null> {
const result = await this.pool.query(
'SELECT * FROM slack_installations WHERE team_id = $1',
[teamId]
);
return result.rows[0] || null;
}
}
```
Create the database table:
```sql
CREATE TABLE slack_installations (
team_id VARCHAR(32) PRIMARY KEY,
enterprise_id VARCHAR(32),
bot_access_token TEXT NOT NULL,
bot_user_id VARCHAR(32),
authed_user_id VARCHAR(32),
refresh_token TEXT,
expires_at BIGINT,
scope TEXT,
installed_at BIGINT NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX idx_enterprise_id ON slack_installations(enterprise_id);
```
Update `src/index.ts`:
```typescript
import { PostgresInstallStore } from './postgresInstallStore.js';
// Replace this:
// const store = new InMemoryInstallStore();
// With this:
const store = new PostgresInstallStore(process.env.DATABASE_URL!);
```
#### Example: Redis Implementation
Create `src/redisInstallStore.ts`:
```typescript
import { createClient } from 'redis';
import type { InstallStore, SlackInstallation } from './installStore.js';
export class RedisInstallStore implements InstallStore {
private client: ReturnType<typeof createClient>;
constructor(url: string) {
this.client = createClient({ url });
this.client.connect();
}
async upsert(install: SlackInstallation): Promise<void> {
const key = `slack:install:${install.team_id}`;
await this.client.set(key, JSON.stringify(install));
}
async getByTeamId(teamId: string): Promise<SlackInstallation | null> {
const key = `slack:install:${teamId}`;
const data = await this.client.get(key);
return data ? JSON.parse(data) : null;
}
}
```
---
## 5. Security Hardening
### Required for Production
#### 1. Use HTTPS
Slack **requires** HTTPS for OAuth redirect URIs in production. Use:
- Let's Encrypt (free)
- Cloudflare (free tier available)
- AWS Certificate Manager
- Your cloud provider's SSL/TLS
#### 2. Set Strong API Key
```bash
# Generate a strong key
openssl rand -base64 32
# Set in environment
AFFINITYBOTS_MCP_API_KEY=your_strong_key_here
```
#### 3. Configure CORS
Restrict origins to your frontend domains:
```bash
ALLOWED_ORIGINS=https://yourdomain.com,https://app.yourdomain.com
```
#### 4. Use Reverse Proxy
Use nginx or Caddy in front of your Node.js server:
**nginx example:**
```nginx
server {
listen 443 ssl http2;
server_name yourdomain.com;
ssl_certificate /path/to/cert.pem;
ssl_certificate_key /path/to/key.pem;
location / {
proxy_pass http://localhost:8080;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_cache_bypass $http_upgrade;
}
}
```
### Recommended Enhancements
#### 1. Add Rate Limiting
Install express-rate-limit:
```bash
pnpm add express-rate-limit
```
Add to `src/index.ts`:
```typescript
import rateLimit from 'express-rate-limit';
const limiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 100, // limit each IP to 100 requests per windowMs
message: 'Too many requests from this IP'
});
app.use('/mcp', limiter);
```
#### 2. Add Request ID Tracking
```bash
pnpm add express-request-id
```
```typescript
import addRequestId from 'express-request-id';
app.use(addRequestId());
```
#### 3. Monitor and Alert
Set up monitoring for:
- Server uptime
- Error rates
- OAuth failures
- Token refresh failures
- API response times
Use services like:
- Datadog
- New Relic
- Sentry (for error tracking)
- Prometheus + Grafana
---
## 6. Testing the Integration
### Step 1: Verify Server is Running
```bash
curl http://localhost:8080/health
# Should return: ok
```
### Step 2: Test OAuth Flow
1. Open browser to `http://localhost:8080/slack/install`
2. You should be redirected to Slack
3. Authorize the app
4. You should be redirected back with success message
### Step 3: Get Team ID
After installation, check your logs for the team ID:
```
{"level":"info","team_id":"T01234567","msg":"Slack workspace connected successfully"}
```
Or use Slack API to get it:
```bash
curl https://slack.com/api/auth.test \
-H "Authorization: Bearer xoxb-your-bot-token"
```
### Step 4: Test MCP Connection
Use an MCP client or curl:
```bash
# Initialize session
curl -X POST http://localhost:8080/mcp \
-H "Content-Type: application/json" \
-H "Authorization: Bearer your_api_key" \
-d '{
"jsonrpc": "2.0",
"id": 1,
"method": "initialize",
"params": {
"protocolVersion": "2024-11-05",
"capabilities": {},
"clientInfo": {
"name": "test-client",
"version": "1.0.0"
}
}
}'
```
Save the `Mcp-Session-Id` from the response headers.
### Step 5: Test Slack Tools
List channels:
```bash
curl -X POST http://localhost:8080/mcp \
-H "Content-Type: application/json" \
-H "Authorization: Bearer your_api_key" \
-H "Mcp-Session-Id: your_session_id" \
-d '{
"jsonrpc": "2.0",
"id": 2,
"method": "tools/call",
"params": {
"name": "slack_list_channels",
"arguments": {
"team_id": "T01234567"
}
}
}'
```
Post a message:
```bash
curl -X POST http://localhost:8080/mcp \
-H "Content-Type: application/json" \
-H "Authorization: Bearer your_api_key" \
-H "Mcp-Session-Id: your_session_id" \
-d '{
"jsonrpc": "2.0",
"id": 3,
"method": "tools/call",
"params": {
"name": "slack_post_message",
"arguments": {
"team_id": "T01234567",
"channel_id": "C01234567",
"text": "Hello from MCP!"
}
}
}'
```
---
## Common Issues and Solutions
### Issue: "Origin not allowed"
**Solution:** Add your frontend origin to `ALLOWED_ORIGINS`:
```bash
ALLOWED_ORIGINS=https://yourdomain.com
```
### Issue: "Unauthorized" on MCP endpoint
**Solution:** Include the API key in Authorization header:
```bash
Authorization: Bearer your_api_key
```
### Issue: OAuth redirect mismatch
**Solution:** Ensure `SLACK_REDIRECT_URI` exactly matches the URL in Slack app settings.
### Issue: Tokens lost on restart
**Solution:** Implement persistent storage (PostgreSQL, Redis) instead of `InMemoryInstallStore`.
### Issue: "Token refresh failed"
**Solution:**
1. Check if token rotation is enabled in Slack app
2. Verify `SLACK_CLIENT_ID` and `SLACK_CLIENT_SECRET` are correct
3. User may need to reinstall the app
---
## Next Steps
1. ✅ Complete Slack app configuration
2. ✅ Set up OAuth with correct redirect URI
3. ✅ Deploy server with proper environment variables
4. ✅ Replace InMemoryInstallStore with persistent storage
5. ✅ Configure HTTPS and reverse proxy
6. ✅ Set up monitoring and logging
7. ✅ Test OAuth flow end-to-end
8. ✅ Test MCP tools with real Slack workspace
9. ✅ Document team IDs for your users
10. ✅ Set up backup and disaster recovery
---
## Support Resources
- **Slack API Docs:** [api.slack.com/docs](https://api.slack.com/docs)
- **MCP Specification:** [modelcontextprotocol.io](https://modelcontextprotocol.io)
- **OAuth 2.0 Guide:** [oauth.net/2](https://oauth.net/2/)
For server-specific issues, check the logs with `LOG_LEVEL=debug`.