slack-readonly-mcp
by tomfunk
README.md
# slack-readonly-mcp
A self-hosted, **read-only** Slack MCP server for Claude Code (and any MCP client).
## Why this exists
Hosted Slack MCP connectors route your Slack message content through a
third-party hosted MCP layer. If you care about where that data goes, that hop
sits outside your control. This server avoids it:
- It runs **locally** as a stdio subprocess that the MCP client launches.
- It calls the **Slack Web API directly** with your own token.
- Tool results flow only into your MCP client's context → whatever model
endpoint you've configured (e.g. an org with zero data retention).
So the full path is: your machine → Slack API → your MCP client → your model
endpoint. No third-party MCP intermediary. Data-retention guarantees are a
property of the model endpoint you point the client at — this server doesn't
change them, it only adds tools.
> **Note:** Data that lives *in Slack* is governed by your Slack plan, not by
> this server. This server only governs the path between Slack and your client.
## Tools (all read-only)
| Tool | Purpose |
|---|---|
| `slack_list_channels` | List channels (id, name, topic, members) |
| `slack_channel_history` | Recent messages in a channel |
| `slack_thread_replies` | All replies in a thread |
| `slack_conversations_info` | Metadata for one channel |
| `slack_user_info` | Look up a user (email omitted) |
| `slack_list_users` | List workspace users (email omitted) |
| `slack_search_messages` | Search messages — **requires a user token** (see below) |
## Setup
### 1. Create a Slack app with read-only scopes
At <https://api.slack.com/apps> → **Create New App → From a manifest**, paste:
```yaml
display_information:
name: Claude Read-Only
oauth_config:
scopes:
bot:
- channels:read
- channels:history
- groups:read
- groups:history
- im:read
- im:history
- mpim:read
- mpim:history
- users:read
user:
- search:read # optional — only needed for slack_search_messages
settings:
org_deploy_enabled: false
socket_mode_enabled: false
```
Install it to the workspace, then copy the **Bot User OAuth Token** (`xoxb-…`).
If you want search, also copy the **User OAuth Token** (`xoxp-…`). Invite the bot
to any private channels you want it to read (`/invite @Claude Read-Only`).
### 2. Build
```sh
npm install
npm run build
```
### 3. Register with Claude Code
```sh
claude mcp add slack-readonly \
--env SLACK_BOT_TOKEN=xoxb-your-token \
-- node /path/to/slack-readonly-mcp/dist/index.js
```
Or, for a project-scoped `.mcp.json` (keep the token in your shell env, not in
the file — Claude Code expands `${VAR}`):
```json
{
"mcpServers": {
"slack-readonly": {
"command": "node",
"args": ["/path/to/slack-readonly-mcp/dist/index.js"],
"env": {
"SLACK_BOT_TOKEN": "${SLACK_BOT_TOKEN}",
"SLACK_USER_TOKEN": "${SLACK_USER_TOKEN}"
}
}
}
}
```
## Search caveat
Slack's `search.messages` endpoint **cannot be called with a bot token** — it
requires a user token. Set `SLACK_USER_TOKEN` (`xoxp-…`, `search:read`) to enable
`slack_search_messages`; without it, the other six tools work and search returns
a clear error.
## Notes
- All logging goes to stderr; stdout is the JSON-RPC channel.
- The server fails fast (exit 1) if `SLACK_BOT_TOKEN` is unset.
- Read-only by design. Adding write tools (post/reply/react) means adding write
scopes — a deliberately separate, larger-blast-radius change.
## License
MIT
TDQS
A4.2/5.0
Scored across 7 tools
Disambiguation5/5
Each tool has a clearly distinct purpose: listing vs single item, history vs search, threads vs general messages, users vs user info. No overlap.
Naming Consistency5/5
All tools follow the consistent 'slack_verb_noun' pattern (e.g., slack_list_channels, slack_user_info). Minor variation between 'channel_history' and 'conversations_info' but both are noun phrases in the same style.
Tool Count5/5
7 tools is well-scoped for a read-only Slack server, covering channel exploration, history, threads, user info, and search without being excessive.
Completeness4/5
Covers core read operations (channel list, history, threads, users, search). Minor gaps like reading pinned messages or files, but these are less critical for a read-only interface.
Maintenance
ActivityStale
ResponsivenessNo issues