Skip to main content
Glama

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:

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:

uv tool install --editable .
{
  "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"].

Related MCP server: Slack MCP Server

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:

"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

Available Tools

10 tools
slack_channel_historyA

Read messages from one channel over a time window.

Accepts a channel name ('project-x' or '#project-x') or a channel ID. Name lookups are cached. Returns top-level messages only — use slack_thread to expand any message whose reply_count is non-zero.

Args: channel: Channel name or ID. oldest: Start bound as 'YYYY-MM-DD', 'YYYY-MM-DD HH:MM:SS', an epoch, or a relative offset like '-7d'. latest: End bound, same formats. A bare date means end-of-day. limit: Maximum messages (default 100). resolve_names: Resolve user IDs to display names (costs one call per user). include_broadcasts: When False, drop @here/@channel/@everyone messages.

Inputs: channel (required), limit, latest, oldest, +2 more. Returns: object

slack_channels_historyA

History for several channels over one time range, threads nested inline.

Give either an explicit channels list or a pattern to discover them. Replies are attached to their parent message rather than returned flat, which matters for channels whose substance lives in threads.

Discovery by pattern only sees channels you have joined — a matching channel you are not a member of is invisible. That limit is deliberate: enumerating an entire Enterprise Grid workspace is throttled past usability.

Args: channels: Channel names or IDs. Mutually exclusive with pattern. pattern: fnmatch glob over joined channel names, e.g. 'team-*'. oldest: Window start — 'YYYY-MM-DD', an epoch, or an offset like '-7d'. latest: Window end, same formats. Defaults to now. expand_threads: Fetch replies for messages that have them. min_members: With pattern, skip channels below this membership. Costs one extra (cached) API call per matching channel. max_text: Truncate message text to this many characters. None keeps all.

Inputs: latest, oldest, pattern, channels, +3 more. Returns: object

slack_dm_historyA

Read direct-message history with one person.

Args: user: Username, @username, or user ID. limit: Maximum messages (default 50). oldest: Optional start bound as 'YYYY-MM-DD' or epoch. resolve_names: Resolve user IDs to display names.

Inputs: user (required), limit, oldest, resolve_names. Returns: object

slack_list_channelsA

Discover channels by glob pattern and minimum member count.

EXPENSIVE: this enumerates conversations.list, which Enterprise Grid throttles hard, so it may return a rate_limited error on large workspaces. If you already know the channel name, skip this and pass the name straight to slack_channel_history — that resolves via one fast search call. For a glob over channels you have joined, slack_channels_history is far cheaper.

Args: pattern: fnmatch glob against the channel name, e.g. 'team-*'. min_members: Only return channels with at least this many members. include_private: Include private channels you have joined. limit: Maximum channels to return (default 200).

Inputs: limit, pattern, min_members, include_private. Returns: object

slack_post_messageA

Post a message to Slack as yourself. DISABLED by default.

Requires SLACK_MCP_ALLOW_WRITE=1 in the server environment. Without it this tool refuses and returns an error, so no agent action can post by accident.

Args: channel: Channel name or ID to post into. text: Message body (Slack mrkdwn). thread_ts: Optional thread parent timestamp, to reply in-thread.

Inputs: text (required), channel (required), thread_ts. Returns: object

slack_profilesA

Look up full profiles for several people at once.

Richer than slack_user: resolves workspace custom fields to their labels and rewrites user-typed fields (for example Manager or Direct Reports) from raw IDs to display names. Returns field_definitions alongside profiles. Use slack_user for a quick single ID-to-name lookup; use this to populate records for several people.

Args: handles: Usernames, @usernames, or user IDs. Unresolvable entries come back as {'handle': ..., 'error': ...} rather than failing the batch.

Inputs: handles (required). Returns: object

slack_threadA

Fetch every reply in a thread, including the parent message.

Args: channel: Channel name or ID containing the thread. thread_ts: Timestamp of the thread parent (the ts or thread_ts field from a previous result). limit: Maximum messages to return (default 100). resolve_names: Resolve user IDs to display names.

Inputs: channel (required), thread_ts (required), limit, resolve_names. Returns: object

slack_userA

Resolve a username or user ID to a profile.

Use this to turn a raw user ID from another result (e.g. 'U01234ABCDE') into a name, or to look up someone's title, timezone, and custom fields.

Args: name_or_id: Username, @username, display name, or user ID. include_custom_fields: Resolve workspace custom profile fields to labels (costs one extra API call).

Inputs: name_or_id (required), include_custom_fields. Returns: object

slack_user_activityA

Everything one person said or received in a time range, grouped by channel.

Searches messages from and to the user, optionally pulls surrounding channel history so replies have antecedents, and optionally expands every thread touched. Returns messages grouped by channel with replies nested under their parents, plus a stats block.

Prefer this over assembling the same result from slack_search plus slack_channel_history plus slack_thread: it deduplicates across all three passes, which a sequence of separate calls cannot do without replaying every message. Cost scales with the number of channels the user was active in.

Args: user: Slack handle or user ID. Defaults to the authenticated user. oldest: Window start — 'YYYY-MM-DD', an epoch, or a relative offset such as '-7d'. Defaults to one day before latest. latest: Window end, same formats. A bare date means end-of-day. Defaults to now. context_window: Seconds of surrounding channel history to include on either side of the user's own messages. 0 (the default) fetches no context; a few thousand seconds gives conversational lead-in. expand_threads: Fetch replies for threads that were touched. max_text: Truncate message text to this many characters. None keeps the full text.

Inputs: user, latest, oldest, max_text, +2 more. Returns: object

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

Loading tool schema changelog…

    TDQS

    A4.4/5.0

    Scored across 10 tools

    Disambiguation4/5

    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.

    Naming Consistency4/5

    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.

    Tool Count5/5

    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.

    Completeness4/5

    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.

    Related MCP Connectors

    Related MCP Servers

    • A
      license
      Not graded
      quality
      D
      maintenance
      Read-only MCP server for Slack with OAuth 2.1 authentication, enabling message retrieval, thread replies, search, user and channel listing.
      9
      Apache 2.0
    • A
      license
      Not graded
      quality
      C
      maintenance
      A production-ready MCP server for the Slack API that enables searching, listing channels, reading history, inspecting users, fetching threads, and sending messages through controlled Slack tools.
      53,839 npm
      MIT
    • A
      license
      Not graded
      quality
      D
      maintenance
      MCP server for interacting with Slack using a user token. It allows reading channels, DMs, threads, search, and posting messages as the authenticated user.
      156 npm
      MIT