slack-huddle-mcp 0.3.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. slack_huddle_mcp-0.3.0/.github/workflows/ci.yml +36 -0
  2. slack_huddle_mcp-0.3.0/.gitignore +59 -0
  3. slack_huddle_mcp-0.3.0/CHANGELOG.md +45 -0
  4. slack_huddle_mcp-0.3.0/LICENSE +21 -0
  5. slack_huddle_mcp-0.3.0/PKG-INFO +415 -0
  6. slack_huddle_mcp-0.3.0/README.md +375 -0
  7. slack_huddle_mcp-0.3.0/pyproject.toml +98 -0
  8. slack_huddle_mcp-0.3.0/skill-runs.jsonl +1 -0
  9. slack_huddle_mcp-0.3.0/src/slack_huddle/__init__.py +28 -0
  10. slack_huddle_mcp-0.3.0/src/slack_huddle/__main__.py +6 -0
  11. slack_huddle_mcp-0.3.0/src/slack_huddle/_cli_helpers.py +103 -0
  12. slack_huddle_mcp-0.3.0/src/slack_huddle/api.py +255 -0
  13. slack_huddle_mcp-0.3.0/src/slack_huddle/bookmarklet.py +128 -0
  14. slack_huddle_mcp-0.3.0/src/slack_huddle/cli.py +94 -0
  15. slack_huddle_mcp-0.3.0/src/slack_huddle/cli_huddles.py +230 -0
  16. slack_huddle_mcp-0.3.0/src/slack_huddle/cli_setup.py +144 -0
  17. slack_huddle_mcp-0.3.0/src/slack_huddle/extractor.py +247 -0
  18. slack_huddle_mcp-0.3.0/src/slack_huddle/keychain.py +108 -0
  19. slack_huddle_mcp-0.3.0/src/slack_huddle/mcp_server.py +283 -0
  20. slack_huddle_mcp-0.3.0/src/slack_huddle/parser.py +166 -0
  21. slack_huddle_mcp-0.3.0/src/slack_huddle/workspace.py +107 -0
  22. slack_huddle_mcp-0.3.0/tests/__init__.py +0 -0
  23. slack_huddle_mcp-0.3.0/tests/conftest.py +40 -0
  24. slack_huddle_mcp-0.3.0/tests/fixtures/auth_test.json +8 -0
  25. slack_huddle_mcp-0.3.0/tests/fixtures/files_info_canvas.json +19 -0
  26. slack_huddle_mcp-0.3.0/tests/fixtures/files_info_transcript.json +25 -0
  27. slack_huddle_mcp-0.3.0/tests/fixtures/huddles_history.json +32 -0
  28. slack_huddle_mcp-0.3.0/tests/test_api.py +362 -0
  29. slack_huddle_mcp-0.3.0/tests/test_bookmarklet.py +79 -0
  30. slack_huddle_mcp-0.3.0/tests/test_cli.py +335 -0
  31. slack_huddle_mcp-0.3.0/tests/test_extractor.py +275 -0
  32. slack_huddle_mcp-0.3.0/tests/test_keychain.py +112 -0
  33. slack_huddle_mcp-0.3.0/tests/test_mcp_server.py +165 -0
  34. slack_huddle_mcp-0.3.0/tests/test_parser.py +162 -0
  35. slack_huddle_mcp-0.3.0/tests/test_workspace.py +108 -0
@@ -0,0 +1,36 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+ branches: [main]
8
+
9
+ jobs:
10
+ test:
11
+ runs-on: ubuntu-latest
12
+ strategy:
13
+ matrix:
14
+ python-version: ["3.10", "3.11", "3.12"]
15
+
16
+ steps:
17
+ - uses: actions/checkout@v4
18
+
19
+ - name: Set up Python ${{ matrix.python-version }}
20
+ uses: actions/setup-python@v5
21
+ with:
22
+ python-version: ${{ matrix.python-version }}
23
+
24
+ - name: Install package
25
+ run: |
26
+ python -m pip install --upgrade pip
27
+ pip install -e ".[dev]"
28
+
29
+ - name: Ruff
30
+ run: ruff check src/ tests/
31
+
32
+ - name: Mypy
33
+ run: mypy --strict src/
34
+
35
+ - name: Pytest
36
+ run: pytest -q --cov --cov-report=term --cov-fail-under=80
@@ -0,0 +1,59 @@
1
+ # Python
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+ *.so
6
+ .Python
7
+ build/
8
+ develop-eggs/
9
+ dist/
10
+ downloads/
11
+ eggs/
12
+ .eggs/
13
+ lib/
14
+ lib64/
15
+ parts/
16
+ sdist/
17
+ var/
18
+ wheels/
19
+ *.egg-info/
20
+ .installed.cfg
21
+ *.egg
22
+ MANIFEST
23
+ .venv/
24
+ venv/
25
+ env/
26
+ ENV/
27
+
28
+ # Test/coverage
29
+ .coverage
30
+ .coverage.*
31
+ htmlcov/
32
+ .pytest_cache/
33
+ .mypy_cache/
34
+ .ruff_cache/
35
+ coverage.xml
36
+ *.cover
37
+
38
+ # Tokens / secrets
39
+ .env
40
+ .env.*
41
+ tokens/
42
+ *.token
43
+ *.xoxc
44
+ *.xoxd
45
+ secrets.json
46
+
47
+ # IDE
48
+ .vscode/
49
+ .idea/
50
+ *.swp
51
+ *.swo
52
+ .DS_Store
53
+
54
+ # Local
55
+ *.local
56
+ scratch/
57
+
58
+ # Claude Code session state
59
+ .claude/
@@ -0,0 +1,45 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [0.3.0] - 2026-05-22
9
+
10
+ ### Added
11
+ - `slack-huddle-mcp serve --http` — streamable HTTP transport, for use with
12
+ Claude.ai web / Claude Cowork via a public tunnel (ngrok/cloudflared).
13
+ - Path-based bearer-token auth: server listens at ``/mcp/<token>`` only;
14
+ everything else returns 404. Token is read from ``--auth-token`` or
15
+ ``MCP_AUTH_TOKEN`` env var, or auto-generated for the current run.
16
+ - README now documents three setup paths (Code, Desktop/Cursor, Cowork)
17
+ and includes a self-contained agent prompt that automates the Cowork
18
+ setup end-to-end.
19
+
20
+ ## [0.2.0] - 2026-05-17
21
+
22
+ ### Added
23
+ - `slack-huddle-mcp setup --auto` — extract `xoxc` from the macOS Slack desktop
24
+ app's Local Storage LevelDB and decrypt `xoxd` from its Cookies SQLite via
25
+ the macOS Keychain. Zero browser steps.
26
+ - `slack-huddle-mcp setup --auto --dry-run` — extract and validate without
27
+ storing.
28
+ - `slack-huddle-mcp bookmarklet` — generate a one-click browser bookmarklet
29
+ helper page; drag a link to the bookmarks bar, click on `app.slack.com`,
30
+ paste the result into the terminal.
31
+ - Global `-v` / `--verbose` flag for DEBUG-level logging on stderr.
32
+ - `cryptography` is now an explicit dependency (used for cookie decryption).
33
+
34
+ ## [0.1.0] - 2026-05-16
35
+
36
+ ### Added
37
+ - Initial release.
38
+ - Three-step pipeline that fetches Slack huddle transcripts via `huddles.history` + `files.info`.
39
+ - FastMCP server exposing four tools: `list_huddles`, `get_huddle_transcript`,
40
+ `get_huddle_summary`, `list_workspaces`.
41
+ - Click CLI: `setup`, `list`, `get`, `smoke-test`, `status`.
42
+ - OS-keychain token storage (`keyring`) — never on disk.
43
+ - Markdown / JSON / lines transcript formats with consecutive-speaker merging.
44
+ - Multi-workspace support resolved via `auth.test`.
45
+ - Anonymized fixtures and `respx`-based unit tests, ≥80% coverage on `api.py` and `parser.py`.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Prithvi
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,415 @@
1
+ Metadata-Version: 2.4
2
+ Name: slack-huddle-mcp
3
+ Version: 0.3.0
4
+ Summary: MCP server that exposes Slack AI-generated huddle transcripts to LLM agents
5
+ Project-URL: Homepage, https://github.com/prithvi-bharadwaj/slack-huddle-mcp
6
+ Project-URL: Repository, https://github.com/prithvi-bharadwaj/slack-huddle-mcp
7
+ Project-URL: Issues, https://github.com/prithvi-bharadwaj/slack-huddle-mcp/issues
8
+ Author: Prithvi
9
+ License: MIT
10
+ License-File: LICENSE
11
+ Keywords: agent,huddle,llm,mcp,slack,transcript
12
+ Classifier: Development Status :: 4 - Beta
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: License :: OSI Approved :: MIT License
15
+ Classifier: Operating System :: OS Independent
16
+ Classifier: Programming Language :: Python :: 3
17
+ Classifier: Programming Language :: Python :: 3.10
18
+ Classifier: Programming Language :: Python :: 3.11
19
+ Classifier: Programming Language :: Python :: 3.12
20
+ Classifier: Programming Language :: Python :: 3.13
21
+ Classifier: Topic :: Communications :: Chat
22
+ Classifier: Topic :: Software Development :: Libraries
23
+ Requires-Python: >=3.10
24
+ Requires-Dist: anyio>=4.0.0
25
+ Requires-Dist: click>=8.1.0
26
+ Requires-Dist: cryptography>=42.0.0
27
+ Requires-Dist: fastmcp>=0.2.0
28
+ Requires-Dist: httpx>=0.27.0
29
+ Requires-Dist: keyring>=24.0.0
30
+ Requires-Dist: pydantic>=2.0.0
31
+ Provides-Extra: dev
32
+ Requires-Dist: mypy>=1.10.0; extra == 'dev'
33
+ Requires-Dist: pytest-asyncio>=0.23.0; extra == 'dev'
34
+ Requires-Dist: pytest-cov>=5.0.0; extra == 'dev'
35
+ Requires-Dist: pytest>=8.0.0; extra == 'dev'
36
+ Requires-Dist: respx>=0.21.0; extra == 'dev'
37
+ Requires-Dist: ruff>=0.6.0; extra == 'dev'
38
+ Requires-Dist: types-requests; extra == 'dev'
39
+ Description-Content-Type: text/markdown
40
+
41
+ # slack-huddle-mcp
42
+
43
+ > **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.
44
+
45
+ [![PyPI](https://img.shields.io/pypi/v/slack-huddle-mcp.svg)](https://pypi.org/project/slack-huddle-mcp/)
46
+ [![Python](https://img.shields.io/pypi/pyversions/slack-huddle-mcp.svg)](https://pypi.org/project/slack-huddle-mcp/)
47
+ [![CI](https://github.com/prithvi-bharadwaj/slack-huddle-mcp/actions/workflows/ci.yml/badge.svg)](https://github.com/prithvi-bharadwaj/slack-huddle-mcp/actions/workflows/ci.yml)
48
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
49
+
50
+ ---
51
+
52
+ ## 1. The problem
53
+
54
+ 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.
55
+
56
+ **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).
57
+
58
+ 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.
59
+
60
+ ### How it compares
61
+
62
+ | | slack-huddle-mcp | Recall.ai / similar | Granola / Otter / Fireflies | Slack public API |
63
+ | ------------------------------------ | :--------------: | :-----------------: | :-------------------------: | :--------------: |
64
+ | Reads Slack's **own** AI transcript | ✅ | ❌ | ❌ | ❌ |
65
+ | No recorder bot in the meeting | ✅ | ❌ | ❌ | ✅ |
66
+ | No third-party server in data path | ✅ | ❌ | ❌ | ✅ |
67
+ | Works on existing huddles (no setup) | ✅ | ❌ | ❌ | ❌ |
68
+ | Cost | **free** | $$$/mo | $$/seat/mo | free |
69
+ | Setup time | **2 min** | hours | hours | n/a |
70
+
71
+ ## 2. Quickstart
72
+
73
+ ```bash
74
+ # until PyPI publish:
75
+ pipx install git+https://github.com/prithvi-bharadwaj/slack-huddle-mcp.git
76
+ # (once published: pipx install slack-huddle-mcp)
77
+
78
+ slack-huddle-mcp setup --auto # macOS: auto-extract from the Slack desktop app
79
+ slack-huddle-mcp smoke-test # end-to-end check on your latest huddle
80
+ ```
81
+
82
+ Three ways to give the tool your tokens, in order of how much effort they take:
83
+
84
+ | Setup path | Effort | What you need |
85
+ | ---------- | ------ | ------------- |
86
+ | `setup --auto` *(macOS)* | **zero clicks** after one Keychain "Always Allow" | Slack desktop app installed and logged in |
87
+ | `bookmarklet` | one click on `app.slack.com`, paste `xoxd` once | Any modern browser |
88
+ | `setup` *(manual)* | DevTools console + cookies tab | Any browser |
89
+
90
+ After setup, paste the MCP config snippet that `setup` prints into your MCP client, restart it, and ask your agent:
91
+
92
+ > *"Summarize today's standup from #standups in three bullets and list any blockers by owner."*
93
+
94
+ ### Everyday commands (once set up)
95
+
96
+ ```bash
97
+ slack-huddle-mcp status # are my tokens still valid?
98
+ slack-huddle-mcp list -n 10 # 10 most recent huddles (id, time, duration)
99
+ slack-huddle-mcp list -c C0XXXXXXX # scope to one channel
100
+ slack-huddle-mcp get <huddle_id> # markdown transcript
101
+ slack-huddle-mcp get <huddle_id> --format summary # Slack's AI summary canvas
102
+ slack-huddle-mcp smoke-test # re-verify the whole pipeline
103
+ ```
104
+
105
+ If `status` ever shows `AUTH_FAILED`, you logged out of Slack and the `xoxc` rotated — just re-run `slack-huddle-mcp setup --auto`.
106
+
107
+ ## 3. Use cases
108
+
109
+ - **Daily-standup briefing** — pulled into your morning agent flow.
110
+ - **Action-item extraction** — across the last N huddles, by owner.
111
+ - **Async catch-up** — "I missed the design huddle, here's what I need to know."
112
+ - **Post-meeting drafting** — first-draft Slack follow-ups in your channel's voice.
113
+ - **Cross-meeting retros** — what did the team commit to last sprint, what slipped?
114
+
115
+ ### Who this is for
116
+
117
+ 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.
118
+
119
+ ## 4. Security model
120
+
121
+ Your `xoxc` token is functionally your Slack password. Read this section before installing.
122
+
123
+ - Tokens live in your **OS keychain** (macOS Keychain, Linux libsecret/KWallet, Windows Credential Manager) via [`keyring`](https://pypi.org/project/keyring/). They are not written to disk, not stored in env vars, and are **never logged**.
124
+ - 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.
125
+ - A `401`/`403` halts immediately with a remediation message. The tool will not loop on bad auth.
126
+ - ~600 LOC across `api.py`, `parser.py`, `keychain.py`, `workspace.py`, `mcp_server.py`. Audit it yourself in 15 minutes.
127
+
128
+ 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.
129
+
130
+ ## 5. How it works
131
+
132
+ 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`.
133
+
134
+ ```
135
+ ┌────────────────────┐ 1. POST huddles.history (Cookie: d={xoxd}, token={xoxc})
136
+ │ huddles.history │ ──────────────────────────────────────────────────────────►
137
+ └─────────┬──────────┘
138
+ │ huddle.transcript_file_id ← this is the CANVAS file id (counterintuitive)
139
+
140
+ ┌────────────────────┐ 2. POST files.info?file={canvas_id}
141
+ │ files.info (canvas) │ ──────────────────────────────────────────────────────────►
142
+ └─────────┬──────────┘
143
+ │ canvas.huddle_transcript_file_id ← the raw transcript file id
144
+
145
+ ┌──────────────────────────┐ 3. POST files.info?file={transcript_id}&include_transcription=true
146
+ │ files.info (transcript) │ ──────────────────────────────────────────────────────►
147
+ └─────────┬────────────────┘
148
+ │ file.huddle_transcription.lines[] ← {user_id, start_time_ms, contents}
149
+
150
+ parse + merge consecutive speakers → markdown / json / lines
151
+ ```
152
+
153
+ Three calls. All `POST multipart/form-data`. `token=xoxc-...` in the form body, `Cookie: d=<xoxd>` in the headers.
154
+
155
+ ### Example output
156
+
157
+ Given a 3-person huddle transcript, `get_huddle_transcript(huddle_id="H...", format="markdown", user_map={...})` returns:
158
+
159
+ ```markdown
160
+ **Alice** [00:00]: Lorem ipsum dolor sit amet. Consectetur adipiscing elit.
161
+
162
+ **Bob** [00:09]: Sed do eiusmod tempor.
163
+
164
+ **Carol** [00:17]: Incididunt ut labore et dolore. Magna aliqua.
165
+
166
+ **Alice** [00:25]: Ut enim ad minim veniam.
167
+ ```
168
+
169
+ Consecutive same-speaker lines are merged. Timestamps use the start of each turn.
170
+
171
+ ## 6. Dead ends I hit (so you don't have to)
172
+
173
+ <details>
174
+ <summary><b>Things I tried that didn't work — saves contributors time. Click to expand.</b></summary>
175
+
176
+ - `files.info` **without** `include_transcription=true` — the field is recognized but `huddle_transcription` returns empty `{}`.
177
+ - 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).
178
+ - `files.sharedPublicURL` — returns `not_allowed` on most workspaces (admin policy).
179
+ - Public URL with `pub_secret=...` — only works after `files.sharedPublicURL` succeeds.
180
+ - 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.
181
+ - `assistant.search.context` and `calls.info` — exist but return `not_allowed_token_type` for `xoxc`.
182
+ - `canvases.*` family on `slack.com` — `not_allowed_token_type` for `xoxc`.
183
+ - Slack search modifier `from:<@USERID>` — returns 0 results. Use `from:username` instead (no angle-bracket wrap).
184
+
185
+ </details>
186
+
187
+ ## 7. Token extraction guide
188
+
189
+ ### Option A — auto-extract from the Slack desktop app (macOS)
190
+
191
+ ```bash
192
+ slack-huddle-mcp setup --auto # extract, validate, store
193
+ slack-huddle-mcp setup --auto --dry-run # extract + validate without storing
194
+ slack-huddle-mcp -v setup --auto # add DEBUG logs on stderr
195
+ ```
196
+
197
+ 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).
198
+
199
+ 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.
200
+
201
+ ### Option B — one-click browser bookmarklet
202
+
203
+ ```bash
204
+ slack-huddle-mcp bookmarklet # writes + opens helper page in your browser
205
+ slack-huddle-mcp bookmarklet --no-open # just write the helper file
206
+ slack-huddle-mcp bookmarklet --print-only # print the bookmarklet URL
207
+ ```
208
+
209
+ 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.
210
+
211
+ (JavaScript cannot read `xoxd` directly because it's HttpOnly — that's the one manual step in this path.)
212
+
213
+ ### Option C — manual (works everywhere)
214
+
215
+ Slack tokens come out of the browser. They are not part of any developer-facing API.
216
+
217
+ #### `xoxc` (user-session token, form body)
218
+
219
+ 1. Open Slack in your browser (`https://app.slack.com`) and log in.
220
+ 2. Open DevTools → Console.
221
+ 3. Paste this and copy the result:
222
+
223
+ ```js
224
+ JSON.parse(localStorage.localConfig_v2).teams[
225
+ Object.keys(JSON.parse(localStorage.localConfig_v2).teams)[0]
226
+ ].token
227
+ ```
228
+
229
+ Format: `xoxc-{team_id}-{user_id}-{session_id}-{secret}`.
230
+
231
+ > ⚠️ The `xoxc` rotates **every time you log out** of Slack. When this tool returns a `401`, re-run `slack-huddle-mcp setup`.
232
+
233
+ ### `xoxd` (session cookie, header)
234
+
235
+ 1. DevTools → Application → Cookies → `https://app.slack.com`.
236
+ 2. Find the cookie named `d` (HttpOnly).
237
+ 3. Copy the **raw value** — it's URL-encoded as stored. Do not decode.
238
+
239
+ The `xoxd` lifetime is about a year.
240
+
241
+ ## 8. MCP client config snippets
242
+
243
+ All clients use the same command. Pick whichever fits your setup.
244
+
245
+ ### Claude Code (CLI) — easiest, local stdio
246
+
247
+ ```bash
248
+ claude mcp add slack-huddle -s user -- "$(which slack-huddle-mcp)" serve
249
+ ```
250
+
251
+ Then in any Claude Code session, run `/mcp` to confirm it's connected.
252
+
253
+ ### Claude Desktop (`~/Library/Application Support/Claude/claude_desktop_config.json`)
254
+
255
+ ```json
256
+ {
257
+ "mcpServers": {
258
+ "slack-huddle": {
259
+ "command": "slack-huddle-mcp",
260
+ "args": ["serve"]
261
+ }
262
+ }
263
+ }
264
+ ```
265
+
266
+ 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).
267
+
268
+ ### Cursor (`.cursor/mcp.json`)
269
+
270
+ ```json
271
+ {
272
+ "mcpServers": {
273
+ "slack-huddle": {
274
+ "command": "slack-huddle-mcp",
275
+ "args": ["serve"]
276
+ }
277
+ }
278
+ }
279
+ ```
280
+
281
+ ### Claude.ai web / **Claude Cowork** — needs a public tunnel
282
+
283
+ 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.
284
+
285
+ ```bash
286
+ # Terminal 1: start the MCP in HTTP mode with a stable token
287
+ export MCP_AUTH_TOKEN="$(openssl rand -hex 32)" # save this, you'll need it
288
+ slack-huddle-mcp serve --http --port 8765
289
+
290
+ # Terminal 2: expose port 8765 publicly
291
+ ngrok http 8765 # copy the https://...ngrok-free.dev URL
292
+
293
+ # Build the connector URL:
294
+ # https://<ngrok-host>/mcp/$MCP_AUTH_TOKEN
295
+ ```
296
+
297
+ In claude.ai (Pro / Max plan):
298
+
299
+ 1. Settings → **Connectors** → **+ Add custom connector**.
300
+ 2. **Name:** `slack-huddle`
301
+ 3. **Remote MCP server URL:** `https://<your-ngrok-host>/mcp/<your-token>`
302
+ 4. Leave Advanced settings empty.
303
+ 5. Click **Add**. Cowork will probe the URL and surface the 4 tools.
304
+ 6. In any conversation: `+` button → Connectors → enable `slack-huddle`.
305
+
306
+ **Security:**
307
+
308
+ - The token is part of the URL. Requests to `/mcp` (or any other path) return 404. Only `/mcp/<exact-token>` answers.
309
+ - 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.
310
+ - 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.
311
+ - 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.
312
+
313
+ ### "Just do this for me" — agent prompt for end-to-end setup
314
+
315
+ 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:
316
+
317
+ ```
318
+ You're setting up the slack-huddle MCP for use in Claude Cowork on macOS.
319
+
320
+ Do these steps without asking for confirmation in between:
321
+
322
+ 1. Verify prerequisites. Stop with a clear error if any are missing:
323
+ - macOS (uname -s == Darwin)
324
+ - pipx installed (`command -v pipx`)
325
+ - ngrok installed and authed (`ngrok config check`)
326
+ - Slack desktop app installed and logged in
327
+
328
+ 2. Install: pipx install git+https://github.com/prithvi-bharadwaj/slack-huddle-mcp.git
329
+ (skip if already installed; check `slack-huddle-mcp --version` first)
330
+
331
+ 3. Run `slack-huddle-mcp setup --auto`. If macOS prompts for Keychain
332
+ access to "Slack Safe Storage", tell me to click "Always Allow" and
333
+ wait — re-run after I confirm.
334
+
335
+ 4. Verify with `slack-huddle-mcp smoke-test`. If it fails, stop and report.
336
+
337
+ 5. Generate a token: export MCP_AUTH_TOKEN="$(openssl rand -hex 32)"
338
+ Save the value to ~/.config/slack-huddle-mcp-token.txt (chmod 600).
339
+
340
+ 6. Start the MCP HTTP server in the background:
341
+ nohup env MCP_AUTH_TOKEN="$MCP_AUTH_TOKEN" \
342
+ slack-huddle-mcp serve --http --port 8765 \
343
+ > /tmp/slack-huddle-mcp.log 2>&1 &
344
+ Verify it's listening with curl on the local URL.
345
+
346
+ 7. Start ngrok in the background:
347
+ nohup ngrok http 8765 --log /tmp/ngrok.log --log-format json \
348
+ > /dev/null 2>&1 &
349
+ Wait until http://127.0.0.1:4040/api/tunnels returns a public_url.
350
+
351
+ 8. Print:
352
+ CONNECTOR URL: https://<ngrok-host>/mcp/<token>
353
+ Add it in claude.ai → Settings → Connectors → + Add custom connector.
354
+ Leave Advanced settings empty. Click Add. Done.
355
+
356
+ 9. Also print the PIDs of the MCP and ngrok processes, and the exact
357
+ commands to stop them later.
358
+ ```
359
+
360
+ ### Tools your agent will see
361
+
362
+ | Tool | What it does |
363
+ | ---- | ------------ |
364
+ | `list_huddles(channel_id?, after?, before?, limit?, workspace?)` | List recent huddles, optionally scoped to a channel or time range. |
365
+ | `get_huddle_transcript(huddle_id? \| transcript_file_id?, format="markdown"\|"json"\|"lines", merge_consecutive?, user_map?, workspace?)` | Fetch the AI transcript for one huddle. |
366
+ | `get_huddle_summary(canvas_id? \| huddle_id?, workspace?)` | Fetch the AI-generated summary canvas. |
367
+ | `list_workspaces()` | List workspaces with stored tokens on this machine. |
368
+
369
+ ## 9. FAQ
370
+
371
+ **Q: Does this work on free Slack workspaces?**
372
+ 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.
373
+
374
+ **Q: Will my `xoxc` token leak through this?**
375
+ 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.
376
+
377
+ **Q: Will Slack ban me for using this?**
378
+ 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.
379
+
380
+ **Q: My token stopped working.**
381
+ You logged out of Slack (the `xoxc` rotates on logout). Run `slack-huddle-mcp setup` again to refresh.
382
+
383
+ **Q: How do I use this with multiple workspaces?**
384
+ 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.
385
+
386
+ **Q: Why is `transcript_file_id` on the huddle actually pointing at a canvas, not the transcript?**
387
+ 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.
388
+
389
+ **Q: Will you support live (streaming) transcripts?**
390
+ On the roadmap, once the source endpoint stabilizes. Today the AI transcript is generated post-huddle.
391
+
392
+ ## 10. Roadmap
393
+
394
+ - Huddle audio download (Slack stores the m4a behind the same file machinery).
395
+ - Async client (`httpx.AsyncClient`) for parallel canvas resolution.
396
+ - Streaming transcripts for live huddles.
397
+ - Better multi-workspace UX — env-variable overrides, profile switching.
398
+ - Native Slack endpoint when/if Slack publishes one — drop the web-client shim.
399
+
400
+ ## 11. Contributing & license
401
+
402
+ PRs welcome — especially for new MCP clients in section 8, additional dead ends in section 6, and tighter parsers in `parser.py`.
403
+
404
+ ```bash
405
+ git clone https://github.com/prithvi-bharadwaj/slack-huddle-mcp.git
406
+ cd slack-huddle-mcp
407
+ pip install -e ".[dev]"
408
+ pytest -q --cov
409
+ ruff check src/ tests/
410
+ mypy --strict src/
411
+ ```
412
+
413
+ This project follows the [Contributor Covenant](https://www.contributor-covenant.org/version/2/1/code_of_conduct/). Be kind.
414
+
415
+ Licensed under the [MIT License](LICENSE).