@jtalk22/slack-mcp 3.2.4 → 4.0.0

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.
package/README.md CHANGED
@@ -1,24 +1,28 @@
1
1
  # Slack MCP Server
2
2
 
3
3
  [![npm version](https://img.shields.io/npm/v/@jtalk22/slack-mcp)](https://www.npmjs.com/package/@jtalk22/slack-mcp)
4
- [![npm downloads](https://img.shields.io/npm/dm/@jtalk22/slack-mcp)](https://www.npmjs.com/package/@jtalk22/slack-mcp)
5
4
  [![MCP Registry](https://img.shields.io/badge/MCP_Registry-listed-blue)](https://registry.modelcontextprotocol.io)
6
5
  [![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](https://opensource.org/licenses/MIT)
7
6
 
8
- Give Claude your Slack. 16 self-hosted tools for channels, search, replies, reactions, unread triage, and user search. Self-host free or use Slack MCP Cloud for managed transport, credential handling, and support.
9
-
10
- ## Verify & Proof
7
+ Give your AI agent full Slack access. No app registration, no admin approval, no OAuth. One command, 16 tools, works with any MCP client.
11
8
 
12
9
  ```bash
13
10
  npx -y @jtalk22/slack-mcp --setup
14
- npx -y @jtalk22/slack-mcp@latest --version
15
- npx -y @jtalk22/slack-mcp@latest --doctor
16
- npx -y @jtalk22/slack-mcp@latest --status
17
11
  ```
18
12
 
19
- [20-second demo](https://jtalk22.github.io/slack-mcp-server/public/demo-video.html) · [Interactive demo](https://jtalk22.github.io/slack-mcp-server/public/demo.html) · [Latest release notes](https://github.com/jtalk22/slack-mcp-server/releases/latest) · [Release-day runbook](docs/LAUNCH-OPS.md) · [Deployment intake](https://github.com/jtalk22/slack-mcp-server/issues/new?template=deployment-intake.md) · [Support boundaries](docs/SUPPORT-BOUNDARIES.md)
13
+ ![demo](docs/images/demo-readme.gif)
14
+
15
+ > **Ask Claude to catch you up on #engineering from the last 24 hours.** Search for that deployment thread from last week. Find every message mentioning the API key. Send a reply. All from your editor.
16
+
17
+ [Interactive demo](https://jtalk22.github.io/slack-mcp-server/public/demo.html) · [Latest release](https://github.com/jtalk22/slack-mcp-server/releases/latest)
18
+
19
+ ## Why This Exists
20
+
21
+ Slack's official MCP server requires a registered app, admin approval, and [doesn't work with Claude Code or GitHub Copilot](https://github.com/anthropics/claude-code/issues/30564) due to OAuth/DCR incompatibility. Screenshotting messages is not a workflow.
20
22
 
21
- [![Slack MCP proof surface](docs/images/demo-poster.png)](https://jtalk22.github.io/slack-mcp-server/public/demo-video.html)
23
+ This server uses your browser's session tokens instead. If you can see it in Slack, your AI agent can see it too. No app install, no scopes, no admin.
24
+
25
+ ![OAuth vs Session](docs/images/diagram-oauth-comparison.svg)
22
26
 
23
27
  ## Tools
24
28
 
@@ -41,35 +45,19 @@ npx -y @jtalk22/slack-mcp@latest --status
41
45
  | `slack_remove_reaction` | Remove an emoji reaction from a message | **destructive** |
42
46
  | `slack_conversations_mark` | Mark a conversation as read | **destructive** |
43
47
 
44
- All tools carry [MCP safety annotations](https://modelcontextprotocol.io/specification/2025-03-26/server/tools#annotations): 12 read-only (`readOnlyHint: true`), 4 write-path (`destructiveHint: true`). Only `slack_send_message` is non-idempotent.
45
-
46
- \* `slack_refresh_tokens` modifies local token file only — no external Slack state.
47
-
48
- ## Cloud
49
-
50
- Slack MCP Cloud provides 15 managed tools with hosted credential handling. Team adds 3 AI compound workflows for summaries, action items, and decisions.
51
-
52
- | Plan | Price | Includes |
53
- |------|-------|----------|
54
- | Solo | $19/mo | 15 standard tools, AES-256-GCM encrypted storage, 5K requests/mo |
55
- | Team | $49/mo | 15 standard + 3 AI compound tools, 3 workspaces, 25K requests/mo |
48
+ 12 read-only, 4 write-path. All carry [MCP safety annotations](https://modelcontextprotocol.io/specification/2025-03-26/server/tools#annotations).
56
49
 
57
- [Get Started](https://mcp.revasserlabs.com) · [Privacy Policy](https://mcp.revasserlabs.com/privacy) · [Support Boundaries](docs/SUPPORT-BOUNDARIES.md)
50
+ \* `slack_refresh_tokens` modifies local token file only.
58
51
 
59
- For rollout help or managed deployment review, open a [deployment intake](https://github.com/jtalk22/slack-mcp-server/issues/new?template=deployment-intake.md). Reproducible bugs stay in standard issues; rollout requests belong in deployment intake.
52
+ ## Install
60
53
 
61
- ## Install & Verify (Self-Hosted)
62
-
63
- **Runtime:** Node.js 20+
54
+ **Node.js 20+**
64
55
 
65
56
  ```bash
66
57
  npx -y @jtalk22/slack-mcp --setup
67
- npx -y @jtalk22/slack-mcp@latest --version
68
- npx -y @jtalk22/slack-mcp@latest --doctor
69
- npx -y @jtalk22/slack-mcp@latest --status
70
58
  ```
71
59
 
72
- The setup wizard handles token extraction and validation automatically.
60
+ The setup wizard handles token extraction and validation.
73
61
 
74
62
  <details>
75
63
  <summary><strong>Claude Desktop (macOS)</strong></summary>
@@ -114,7 +102,7 @@ Edit `%APPDATA%\Claude\claude_desktop_config.json`:
114
102
  </details>
115
103
 
116
104
  <details>
117
- <summary><strong>Claude Code CLI</strong></summary>
105
+ <summary><strong>Claude Code</strong></summary>
118
106
 
119
107
  Add to `~/.claude.json`:
120
108
 
@@ -132,6 +120,41 @@ Add to `~/.claude.json`:
132
120
 
133
121
  </details>
134
122
 
123
+ <details>
124
+ <summary><strong>Cursor / Copilot / Other MCP clients</strong></summary>
125
+
126
+ Any client that supports stdio MCP servers works. Add to your client's MCP config:
127
+
128
+ ```json
129
+ {
130
+ "slack": {
131
+ "command": "npx",
132
+ "args": ["-y", "@jtalk22/slack-mcp"],
133
+ "env": {
134
+ "SLACK_TOKEN": "xoxc-your-token",
135
+ "SLACK_COOKIE": "xoxd-your-cookie"
136
+ }
137
+ }
138
+ }
139
+ ```
140
+
141
+ On macOS, tokens are auto-extracted from Chrome — `env` block is optional.
142
+
143
+ </details>
144
+
145
+ <details>
146
+ <summary><strong>Claude Web / Remote MCP</strong></summary>
147
+
148
+ For browser-based clients that can't run local processes, use the hosted HTTP endpoint:
149
+
150
+ ```
151
+ https://mcp.revasserlabs.com/oauth/mcp
152
+ ```
153
+
154
+ Add this as a remote MCP server in your client's settings. Transport: Streamable HTTP. Auth: OAuth 2.1 + PKCE.
155
+
156
+ </details>
157
+
135
158
  <details>
136
159
  <summary><strong>Docker</strong></summary>
137
160
 
@@ -154,7 +177,19 @@ docker pull ghcr.io/jtalk22/slack-mcp-server:latest
154
177
 
155
178
  </details>
156
179
 
157
- Restart Claude after configuration. Full setup guide: [docs/SETUP.md](docs/SETUP.md)
180
+ Restart your client after configuration. Full setup: [docs/SETUP.md](docs/SETUP.md)
181
+
182
+ ## How It Works
183
+
184
+ Session tokens (`xoxc-` + `xoxd-`) from your browser. If you can see it in Slack, this server can see it too.
185
+
186
+ **Token persistence** — four-layer fallback:
187
+ 1. Environment variables (`SLACK_TOKEN`, `SLACK_COOKIE`)
188
+ 2. Token file (`~/.slack-mcp-tokens.json`, chmod 600)
189
+ 3. macOS Keychain (encrypted)
190
+ 4. Chrome auto-extraction (macOS)
191
+
192
+ Tokens expire. The server notices before you do — proactive health monitoring, automatic refresh on macOS, warnings when tokens age out. File writes are atomic (temp file → chmod → rename) to prevent corruption. Concurrent refresh attempts are mutex-locked.
158
193
 
159
194
  ## Hosted HTTP Mode
160
195
 
@@ -172,32 +207,31 @@ Details: [docs/DEPLOYMENT-MODES.md](docs/DEPLOYMENT-MODES.md)
172
207
 
173
208
  ## Troubleshooting
174
209
 
175
- **Tokens expired:** Run `npx -y @jtalk22/slack-mcp --setup` or use `slack_refresh_tokens` in Claude (macOS).
210
+ **Tokens expired:** Run `npx -y @jtalk22/slack-mcp --setup` or use `slack_refresh_tokens` (macOS).
176
211
 
177
- **DMs not showing:** Use `slack_list_conversations` with `discover_dms=true` to force discovery.
212
+ **DMs not showing:** Use `slack_list_conversations` with `discover_dms=true`.
178
213
 
179
- **Claude not seeing tools:** Verify JSON syntax in config, check logs at `~/Library/Logs/Claude/mcp*.log`, fully restart Claude (Cmd+Q).
214
+ **Client not seeing tools:** Check JSON syntax in config, restart client fully.
180
215
 
181
216
  More: [docs/TROUBLESHOOTING.md](docs/TROUBLESHOOTING.md)
182
217
 
183
218
  ## Docs
184
219
 
185
- - [Setup Guide](docs/SETUP.md) — Token extraction and configuration
186
- - [API Reference](docs/API.md) — All 16 tools with parameters and examples
187
- - [Deployment Modes](docs/DEPLOYMENT-MODES.md) — stdio, web, hosted HTTP, Cloudflare Worker
188
- - [Use Case Recipes](docs/USE_CASE_RECIPES.md) — 12 copy-paste prompts
189
- - [Troubleshooting](docs/TROUBLESHOOTING.md) — Common issues and fixes
190
- - [Compatibility](docs/COMPATIBILITY.md) — Client compatibility matrix
191
- - [Support Boundaries](docs/SUPPORT-BOUNDARIES.md) — Scope and response targets
192
- - [Docs Index](docs/INDEX.md) — Full documentation index
220
+ - [Setup Guide](docs/SETUP.md)
221
+ - [API Reference](docs/API.md)
222
+ - [Architecture](docs/ARCHITECTURE.md)
223
+ - [Deployment Modes](docs/DEPLOYMENT-MODES.md)
224
+ - [Use Case Recipes](docs/USE_CASE_RECIPES.md)
225
+ - [Troubleshooting](docs/TROUBLESHOOTING.md)
226
+ - [Compatibility](docs/COMPATIBILITY.md)
193
227
 
194
228
  ## Security
195
229
 
196
- - Token files stored with `chmod 600` (owner-only)
197
- - macOS Keychain provides encrypted backup
230
+ - Token files: `chmod 600` (owner-only)
231
+ - macOS Keychain encrypted backup
198
232
  - Web server binds to localhost only
199
- - API keys are cryptographically random (`crypto.randomBytes`)
200
- - See [SECURITY.md](SECURITY.md) for vulnerability reporting
233
+ - API keys: `crypto.randomBytes`
234
+ - See [SECURITY.md](SECURITY.md)
201
235
 
202
236
  ## Contributing
203
237
 
@@ -209,4 +243,8 @@ MIT — See [LICENSE](LICENSE)
209
243
 
210
244
  ## Disclaimer
211
245
 
212
- This project accesses Slack's Web API using browser session credentials. It is not affiliated with or endorsed by Slack Technologies, Inc. Slack workspace administrators should review their acceptable use policies.
246
+ Not affiliated with Slack Technologies, Inc. Uses browser session credentials check your workspace's acceptable use policy.
247
+
248
+ ---
249
+
250
+ Managed hosting available — [mcp.revasserlabs.com](https://mcp.revasserlabs.com)
package/docs/API.md CHANGED
@@ -259,7 +259,7 @@ Get all replies in a thread.
259
259
  "messages": [
260
260
  {
261
261
  "ts": "1767368030.607599",
262
- "user": "James Lambert",
262
+ "user": "Example User",
263
263
  "text": "Original message",
264
264
  "datetime": "2026-01-02T15:33:50.000Z",
265
265
  "is_parent": true
@@ -9,13 +9,14 @@ export const RELEASE_VERSION = packageJson.version;
9
9
  export const PUBLIC_METADATA = Object.freeze({
10
10
  projectName: "slack-mcp-server",
11
11
  packageName: packageJson.name,
12
+ canonicalShortDescription: packageJson.description,
12
13
  canonicalRepoUrl: "https://github.com/jtalk22/slack-mcp-server",
13
14
  canonicalSiteUrl: "https://mcp.revasserlabs.com",
15
+ cloudPricingUrl: "https://mcp.revasserlabs.com/pricing",
16
+ cloudDocsUrl: "https://mcp.revasserlabs.com/docs",
17
+ cloudSecurityUrl: "https://mcp.revasserlabs.com/security",
18
+ cloudSupportUrl: "https://mcp.revasserlabs.com/support",
19
+ cloudStatusUrl: "https://mcp.revasserlabs.com/status",
14
20
  supportEmail: "support@revasserlabs.com",
15
- privacyEmail: "privacy@revasserlabs.com",
16
21
  selfHostedToolCount: 16,
17
- cloudManagedToolCount: 15,
18
- teamAiWorkflowCount: 3,
19
- cloudSoloPrice: "$19/mo",
20
- cloudTeamPrice: "$49/mo",
21
22
  });
@@ -0,0 +1,139 @@
1
+ import { readFileSync } from "node:fs";
2
+ import { dirname, resolve } from "node:path";
3
+ import { fileURLToPath } from "node:url";
4
+
5
+ import { PUBLIC_METADATA } from "./public-metadata.js";
6
+
7
+ const __dirname = dirname(fileURLToPath(import.meta.url));
8
+ const ROOT = resolve(__dirname, "..");
9
+ const TEMPLATE_DIR = resolve(ROOT, "templates", "public-pages");
10
+
11
+ const GITHUB_PAGES_ROOT = "https://jtalk22.github.io/slack-mcp-server";
12
+ const GITHUB_DOCS_ROOT = `${PUBLIC_METADATA.canonicalRepoUrl}/blob/main/docs`;
13
+ const SOCIAL_IMAGE_URL = `${GITHUB_PAGES_ROOT}/docs/images/social-preview-v3.png`;
14
+ const ICON_URL = `${GITHUB_PAGES_ROOT}/docs/assets/icon-512.png`;
15
+ const NPM_URL = "https://www.npmjs.com/package/@jtalk22/slack-mcp";
16
+ const RELEASES_URL = `${PUBLIC_METADATA.canonicalRepoUrl}/releases/latest`;
17
+ const SETUP_URL = `${PUBLIC_METADATA.canonicalRepoUrl}/blob/main/docs/SETUP.md`;
18
+ const DEMO_VIDEO_URL = `${GITHUB_PAGES_ROOT}/docs/videos/demo-claude-mobile-20s.mp4`;
19
+
20
+ function template(name) {
21
+ return readFileSync(resolve(TEMPLATE_DIR, name), "utf8");
22
+ }
23
+
24
+ function replaceTokens(source, replacements) {
25
+ return source.replace(/\{\{([A-Z0-9_]+)\}\}/g, (match, key) => {
26
+ if (!(key in replacements)) {
27
+ throw new Error(`Missing template token: ${key}`);
28
+ }
29
+ return replacements[key];
30
+ });
31
+ }
32
+
33
+ function rootDecisionPanel() {
34
+ return `
35
+ <section class="stage" style="padding-top:0">
36
+ <div class="decision-grid" aria-label="Self-host info">
37
+ <article class="decision-card">
38
+ <span class="decision-label">Self-host</span>
39
+ <h2>${PUBLIC_METADATA.selfHostedToolCount} tools and full operator control.</h2>
40
+ <p>Session-based auth, stdio transport. Works with Claude, ChatGPT, Cursor, Copilot, Gemini, Windsurf, and any other MCP client.</p>
41
+ <ul>
42
+ <li>stdio, web, and Docker paths stay fully under your control</li>
43
+ <li>No OAuth app registration or admin approval needed</li>
44
+ <li>Token persistence with automatic refresh on macOS</li>
45
+ </ul>
46
+ <p class="decision-links"><a href="${SETUP_URL}">Setup guide</a> · <a href="${RELEASES_URL}">Latest release</a> · <a href="${NPM_URL}">npm</a></p>
47
+ </article>
48
+ </div>
49
+ </section>
50
+ `.trim();
51
+ }
52
+
53
+ function shareLinks() {
54
+ return `
55
+ <a href="${SETUP_URL}" rel="noopener">Install (\`--setup\`)</a>
56
+ <a href="${SETUP_URL}" rel="noopener">Verify (\`--version/--doctor/--status\`)</a>
57
+ <a href="${RELEASES_URL}" rel="noopener">Latest Release</a>
58
+ <a href="${GITHUB_PAGES_ROOT}/" rel="noopener">Autoplay Demo Landing</a>
59
+ <a href="${DEMO_VIDEO_URL}" rel="noopener">20s Mobile Clip</a>
60
+ <a href="${NPM_URL}" rel="noopener">npm Package</a>
61
+ <a href="${PUBLIC_METADATA.canonicalSiteUrl}" rel="noopener" style="background:rgba(240,194,70,0.18);border-color:rgba(240,194,70,0.45);color:#f0c246">Cloud</a>
62
+ `.trim();
63
+ }
64
+
65
+ function shareNote() {
66
+ return `<strong>Verify in 30 seconds:</strong> <code>--version</code>, <code>--doctor</code>, <code>--status</code>. Self-host gives ${PUBLIC_METADATA.selfHostedToolCount} tools with session-based auth. Works with any MCP client — Claude, ChatGPT, Cursor, Copilot, Gemini, Windsurf. Managed hosting available at <a href="${PUBLIC_METADATA.canonicalSiteUrl}">mcp.revasserlabs.com</a>.`;
67
+ }
68
+
69
+ function demoLinks() {
70
+ return `
71
+ <a href="${PUBLIC_METADATA.canonicalSiteUrl}" target="_blank" rel="noopener noreferrer" style="background:rgba(240,194,70,0.18);border-color:rgba(240,194,70,0.45);color:#f0c246">Cloud</a>
72
+ <a href="${NPM_URL}" target="_blank" rel="noopener noreferrer">npm Install</a>
73
+ <a href="${SETUP_URL}" target="_blank" rel="noopener noreferrer">Setup Guide</a>
74
+ `.trim();
75
+ }
76
+
77
+ function demoNote() {
78
+ return `Self-host free for ${PUBLIC_METADATA.selfHostedToolCount} tools with session-based auth. Works with Claude, ChatGPT, Cursor, Copilot, Gemini, Windsurf, and any other MCP client. No OAuth app, no admin approval. Managed hosting available at <a href="${PUBLIC_METADATA.canonicalSiteUrl}" target="_blank" rel="noopener noreferrer">mcp.revasserlabs.com</a>.`;
79
+ }
80
+
81
+ function demoFooterLinks() {
82
+ return `<a href="${PUBLIC_METADATA.canonicalRepoUrl}">GitHub</a> · <a href="${NPM_URL}" style="color:#94a3b8;text-decoration:none;font-size:0.875rem">npm</a> · <a href="${PUBLIC_METADATA.canonicalSiteUrl}" style="color:#f0c246;text-decoration:none;font-size:0.875rem">Cloud</a>`;
83
+ }
84
+
85
+ function commonTokens() {
86
+ return {
87
+ CANONICAL_SITE_URL: PUBLIC_METADATA.canonicalSiteUrl,
88
+ CLOUD_PRICING_URL: PUBLIC_METADATA.cloudPricingUrl,
89
+ CLOUD_WORKFLOWS_URL: PUBLIC_METADATA.canonicalSiteUrl + "/workflows",
90
+ CLOUD_OFFICIAL_COMPARISON_URL: PUBLIC_METADATA.canonicalSiteUrl + "/official-slack-mcp-vs-managed",
91
+ CLOUD_GEMINI_CLI_URL: PUBLIC_METADATA.canonicalSiteUrl + "/gemini-cli",
92
+ CLOUD_READINESS_URL: PUBLIC_METADATA.canonicalSiteUrl + "/readiness",
93
+ CLOUD_DOCS_URL: PUBLIC_METADATA.cloudDocsUrl,
94
+ CLOUD_SECURITY_URL: PUBLIC_METADATA.cloudSecurityUrl,
95
+ CLOUD_PROCUREMENT_URL: PUBLIC_METADATA.canonicalSiteUrl + "/procurement",
96
+ CLOUD_MARKETPLACE_READINESS_URL: PUBLIC_METADATA.canonicalSiteUrl + "/marketplace-readiness",
97
+ CLOUD_SUPPORT_URL: PUBLIC_METADATA.cloudSupportUrl,
98
+ CLOUD_DEPLOYMENT_URL: PUBLIC_METADATA.canonicalSiteUrl + "/deployment",
99
+ CLOUD_STATUS_URL: PUBLIC_METADATA.cloudStatusUrl,
100
+ CLOUD_SELF_HOST_URL: PUBLIC_METADATA.canonicalSiteUrl + "/self-host",
101
+ CLOUD_ACCOUNT_URL: PUBLIC_METADATA.canonicalSiteUrl + "/account",
102
+ GITHUB_REPO_URL: PUBLIC_METADATA.canonicalRepoUrl,
103
+ GITHUB_PAGES_ROOT,
104
+ GITHUB_DOCS_ROOT,
105
+ ICON_URL,
106
+ SOCIAL_IMAGE_URL,
107
+ NPM_URL,
108
+ RELEASES_URL,
109
+ SETUP_URL,
110
+ RELEASE_HEALTH_URL: RELEASES_URL,
111
+ VERSION_PARITY_URL: RELEASES_URL,
112
+ RUNBOOK_URL: SETUP_URL,
113
+ SELF_HOSTED_TOOL_COUNT: String(PUBLIC_METADATA.selfHostedToolCount),
114
+ CLOUD_MANAGED_TOOL_COUNT: "15",
115
+ TEAM_AI_WORKFLOW_COUNT: "3",
116
+ CLOUD_SOLO_PRICE: "$19/mo",
117
+ CLOUD_TEAM_PRICE: "$49/mo",
118
+ CLOUD_TURNKEY_LAUNCH_PRICE: "$2.5k+",
119
+ CLOUD_MANAGED_RELIABILITY_PRICE: "$800/mo+",
120
+ SUPPORT_EMAIL: PUBLIC_METADATA.supportEmail,
121
+ ROOT_DECISION_PANEL: rootDecisionPanel(),
122
+ SHARE_LINKS: shareLinks(),
123
+ SHARE_NOTE: shareNote(),
124
+ DEMO_LINKS: demoLinks(),
125
+ DEMO_NOTE: demoNote(),
126
+ DEMO_FOOTER_LINKS: demoFooterLinks(),
127
+ };
128
+ }
129
+
130
+ export function buildPublicPages() {
131
+ const tokens = commonTokens();
132
+ return {
133
+ "index.html": replaceTokens(template("index.html.tpl"), tokens),
134
+ "public/share.html": replaceTokens(template("share.html.tpl"), tokens),
135
+ "public/demo.html": replaceTokens(template("demo.html.tpl"), tokens),
136
+ "public/demo-video.html": replaceTokens(template("demo-video.html.tpl"), tokens),
137
+ "public/demo-claude.html": replaceTokens(template("demo-claude.html.tpl"), tokens),
138
+ };
139
+ }
@@ -8,7 +8,7 @@
8
8
  * 4. Chrome auto-extraction (fallback)
9
9
  */
10
10
 
11
- import { readFileSync, writeFileSync, existsSync, renameSync, unlinkSync } from "fs";
11
+ import { readFileSync, writeFileSync, existsSync, renameSync, unlinkSync, chmodSync } from "fs";
12
12
  import { homedir, platform } from "os";
13
13
  import { join } from "path";
14
14
  import { execSync, execFileSync } from "child_process";
@@ -79,7 +79,7 @@ function atomicWriteSync(filePath, content) {
79
79
  try {
80
80
  writeFileSync(tempPath, content);
81
81
  if (IS_MACOS || platform() === 'linux') {
82
- try { execSync(`chmod 600 "${tempPath}"`); } catch {}
82
+ try { chmodSync(tempPath, 0o600); } catch {}
83
83
  }
84
84
  renameSync(tempPath, filePath); // Atomic on POSIX systems
85
85
  } catch (e) {
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "@jtalk22/slack-mcp",
3
3
  "mcpName": "io.github.jtalk22/slack-mcp-server",
4
- "version": "3.2.4",
5
- "description": "Session-based Slack MCP for Claude and MCP clients: local-first workflows, secure-default HTTP.",
4
+ "version": "4.0.0",
5
+ "description": "Slack MCP server. Session-based auth, 16 tools, stdio transport. Works with any MCP client.",
6
6
  "type": "module",
7
7
  "main": "src/server.js",
8
8
  "bin": {
@@ -25,19 +25,19 @@
25
25
  "tokens:clear": "node scripts/token-cli.js clear",
26
26
  "screenshot": "node scripts/capture-screenshots.js",
27
27
  "build:social-preview": "node scripts/build-social-preview.js",
28
+ "build:public-pages": "node scripts/generate-public-pages.js",
28
29
  "build:demo-mobile": "node scripts/build-mobile-demo.js",
29
30
  "build:demo-mobile:gif": "node scripts/build-mobile-demo.js --gif",
30
31
  "record-demo": "node scripts/record-demo.js",
31
32
  "social-preview:update": "node scripts/update-github-social-preview.js --headed",
32
33
  "cf:browser": "node scripts/cloudflare-browser-tool.js",
33
- "metrics:release-health": "node scripts/collect-release-health.js",
34
- "metrics:release-health:delta": "node scripts/build-release-health-delta.js",
35
- "impact:push:v3": "node scripts/impact-push-v3.js --dry-run",
36
- "impact:push:v3:apply": "node scripts/impact-push-v3.js --apply",
37
34
  "verify:attribution-guardrail": "node scripts/verify-attribution-guardrail.js",
35
+ "verify:public-pages": "node scripts/verify-generated-public-pages.js",
38
36
  "verify:version-parity": "node scripts/check-version-parity.js",
39
37
  "verify:public-surface": "node scripts/check-public-surface-integrity.js",
40
- "verify:release-dry-run": "node scripts/release-preflight.js"
38
+ "verify:release-dry-run": "node scripts/release-preflight.js",
39
+ "smoke:browser": "node scripts/browser-smoke.js",
40
+ "smoke:browser:live": "node scripts/browser-smoke.js --mode live"
41
41
  },
42
42
  "keywords": [
43
43
  "claude",
@@ -51,7 +51,9 @@
51
51
  "mcp-server",
52
52
  "session-based",
53
53
  "claude-code",
54
+ "gemini-cli",
54
55
  "local-first",
56
+ "remote-mcp",
55
57
  "session-mirroring",
56
58
  "slack-mcp",
57
59
  "secure-by-default",
@@ -62,9 +64,9 @@
62
64
  "productivity"
63
65
  ],
64
66
  "author": {
65
- "name": "James Lambert",
67
+ "name": "Revasser",
66
68
  "email": "support@revasserlabs.com",
67
- "url": "https://github.com/jtalk22"
69
+ "url": "https://mcp.revasserlabs.com"
68
70
  },
69
71
  "license": "MIT",
70
72
  "repository": {
package/public/share.html CHANGED
@@ -4,18 +4,19 @@
4
4
  <meta charset="utf-8">
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1">
6
6
  <title>Slack MCP Server</title>
7
- <meta name="description" content="Session-based Slack MCP for Claude. Local-first stdio/web with secure-default hosted HTTP in v3.">
7
+ <meta name="description" content="Session-based Slack MCP for Claude. Self-host locally or use the managed Cloud path with Gemini CLI support, pricing, security/procurement review, deployment review, and hosted credentials.">
8
8
  <meta property="og:type" content="website">
9
9
  <meta property="og:title" content="Slack MCP Server">
10
- <meta property="og:description" content="Session-based Slack MCP for Claude. Self-host 16 tools for free or use Cloud for 15 managed tools and support.">
10
+ <meta property="og:description" content="Session-based Slack MCP for Claude. Self-host 16 tools for free or use Cloud for 15 managed tools, Gemini CLI support, security/procurement review, deployment review, and support.">
11
11
  <meta property="og:url" content="https://jtalk22.github.io/slack-mcp-server/public/share.html">
12
12
  <meta property="og:image" content="https://jtalk22.github.io/slack-mcp-server/docs/images/social-preview-v3.png">
13
13
  <meta property="og:image:width" content="1280">
14
14
  <meta property="og:image:height" content="640">
15
15
  <meta name="twitter:card" content="summary_large_image">
16
16
  <meta name="twitter:title" content="Slack MCP Server">
17
- <meta name="twitter:description" content="Session-based Slack MCP for Claude. Self-host 16 tools for free or use Cloud for 15 managed tools and support.">
17
+ <meta name="twitter:description" content="Session-based Slack MCP for Claude. Self-host 16 tools for free or use Cloud for 15 managed tools, Gemini CLI support, security/procurement review, deployment review, and support.">
18
18
  <meta name="twitter:image" content="https://jtalk22.github.io/slack-mcp-server/docs/images/social-preview-v3.png">
19
+ <link rel="icon" href="https://jtalk22.github.io/slack-mcp-server/docs/assets/icon-512.png" type="image/png">
19
20
  <style>
20
21
  :root {
21
22
  --bg-1: #0b1436;
@@ -106,25 +107,23 @@
106
107
  <body>
107
108
  <main class="wrap">
108
109
  <h1>Slack MCP Server</h1>
109
- <p class="sub">Give Claude full access to your Slack. Self-host 16 tools for free, or use Cloud for 15 managed tools with support and hosted credentials.</p>
110
+ <p class="sub">Give Claude full access to your Slack. Self-host 16 tools for free, or use Cloud for 15 managed tools, Gemini CLI support, hosted credentials, rollout support, and buyer-facing security review.</p>
110
111
 
111
112
  <a class="preview" href="https://github.com/jtalk22/slack-mcp-server" rel="noopener">
112
113
  <img src="https://jtalk22.github.io/slack-mcp-server/docs/images/social-preview-v3.png" alt="Slack MCP Server social preview card">
113
114
  </a>
114
115
 
115
116
  <div class="links">
116
- <a href="https://github.com/jtalk22/slack-mcp-server/blob/main/docs/SETUP.md" rel="noopener">Install (`--setup`)</a>
117
+ <a href="https://github.com/jtalk22/slack-mcp-server/blob/main/docs/SETUP.md" rel="noopener">Install (`--setup`)</a>
117
118
  <a href="https://github.com/jtalk22/slack-mcp-server/blob/main/docs/SETUP.md" rel="noopener">Verify (`--version/--doctor/--status`)</a>
118
119
  <a href="https://github.com/jtalk22/slack-mcp-server/releases/latest" rel="noopener">Latest Release</a>
119
- <a href="https://github.com/jtalk22/slack-mcp-server/issues/new?template=deployment-intake.md" rel="noopener">Deployment Intake</a>
120
- <a href="https://github.com/jtalk22/slack-mcp-server/blob/main/docs/SUPPORT-BOUNDARIES.md" rel="noopener">Support</a>
121
120
  <a href="https://jtalk22.github.io/slack-mcp-server/" rel="noopener">Autoplay Demo Landing</a>
122
121
  <a href="https://jtalk22.github.io/slack-mcp-server/docs/videos/demo-claude-mobile-20s.mp4" rel="noopener">20s Mobile Clip</a>
123
122
  <a href="https://www.npmjs.com/package/@jtalk22/slack-mcp" rel="noopener">npm Package</a>
124
123
  <a href="https://mcp.revasserlabs.com" rel="noopener" style="background:rgba(240,194,70,0.18);border-color:rgba(240,194,70,0.45);color:#f0c246">Cloud</a>
125
124
  </div>
126
125
 
127
- <p class="note"><strong>Verify in 30 seconds:</strong> <code>--version</code>, <code>--doctor</code>, <code>--status</code>. For rollout support, use deployment intake. Maintainer/operator: <code>jtalk22</code> (<code>support@revasserlabs.com</code>).</p>
126
+ <p class="note"><strong>Verify in 30 seconds:</strong> <code>--version</code>, <code>--doctor</code>, <code>--status</code>. Self-host gives 16 tools with session-based auth. Works with any MCP client — Claude, ChatGPT, Cursor, Copilot, Gemini, Windsurf. Managed hosting available at <a href="https://mcp.revasserlabs.com">mcp.revasserlabs.com</a>.</p>
128
127
  </main>
129
128
  </body>
130
129
  </html>
package/server.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "$schema": "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json",
3
3
  "name": "io.github.jtalk22/slack-mcp-server",
4
4
  "title": "Slack MCP Server",
5
- "description": "Session-based Slack MCP for Claude and MCP clients: local-first workflows, secure-default HTTP.",
5
+ "description": "Slack MCP server. Session-based auth, 16 tools, stdio transport. Works with any MCP client.",
6
6
  "websiteUrl": "https://mcp.revasserlabs.com",
7
7
  "icons": [
8
8
  {
@@ -17,7 +17,7 @@
17
17
  "url": "https://github.com/jtalk22/slack-mcp-server",
18
18
  "source": "github"
19
19
  },
20
- "version": "3.2.4",
20
+ "version": "4.0.0",
21
21
  "remotes": [
22
22
  {
23
23
  "type": "streamable-http",
@@ -28,7 +28,7 @@
28
28
  {
29
29
  "registryType": "npm",
30
30
  "identifier": "@jtalk22/slack-mcp",
31
- "version": "3.2.4",
31
+ "version": "4.0.0",
32
32
  "transport": {
33
33
  "type": "stdio"
34
34
  },
package/src/server.js CHANGED
@@ -150,7 +150,7 @@ server.setRequestHandler(GetPromptRequestSchema, async (request) => {
150
150
  }
151
151
  case "summarize-channel": {
152
152
  const channelId = args?.channel_id || "";
153
- const days = parseInt(args?.days) || 7;
153
+ const days = parseInt(args?.days, 10) || 7;
154
154
  const since = Math.floor(Date.now() / 1000) - (days * 24 * 60 * 60);
155
155
  return {
156
156
  messages: [
package/src/web-server.js CHANGED
@@ -11,8 +11,7 @@ import express from "express";
11
11
  import { randomBytes } from "crypto";
12
12
  import { fileURLToPath } from "url";
13
13
  import { dirname, join } from "path";
14
- import { existsSync, readFileSync, writeFileSync } from "fs";
15
- import { execSync } from "child_process";
14
+ import { existsSync, readFileSync, writeFileSync, chmodSync } from "fs";
16
15
  import { homedir } from "os";
17
16
  import { loadTokensReadOnly } from "../lib/token-store.js";
18
17
  import { PUBLIC_METADATA, RELEASE_VERSION } from "../lib/public-metadata.js";
@@ -60,7 +59,7 @@ function getOrCreateAPIKey() {
60
59
  const newKey = `smcp_${randomBytes(24).toString('base64url')}`;
61
60
  try {
62
61
  writeFileSync(API_KEY_FILE, newKey);
63
- execSync(`chmod 600 "${API_KEY_FILE}"`);
62
+ chmodSync(API_KEY_FILE, 0o600);
64
63
  } catch {}
65
64
 
66
65
  return newKey;
@@ -98,6 +97,12 @@ app.use((req, res, next) => {
98
97
  next();
99
98
  });
100
99
 
100
+ function safeParseInt(value, fallback, max = 10000) {
101
+ const n = parseInt(value, 10);
102
+ if (isNaN(n) || n < 1) return fallback;
103
+ return Math.min(n, max);
104
+ }
105
+
101
106
  // API Key authentication
102
107
  function authenticate(req, res, next) {
103
108
  const authHeader = req.headers.authorization;
@@ -209,7 +214,7 @@ app.get("/conversations", authenticate, async (req, res) => {
209
214
  try {
210
215
  const result = await handleListConversations({
211
216
  types: req.query.types || "im,mpim",
212
- limit: parseInt(req.query.limit) || 100
217
+ limit: safeParseInt(req.query.limit, 100)
213
218
  });
214
219
  res.json(extractContent(result));
215
220
  } catch (e) {
@@ -222,7 +227,7 @@ app.get("/conversations/unreads", authenticate, async (req, res) => {
222
227
  try {
223
228
  const result = await handleConversationsUnreads({
224
229
  types: req.query.types || "im,mpim,public_channel,private_channel",
225
- limit: parseInt(req.query.limit) || 50
230
+ limit: safeParseInt(req.query.limit, 50)
226
231
  });
227
232
  res.json(extractContent(result));
228
233
  } catch (e) {
@@ -235,7 +240,7 @@ app.get("/conversations/:id/history", authenticate, async (req, res) => {
235
240
  try {
236
241
  const result = await handleConversationsHistory({
237
242
  channel_id: req.params.id,
238
- limit: parseInt(req.query.limit) || 50,
243
+ limit: safeParseInt(req.query.limit, 50),
239
244
  oldest: req.query.oldest,
240
245
  latest: req.query.latest,
241
246
  resolve_users: req.query.resolve_users !== "false"
@@ -253,7 +258,7 @@ app.get("/conversations/:id/full", authenticate, async (req, res) => {
253
258
  channel_id: req.params.id,
254
259
  oldest: req.query.oldest,
255
260
  latest: req.query.latest,
256
- max_messages: parseInt(req.query.max_messages) || 2000,
261
+ max_messages: safeParseInt(req.query.max_messages, 2000, 50000),
257
262
  include_threads: req.query.include_threads !== "false",
258
263
  output_file: req.query.output_file
259
264
  });
@@ -312,7 +317,7 @@ app.get("/search", authenticate, async (req, res) => {
312
317
  }
313
318
  const result = await handleSearchMessages({
314
319
  query: req.query.q,
315
- count: parseInt(req.query.count) || 20
320
+ count: safeParseInt(req.query.count, 20)
316
321
  });
317
322
  res.json(extractContent(result));
318
323
  } catch (e) {
@@ -347,7 +352,7 @@ app.post("/messages", authenticate, async (req, res) => {
347
352
  app.get("/users", authenticate, async (req, res) => {
348
353
  try {
349
354
  const result = await handleListUsers({
350
- limit: parseInt(req.query.limit) || 100
355
+ limit: safeParseInt(req.query.limit, 100)
351
356
  });
352
357
  res.json(extractContent(result));
353
358
  } catch (e) {
@@ -369,7 +374,7 @@ app.get("/users/search", authenticate, async (req, res) => {
369
374
  }
370
375
  const result = await handleUsersSearch({
371
376
  query: req.query.q,
372
- limit: parseInt(req.query.limit) || 20
377
+ limit: safeParseInt(req.query.limit, 20)
373
378
  });
374
379
  res.json(extractContent(result));
375
380
  } catch (e) {
@@ -452,9 +457,9 @@ async function main() {
452
457
  console.error(`\n${"═".repeat(60)}`);
453
458
  console.error(` Slack Web API Server v${RELEASE_VERSION}`);
454
459
  console.error(`${"═".repeat(60)}`);
455
- console.error(`\n Dashboard: http://localhost:${PORT}/?key=${API_KEY}`);
456
- console.error(`\n API Key: ${API_KEY}`);
457
- console.error(`\n curl -H "Authorization: Bearer ${API_KEY}" http://localhost:${PORT}/health`);
460
+ console.error(`\n Dashboard: http://localhost:${PORT}/?key=${API_KEY.slice(0, 8)}...`);
461
+ console.error(`\n API Key: ${API_KEY.slice(0, 8)}${"*".repeat(12)}`);
462
+ console.error(`\n curl -H "Authorization: Bearer <key>" http://localhost:${PORT}/health`);
458
463
  console.error(`\n Security: Bound to localhost only (127.0.0.1)`);
459
464
  console.error(`\n${"═".repeat(60)}\n`);
460
465
  });