
> **TL;DR.** One Slack CLI (`slackcli`), three editors (Claude Code, Cursor, Codex CLI), `xoxc/xoxd` browser tokens for personal use or `xoxb/xoxp` for production. Total setup: 10 minutes. Token-efficient JSON output keeps your context window clean.

If you spend half your day in Slack and the other half in **Claude Code**, **Cursor**, or **Codex CLI**, there's an obvious win: let the AI agent read Slack itself. Don't paste threads into the chat. Don't summarise channels by hand. Wire a **Slack CLI** into the agent and let it pull what it needs.

This guide is the 10-minute version: install, authenticate, register with each editor, run five real workflows, then the security and production-deployment caveats. Skip to the section you need.

## Why a Slack CLI (and not the official Slack CLI)?

Quick disambiguation. Slack ships its own CLI called **`slack`** ([docs.slack.dev/tools/slack-cli](https://docs.slack.dev/tools/slack-cli/)). It's for **building and deploying Slack apps** — `slack create`, `slack run`, `slack deploy`. It cannot read a channel, send a message ad hoc, or summarise a thread for an agent.

What you want is a **third-party Slack CLI** focused on *using* Slack: reading messages, posting messages, listing users. There are a few options as of May 2026:

| Tool | Stars | Auth modes | Output | AI-agent fit |
| --- | --- | --- | --- | --- |
| [`slackcli`](https://github.com/shaharia-lab/slackcli) | ~500 | xoxc/xoxd browser tokens, xoxb/xoxp app tokens | `--json` for agents, human for terminal | Built for agents from day one |
| [`stablyai/agent-slack`](https://github.com/stablyai/agent-slack) | ~120 | xoxb only | JSON | OK, README only — no docs site |
| [Composio Slack tool](https://composio.dev) | n/a (SaaS) | OAuth via Composio | JSON over MCP | Heavy — pulls in Composio's auth layer |

For this post I'll use [`slackcli`](https://github.com/shaharia-lab/slackcli) because (a) I wrote it, and (b) the JSON output is shaped specifically for LLM consumption — short keys, no nested arrays the model has to flatten, optional `thread_ts` fields exposed at the top level. If you're using a different tool, the integration shape is identical; just swap the binary name.

If you want the **CLI vs MCP** comparison: an MCP server is great for long-lived agents that benefit from typed tool descriptors. A CLI is great when your agent is a one-shot shell-tool runner (Claude Code, Codex CLI, Cursor's tool runner) and when you want JSON you can pipe into `jq`. Many teams ship both — same backend, two front-ends.

## Step 1 — Install `slackcli`

macOS / Linux:

```bash
curl -sSf https://slackcli.io/install.sh | sh
```

Or with Homebrew:

```bash
brew install shaharia-lab/tap/slackcli
```

Verify:

```bash
slackcli --version
# slackcli 0.7.0
```

Windows: download the binary from the [releases page](https://github.com/shaharia-lab/slackcli/releases) and add it to `PATH`.

## Step 2 — Authenticate

You have two options. Pick based on **whose Slack you're automating**.

### Option A — `xoxc` / `xoxd` browser tokens (personal automation)

Best for personal use on workspaces you own or where you have a verbal agreement with the admin to automate your own account. **No Slack app to create**, **no OAuth dance**.

1. Open Slack in a browser, log in, land on `app.slack.com`.
2. Open DevTools → **Application** tab → **Cookies** → copy the value of the `d` cookie (that's your `xoxd-…` token, URL-decoded).
3. DevTools → **Network** tab → filter by `slack.com/api`, click any request, find `Authorization: Bearer xoxc-…` in the request headers — that's your `xoxc` token.
4. Run:

   ```bash
   slackcli auth login-browser
   # Paste xoxc when prompted
   # Paste xoxd when prompted
   ```

The detailed step-by-step (with screenshots) is in [Using Slack browser session tokens with Go SDK](/blog/slack-browser-tokens-golang-sdk-bypass-app-creation/), which also explains how the four token formats — `xoxb`, `xoxp`, `xoxc`, `xoxd` — relate to each other.

### Option B — Slack app with `xoxb` / `xoxp` (production)

For shared automation, build a real Slack app. The **minimum scopes** for read+write are:

- `channels:history`, `channels:read`
- `groups:history`, `groups:read` (private channels)
- `chat:write`
- `users:read`

Add `im:history`, `im:read`, `im:write` if the agent should handle DMs. Add `chat:write.customize` if it should post under a friendly username/icon.

Install the app, copy the **Bot User OAuth Token** (`xoxb-…`), then:

```bash
slackcli auth login-app
# Paste xoxb-... when prompted
```

## Step 3 — Verify

```bash
slackcli conversations list --limit 5
slackcli conversations read C012345 --limit 10 --json | head -40
```

If both succeed, you're set. The `--json` mode is what the agents will use; the human-readable mode is for you.

## Step 4 — Wire it into each editor

### Claude Code (`~/.claude/settings.json`)

```json
{
  "permissions": {
    "allow": [
      "Bash(slackcli conversations list:*)",
      "Bash(slackcli conversations read:*)",
      "Bash(slackcli messages send:*)",
      "Bash(slackcli messages list:*)"
    ]
  }
}
```

Pre-approving these subcommands prevents Claude Code from prompting on every call. Drop `messages send` from the allowlist if you want a human in the loop for posts.

### Cursor (`Settings → Tools → Add Custom Tool`)

1. **Name:** `slackcli`
2. **Command:** `slackcli`
3. **Default args:** `--json`
4. **Description:** "Read Slack channels, post messages, list users. Always run with --json for parseable output."

Cursor's tool runner appends arguments after the default ones, so any subcommand the model invokes will inherit `--json` automatically.

### Codex CLI (`~/.codex/config.toml`)

```toml
[tools.shell]
enabled = true

[tools.shell.allowlist]
commands = ["slackcli"]
```

Codex auto-detects JSON in tool output, so you don't need to force `--json` — the model will choose it when it wants structured data.

## Five real agent workflows

These are the prompts I actually use day-to-day. All five work in Claude Code, Cursor, and Codex without further config — the agent figures out which `slackcli` subcommand to invoke.

### 1. Catch-up summary

> "Read the last 50 messages in `#engineering` and `#oncall`, then give me a 5-bullet summary of what happened while I was off."

The agent runs:

```bash
slackcli conversations read C0ENG --limit 50 --json
slackcli conversations read C0OPS --limit 50 --json
```

…then summarises. Works because the JSON output is small enough to fit in one context window for 100 messages.

### 2. Threaded reply

> "There's a thread in `#bug-reports` about the auth 500s — read it, write a reply that explains the root cause from PR #1234."

The agent reads the channel, finds the parent message's `ts`, then:

```bash
slackcli messages send --recipient-id=C0BUG --thread-ts=1747000000.123456 \
  --message="Root cause: missing nil-check on session pointer; fixed in #1234."
```

The `--thread-ts` flag is the bit every agent gets right because it shows up cleanly in the JSON output of the read call.

### 3. Standup draft from your week's activity

> "Read my DMs and the channels I posted in this week, then draft a Monday standup."

This one needs `users:read` to know which messages were yours; the agent filters the JSON for messages where `user == <your-id>` and summarises.

### 4. Triage notifications

> "Find all messages where I'm mentioned in the last 24 hours, group them by urgency, and tell me which to handle first."

Uses `slackcli messages list --mentions-me --since=24h --json`, sorts by reaction count and presence of "urgent" / "blocker" keywords.

### 5. Auto-post deploys

In a CI script, not an interactive agent:

```bash
slackcli messages send --recipient-id=C0DEPLOYS --message=":rocket: Deployed v$VERSION to prod"
```

This is the boring use case but it removes a Slack-app-and-webhook step.

## Security — three guardrails I never skip

### Guardrail 1: channel allowlist for `messages send`

Set `SLACKCLI_SEND_ALLOWLIST` in your shell rc to a comma-separated list of channel IDs the CLI will let you post to. `slackcli messages send` refuses anything outside that list with a clear error. The agent can still **read** any channel it has token-level access to — the allowlist only constrains writes.

```bash
export SLACKCLI_SEND_ALLOWLIST="C0ENG,C0DEPLOYS,C0BUG-REPORTS"
```

If the agent goes off-script and tries to post to `#exec-team`, it gets an error, not a public-relations incident.

### Guardrail 2: confirm-before-send (Claude Code)

Drop `Bash(slackcli messages send:*)` out of `permissions.allow` and let Claude Code prompt you on every send. The friction is fine — you're posting in Slack, you can afford one extra keypress. For Cursor and Codex, set the equivalent flag (Cursor: "require confirmation"; Codex: `[tools.shell.confirm]`).

### Guardrail 3: audit log

Wrap `slackcli messages send` in a tiny shim:

```bash
# ~/.local/bin/slackcli-audit
#!/usr/bin/env bash
exec 9>>"$HOME/.slackcli-audit.log"
echo "$(date -Iseconds) $*" >&9
exec slackcli "$@"
```

Then point your editor at `slackcli-audit` instead of `slackcli`. You get an append-only log of every command the agent ran, which is exactly the evidence you want when something goes sideways.

## When to graduate from `xoxc` to a real Slack app

Browser tokens are personal and rotate when Slack invalidates your session. They're also tied to *you* — if your colleague runs an agent with your tokens, every action looks like it came from your account. Move to a real Slack app when:

- You have **more than one human** running the agent against the same workspace.
- The agent runs **without an interactive user present** (CI, scheduled jobs, hosted MCP).
- You need an **audit trail in Slack itself** showing the bot acted, not the user.
- Your security team asks. They will.

The migration is mechanical: create a Slack app, install with the right scopes, swap `slackcli auth login-browser` for `slackcli auth login-app`, done. Every other command works identically.

## Honest comparison — when not to use this

This stack is **wrong** if:

- **Your agent already speaks MCP.** Plug in a Slack MCP server (e.g. `modelcontextprotocol/servers/slack`) and skip the CLI layer. Less marshalling, persistent connection.
- **You need realtime push** (the agent must react to new messages). CLIs are pull-based. Use Slack Events API + a webhook.
- **You're integrating into a non-coding agent** (a chatbot in Slack itself). Use Slack's Bolt framework, not a CLI.
- **Compliance forbids browser tokens.** Some workspaces require all automation to go through audited Slack apps. Skip Step 2 Option A entirely.

For the 80% case — a developer who wants their AI coding agent to read and write Slack — the CLI route is the fastest and the most debuggable.

## Related

- [Slack token formats explained (xoxb, xoxp, xoxc, xoxd) + using xoxc/xoxd in Go](/blog/slack-browser-tokens-golang-sdk-bypass-app-creation/) — the deep dive on browser tokens.
- [SlackCLI: command-line tool for Slack automation](/blog/slackcli-command-line-tool-slack-automation/) — full CLI reference.
- [Run Claude Code in Docker with network isolation](/blog/run-claude-code-docker-network-isolation/) — sandbox the agent so a bad prompt can't exfiltrate Slack data.

Ten minutes from `curl install.sh` to your agent posting a Slack thread. That's the entire pitch.
