nomos-slack-mcp
# nomos-slack-mcp
An MCP server that lets AI agents read and write Slack messages **as you** — not as a bot. Messages, reactions, and status updates appear exactly as if you typed them yourself.
Built for [Claude Code](https://docs.anthropic.com/en/docs/claude-code) and any MCP-compatible client. Supports multiple Slack workspaces with independent authentication.
## Why?
Existing Slack integrations send messages as a bot with an `APP` badge. This uses **Slack user tokens** (`xoxp-`) so your agent operates as you — indistinguishable from you being at the keyboard.
## Features
- **Send as you** — no bot badge, no "APP" indicator
- **Multi-workspace** — manage 2+ Slack workspaces with separate tokens and permissions
- **10 tools** — read, write, search, react, set status
- **Smart resolution** — use `#channel-name`, `@username`, or raw channel IDs
- **Local only** — tokens stay on your machine at `~/.nomos/slack/config.json` with `0600` permissions
## Quick Start
### 1. Create a Slack App
For **each** workspace you want to connect:
1. Go to [api.slack.com/apps](https://api.slack.com/apps) → **Create New App** → **From app manifest**
2. Paste the contents of [`slack-app-manifest.yml`](./slack-app-manifest.yml) (or use the JSON version)
3. Click **Create**
4. Go to **Install App** → **Install to Workspace** → **Allow**
> [!TIP]
> The manifest pre-configures all required User Token Scopes and the OAuth redirect URL. No manual scope configuration needed.
### 2. Install
```bash
git clone https://github.com/project-nomos/nomos-slack-mcp.git
cd nomos-slack-mcp
pnpm install
pnpm build
```
### 3. Add Your Workspaces
**Option A: OAuth flow** (recommended)
```bash
pnpm add-workspace
```
You'll be prompted for:
- A workspace alias (e.g. `work`, `side-project`)
- Your Slack App's **Client ID** and **Client Secret** (found under **Basic Information** in your Slack App settings)
The script opens your browser for OAuth authorization and stores the user token automatically.
**Option B: Manual token**
```bash
pnpm add-workspace -- --manual
```
Paste your `xoxp-` token directly. Find it in your Slack App under **OAuth & Permissions** → **User OAuth Token**.
Repeat for each workspace.
### 4. Connect to Claude Code
```bash
claude mcp add nomos-slack -- node /path/to/nomos-slack-mcp/dist/src/index.js
```
Or add to your MCP config manually:
```json
{
"mcpServers": {
"nomos-slack": {
"command": "node",
"args": ["/path/to/nomos-slack-mcp/dist/src/index.js"]
}
}
}
```
## Tools
| Tool | Description |
| ----------------------- | ---------------------------------------------------------------------- |
| `slack_list_workspaces` | List configured workspaces and which is default |
| `slack_list_channels` | Browse channels, DMs, groups — filter by type or name |
| `slack_read_messages` | Read recent messages from any channel or DM |
| `slack_read_thread` | Read all replies in a thread |
| `slack_send_message` | Send a message as you |
| `slack_reply_thread` | Reply in a thread as you |
| `slack_search` | Search messages with Slack operators (`from:`, `in:`, `before:`, etc.) |
| `slack_list_users` | List or search workspace members |
| `slack_set_status` | Set your status emoji and text |
| `slack_react` | Add a reaction to a message |
### Channel Resolution
All tools that accept a `channel` parameter understand:
- `#general` — public/private channel by name
- `@john` — opens a DM with that user
- `C0123ABCDEF` — raw Slack channel ID
### Multi-Workspace
Every tool accepts an optional `workspace` parameter. Omit it to use your default workspace.
```
"Read my DMs in the side-project workspace"
→ slack_read_messages({ workspace: "side-project", channel: "@alice" })
```
## Configuration
Config is stored at `~/.nomos/slack/config.json`:
```json
{
"workspaces": {
"work": {
"token": "xoxp-...",
"teamId": "T01ABC123",
"teamName": "Acme Corp",
"userId": "U01XYZ789",
"addedAt": "2026-03-26T10:00:00Z"
},
"side-project": {
"token": "xoxp-...",
"teamId": "T02DEF456",
"teamName": "Side Project",
"userId": "U02UVW012",
"addedAt": "2026-03-26T11:00:00Z"
}
},
"defaultWorkspace": "work"
}
```
File permissions are set to `0600` (owner read/write only).
## Required Slack Scopes
All scopes are **User Token Scopes** (not Bot Token Scopes). The included [app manifest](./slack-app-manifest.yml) configures these automatically.
| Scope | Purpose |
| --------------------- | --------------------------------- |
| `channels:history` | Read messages in public channels |
| `channels:read` | List public channels |
| `groups:history` | Read messages in private channels |
| `groups:read` | List private channels |
| `im:history` | Read direct messages |
| `im:read` | List direct messages |
| `mpim:history` | Read group DMs |
| `mpim:read` | List group DMs |
| `chat:write` | Send messages as you |
| `users:read` | List workspace members |
| `users:read.email` | See member emails |
| `search:read` | Search messages |
| `users.profile:write` | Set your status |
| `reactions:write` | Add reactions |
| `reactions:read` | Read reactions |
## Development
```bash
# Install dependencies
npm install
# Build
npm run build
# Type-check without emitting
pnpm check
# Watch mode
pnpm dev
```
### Project Structure
```
src/
├── index.ts # MCP server entry point (stdio transport)
├── config/
│ ├── config-manager.ts # Read/write ~/.nomos/slack/config.json
│ └── types.ts # TypeScript interfaces
├── slack/
│ ├── client-manager.ts # Per-workspace WebClient instances + channel/user resolution
│ └── formatters.ts # Format Slack API responses for LLM consumption
├── tools/
│ ├── index.ts # Tool registry
│ ├── list-workspaces.ts
│ ├── list-channels.ts
│ ├── read-messages.ts
│ ├── read-thread.ts
│ ├── send-message.ts
│ ├── reply-thread.ts
│ ├── search.ts
│ ├── list-users.ts
│ ├── set-status.ts
│ └── react.ts
├── oauth/
│ ├── flow.ts # OAuth 2.0 token exchange
│ └── server.ts # Local callback server for OAuth
scripts/
└── add-workspace.ts # CLI for adding workspaces
```
## Security
- **Tokens are stored locally** at `~/.nomos/slack/config.json` with `0600` file permissions
- **User tokens (`xoxp-`) are sensitive** — they can act as you. Treat them like passwords
- **No data leaves your machine** except Slack API calls and whatever you send to your LLM provider as context
- **Bot tokens are rejected** — the config manager validates that tokens start with `xoxp-`
## Troubleshooting
**"No workspaces configured"**
Run `pnpm add-workspace` to add a workspace.
**"Token must be a user token (xoxp-...)"**
You're using a bot token (`xoxb-`). Go to your Slack App → OAuth & Permissions → copy the **User OAuth Token**, not the Bot User OAuth Token.
**"Channel #foo not found"**
The user token can only see channels you've joined. Join the channel in Slack first.
**"missing_scope" errors**
Your Slack App is missing required scopes. Re-create the app using the provided [manifest](./slack-app-manifest.yml) or manually add the missing scope under OAuth & Permissions → User Token Scopes, then reinstall the app.
**Rate limiting**
The Slack SDK handles `429` responses with automatic retry. If you hit persistent rate limits, reduce the frequency of requests. History endpoints are Tier 2 (~20 req/min), most others are Tier 3 (~50 req/min).
## License
MIT
TDQS
Scored across 10 tools
Each tool targets a distinct Slack operation: listing, reading, sending, replying, searching, status, and reactions. There is no meaningful overlap between tools; even read_messages and read_thread are clearly separated by message stream vs. thread replies.
All tools follow the slack_ prefix with snake_case and mostly use a verb_noun pattern like slack_list_channels and slack_send_message. The only minor deviation is slack_search, which omits an explicit object, but it remains clear and consistent in style.
Ten tools is well-scoped for a Slack integration server. Each tool covers a distinct common Slack action without unnecessary redundancy or bloat.
The toolset covers the core Slack workflows: reading, sending, replying, searching, listing users/channels/workspaces, setting status, and reacting. Minor gaps like file uploads, channel creation, or message deletion exist but do not break typical assistant use cases.