Giving Claude access to Slack — every option
ai/claude-slack-access-options · Updated · Tags: ai, claude, slack
正在用 AI 翻譯成中文,可能需要一分鐘… 以下暫時顯示英文原文。
Giving Claude access to Slack — every option
TL;DR
- Easiest route for Claude Code: Slack's official MCP server, if your workspace admin allows it.
- If the MCP is blocked, create your own Slack app with a read-only user token (
xoxp-) and let Claude call the Web API. - Claude in Slack (
@Claude) works inside Slack only. It doesn't give Claude Code in your terminal or IDE any Slack access. - The Slack CLI and webhook or bot tokens you may already have can't read message history.
- Posting needs
chat:write: a user token posts as you, a bot token posts as the app, and a webhook posts to one fixed channel. - Start with the MCP. If it's blocked, request a private read-only app; don't scrape the desktop app's session token.
Contents
- The options at a glance
- How to choose
- Option A — Slack's official MCP server
- Option B — Your own Slack app with a user token
- Option C — Your own Slack app with a bot token
- Option D — A community MCP server with your token
- Option E — Claude in Slack (
@Claude) - Writing messages
- Things that look like options but aren't
- Scopes reference
- Security notes
The options at a glance
| Option | Where Claude runs | Reads channels | Reads DMs | Search | Posts messages | Admin approval | Effort |
|---|---|---|---|---|---|---|---|
| A. Slack MCP server | Claude Code, other MCP clients | Yes | Yes (yours) | Yes | Yes, as you | Usually | Low |
| B. Own app + user token | Claude Code via a script or skill | Yes | Yes (yours) | Yes | With chat:write, as you | Usually | Medium |
| C. Own app + bot token | Claude Code via a script or skill | Only channels the bot is invited to | No | No | With chat:write, as the app | Usually | Medium |
| D. Community MCP + token | Claude Code | Same as the token you give it | Same | Same | If the token and server allow it | Same as B or C | Medium |
| E. Claude in Slack | Inside Slack | Thread it's tagged in, plus public search | Only its own DM with you | Public channels | Replies in the thread, as @Claude | Yes (Team/Enterprise plan) | Low |
| F. Incoming webhook | Anywhere that can send an HTTP POST | No | No | No | Yes, to one fixed channel | Usually | Very low |
Every token-based option sees at most what the token's owner can see. A user token acts as you; a bot token acts as the bot.
How to choose
| Your situation | Pick |
|---|---|
| Your admin allows Slack's MCP server | A |
| The MCP is blocked, but your admin approves custom apps | B |
| You only need a few shared channels, not DMs or search | C |
| You want MCP tools instead of shell calls, and you trust the project | D, using the token from B or C |
| You want to ask Claude about a thread while you're in Slack | E |
| Claude should post to Slack as you (replies, updates) | A, or B with chat:write |
| Claude or a script should post as an app (alerts, summaries, reports) | C with chat:write |
| You only need notifications posted to one channel | F |
| Nothing is approved | Paste the thread text or export into Claude by hand |
Option A — Slack's official MCP server
Slack hosts an MCP server at https://mcp.slack.com/mcp. Claude Code connects to it over HTTP and logs in with OAuth as you.
Setup in Claude Code
-
Check Slack's MCP documentation for the client ID and callback port to use with Claude Code. Slack's server needs a client it has registered in advance, so a plain URL isn't enough.
-
Add the server:
claude mcp add --transport http --client-id <client-id> --callback-port <port> \ slack https://mcp.slack.com/mcp -
In an interactive Claude Code session, run
/mcp, pick slack, and finish the browser login. -
Ask something that needs Slack, for example "summarise yesterday in #dev".
Gotchas
/mcpshowing Needs authentication means the OAuth step hasn't been done. Non-interactive sessions (claude -p, cron) can't complete it.- Workspace admins can block the MCP app. When they have, the login fails and nothing on your side fixes it. Move on to B.
- If claude.ai offers a Slack connector, it probably goes through Slack's MCP too, so the same admin block would apply.
- The server includes tools that send messages as you, not just read ones. To make Claude ask before posting, leave those tools out of the allow list in
settings.json, so each send prompts you for approval.
Option B — Your own Slack app with a user token
This is the fallback that works when the MCP is blocked but your admin approves custom apps. You create a private Slack app that requests only user read permissions, install it, and give Claude the resulting xoxp- token.
1. Create the app from a manifest
Go to https://api.slack.com/apps, click Create New App, choose From a manifest, pick your workspace, and paste:
display_information:
name: Claude Reader
description: Read-only access to my own Slack for Claude Code
oauth_config:
scopes:
user:
- channels:read
- channels:history
- groups:read
- groups:history
- im:read
- im:history
- mpim:read
- mpim:history
- users:read
- search:read
settings:
token_rotation_enabled: false
Drop any permissions you don't need. For example, remove im:* and mpim:* if Claude shouldn't read DMs. A shorter list is also easier to get approved.
To let Claude post too, add write permissions under the same user: list:
- chat:write # post, edit and delete your own messages
- reactions:write # add emoji reactions
- im:write # open a DM with someone before posting to it
- files:write # upload files
These permissions let Claude post as you. Consider a separate app (or a bot token, option C) for writing, so a read-only token stays read-only. See Writing messages.
2. Install it
- Open OAuth & Permissions and click Install to Workspace.
- If the button says Request to Install, your admin has to approve it. Explain in one line why you need it and that it has no write permissions.
- Once installed, copy the User OAuth Token (
xoxp-…) from the same page.
Keep token rotation off. With rotation on, the token expires every 12 hours and you'd need a refresh flow.
3. Store the token outside your shell history
| OS | Store | Read |
|---|---|---|
| macOS | security add-generic-password -a "$USER" -s SLACK_READ_TOKEN -w (prompts for the value) | security find-generic-password -a "$USER" -s SLACK_READ_TOKEN -w |
| Windows | Credential Manager (cmdkey or the CredentialManager PowerShell module) | The same module's Get-StoredCredential |
| Linux | secret-tool store --label=slack service SLACK_READ_TOKEN | secret-tool lookup service SLACK_READ_TOKEN |
Don't paste the token into a prompt, a committed file or a shared doc.
4. Test it
T=$(security find-generic-password -a "$USER" -s SLACK_READ_TOKEN -w) # macOS
curl -s -H "Authorization: Bearer $T" https://slack.com/api/auth.test
curl -s -H "Authorization: Bearer $T" "https://slack.com/api/search.messages?query=deploy&count=5"
auth.test should return "ok": true with your user name. A missing_scope error names the permission you still need to add.
5. Connect it to Claude Code
Pick one, or combine them:
| Approach | What it is | Good for |
|---|---|---|
Skill (~/.claude/skills/slack/SKILL.md) | Instructions telling Claude where the token is and which API methods to call | Loads automatically whenever you mention Slack |
Wrapper script (e.g. slack.sh history <channel> <n>) | Turns raw JSON into short readable text | Saves context; easy to allow in permissions |
| Community MCP server | See option D | When you'd rather have tools than shell calls |
A minimal skill could look like this:
---
name: slack
description: Read Slack channels, threads, DMs and search results with my read-only user token. Use when I ask about anything in Slack.
---
Read the token with `security find-generic-password -a "$USER" -s SLACK_READ_TOKEN -w`
and send it as `Authorization: Bearer`. Never print it.
| Need | Method |
|---|---|
| Find a channel ID | `conversations.list` (types=public_channel,private_channel) |
| Recent messages | `conversations.history?channel=<id>&limit=<n>` |
| A thread | `conversations.replies?channel=<id>&ts=<thread_ts>` |
| Search | `search.messages?query=<q>` |
| A user's name | `users.info?user=<id>` |
Read-only: never call chat.*, reactions.* or any other write method.
For a skill that can also post, swap the last line for a confirmation rule, e.g.
"Before calling chat.postMessage, show me the channel and the exact text and wait for my OK."
The Web API methods you'll use
| Method | Returns |
|---|---|
conversations.list / users.conversations | Channels (all of them, or only the ones you're in) |
conversations.history | A channel's messages, newest first, paged with cursor |
conversations.replies | Every message in one thread |
search.messages | Search results, using the same syntax as the Slack search box (in:#dev from:@x after:2026-01-01) |
users.info / users.list | Turns user IDs (U…) into names |
Rate limits: in 2025 Slack tightened the limits on conversations.history and conversations.replies for apps outside its Marketplace. Its announcement exempted apps a workspace builds for its own use. If reads seem throttled, check the current limits page.
Option C — Your own Slack app with a bot token
The setup is the same as B, but you add bot permissions (channels:history, channels:read, users:read, …) and use the Bot User OAuth Token (xoxb-…).
- The bot only sees channels it's been invited to (
/invite @YourApp). - It can't see your DMs.
- It can't use
search.messages, because search is only available to user tokens.
Pick C when you want Claude's access limited to a few shared channels and not tied to your own account.
C is also the usual choice for posting. Messages show the app's name and icon, so readers can see they came from automation, not from a person. Useful bot write permissions:
| Scope | Lets the bot |
|---|---|
chat:write | Post in channels it has been invited to |
chat:write.public | Post in any public channel without being invited |
chat:write.customize | Change the name and icon shown on each message |
Option D — A community MCP server with your token
Open-source Slack MCP servers, such as korotovsky/slack-mcp-server, take the token from B or C and expose it to Claude Code as MCP tools.
- Pros: proper tools (
channels_list,conversations_history, search), so you don't maintain your own script. - Cons: third-party code holds your token. Read the source, pin a version, and give it the read-only token from B, never one with write permissions.
- Some of these servers also accept browser session tokens. See the warning under Things that look like options but aren't.
- Message-posting tools are often turned off by default and switched on with a setting or environment variable. Check the project's README, and only switch them on if the token has
chat:write.
Option E — Claude in Slack (@Claude)
Anthropic's Slack app, also called Claude Tag, puts Claude inside Slack. It needs a Claude Team or Enterprise plan, and a workspace admin installs it and links it to your Claude organisation.
| Where | How | What Claude sees |
|---|---|---|
| A channel thread | Tag @Claude with a request | That thread, recent channel history, public-channel search |
| A private channel | Run /invite @Claude first | That channel |
| Its DM | Message the app | Your own Claude account, if you linked it on the app's Home tab |
| Channels with guests | — | Workspace search is turned off |
It runs separately from Slack's MCP server, so a block on the MCP doesn't affect it. It doesn't give Claude Code in your terminal or IDE any Slack access.
Writing messages
Which identity posts
| Route | Message appears as | Can post to |
|---|---|---|
User token with chat:write (A, B) | You | Anywhere you can post, including DMs |
Bot token with chat:write (C) | The app | Channels it's invited to, or any public channel with chat:write.public |
| Incoming webhook (F) | The app | One channel, chosen when the webhook was created |
| Claude in Slack (E) | @Claude | The thread it was tagged in |
Option F — Incoming webhook
This is the simplest way to post, and it can't read anything. In your app's settings, turn on Incoming Webhooks, click Add New Webhook to Workspace, and pick a channel. You get a URL like https://hooks.slack.com/services/T…/B…/….
curl -s -X POST -H 'Content-Type: application/json' \
--data '{"text":"Deploy finished :white_check_mark:"}' "$SLACK_WEBHOOK_URL"
- The URL is the secret. Anyone who has it can post to that channel, so store it like a token.
- A webhook can't post to another channel, read messages, reply in a thread it didn't start, or edit or delete messages.
Web API methods for writing
| Method | Does | Scope |
|---|---|---|
chat.postMessage | Post a message. Pass thread_ts to reply in a thread | chat:write |
chat.update / chat.delete | Edit or delete a message the same token posted | chat:write |
chat.scheduleMessage | Post at a set time (post_at, a Unix timestamp) | chat:write |
chat.postEphemeral | Show a message to one person in a channel, visible only to them | chat:write |
conversations.open | Get the DM channel ID for a user before posting to them | im:write |
reactions.add | Add an emoji reaction | reactions:write |
files.getUploadURLExternal + files.completeUploadExternal | Upload a file (the older files.upload is retired) | files:write |
# Reply in a thread
curl -s -X POST https://slack.com/api/chat.postMessage \
-H "Authorization: Bearer $T" -H 'Content-Type: application/json; charset=utf-8' \
--data '{"channel":"C0123456789","thread_ts":"1735689600.123456","text":"Fixed in the latest deploy."}'
channeltakes an ID (C…,G…,D…). Look IDs up withconversations.list, or open a channel's details in Slack and copy the ID from the bottom.- Slack returns HTTP 200 even when a call fails, so check
"ok"in the response. Common errors arenot_in_channel(invite the bot),channel_not_foundandmissing_scope. - Keep posts to about one message per second per channel. Slack rate-limits bursts beyond that.
Formatting
Slack uses mrkdwn, which isn't standard Markdown. Claude writes standard Markdown by default, so say so in your skill or prompt.
| Want | mrkdwn |
|---|---|
| Bold / italic / strike | *bold* _italic_ ~strike~ |
| Link | <https://example.com|label> (one pipe, between URL and label) |
| Mention a person / channel | <@U0123456789> <#C0123456789> |
| Mention a group | <!here> <!channel> <!subteam^S0123456789> |
| Code | `inline` and ``` blocks |
There are no headings or tables in mrkdwn. For richer layouts, send blocks (Block Kit) and keep a plain text fallback for notifications.
Keeping Claude's posting safe
- Ask before every send. Don't add the post command or the MCP's send tools to the Claude Code allow list, so each one asks for your approval. Your skill should also tell Claude to show the channel and text first.
- Draft, then send. Have Claude write the message into the chat, then send it yourself or say "send it". Many people only need drafting help.
- Separate the tokens. Use a read-only user token for research and a bot token for posting. A mistake in a read step then can't post as you.
- Never let unattended runs post as you. Cron jobs,
claude -pand CI should use a bot token or a webhook. - Watch the mentions.
<!channel>and<!here>notify everyone in the channel. Tell Claude never to use them unless you ask.
Things that look like options but aren't
| Thing | Why it doesn't work |
|---|---|
Slack CLI (slack login) | It's for building Slack apps. Its login token has a fixed set of permissions (list channels and users, manage apps) and can't be given *:history, so conversations.history fails with missing_scope |
| Incoming webhook or "post-only" bot token (as a way to read) | Fine for posting (option F), but they can't read. Check with auth.test and look at the x-oauth-scopes response header |
The desktop app's session token (xoxc- plus the d cookie) | Technically it can read everything, but it gets around your admin's app controls, probably breaks workspace policy, and stops working when you log out. Don't use it as a workaround when your admin has said no |
| Workspace data export | Admin-only, a one-off snapshot, and usually public channels only |
Scopes reference
| Scope | Lets you | Token types |
|---|---|---|
channels:read / channels:history | List and read public channels | User, bot |
groups:read / groups:history | List and read private channels you're in | User, bot |
im:read / im:history | List and read direct messages | User (yours); bot (its own DMs only) |
mpim:read / mpim:history | List and read group DMs | User, bot |
users:read | Turn user IDs into names | User, bot |
search:read | search.messages | User only |
chat:write | Post, edit and delete messages | User (as you), bot (as the app) |
chat:write.public | Post to public channels without being invited | Bot only |
chat:write.customize | Change the name and icon on each message | Bot only |
im:write / mpim:write | Open DMs and group DMs to post to | User, bot |
reactions:write | Add emoji reactions | User, bot |
files:write | Upload files | User, bot |
incoming-webhook | Create a webhook URL for one channel | Added when you set up a webhook |
To see what a token can do, run curl -sD - -o /dev/null -H "Authorization: Bearer $T" https://slack.com/api/auth.test | grep -i x-oauth-scopes.
Security notes
- Prefer read-only permissions. A token with
chat:writelets Claude post as you. Add write permissions only when you need them, ideally on a separate app or a bot token. - Treat webhook URLs as secrets. Anyone who has one can post to its channel.
- Keep tokens in the OS credential store, never in prompts, repos,
.envfiles that get committed, or docs. - A user token sees your DMs. If Claude's output ends up somewhere shared, leave the
im:*permissions out. - To revoke access, go to api.slack.com/apps, open your app, and click Revoke tokens, or uninstall it from the workspace.
- If a token leaks, revoke it straight away and reinstall the app to get a new one.
Disclaimer: These documents are personal notes. The information may be incomplete, out of date or wrong, so check it before you rely on it.