mcp-slack
# mcp-slack
An MCP server for reading Slack through your own browser session, for
workspaces where installing a Slack app isn't an option. Ten tools: six
primitives, three multi-call operations, one write that is off by default.
It authenticates **as you**, not as a bot. Everything you can see, it can read;
anything it posts is indistinguishable from a message you typed. Browser-session
auth is not an officially supported Slack integration path, so check your
workspace's policies first.
The tools take explicit ranges and neutral defaults — no assumed reporting
cadence, channel naming scheme, or truncation limit — so workflows can be built
on top rather than baked in.
## Install
Copy the `d` cookie from your browser (developer tools → Application → Cookies →
`https://app.slack.com`) into `~/.slack-tokens.yml`:
```yaml
slack:
- name: myworkspace.slack.com
token: xoxd-your-cookie-value-here
xoxc: null # auto-populated on first run
```
The short-lived `xoxc` API token is derived automatically and written back, so
later runs skip that step. Both values are session credentials — `chmod 600` the
file, and expect to recopy the cookie whenever your browser session ends.
Then install the server and register it:
```bash
uv tool install --editable .
```
```json
{
"mcpServers": {
"slack": {
"command": "/Users/you/.local/bin/mcp-slack",
"args": [],
"lifecycle": "lazy"
}
}
}
```
To skip installing, point at the repo-root shim instead — it carries a PEP 723
header, so `uv` resolves dependencies on the fly:
`"command": "uv", "args": ["run", "/path/to/mcp-slack/server.py"]`.
## Tools
| Tool | Purpose |
|---|---|
| `slack_search` | Native Slack search syntax (`from:@user`, `in:#channel`, `after:`, `has:link`) |
| `slack_channel_history` | Messages from one channel over a time window |
| `slack_thread` | Every reply in a thread |
| `slack_dm_history` | DM history with one person |
| `slack_user` | Resolve a username or user ID to a profile |
| `slack_list_channels` | Channel discovery by glob and member count (expensive) |
Three tools stitch many calls into one result. They exist because their
deduplication isn't reproducible from outside: a message found by search, by
channel history, and by thread expansion is the same message, and only the
server sees all three passes.
| Tool | Purpose |
|---|---|
| `slack_user_activity` | Everything one person said or received in a range, with optional surrounding context and thread expansion, grouped by channel |
| `slack_channels_history` | History for many channels at once, by list or glob, with replies nested under their parents |
| `slack_profiles` | Batch profiles with custom fields resolved to labels |
Time ranges accept `YYYY-MM-DD`, an epoch, or a relative offset like `-7d`.
`slack_post_message` posts as you, and refuses unless
`SLACK_MCP_ALLOW_WRITE=1` is set in the server's environment:
```json
"env": { "SLACK_MCP_ALLOW_WRITE": "1" }
```
## Behavior worth knowing
- **A channel with no messages can't be resolved by name.** Names resolve via
`search.messages`, because `conversations.list` is throttled to the point of
uselessness on Enterprise Grid — measured at 11m48s of consecutive 429
backoffs without reaching the target channel. Pass a channel ID (`C…`) for
empty or archived channels, and prefer a channel name over
`slack_list_channels`, which still enumerates and may return `rate_limited`.
- **Glob discovery only sees channels you've joined.** It uses
`users.conversations` for the same throttling reason.
- **Failures come back as data**, not exceptions: `{"error": "not_found", ...}`.
Codes are `not_found`, `rate_limited`, `auth_failed`, and `write_disabled`.
An expired cookie shows up as `auth_failed`.
- **Rate-limit waits are bounded.** A cumulative sleep budget (45s, reset each
tool call) means a call returns `rate_limited` rather than hanging. Library
callers who don't mind waiting can raise it: `SlackClient(ws, wait_budget=600)`.
- **Messages are projected, not passed through.** Raw Slack records run to
several KB each; tools return `ts`, `time`, `user`, `user_name`, `text`, and
`permalink`, plus thread fields when meaningful. Mentions and links are
rewritten to readable text, and permalinks are built locally, so citing a
message costs no extra call.
- **Lookups are cached, message content is not.** One JSON file per workspace,
with a timestamp per entry:
| Cached | TTL | |
|---|---|---|
| DM channel ID | never | assigned once per pair of users |
| user name → ID | 30d | only a handle change invalidates it |
| user ID → name | 7d | display names change occasionally |
| channel name → ID | 7d | renames are rare but real |
| team profile schema | 30d | effectively static |
| channel member counts | 24h | drifts slowly, only gates a filter |
| failed lookups | 1h | stops a typo being re-searched in a loop |
A negative is only recorded after a search completes and matches nothing, so
a rate limit or transport error is never cached as "does not exist".
## Library use
The multi-call operations are plain functions in `slack_mcp/aggregate.py`
(`user_activity`, `channels_history`, `profiles`) taking an explicit
`SlackClient`. Import them directly rather than speaking MCP to a subprocess.
## License
MIT
TDQS
Scored across 10 tools
Most tools have clearly distinct purposes: search, channel history, thread expansion, DM history, user lookup, and posting. Minor overlap exists between slack_user_activity and the composition of search/history/threads, and between slack_user vs slack_profiles, but descriptions clarify their intended uses.
All tools share the slack_ prefix and use lowercase snake_case, which is consistent. Some names are verbs (search, list, post) while others are nouns (thread, user, profiles), and history variants differ (channel_history vs channels_history), but the pattern is readable and predictable overall.
Ten tools is well within the ideal range for a Slack-focused server. The count covers search, messaging history, threads, DMs, channels, users, profiles, and posting without being bloated or sparse.
The tool set provides solid coverage of read operations: search, channel/thread/DM history, multi-channel history, user activity, and profile lookups. Minor gaps exist such as channel metadata, reactions, or message send (disabled by default), but core agent workflows are well supported.