John KuehJohn Kueh
All skills

Claude Code skill

slack-search

Search and pull Slack threads.

Install all skills in one command:

claude mcp add-plugin johnkueh/claude-skills

Why it exists

Slack's search UI surfaces 5 messages with no thread context. Reading a thread means clicking through. This skill takes a query, a username, or a permalink and returns the full thread — every reply, in order, as plain text — so you can paste an entire conversation into a Claude prompt without losing context.

In practice

Keyword search
Input
slack-search "auth migration"
Output
Matched messages across all channels with timestamp, author, channel and a thread permalink.
Fetch a thread
Input
slack-search --thread <permalink>
Output
Full thread as ordered text: parent message + every reply with author and timestamp.
skills/slack-search/SKILL.mdRaw
---
name: slack-search
description: Search Slack messages and fetch threads. Use when the user asks to find Slack conversations, look up a colleague, list channels, or pull a specific thread by URL. Personal-token-based (xoxp-...). Triggers on "search Slack", "find that Slack thread", "what did X say in Slack", "Slack DM history", "pull this Slack URL", "who in [channel]", "Slack message about", "look up [name] on Slack", or any Slack permalink (slack.com/archives/...).
---

# slack-search

Search Slack workspace content using your personal user token (xoxp-...).

## Setup

Add your Slack User Token to `~/.claude/settings.json` under `env`:

```json
{
  "env": {
    "SLACK_USER_TOKEN": "xoxp-...",
    "SLACK_USER_ID": "U..."
  }
}
```

The skill also falls back to a `.env` in the current working directory if present.

## Commands

All commands accept `--json` for structured output (useful when piping to `jq`).

### Search messages

```bash
uv run python slack_search.py search "keyword"

# Filters
uv run python slack_search.py search "deploy" --channel engineering --from sam --limit 10

# Slack search modifiers also work inside the query
uv run python slack_search.py search "bug after:2026-01-01 has:link"
```

### Fetch a thread

Paste any Slack permalink, or pass `<channel-id>:<ts>`:

```bash
uv run python slack_search.py thread https://your-team.slack.com/archives/C0123456/p1700000000000001
uv run python slack_search.py thread C0123456:1700000000.000001 --json
```

Returns the parent message + all replies, with `<@U…>` mentions resolved to `@username`. Each message also includes a `files` array with metadata for any attached images / videos / docs.

### Download attachments

`search` and `thread` both accept `--download [DIR]`. When set, file attachments
on each message are downloaded and the local path is added to the `files`
entry as `local_path`. Omit DIR to use `~/.cache/slack-search/files/<scope>/`.

```bash
# default cache dir
uv run python slack_search.py thread C0123456:1700000000.000001 --download --json

# explicit dir
uv run python slack_search.py thread <permalink> --download /tmp/slack-files
```

Requires the `files:read` scope on the user token; without it Slack returns the
workspace login HTML and the downloader prints a clear error.

### List channels / users / profile (lookup helpers for filters)

```bash
uv run python slack_search.py channels   # public + private the user is in
uv run python slack_search.py users      # active workspace members
uv run python slack_search.py me         # your profile (auth check)
```

## Search modifiers

Slack's native modifiers work inside the query string:

- `in:channel` — restrict to channel
- `from:user` — restrict to author
- `before:YYYY-MM-DD`, `after:YYYY-MM-DD`
- `has:link`, `has:reaction`

`--channel` / `--from` are sugar for `in:` / `from:`.

## Required scopes

Personal user token (xoxp-…) needs:

- `search:read`
- `channels:read`, `groups:read`
- `users:read`
- `channels:history`, `groups:history` (for `thread`)
- `files:read` (only required for `--download`; without it, Slack bounces file URLs to the workspace login page)

## Troubleshooting

- **`missing_scope`**: add the missing scope in your Slack App's OAuth settings, then reinstall the app.
- **`invalid_auth`**: token expired — regenerate at https://api.slack.com/apps.
- **Token not picked up**: confirm with `printenv SLACK_USER_TOKEN`. If empty, the env block in `~/.claude/settings.json` may not have been reloaded — restart Claude Code.