Skip to content
← articles
updated SlackMCPHermes AgentClaude CodeAI Agents

Slack MCP Server: Read Access for Agents, Not a Voice

What a Slack MCP server actually gives an agent, the official Slack-hosted option versus the self-hosted korotovsky server, how to wire either into Claude Code or Hermes Agent, and why posting should stay off by default.

The useful Slack agent reads. It pulls a channel’s history, searches for a decision someone made three weeks ago, and tells you what it found. It does not post. Posting is a privilege you hand out on purpose, not a default you forgot to turn off.

That is the whole thesis of this page. Everything below is how to get a Slack MCP server into an agent, verified against Slack’s own developer docs, the two server options worth using, and Anthropic’s current Claude Code docs, checked today.

Which Slack MCP server should you use?

Two options cover almost every case. Slack itself hosts one, and the community runs one.

Slack’s own server lives at mcp.slack.com/mcp, reachable over Streamable HTTP only, no SSE, no Dynamic Client Registration. It authenticates with user-token OAuth 2.0 behind a confidential client, which means your request has to come from a registered Slack app with a fixed app ID, and only a Marketplace-listed or internal app may use it. The agent that connects through it operates under the OAuth grant of whoever authorized it: it reads what that person can read, nothing more.

korotovsky/slack-mcp-server is the self-hosted alternative, MIT-licensed. It supports three authentication modes (more on that below), stdio, SSE and HTTP transports, and it ships read-only by default: every write tool is disabled until you explicitly turn it on.

Slack MCP server, October 2026

Both read the same Slack API underneath. The difference is who approves the connection and what ships on by default.
Official (mcp.slack.com)korotovsky/slack-mcp-server
HostingRemote, hosted by SlackSelf-hosted: npx, Docker, or a DXT extension
TransportStreamable HTTP onlyStdio, SSE, or HTTP
AuthUser-token OAuth 2.0, confidential client, admin-approvedUser (xoxp), bot (xoxb), or browser session (xoxc/xoxd)
Default write accessWhatever OAuth scopes you requestOff. Posting and reactions need their own env var
FitsA company that wants admin approval and an audit trailA developer wiring Slack into Claude Code or Hermes today
Both read the same Slack API underneath. The difference is who approves the connection and what ships on by default.

Auth: what each token actually grants

Slack’s official server needs your app’s client_id and client_secret for a confidential OAuth flow, and it requests scopes per tool: channels:history to read a channel, chat:write to post, search:read.public plus three more search:read.* scopes to search across visibility levels. Request only what the agent needs to read.

Read-only scopes worth requesting

  • Required:
    channels:history, groups:history, im:history, mpim:historyRead messages in public channels, private channels, DMs and group DMs.
  • Required:
    channels:read, groups:read, im:read, mpim:readList conversations and their metadata without reading content.
  • Required:
    search:read.public, search:read.private, search:read.mpim, search:read.imSearch across the visibility levels you actually need, not the full set by default.
  • Required:
    users:readResolve user IDs to names, needed for almost any useful summary.
Leave out chat:write, reactions:write and anything with channels:write unless the agent has an approved reason to post.

korotovsky’s server takes a token through an environment variable instead of a full OAuth dance: SLACK_MCP_XOXP_TOKEN for a user token, SLACK_MCP_XOXB_TOKEN for a bot token, or both SLACK_MCP_XOXC_TOKEN and SLACK_MCP_XOXD_TOKEN for a browser-session pair pulled from your own logged-in Slack tab. A bot token is the most contained option: it only sees channels it has been invited to and cannot call search.messages at all, so conversations_search_messages never registers. Whichever token you use, the server’s own defaults already match the thesis here: write tools (conversations_add_message, reactions_add, reactions_remove, attachment_get_data) are not registered unless you set their specific environment variable or list them explicitly in SLACK_MCP_ENABLED_TOOLS. Read-only is not a setting you choose. It is what you get until you choose otherwise.

Slack MCP in Claude Code: the claude mcp add syntax

Claude Code’s current docs give two paths, and which one you use depends on which server you picked.

For the official hosted server, add it as an HTTP server, then open /mcp inside a Claude Code session, select slack and authenticate:

Official Slack MCP server in Claude Code

  1. claude mcp add --transport http slack https://mcp.slack.com/mcp

To pin the scopes Claude Code requests instead of accepting whatever the server offers, set oauth.scopes on the entry. Anthropic’s own docs use this exact server as the worked example:

{
  "mcpServers": {
    "slack": {
      "type": "http",
      "url": "https://mcp.slack.com/mcp",
      "oauth": {
        "scopes": "channels:history channels:read search:read.public users:read"
      }
    }
  }
}

For korotovsky’s server, add it as a local stdio process with the token as an environment variable:

Community Slack MCP server in Claude Code

  1. claude mcp add --env SLACK_MCP_XOXB_TOKEN=xoxb-your-token -- npx -y slack-mcp-server@latest --transport stdio

The -- separator matters here: everything after it is the command Claude Code runs to start the server, untouched. Without it, Claude Code tries to parse --transport as its own flag instead of the server’s.

Wiring it into Hermes Agent: mcp_servers, not a plugin

Hermes Agent takes the same server through its own mcp_servers block, covered in full in Hermes Agent and MCP. The stdio shape for korotovsky’s server:

mcp_servers:
  slack:
    command: "npx"
    args: ["-y", "slack-mcp-server@latest", "--transport", "stdio"]
    env:
      SLACK_MCP_XOXB_TOKEN: "${env:SLACK_MCP_XOXB_TOKEN}"
    tools:
      include:
        [
          conversations_history,
          conversations_replies,
          conversations_search_messages,
          channels_list,
          users_search,
        ]

tools.include is doing the real work in that entry. It is a second, config-level lock on top of the server’s own read-only default: even if a future version of the server changes its defaults, this agent only ever sees the five tools named here.

Slack as a gateway versus Slack as an MCP source

These are two different wires, and they are easy to conflate. Hermes ships both.

Slack as a gateway

  1. 01Slack talks to Hermes: slash commands, threads, mentions
  2. 02Needs a bot token (xoxb) and an app-level token (xapp) over Socket Mode
  3. 03Lives in Hermes' slack platform plugin, one setup per workspace
  4. 04Hermes is the one posting, with approval blocks on risky actions

Slack as an MCP source

  1. 01Hermes reads Slack's history as data, nothing talks back by default
  2. 02Needs whatever token the MCP server wants: bot, user, or OAuth
  3. 03Configured per entry under mcp_servers, like any other MCP server
  4. 04Posting through this path is off until you explicitly enable it
One lets Slack talk to Hermes. The other lets Hermes read Slack.

A workspace can run both at once: a bot that answers questions when mentioned, and a separate cron-driven agent that only ever reads. Keep them on different tokens. A gateway bot token invited into dozens of channels is not the credential you want sitting in a read-only research agent’s config, and the reverse is just as true.

Confirm the leash before you trust it

  1. 01

    Make the agent list what it cannot do, not just what it can.

    Read-only is a server setting. The agent will not volunteer the boundary unless you ask for it directly.

    Type this

    List every Slack tool you currently have, and tell me which of them, if any, can post a message, add a reaction, or write to a channel.
  2. 02

    Ask for the scope, not the capability.

    A tool appearing in a list and a scope being granted are two different claims. The token decides what a call actually does.

    Type this

    What token type and OAuth scopes is this Slack connection using right now?
  3. 03

    Have it name the exact channels before a real task starts.

    A bot token only sees channels it has been invited to. Confirming the list up front beats a confident wrong answer mid-task.

    Type this

    List every channel you can currently read from, by name, before we start.
Three questions that turn 'it's connected' into something you can actually verify.

Rate limits and tokens in logs

Slack enforces its Web API rate-limit tiers on MCP calls exactly as it does on direct API calls, 20-plus requests a minute on Tier 2 methods like channel search, 50-plus on Tier 3 methods like reading a channel, 100-plus on Tier 4 methods like reading a user profile. chat.postMessage and message search carry their own special limits documented per method. None of this is unique to MCP. It is the same ceiling you would hit calling the Slack API directly, which is the point: an MCP server does not grant the agent more than the token already allows.

The token itself is the part worth guarding. I rotated a set of API tokens once, after a dump of config.yaml inside a Hermes session wrote them into the session log. A Slack bot or user token sitting in env: is exactly as exposed as anything else in that file. Do not let an agent print its own config, and treat a leaked Slack token the same way you would any other credential: rotate it, do not just hope the log gets cleaned up.

My actual use: a briefing, not a chatbot

At my day job, a Hermes cron job reads the company’s Slack alongside Google Workspace, Git and Confluence or Jira, and writes me a daily briefing of what is happening. Its job is to read: four sources through MCP servers, turned into one message to me. The full setup, including why a briefing beats a chat interface for this job, is in Hermes Agent and the daily briefing.

That is the shape I would recommend for most Slack-plus-agent setups: narrow scopes, a server that defaults to read-only, and a cron job instead of a bot that is always listening for a reason to reply.

Slack MCP server, quick answers

Is there an official Slack MCP server?

Yes. Slack hosts one at mcp.slack.com/mcp, reachable over Streamable HTTP, authenticated with user-token OAuth 2.0 behind an admin-approved, Marketplace-listed or internal Slack app.

What is the best Slack MCP server for Claude Code?

The official server if your workspace requires admin approval for third-party access. korotovsky/slack-mcp-server if you want a self-hosted option you can wire in today with a single token and no app review, and it already defaults to read-only.

How do I connect Slack MCP to Claude Code?

claude mcp add --transport http slack https://mcp.slack.com/mcp for the official server, then authenticate through /mcp in a session. For the community server, claude mcp add --env SLACK_MCP_XOXB_TOKEN=... -- npx -y slack-mcp-server@latest --transport stdio, with -- separating Claude Code's own flags from the server's.

Can a Slack MCP server post messages?

korotovsky's server ships with every write tool disabled until you set its specific environment variable or list it explicitly. The official server can post if the OAuth scope you request includes chat:write. Request read-only scopes unless the agent has an approved reason to post.

What is the difference between Slack MCP and the Hermes Slack gateway?

The gateway plugin lets Slack talk to Hermes: slash commands, mentions, a bot that answers in threads. An MCP server lets Hermes read Slack as a data source, through mcp_servers, with no posting unless you turn it on. They use different tokens and solve different problems.