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.
- slack_huddle_mcp-0.3.0/.github/workflows/ci.yml +36 -0
- slack_huddle_mcp-0.3.0/.gitignore +59 -0
- slack_huddle_mcp-0.3.0/CHANGELOG.md +45 -0
- slack_huddle_mcp-0.3.0/LICENSE +21 -0
- slack_huddle_mcp-0.3.0/PKG-INFO +415 -0
- slack_huddle_mcp-0.3.0/README.md +375 -0
- slack_huddle_mcp-0.3.0/pyproject.toml +98 -0
- slack_huddle_mcp-0.3.0/skill-runs.jsonl +1 -0
- slack_huddle_mcp-0.3.0/src/slack_huddle/__init__.py +28 -0
- slack_huddle_mcp-0.3.0/src/slack_huddle/__main__.py +6 -0
- slack_huddle_mcp-0.3.0/src/slack_huddle/_cli_helpers.py +103 -0
- slack_huddle_mcp-0.3.0/src/slack_huddle/api.py +255 -0
- slack_huddle_mcp-0.3.0/src/slack_huddle/bookmarklet.py +128 -0
- slack_huddle_mcp-0.3.0/src/slack_huddle/cli.py +94 -0
- slack_huddle_mcp-0.3.0/src/slack_huddle/cli_huddles.py +230 -0
- slack_huddle_mcp-0.3.0/src/slack_huddle/cli_setup.py +144 -0
- slack_huddle_mcp-0.3.0/src/slack_huddle/extractor.py +247 -0
- slack_huddle_mcp-0.3.0/src/slack_huddle/keychain.py +108 -0
- slack_huddle_mcp-0.3.0/src/slack_huddle/mcp_server.py +283 -0
- slack_huddle_mcp-0.3.0/src/slack_huddle/parser.py +166 -0
- slack_huddle_mcp-0.3.0/src/slack_huddle/workspace.py +107 -0
- slack_huddle_mcp-0.3.0/tests/__init__.py +0 -0
- slack_huddle_mcp-0.3.0/tests/conftest.py +40 -0
- slack_huddle_mcp-0.3.0/tests/fixtures/auth_test.json +8 -0
- slack_huddle_mcp-0.3.0/tests/fixtures/files_info_canvas.json +19 -0
- slack_huddle_mcp-0.3.0/tests/fixtures/files_info_transcript.json +25 -0
- slack_huddle_mcp-0.3.0/tests/fixtures/huddles_history.json +32 -0
- slack_huddle_mcp-0.3.0/tests/test_api.py +362 -0
- slack_huddle_mcp-0.3.0/tests/test_bookmarklet.py +79 -0
- slack_huddle_mcp-0.3.0/tests/test_cli.py +335 -0
- slack_huddle_mcp-0.3.0/tests/test_extractor.py +275 -0
- slack_huddle_mcp-0.3.0/tests/test_keychain.py +112 -0
- slack_huddle_mcp-0.3.0/tests/test_mcp_server.py +165 -0
- slack_huddle_mcp-0.3.0/tests/test_parser.py +162 -0
- 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
|
+
[](https://pypi.org/project/slack-huddle-mcp/)
|
|
46
|
+
[](https://pypi.org/project/slack-huddle-mcp/)
|
|
47
|
+
[](https://github.com/prithvi-bharadwaj/slack-huddle-mcp/actions/workflows/ci.yml)
|
|
48
|
+
[](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).
|