Skip to main content

MCP server that exposes Slack AI-generated huddle transcripts to LLM agents

Project description

slack-huddle-mcp

Slack already writes a transcript of every huddle. This MCP server gives it to your agent. No Recall.ai, no Granola, no recorder bot in the meeting.

PyPI Python CI License: MIT


1. The problem

Slack huddles are the default daily-standup tool for a huge number of teams. On Pro / Business+ workspaces, Slack automatically generates an AI transcript and a summary canvas after each huddle.

But Slack's public Web API does not expose those transcripts. Every existing guide tells you to run a third-party recorder bot (Recall.ai, Granola, Otter, Fireflies, Circleback).

There's an undocumented endpoint the Slack web client itself uses. This MCP server wraps it. Your transcripts become available to any MCP-aware agent — Claude Code, Claude Cowork, Cursor, Cline, Continue, Zed — in ~200 lines of Python.

How it compares

slack-huddle-mcp Recall.ai / similar Granola / Otter / Fireflies Slack public API
Reads Slack's own AI transcript
No recorder bot in the meeting
No third-party server in data path
Works on existing huddles (no setup)
Cost free $$$/mo $$/seat/mo free
Setup time 2 min hours hours n/a

2. Quickstart

# until PyPI publish:
pipx install git+https://github.com/prithvi-bharadwaj/slack-huddle-mcp.git
# (once published: pipx install slack-huddle-mcp)

slack-huddle-mcp setup --auto        # macOS: auto-extract from the Slack desktop app
slack-huddle-mcp smoke-test          # end-to-end check on your latest huddle

Three ways to give the tool your tokens, in order of how much effort they take:

Setup path Effort What you need
setup --auto (macOS) zero clicks after one Keychain "Always Allow" Slack desktop app installed and logged in
bookmarklet one click on app.slack.com, paste xoxd once Any modern browser
setup (manual) DevTools console + cookies tab Any browser

After setup, paste the MCP config snippet that setup prints into your MCP client, restart it, and ask your agent:

"Summarize today's standup from #standups in three bullets and list any blockers by owner."

Everyday commands (once set up)

slack-huddle-mcp status              # are my tokens still valid?
slack-huddle-mcp list -n 10          # 10 most recent huddles (id, time, duration)
slack-huddle-mcp list -c C0XXXXXXX   # scope to one channel
slack-huddle-mcp get <huddle_id>                    # markdown transcript
slack-huddle-mcp get <huddle_id> --format summary   # Slack's AI summary canvas
slack-huddle-mcp smoke-test          # re-verify the whole pipeline

If status ever shows AUTH_FAILED, you logged out of Slack and the xoxc rotated — just re-run slack-huddle-mcp setup --auto.

3. Use cases

  • Daily-standup briefing — pulled into your morning agent flow.
  • Action-item extraction — across the last N huddles, by owner.
  • Async catch-up — "I missed the design huddle, here's what I need to know."
  • Post-meeting drafting — first-draft Slack follow-ups in your channel's voice.
  • Cross-meeting retros — what did the team commit to last sprint, what slipped?

Who this is for

Teams that already run their standups in Slack huddles and want their agents to read them — not transcribe them again with a separate bot, not pay per seat, not invite a stranger into every meeting.

4. Security model

Your xoxc token is functionally your Slack password. Read this section before installing.

  • Tokens live in your OS keychain (macOS Keychain, Linux libsecret/KWallet, Windows Credential Manager) via keyring. They are not written to disk, not stored in env vars, and are never logged.
  • All API calls go directly from your machine to *.slack.com. No third-party servers. No telemetry. No analytics. grep -rE 'https?://[a-z0-9.-]+' src/ to verify.
  • A 401/403 halts immediately with a remediation message. The tool will not loop on bad auth.
  • ~600 LOC across api.py, parser.py, keychain.py, workspace.py, mcp_server.py. Audit it yourself in 15 minutes.

If you don't want any tool with your xoxc token on disk — fair. You can still copy transcripts out of Slack's web UI manually. This package is the automation layer.

5. How it works

Slack's web client posts files.info with include_transcription=true as multipart/form-data, with the user-session token (xoxc) in the form body and the session cookie (xoxd) in the Cookie header. The response includes a populated huddle_transcription object with lines[], blocks, and transcription_time_ranges.

┌────────────────────┐    1. POST huddles.history (Cookie: d={xoxd}, token={xoxc})
│  huddles.history   │  ──────────────────────────────────────────────────────────►
└─────────┬──────────┘
          │ huddle.transcript_file_id  ← this is the CANVAS file id (counterintuitive)
          ▼
┌────────────────────┐    2. POST files.info?file={canvas_id}
│  files.info (canvas) │  ──────────────────────────────────────────────────────────►
└─────────┬──────────┘
          │ canvas.huddle_transcript_file_id  ← the raw transcript file id
          ▼
┌──────────────────────────┐    3. POST files.info?file={transcript_id}&include_transcription=true
│  files.info (transcript) │  ──────────────────────────────────────────────────────►
└─────────┬────────────────┘
          │ file.huddle_transcription.lines[]  ← {user_id, start_time_ms, contents}
          ▼
   parse + merge consecutive speakers → markdown / json / lines

Three calls. All POST multipart/form-data. token=xoxc-... in the form body, Cookie: d=<xoxd> in the headers.

Example output

Given a 3-person huddle transcript, get_huddle_transcript(huddle_id="H...", format="markdown", user_map={...}) returns:

**Alice** [00:00]: Lorem ipsum dolor sit amet. Consectetur adipiscing elit.

**Bob** [00:09]: Sed do eiusmod tempor.

**Carol** [00:17]: Incididunt ut labore et dolore. Magna aliqua.

**Alice** [00:25]: Ut enim ad minim veniam.

Consecutive same-speaker lines are merged. Timestamps use the start of each turn.

6. Dead ends I hit (so you don't have to)

Things I tried that didn't work — saves contributors time. Click to expand.
  • files.info without include_transcription=true — the field is recognized but huddle_transcription returns empty {}.
  • Direct GET https://files.slack.com/files-pri/{team}-{file}/huddle_transcript with cookie + bearer — redirects to the Slack React app HTML shell. The content is loaded by a follow-up XHR (the one this tool uses).
  • files.sharedPublicURL — returns not_allowed on most workspaces (admin policy).
  • Public URL with pub_secret=... — only works after files.sharedPublicURL succeeds.
  • Endpoint guesses that all returned unknown_method: huddleSummary.*, huddleTranscript.*, huddles.transcript.*, huddles.summary.*, ml.huddles.*, transcripts.*, files.transcribe.*, files.preview.*, files.huddleTranscription, calls.summary, calls.transcript.get, plus ~25 other variants.
  • assistant.search.context and calls.info — exist but return not_allowed_token_type for xoxc.
  • canvases.* family on slack.comnot_allowed_token_type for xoxc.
  • Slack search modifier from:<@USERID> — returns 0 results. Use from:username instead (no angle-bracket wrap).

7. Token extraction guide

Option A — auto-extract from the Slack desktop app (macOS)

slack-huddle-mcp setup --auto              # extract, validate, store
slack-huddle-mcp setup --auto --dry-run    # extract + validate without storing
slack-huddle-mcp -v setup --auto           # add DEBUG logs on stderr

Reads xoxc from ~/Library/Application Support/Slack/Local Storage/leveldb/ (regex-scanned) and decrypts xoxd from the Cookies SQLite via the macOS Keychain (PBKDF2-SHA1 → AES-128-CBC, the standard Chromium scheme).

The first run triggers a one-time macOS Keychain prompt asking permission to read Slack Safe Storage. Click Always Allow. After that, future runs are silent.

Option B — one-click browser bookmarklet

slack-huddle-mcp bookmarklet               # writes + opens helper page in your browser
slack-huddle-mcp bookmarklet --no-open     # just write the helper file
slack-huddle-mcp bookmarklet --print-only  # print the bookmarklet URL

Drag the link from the helper page to your bookmarks bar. Click it from any tab on app.slack.com, paste your xoxd cookie when prompted, and a complete slack-huddle-mcp setup --xoxc ... --xoxd ... command lands on your clipboard.

(JavaScript cannot read xoxd directly because it's HttpOnly — that's the one manual step in this path.)

Option C — manual (works everywhere)

Slack tokens come out of the browser. They are not part of any developer-facing API.

xoxc (user-session token, form body)

  1. Open Slack in your browser (https://app.slack.com) and log in.

  2. Open DevTools → Console.

  3. Paste this and copy the result:

    JSON.parse(localStorage.localConfig_v2).teams[
      Object.keys(JSON.parse(localStorage.localConfig_v2).teams)[0]
    ].token
    

    Format: xoxc-{team_id}-{user_id}-{session_id}-{secret}.

⚠️ The xoxc rotates every time you log out of Slack. When this tool returns a 401, re-run slack-huddle-mcp setup.

xoxd (session cookie, header)

  1. DevTools → Application → Cookies → https://app.slack.com.
  2. Find the cookie named d (HttpOnly).
  3. Copy the raw value — it's URL-encoded as stored. Do not decode.

The xoxd lifetime is about a year.

8. MCP client config snippets

All clients use the same command. Pick whichever fits your setup.

Claude Code (CLI) — easiest, local stdio

claude mcp add slack-huddle -s user -- "$(which slack-huddle-mcp)" serve

Then in any Claude Code session, run /mcp to confirm it's connected.

Claude Desktop (~/Library/Application Support/Claude/claude_desktop_config.json)

{
  "mcpServers": {
    "slack-huddle": {
      "command": "slack-huddle-mcp",
      "args": ["serve"]
    }
  }
}

If your MCP client doesn't see your shell's PATH (common with GUI launchers), use the absolute path: ~/.local/bin/slack-huddle-mcp (or wherever which slack-huddle-mcp shows).

Cursor (.cursor/mcp.json)

{
  "mcpServers": {
    "slack-huddle": {
      "command": "slack-huddle-mcp",
      "args": ["serve"]
    }
  }
}

Claude.ai web / Claude Cowork — needs a public tunnel

Claude Cowork can't run local stdio MCPs — it connects to remote servers from Anthropic's cloud. You expose your local MCP as HTTPS via a tunnel.

# Terminal 1: start the MCP in HTTP mode with a stable token
export MCP_AUTH_TOKEN="$(openssl rand -hex 32)"   # save this, you'll need it
slack-huddle-mcp serve --http --port 8765

# Terminal 2: expose port 8765 publicly
ngrok http 8765                                   # copy the https://...ngrok-free.dev URL

# Build the connector URL:
#   https://<ngrok-host>/mcp/$MCP_AUTH_TOKEN

In claude.ai (Pro / Max plan):

  1. Settings → Connectors+ Add custom connector.
  2. Name: slack-huddle
  3. Remote MCP server URL: https://<your-ngrok-host>/mcp/<your-token>
  4. Leave Advanced settings empty.
  5. Click Add. Cowork will probe the URL and surface the 4 tools.
  6. In any conversation: + button → Connectors → enable slack-huddle.

Security:

  • The token is part of the URL. Requests to /mcp (or any other path) return 404. Only /mcp/<exact-token> answers.
  • Don't paste the URL into shared chats, screenshots, or commit it. Anyone who has it can read your Slack huddles for as long as your tokens are valid.
  • The ngrok tunnel dies when you close the terminal. On the free tier the host changes each restart — re-add to Cowork. Pay for a fixed subdomain (~$8/mo) if you want persistence.
  • This is single-tenant on purpose: each user runs their own MCP with their own Slack tokens. No multi-tenant data isolation needed because nobody else's tokens are involved.

"Just do this for me" — agent prompt for end-to-end setup

If you have Claude Code (CLI) installed, give an agent this prompt and it'll handle everything except the click-in-Cowork step at the end:

You're setting up the slack-huddle MCP for use in Claude Cowork on macOS.

Do these steps without asking for confirmation in between:

1. Verify prerequisites. Stop with a clear error if any are missing:
   - macOS (uname -s == Darwin)
   - pipx installed (`command -v pipx`)
   - ngrok installed and authed (`ngrok config check`)
   - Slack desktop app installed and logged in

2. Install: pipx install git+https://github.com/prithvi-bharadwaj/slack-huddle-mcp.git
   (skip if already installed; check `slack-huddle-mcp --version` first)

3. Run `slack-huddle-mcp setup --auto`. If macOS prompts for Keychain
   access to "Slack Safe Storage", tell me to click "Always Allow" and
   wait — re-run after I confirm.

4. Verify with `slack-huddle-mcp smoke-test`. If it fails, stop and report.

5. Generate a token: export MCP_AUTH_TOKEN="$(openssl rand -hex 32)"
   Save the value to ~/.config/slack-huddle-mcp-token.txt (chmod 600).

6. Start the MCP HTTP server in the background:
   nohup env MCP_AUTH_TOKEN="$MCP_AUTH_TOKEN" \
     slack-huddle-mcp serve --http --port 8765 \
     > /tmp/slack-huddle-mcp.log 2>&1 &
   Verify it's listening with curl on the local URL.

7. Start ngrok in the background:
   nohup ngrok http 8765 --log /tmp/ngrok.log --log-format json \
     > /dev/null 2>&1 &
   Wait until http://127.0.0.1:4040/api/tunnels returns a public_url.

8. Print:
     CONNECTOR URL: https://<ngrok-host>/mcp/<token>
     Add it in claude.ai → Settings → Connectors → + Add custom connector.
     Leave Advanced settings empty. Click Add. Done.

9. Also print the PIDs of the MCP and ngrok processes, and the exact
   commands to stop them later.

Tools your agent will see

Tool What it does
list_huddles(channel_id?, after?, before?, limit?, workspace?) List recent huddles, optionally scoped to a channel or time range.
get_huddle_transcript(huddle_id? | transcript_file_id?, format="markdown"|"json"|"lines", merge_consecutive?, user_map?, workspace?) Fetch the AI transcript for one huddle.
get_huddle_summary(canvas_id? | huddle_id?, workspace?) Fetch the AI-generated summary canvas.
list_workspaces() List workspaces with stored tokens on this machine.

9. FAQ

Q: Does this work on free Slack workspaces? No. Slack only generates huddle transcripts on Pro / Business+ workspaces. If your huddles don't get an automatic summary canvas, this tool has nothing to read.

Q: Will my xoxc token leak through this? The token never leaves your machine except as a POST body to *.slack.com. It is stored in your OS keychain, never on disk in plaintext, and grep -rE 'xox[abc]-[a-z0-9]+' src/ returns zero matches in shipping code. Audit it.

Q: Will Slack ban me for using this? Same risk profile as using the Slack web client. The tool sends the same requests your browser does. No scraping, no rate-limit games, no fake user agents. Use sensible limit values.

Q: My token stopped working. You logged out of Slack (the xoxc rotates on logout). Run slack-huddle-mcp setup again to refresh.

Q: How do I use this with multiple workspaces? Run slack-huddle-mcp setup --workspace <subdomain> once per workspace. Tools take an optional workspace argument; without it, the tool defaults to the only configured workspace, or errors if there are several.

Q: Why is transcript_file_id on the huddle actually pointing at a canvas, not the transcript? Slack's internal naming is a known historical quirk. The "transcript file id" on huddles.history is the AI-summary canvas; the canvas's huddle_transcript_file_id field is the raw transcript. The 3-step pipeline in section 5 walks both.

Q: Will you support live (streaming) transcripts? On the roadmap, once the source endpoint stabilizes. Today the AI transcript is generated post-huddle.

10. Roadmap

  • Huddle audio download (Slack stores the m4a behind the same file machinery).
  • Async client (httpx.AsyncClient) for parallel canvas resolution.
  • Streaming transcripts for live huddles.
  • Better multi-workspace UX — env-variable overrides, profile switching.
  • Native Slack endpoint when/if Slack publishes one — drop the web-client shim.

11. Contributing & license

PRs welcome — especially for new MCP clients in section 8, additional dead ends in section 6, and tighter parsers in parser.py.

git clone https://github.com/prithvi-bharadwaj/slack-huddle-mcp.git
cd slack-huddle-mcp
pip install -e ".[dev]"
pytest -q --cov
ruff check src/ tests/
mypy --strict src/

This project follows the Contributor Covenant. Be kind.

Licensed under the MIT License.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

slack_huddle_mcp-0.3.0.tar.gz (41.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

slack_huddle_mcp-0.3.0-py3-none-any.whl (35.1 kB view details)

Uploaded Python 3

File details

Details for the file slack_huddle_mcp-0.3.0.tar.gz.

File metadata

  • Download URL: slack_huddle_mcp-0.3.0.tar.gz
  • Upload date:
  • Size: 41.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.0

File hashes

Hashes for slack_huddle_mcp-0.3.0.tar.gz
Algorithm Hash digest
SHA256 d54954ad6b7a3e10816459e597b911521b4629b7e8d2750128386f5a8bd2cab2
MD5 026bf3e864d81c2fc74abf04dd4591aa
BLAKE2b-256 08c9458eea4a203678f1c93c101fbf1858e28b74286e7ead8fae844eb4879df4

See more details on using hashes here.

File details

Details for the file slack_huddle_mcp-0.3.0-py3-none-any.whl.

File metadata

File hashes

Hashes for slack_huddle_mcp-0.3.0-py3-none-any.whl
Algorithm Hash digest
SHA256 bb3f1e9f207aa86d840b06266bc69305c14e91046d3d4bd623781e6a1a342957
MD5 34798b182dc1f97654c730e6d8721a70
BLAKE2b-256 94ba94317c9167642fd7e7ca25a3c7b59fce2a37ae8bcc8fc4b43c2515be53d1

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page