# Slack MCP Server
A production-ready Model Context Protocol (MCP) server for Slack integration. This server enables AI assistants to interact with Slack workspaces through a secure, multi-tenant OAuth flow.
## Features
- π **Secure OAuth 2.0 Flow** - Full Slack OAuth v2 implementation with state validation
- π’ **Multi-Tenant Support** - Handle multiple Slack workspaces simultaneously
- π **Token Rotation** - Automatic token refresh for long-lived installations
- π‘οΈ **Production Security** - CORS, API key protection, Helmet.js, request timeouts
- π **Structured Logging** - Pino logger with configurable log levels
- π³ **Docker Ready** - Multi-stage Dockerfile with non-root user
- β‘ **MCP Tools** - Post messages, read channel history, list channels
## Architecture
This server uses the **Streamable HTTP transport** for MCP, making it suitable for web-based AI assistants. It manages:
- **OAuth Flow**: `/slack/install` and `/slack/oauth/callback` endpoints
- **MCP Endpoint**: `/mcp` for all MCP protocol communication
- **Session Management**: Automatic session pruning with configurable TTL
- **Installation Storage**: Pluggable storage interface (in-memory by default)
## Prerequisites
1. **Slack App** - Create a Slack app at [api.slack.com/apps](https://api.slack.com/apps)
2. **Node.js** - Version 18+ required
3. **pnpm** - Package manager (or npm/yarn)
## Quick Start
### 1. Install Dependencies
```bash
pnpm install
```
### 2. Configure Environment
Copy the example environment file and fill in your values:
```bash
cp .env.example .env
```
Required variables:
- `SLACK_CLIENT_ID` - From your Slack app's OAuth settings
- `SLACK_CLIENT_SECRET` - From your Slack app's OAuth settings
- `SLACK_REDIRECT_URI` - Your callback URL (e.g., `https://yourdomain.com/slack/oauth/callback`)
- `SLACK_SCOPES` - Comma-separated bot scopes (e.g., `chat:write,channels:history,channels:read`)
- `AFFINITYBOTS_MCP_API_KEY` - Secret key to protect your MCP endpoint
### 3. Run Development Server
```bash
pnpm dev
```
The server will start on `http://localhost:8080` by default.
### 4. Install Slack App
1. Navigate to `http://localhost:8080/slack/install`
2. Authorize the app for your workspace
3. You'll be redirected back with a success message
### 5. Use MCP Tools
Connect your MCP client to `http://localhost:8080/mcp` with:
- **Authorization header**: `Bearer YOUR_AFFINITYBOTS_MCP_API_KEY`
- **Origin header**: Must match `ALLOWED_ORIGINS` if configured
## Available MCP Tools
### `slack_post_message`
Post a message to a Slack channel.
**Parameters:**
- `team_id` (string) - Slack workspace/team ID (T...)
- `channel_id` (string) - Channel/DM ID (C... or D...)
- `text` (string) - Message text (1-4000 characters)
- `thread_ts` (string, optional) - Thread timestamp to reply to
### `slack_get_channel_history`
Read messages from a Slack channel.
**Parameters:**
- `team_id` (string) - Slack workspace/team ID
- `channel_id` (string) - Channel/DM ID
- `limit` (number, optional) - Number of messages (1-200, default: 50)
### `slack_list_channels`
List all channels in the workspace.
**Parameters:**
- `team_id` (string) - Slack workspace/team ID
- `types` (string, optional) - Channel types: `public_channel,private_channel,mpim,im` (default: `public_channel`)
- `limit` (number, optional) - Number of channels (1-1000, default: 200)
## Configuration
### Environment Variables
| Variable | Required | Default | Description |
|----------|----------|---------|-------------|
| `PORT` | No | `8080` | Server port |
| `HOST` | No | `0.0.0.0` | Server host |
| `PUBLIC_BASE_URL` | No | `http://localhost:8080` | Public URL for OAuth redirects |
| `SLACK_CLIENT_ID` | **Yes** | - | Slack app client ID |
| `SLACK_CLIENT_SECRET` | **Yes** | - | Slack app client secret |
| `SLACK_REDIRECT_URI` | **Yes** | - | OAuth callback URL |
| `SLACK_SCOPES` | **Yes** | - | Comma-separated bot scopes |
| `AFFINITYBOTS_MCP_API_KEY` | Recommended | - | API key for MCP endpoint |
| `ALLOWED_ORIGINS` | No | - | Comma-separated allowed origins for CORS |
| `SESSION_TTL_MS` | No | `900000` | MCP session timeout (15 min) |
| `LOG_LEVEL` | No | `info` | Log level: trace, debug, info, warn, error, fatal |
### Required Slack Scopes
At minimum, your Slack app needs these bot token scopes:
- `chat:write` - Post messages
- `channels:history` - Read public channel messages
- `channels:read` - List public channels
Additional recommended scopes:
- `groups:history` - Read private channel messages
- `groups:read` - List private channels
- `im:history` - Read direct messages
- `im:read` - List direct messages
- `mpim:history` - Read group DM messages
- `mpim:read` - List group DMs
## Production Deployment
### Using Docker
Build the image:
```bash
docker build -t slack-mcp-server .
```
Run the container:
```bash
docker run -d \
-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_secret_key \
-e ALLOWED_ORIGINS=https://yourdomain.com \
slack-mcp-server
```
### Production Checklist
- [ ] Set `AFFINITYBOTS_MCP_API_KEY` to a strong random value
- [ ] Configure `ALLOWED_ORIGINS` with your frontend domains
- [ ] Use HTTPS for all endpoints (required by Slack)
- [ ] Set `PUBLIC_BASE_URL` to your public HTTPS URL
- [ ] Replace `InMemoryInstallStore` with persistent storage (Redis/PostgreSQL)
- [ ] Set up monitoring and alerting
- [ ] Configure log aggregation
- [ ] Set appropriate `SESSION_TTL_MS` for your use case
- [ ] Enable Slack token rotation in your app settings (recommended)
- [ ] Set up rate limiting at the reverse proxy level
- [ ] Configure health check monitoring on `/health`
## Storage
The default `InMemoryInstallStore` is suitable for development but **not for production**. For production, implement the `InstallStore` interface with a persistent backend:
```typescript
export interface InstallStore {
upsert(install: SlackInstallation): Promise<void>;
getByTeamId(teamId: string): Promise<SlackInstallation | null>;
}
```
Recommended storage options:
- **PostgreSQL** - Best for relational data and complex queries
- **Redis** - Fast, simple key-value storage
- **MongoDB** - Document-based storage
- **DynamoDB** - Serverless AWS option
## Security Features
### Implemented
- β
CSRF protection via OAuth state parameter
- β
API key authentication for MCP endpoint
- β
CORS with origin allowlist
- β
Helmet.js security headers
- β
Request timeouts (30s)
- β
Session expiration and pruning
- β
Automatic token refresh
- β
Secure error messages (no info leakage)
- β
Non-root Docker user
### Recommended Additional Security
- Add rate limiting (e.g., express-rate-limit)
- Use a reverse proxy (nginx, Caddy) with TLS
- Implement request size limits
- Add request ID tracking
- Set up WAF rules
- Enable Slack signing secret verification for webhooks (if added)
## Monitoring
### Health Check
```bash
curl http://localhost:8080/health
```
Returns `ok` if the server is running.
### Logs
The server uses structured JSON logging via Pino. Key log events:
- OAuth flow initiation and completion
- MCP session creation and expiration
- Token refresh operations
- API errors and warnings
- Security events (unauthorized access, invalid origins)
Set `LOG_LEVEL=debug` for detailed debugging.
## Troubleshooting
### OAuth Errors
**"Invalid or expired state"**
- The OAuth state token expired (10 min TTL)
- Try the installation flow again
**"Slack OAuth failed: invalid_code"**
- The authorization code was already used or expired
- Restart the installation flow
**"Origin not allowed"**
- Your frontend origin is not in `ALLOWED_ORIGINS`
- Add your origin to the environment variable
### MCP Errors
**"Unauthorized"**
- Missing or invalid `Authorization` header
- Ensure you're sending `Bearer YOUR_API_KEY`
**"Unknown or expired session"**
- MCP session expired (default 15 min)
- Reinitialize the MCP connection
**"No Slack installation found"**
- The workspace hasn't installed the app
- Complete the OAuth flow first
### Token Errors
**"Token refresh failed"**
- The refresh token is invalid or revoked
- User needs to reinstall the app
- Check if token rotation is enabled in Slack app settings
## Development
### Project Structure
```
slack-mcp-server/
βββ src/
β βββ index.ts # Main server and routing
β βββ mcp.ts # MCP tool definitions
β βββ slackOauth.ts # OAuth flow handlers
β βββ slackClient.ts # Slack API client with token refresh
β βββ installStore.ts # Installation storage interface
β βββ security.ts # Security utilities
βββ Dockerfile
βββ package.json
βββ tsconfig.json
βββ README.md
```
### Adding New Tools
Register new tools in `src/mcp.ts`:
```typescript
server.tool(
"tool_name",
"Tool description",
{
team_id: z.string(),
// ... other parameters
},
async ({ team_id, ...params }) => {
const slack = await getSlackClientForTeam(store, team_id);
// ... implement tool logic
return { content: [{ type: "text", text: "result" }] };
}
);
```
### Testing
```bash
# Run development server with debug logging
LOG_LEVEL=debug pnpm dev
# Build for production
pnpm build
# Run production build
pnpm start
```
## License
MIT
## Support
For issues and questions:
1. Check the [IMPLEMENTATION.md](./IMPLEMENTATION.md) guide
2. Review Slack API documentation at [api.slack.com](https://api.slack.com)
3. Check MCP documentation at [modelcontextprotocol.io](https://modelcontextprotocol.io)
## Contributing
Contributions welcome! Please:
1. Follow the existing code style
2. Add tests for new features
3. Update documentation
4. Ensure all lints pass