@jtalk22/slack-mcp 3.2.5 → 4.1.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.
@@ -15,10 +15,7 @@ const ICON_URL = `${GITHUB_PAGES_ROOT}/docs/assets/icon-512.png`;
15
15
  const NPM_URL = "https://www.npmjs.com/package/@jtalk22/slack-mcp";
16
16
  const RELEASES_URL = `${PUBLIC_METADATA.canonicalRepoUrl}/releases/latest`;
17
17
  const SETUP_URL = `${PUBLIC_METADATA.canonicalRepoUrl}/blob/main/docs/SETUP.md`;
18
- const RELEASE_HEALTH_URL = `${GITHUB_DOCS_ROOT}/release-health/latest.md`;
19
- const VERSION_PARITY_URL = `${GITHUB_DOCS_ROOT}/release-health/version-parity.md`;
20
- const RUNBOOK_URL = `${GITHUB_DOCS_ROOT}/LAUNCH-OPS.md`;
21
- const DEMO_VIDEO_URL = `${GITHUB_PAGES_ROOT}/docs/videos/demo-claude-mobile-20s.mp4`;
18
+ const DEMO_VIDEO_URL = `${GITHUB_PAGES_ROOT}/docs/videos/demo-slack-mcp-mobile-20s.mp4`;
22
19
 
23
20
  function template(name) {
24
21
  return readFileSync(resolve(TEMPLATE_DIR, name), "utf8");
@@ -36,39 +33,17 @@ function replaceTokens(source, replacements) {
36
33
  function rootDecisionPanel() {
37
34
  return `
38
35
  <section class="stage" style="padding-top:0">
39
- <div class="decision-grid" aria-label="Cloud versus self-host decision guide">
36
+ <div class="decision-grid" aria-label="Self-host info">
40
37
  <article class="decision-card">
41
38
  <span class="decision-label">Self-host</span>
42
- <h2>16 tools and full operator control.</h2>
43
- <p>Choose the open-source path if you want npm or Docker, local-first transport control, and direct ownership of token extraction, storage, and process management.</p>
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>
44
41
  <ul>
45
42
  <li>stdio, web, and Docker paths stay fully under your control</li>
46
- <li>Best fit for local development, custom transport, or internal operator ownership</li>
47
- <li>Proof surfaces: install guide, release health, and version parity remain public</li>
43
+ <li>No OAuth app registration or admin approval needed</li>
44
+ <li>Token persistence with automatic refresh on macOS</li>
48
45
  </ul>
49
- <p class="decision-links"><a href="${SETUP_URL}">Setup guide</a> · <a href="${VERSION_PARITY_URL}">Version parity</a> · <a href="${RELEASE_HEALTH_URL}">Release health</a></p>
50
- </article>
51
- <article class="decision-card accent">
52
- <span class="decision-label">Cloud</span>
53
- <h2>${PUBLIC_METADATA.cloudManagedToolCount} managed tools, ${PUBLIC_METADATA.teamAiWorkflowCount} Team AI workflows.</h2>
54
- <p>Choose Cloud if you want one remote endpoint, hosted credential handling, deployment review, and an operational support path instead of running the transport yourself. ${PUBLIC_METADATA.primaryClient} stays primary; ${PUBLIC_METADATA.secondaryClient} is supported as the second client path.</p>
55
- <ul>
56
- <li>Solo: ${PUBLIC_METADATA.cloudSoloPrice} for the managed endpoint and hosted credential handling</li>
57
- <li>Team: ${PUBLIC_METADATA.cloudTeamPrice} and adds ${PUBLIC_METADATA.teamAiWorkflowCount} AI workflows</li>
58
- <li>Turnkey Team Launch: from ${PUBLIC_METADATA.cloudTurnkeyLaunchPrice}; Managed Reliability: from ${PUBLIC_METADATA.cloudManagedReliabilityPrice}</li>
59
- </ul>
60
- <p class="decision-links"><a href="${PUBLIC_METADATA.tracked.pages.pricing}">Pricing</a> · <a href="${PUBLIC_METADATA.tracked.pages.workflows}">Workflows</a> · <a href="${PUBLIC_METADATA.tracked.pages.geminiCli}">Gemini CLI</a> · <a href="${PUBLIC_METADATA.tracked.pages.deployment}">Deployment review</a> · <a href="${PUBLIC_METADATA.tracked.pages.security}">Security</a></p>
61
- </article>
62
- <article class="decision-card">
63
- <span class="decision-label">Buyer proof</span>
64
- <h2>Technical trust surfaces stay public.</h2>
65
- <p>The static Pages root shows npm, GitHub release, and hosted status together. The hosted site publishes <code>/status</code>, <code>/pricing</code>, <code>/docs</code>, <code>/security</code>, <code>/deployment</code>, <code>/support</code>, and <code>/account</code> as the operator-facing Cloud surface.</p>
66
- <ul>
67
- <li>GitHub Pages reads the hosted <code>/status</code> contract live</li>
68
- <li>Registry, npm, runtime parity, and hosted funnel reporting are tracked in the public reports</li>
69
- <li>Rollout and security questions route to hosted review surfaces instead of ad hoc GitHub issues</li>
70
- </ul>
71
- <p class="decision-links"><a href="${RUNBOOK_URL}">Runbook</a> · <a href="${PUBLIC_METADATA.cloudStatusUrl}">Raw status JSON</a> · <a href="${PUBLIC_METADATA.tracked.pages.procurement}">Procurement</a> · <a href="${PUBLIC_METADATA.tracked.pages.readiness}">Readiness</a> · <a href="${PUBLIC_METADATA.tracked.pages.pricing}">Plans & offers</a></p>
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>
72
47
  </article>
73
48
  </div>
74
49
  </section>
@@ -80,65 +55,50 @@ function shareLinks() {
80
55
  <a href="${SETUP_URL}" rel="noopener">Install (\`--setup\`)</a>
81
56
  <a href="${SETUP_URL}" rel="noopener">Verify (\`--version/--doctor/--status\`)</a>
82
57
  <a href="${RELEASES_URL}" rel="noopener">Latest Release</a>
83
- <a href="${PUBLIC_METADATA.tracked.pages.pricing}" rel="noopener">Pricing</a>
84
- <a href="${PUBLIC_METADATA.tracked.pages.workflows}" rel="noopener">Workflows</a>
85
- <a href="${PUBLIC_METADATA.tracked.pages.geminiCli}" rel="noopener">Gemini CLI</a>
86
- <a href="${PUBLIC_METADATA.tracked.pages.docs}" rel="noopener">Cloud Docs</a>
87
- <a href="${PUBLIC_METADATA.tracked.pages.security}" rel="noopener">Security</a>
88
- <a href="${PUBLIC_METADATA.tracked.pages.procurement}" rel="noopener">Procurement</a>
89
- <a href="${PUBLIC_METADATA.tracked.pages.deployment}" rel="noopener">Deployment Review</a>
90
- <a href="${PUBLIC_METADATA.tracked.pages.support}" rel="noopener">Cloud Support</a>
91
- <a href="${PUBLIC_METADATA.cloudUseCasesRootUrl}/support-triage?utm_source=github&utm_medium=pages&utm_campaign=slack_mcp_cloud" rel="noopener">Support Triage Use Case</a>
92
58
  <a href="${GITHUB_PAGES_ROOT}/" rel="noopener">Autoplay Demo Landing</a>
93
59
  <a href="${DEMO_VIDEO_URL}" rel="noopener">20s Mobile Clip</a>
94
60
  <a href="${NPM_URL}" rel="noopener">npm Package</a>
95
- <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>
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">Hosted</a>
96
62
  `.trim();
97
63
  }
98
64
 
99
65
  function shareNote() {
100
- return `<strong>Verify in 30 seconds:</strong> <code>--version</code>, <code>--doctor</code>, <code>--status</code>. Self-host gives ${PUBLIC_METADATA.selfHostedToolCount} tools and full operator control. Cloud starts at ${PUBLIC_METADATA.cloudSoloPrice} for ${PUBLIC_METADATA.cloudManagedToolCount} managed tools, deployment review, procurement-ready security, readiness guidance, and support. Team at ${PUBLIC_METADATA.cloudTeamPrice} adds ${PUBLIC_METADATA.teamAiWorkflowCount} AI workflows. ${PUBLIC_METADATA.primaryClient} is the primary path; ${PUBLIC_METADATA.secondaryClient} is supported on the hosted endpoint.`;
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. Hosted version coming soon at <a href="${PUBLIC_METADATA.canonicalSiteUrl}">mcp.revasserlabs.com</a>.`;
101
67
  }
102
68
 
103
69
  function demoLinks() {
104
70
  return `
105
- <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>
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">Hosted</a>
106
72
  <a href="${NPM_URL}" target="_blank" rel="noopener noreferrer">npm Install</a>
107
- <a href="${PUBLIC_METADATA.tracked.pages.pricing}" target="_blank" rel="noopener noreferrer">Pricing</a>
108
- <a href="${PUBLIC_METADATA.tracked.pages.workflows}" target="_blank" rel="noopener noreferrer">Workflows</a>
109
- <a href="${PUBLIC_METADATA.tracked.pages.geminiCli}" target="_blank" rel="noopener noreferrer">Gemini CLI</a>
110
73
  <a href="${SETUP_URL}" target="_blank" rel="noopener noreferrer">Setup Guide</a>
111
- <a href="${PUBLIC_METADATA.tracked.pages.docs}" target="_blank" rel="noopener noreferrer">Cloud Docs</a>
112
- <a href="${PUBLIC_METADATA.tracked.pages.security}" target="_blank" rel="noopener noreferrer">Security</a>
113
- <a href="${PUBLIC_METADATA.tracked.pages.procurement}" target="_blank" rel="noopener noreferrer">Procurement</a>
114
- <a href="${PUBLIC_METADATA.tracked.pages.deployment}" target="_blank" rel="noopener noreferrer">Deployment Review</a>
115
- <a href="${PUBLIC_METADATA.tracked.pages.support}" target="_blank" rel="noopener noreferrer">Cloud Support</a>
116
74
  `.trim();
117
75
  }
118
76
 
119
77
  function demoNote() {
120
- return `Self-host free for ${PUBLIC_METADATA.selfHostedToolCount} tools and full transport control, or use <a href="${PUBLIC_METADATA.tracked.pages.pricing}" target="_blank" rel="noopener noreferrer">Cloud</a> for ${PUBLIC_METADATA.cloudManagedToolCount} managed tools, deployment review, procurement-ready security, and support. Solo starts at ${PUBLIC_METADATA.cloudSoloPrice}; Team at ${PUBLIC_METADATA.cloudTeamPrice} adds ${PUBLIC_METADATA.teamAiWorkflowCount} AI workflows. Turnkey Team Launch starts at ${PUBLIC_METADATA.cloudTurnkeyLaunchPrice}; Managed Reliability starts at ${PUBLIC_METADATA.cloudManagedReliabilityPrice}.`;
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. Hosted version coming soon at <a href="${PUBLIC_METADATA.canonicalSiteUrl}" target="_blank" rel="noopener noreferrer">mcp.revasserlabs.com</a>.`;
121
79
  }
122
80
 
123
81
  function demoFooterLinks() {
124
- return `<a href="${PUBLIC_METADATA.canonicalRepoUrl}">GitHub</a> · <a href="${PUBLIC_METADATA.tracked.pages.pricing}" style="color:#f0c246;text-decoration:none;font-size:0.875rem">Cloud Plans</a> · <a href="${PUBLIC_METADATA.tracked.pages.workflows}" style="color:#94a3b8;text-decoration:none;font-size:0.875rem">Workflows</a> · <a href="${PUBLIC_METADATA.tracked.pages.geminiCli}" style="color:#94a3b8;text-decoration:none;font-size:0.875rem">Gemini CLI</a> · <a href="${PUBLIC_METADATA.tracked.pages.security}" style="color:#94a3b8;text-decoration:none;font-size:0.875rem">Security</a> · <a href="${PUBLIC_METADATA.tracked.pages.deployment}" style="color:#94a3b8;text-decoration:none;font-size:0.875rem">Deployment Review</a> · <a href="${PUBLIC_METADATA.tracked.pages.support}" style="color:#94a3b8;text-decoration:none;font-size:0.875rem">Cloud Support</a> · <a href="${NPM_URL}" style="color:#94a3b8;text-decoration:none;font-size:0.875rem">npm</a>`;
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">Hosted</a>`;
125
83
  }
126
84
 
127
85
  function commonTokens() {
128
86
  return {
129
87
  CANONICAL_SITE_URL: PUBLIC_METADATA.canonicalSiteUrl,
130
- CLOUD_PRICING_URL: PUBLIC_METADATA.tracked.pages.pricing,
131
- CLOUD_WORKFLOWS_URL: PUBLIC_METADATA.tracked.pages.workflows,
132
- CLOUD_GEMINI_CLI_URL: PUBLIC_METADATA.tracked.pages.geminiCli,
133
- CLOUD_READINESS_URL: PUBLIC_METADATA.tracked.pages.readiness,
134
- CLOUD_DOCS_URL: PUBLIC_METADATA.tracked.pages.docs,
135
- CLOUD_SECURITY_URL: PUBLIC_METADATA.tracked.pages.security,
136
- CLOUD_PROCUREMENT_URL: PUBLIC_METADATA.tracked.pages.procurement,
137
- CLOUD_SUPPORT_URL: PUBLIC_METADATA.tracked.pages.support,
138
- CLOUD_DEPLOYMENT_URL: PUBLIC_METADATA.tracked.pages.deployment,
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",
139
99
  CLOUD_STATUS_URL: PUBLIC_METADATA.cloudStatusUrl,
140
- CLOUD_SELF_HOST_URL: PUBLIC_METADATA.cloudSelfHostUrl,
141
- CLOUD_ACCOUNT_URL: PUBLIC_METADATA.cloudAccountUrl,
100
+ CLOUD_SELF_HOST_URL: PUBLIC_METADATA.canonicalSiteUrl + "/self-host",
101
+ CLOUD_ACCOUNT_URL: PUBLIC_METADATA.canonicalSiteUrl + "/account",
142
102
  GITHUB_REPO_URL: PUBLIC_METADATA.canonicalRepoUrl,
143
103
  GITHUB_PAGES_ROOT,
144
104
  GITHUB_DOCS_ROOT,
@@ -147,16 +107,16 @@ function commonTokens() {
147
107
  NPM_URL,
148
108
  RELEASES_URL,
149
109
  SETUP_URL,
150
- RELEASE_HEALTH_URL,
151
- VERSION_PARITY_URL,
152
- RUNBOOK_URL,
110
+ RELEASE_HEALTH_URL: RELEASES_URL,
111
+ VERSION_PARITY_URL: RELEASES_URL,
112
+ RUNBOOK_URL: SETUP_URL,
153
113
  SELF_HOSTED_TOOL_COUNT: String(PUBLIC_METADATA.selfHostedToolCount),
154
- CLOUD_MANAGED_TOOL_COUNT: String(PUBLIC_METADATA.cloudManagedToolCount),
155
- TEAM_AI_WORKFLOW_COUNT: String(PUBLIC_METADATA.teamAiWorkflowCount),
156
- CLOUD_SOLO_PRICE: PUBLIC_METADATA.cloudSoloPrice,
157
- CLOUD_TEAM_PRICE: PUBLIC_METADATA.cloudTeamPrice,
158
- CLOUD_TURNKEY_LAUNCH_PRICE: PUBLIC_METADATA.cloudTurnkeyLaunchPrice,
159
- CLOUD_MANAGED_RELIABILITY_PRICE: PUBLIC_METADATA.cloudManagedReliabilityPrice,
114
+ CLOUD_MANAGED_TOOL_COUNT: "15",
115
+ TEAM_AI_WORKFLOW_COUNT: "3",
116
+ CLOUD_SOLO_PRICE: "coming soon",
117
+ CLOUD_TEAM_PRICE: "coming soon",
118
+ CLOUD_TURNKEY_LAUNCH_PRICE: "contact us",
119
+ CLOUD_MANAGED_RELIABILITY_PRICE: "contact us",
160
120
  SUPPORT_EMAIL: PUBLIC_METADATA.supportEmail,
161
121
  ROOT_DECISION_PANEL: rootDecisionPanel(),
162
122
  SHARE_LINKS: shareLinks(),
@@ -174,6 +134,6 @@ export function buildPublicPages() {
174
134
  "public/share.html": replaceTokens(template("share.html.tpl"), tokens),
175
135
  "public/demo.html": replaceTokens(template("demo.html.tpl"), tokens),
176
136
  "public/demo-video.html": replaceTokens(template("demo-video.html.tpl"), tokens),
177
- "public/demo-claude.html": replaceTokens(template("demo-claude.html.tpl"), tokens),
137
+ "public/demo-slack-mcp.html": replaceTokens(template("demo-slack-mcp.html.tpl"), tokens),
178
138
  };
179
139
  }
@@ -8,10 +8,11 @@
8
8
  * 4. Chrome auto-extraction (fallback)
9
9
  */
10
10
 
11
- import { readFileSync, writeFileSync, existsSync, renameSync, unlinkSync } from "fs";
12
- import { homedir, platform } from "os";
11
+ import { readFileSync, writeFileSync, existsSync, renameSync, unlinkSync, chmodSync, copyFileSync, mkdtempSync } from "fs";
12
+ import { homedir, platform, tmpdir } from "os";
13
13
  import { join } from "path";
14
- import { execSync, execFileSync } from "child_process";
14
+ import { execFileSync } from "child_process";
15
+ import { pbkdf2Sync, createDecipheriv } from "crypto";
15
16
 
16
17
  const TOKEN_FILE = join(homedir(), ".slack-mcp-tokens.json");
17
18
  const KEYCHAIN_SERVICE = "slack-mcp-server";
@@ -79,7 +80,7 @@ function atomicWriteSync(filePath, content) {
79
80
  try {
80
81
  writeFileSync(tempPath, content);
81
82
  if (IS_MACOS || platform() === 'linux') {
82
- try { execSync(`chmod 600 "${tempPath}"`); } catch {}
83
+ try { chmodSync(tempPath, 0o600); } catch {}
83
84
  }
84
85
  renameSync(tempPath, filePath); // Atomic on POSIX systems
85
86
  } catch (e) {
@@ -102,32 +103,31 @@ export function saveToFile(token, cookie) {
102
103
 
103
104
  // Multiple localStorage paths Slack might use (for robustness)
104
105
  const SLACK_TOKEN_PATHS = [
105
- // Current known path
106
106
  `JSON.parse(localStorage.localConfig_v2).teams[Object.keys(JSON.parse(localStorage.localConfig_v2).teams)[0]].token`,
107
- // Potential future paths
108
107
  `JSON.parse(localStorage.localConfig_v3).teams[Object.keys(JSON.parse(localStorage.localConfig_v3).teams)[0]].token`,
109
- // Redux store path (older Slack)
110
108
  `JSON.parse(localStorage.getItem('reduxPersist:localConfig'))?.teams?.[Object.keys(JSON.parse(localStorage.getItem('reduxPersist:localConfig'))?.teams || {})[0]]?.token`,
111
- // Direct boot data
112
109
  `window.boot_data?.api_token`,
113
110
  ];
114
111
 
112
+ // Chrome profile directories to search (in priority order)
113
+ const CHROME_PROFILES = ['Default', 'Profile 1', 'Profile 2', 'Profile 3'];
114
+
115
115
  function normalizeExtractionError(error) {
116
116
  const raw = String(error?.message || error || "");
117
117
 
118
118
  if (raw.includes("Executing JavaScript through AppleScript is turned off")) {
119
119
  return {
120
120
  code: "apple_events_javascript_disabled",
121
- message: "Chrome blocked JavaScript execution from Apple Events.",
122
- detail: "Enable it in Chrome: View > Developer > Allow JavaScript from Apple Events."
121
+ message: "Chrome needs one setting enabled for token extraction.",
122
+ detail: "In Chrome: View > Developer > Allow JavaScript from Apple Events. Cookie extraction works without this — only the token needs it."
123
123
  };
124
124
  }
125
125
 
126
126
  if (raw.includes("Application isn't running") || raw.includes("Google Chrome got an error")) {
127
127
  return {
128
128
  code: "chrome_not_ready",
129
- message: "Chrome is not ready for token extraction.",
130
- detail: "Open Google Chrome with an active Slack tab at app.slack.com."
129
+ message: "Chrome is not running or has no windows open.",
130
+ detail: "Open Google Chrome with a Slack tab at app.slack.com."
131
131
  };
132
132
  }
133
133
 
@@ -139,6 +139,14 @@ function normalizeExtractionError(error) {
139
139
  };
140
140
  }
141
141
 
142
+ if (raw.includes("Chrome Safe Storage")) {
143
+ return {
144
+ code: "keychain_access_denied",
145
+ message: "Could not access Chrome's encryption key in Keychain.",
146
+ detail: "You may need to allow terminal access in System Settings > Privacy > Full Disk Access."
147
+ };
148
+ }
149
+
142
150
  return {
143
151
  code: "chrome_extraction_failed",
144
152
  message: "Chrome token extraction failed.",
@@ -147,13 +155,133 @@ function normalizeExtractionError(error) {
147
155
  }
148
156
 
149
157
  /**
150
- * Extract tokens from Chrome (macOS only, uses AppleScript)
151
- * Returns null on non-macOS platforms
158
+ * Extract the Slack session cookie from Chrome's encrypted cookie database.
159
+ * The `d` cookie is HttpOnly — JavaScript cannot access it via document.cookie.
160
+ * This reads Chrome's SQLite cookie store and decrypts using the Keychain-stored key.
161
+ */
162
+ function extractCookieFromChromeDB() {
163
+ const chromeBase = join(homedir(), 'Library', 'Application Support', 'Google', 'Chrome');
164
+
165
+ // Find the first profile with a Slack d cookie
166
+ for (const profile of CHROME_PROFILES) {
167
+ const cookiesPath = join(chromeBase, profile, 'Cookies');
168
+ if (!existsSync(cookiesPath)) continue;
169
+
170
+ // Copy DB to temp location (Chrome holds a WAL lock on the original)
171
+ const tmpDir = mkdtempSync(join(tmpdir(), 'slack-mcp-'));
172
+ const tmpDb = join(tmpDir, 'Cookies');
173
+ try {
174
+ copyFileSync(cookiesPath, tmpDb);
175
+
176
+ // Query for the encrypted d cookie
177
+ const queryResult = execFileSync('sqlite3', [
178
+ tmpDb,
179
+ "SELECT hex(encrypted_value) FROM cookies WHERE host_key LIKE '%.slack.com%' AND name = 'd' LIMIT 1;"
180
+ ], { encoding: 'utf-8', timeout: 5000 }).trim();
181
+
182
+ // Clean up temp files
183
+ try { unlinkSync(tmpDb); unlinkSync(tmpDir); } catch {}
184
+
185
+ if (!queryResult) continue;
186
+
187
+ // Convert hex back to buffer
188
+ const encrypted = Buffer.from(queryResult, 'hex');
189
+ if (encrypted.length < 4) continue;
190
+
191
+ // Get Chrome Safe Storage password from Keychain
192
+ const safeStoragePassword = execFileSync('security', [
193
+ 'find-generic-password', '-s', 'Chrome Safe Storage', '-w'
194
+ ], { encoding: 'utf-8', timeout: 5000 }).trim();
195
+
196
+ // Chrome macOS cookies: v10 prefix + AES-128-CBC
197
+ const prefix = encrypted.subarray(0, 3).toString('utf-8');
198
+ if (prefix !== 'v10') continue;
199
+
200
+ const ciphertext = encrypted.subarray(3);
201
+
202
+ // Derive key: PBKDF2-SHA1, 1003 iterations, salt 'saltysalt', 16-byte key
203
+ const key = pbkdf2Sync(safeStoragePassword, 'saltysalt', 1003, 16, 'sha1');
204
+ const iv = Buffer.alloc(16, ' '); // 16 space characters
205
+
206
+ const decipher = createDecipheriv('aes-128-cbc', key, iv);
207
+ let decrypted;
208
+ try {
209
+ decrypted = Buffer.concat([decipher.update(ciphertext), decipher.final()]);
210
+ } catch {
211
+ continue; // Decryption failed for this profile, try next
212
+ }
213
+
214
+ // Find xoxd- in decrypted data (Chrome prepends internal metadata bytes)
215
+ const text = decrypted.toString('utf-8');
216
+ const xoxdIndex = text.indexOf('xoxd-');
217
+ if (xoxdIndex < 0) continue;
218
+
219
+ return text.substring(xoxdIndex);
220
+ } catch (e) {
221
+ // Clean up on error and try next profile
222
+ try { unlinkSync(tmpDb); } catch {}
223
+ try { unlinkSync(tmpDir); } catch {}
224
+ continue;
225
+ }
226
+ }
227
+
228
+ return null;
229
+ }
230
+
231
+ /**
232
+ * Extract Slack token from Chrome via AppleScript (reads localStorage).
233
+ * Uses strict URL matching to avoid hitting non-Slack tabs.
234
+ */
235
+ function extractTokenFromChrome() {
236
+ // Prefer /client URLs (active workspace), fall back to any app.slack.com
237
+ const urlChecks = [
238
+ 'URL of t starts with "https://app.slack.com/client"',
239
+ 'URL of t starts with "https://app.slack.com"',
240
+ ];
241
+
242
+ const tokenPathsJS = SLACK_TOKEN_PATHS.map((path, i) =>
243
+ `try { var t${i} = ${path}; if (t${i} && t${i}.startsWith('xoxc-')) return t${i}; } catch(e) {}`
244
+ ).join(' ');
245
+
246
+ for (const urlCheck of urlChecks) {
247
+ try {
248
+ const script = `tell application "Google Chrome"
249
+ repeat with w in windows
250
+ repeat with t in tabs of w
251
+ if ${urlCheck} then
252
+ return execute t javascript "(function() { ${tokenPathsJS} return ''; })()"
253
+ end if
254
+ end repeat
255
+ end repeat
256
+ return ""
257
+ end tell`;
258
+
259
+ const token = execFileSync('osascript', ['-e', script], {
260
+ encoding: 'utf-8', timeout: 8000
261
+ }).trim();
262
+
263
+ if (token && token.startsWith('xoxc-')) return token;
264
+ } catch {
265
+ continue;
266
+ }
267
+ }
268
+
269
+ return null;
270
+ }
271
+
272
+ /**
273
+ * Extract tokens from Chrome (macOS only).
274
+ *
275
+ * Token: AppleScript executes JS in Chrome to read localStorage (requires
276
+ * "Allow JavaScript from Apple Events" in Chrome > View > Developer).
277
+ * Cookie: Reads Chrome's encrypted SQLite cookie database directly. The `d`
278
+ * session cookie is HttpOnly and cannot be accessed via document.cookie.
279
+ * Decryption uses the Chrome Safe Storage key from macOS Keychain.
152
280
  */
153
281
  function extractFromChromeInternal() {
154
282
  lastExtractionError = null;
283
+
155
284
  if (!IS_MACOS) {
156
- // AppleScript/osascript is macOS-only
157
285
  lastExtractionError = {
158
286
  code: "unsupported_platform",
159
287
  message: "Chrome auto-extraction is only available on macOS.",
@@ -162,68 +290,51 @@ function extractFromChromeInternal() {
162
290
  return null;
163
291
  }
164
292
 
293
+ // Extract cookie from Chrome's encrypted cookie database
294
+ let cookie;
165
295
  try {
166
- // Extract cookie
167
- const cookieScript = `
168
- tell application "Google Chrome"
169
- repeat with w in windows
170
- repeat with t in tabs of w
171
- if URL of t contains "slack.com" then
172
- return execute t javascript "document.cookie.split('; ').find(c => c.startsWith('d='))?.split('=')[1] || ''"
173
- end if
174
- end repeat
175
- end repeat
176
- return ""
177
- end tell
178
- `;
179
- const cookie = execSync(`osascript -e '${cookieScript.replace(/'/g, "'\"'\"'")}'`, {
180
- encoding: 'utf-8', timeout: 5000
181
- }).trim();
182
-
183
- if (!cookie || !cookie.startsWith('xoxd-')) {
184
- lastExtractionError = {
185
- code: "cookie_not_found",
186
- message: "Could not extract Slack cookie from Chrome.",
187
- detail: "Ensure a logged-in Slack tab is open at app.slack.com."
188
- };
189
- return null;
190
- }
296
+ cookie = extractCookieFromChromeDB();
297
+ } catch (e) {
298
+ lastExtractionError = normalizeExtractionError(e);
299
+ return null;
300
+ }
301
+
302
+ if (!cookie) {
303
+ lastExtractionError = {
304
+ code: "cookie_not_found",
305
+ message: "Could not extract Slack session cookie from Chrome.",
306
+ detail: "Ensure you are logged into Slack at app.slack.com in Chrome."
307
+ };
308
+ return null;
309
+ }
191
310
 
192
- // Try multiple token extraction paths
193
- const tokenPathsJS = SLACK_TOKEN_PATHS.map((path, i) =>
194
- `try { var t${i} = ${path}; if (t${i}?.startsWith('xoxc-')) return t${i}; } catch(e) {}`
195
- ).join(' ');
196
-
197
- const tokenScript = `
198
- tell application "Google Chrome"
199
- repeat with w in windows
200
- repeat with t in tabs of w
201
- if URL of t contains "slack.com" then
202
- return execute t javascript "(function() { ${tokenPathsJS} return ''; })()"
203
- end if
204
- end repeat
205
- end repeat
206
- return ""
207
- end tell
208
- `;
209
- const token = execSync(`osascript -e '${tokenScript.replace(/'/g, "'\"'\"'")}'`, {
210
- encoding: 'utf-8', timeout: 5000
211
- }).trim();
212
-
213
- if (!token || !token.startsWith('xoxc-')) {
311
+ // Extract token via AppleScript (localStorage)
312
+ let token;
313
+ try {
314
+ token = extractTokenFromChrome();
315
+ } catch (e) {
316
+ lastExtractionError = normalizeExtractionError(e);
317
+ // If we got the cookie but not the token, give a specific error
318
+ if (cookie && !token) {
214
319
  lastExtractionError = {
215
- code: "token_not_found",
216
- message: "Could not extract Slack token from Chrome.",
217
- detail: "Refresh Slack in Chrome and retry extraction."
320
+ code: "apple_events_javascript_disabled",
321
+ message: "Cookie extracted, but token extraction requires a Chrome setting.",
322
+ detail: "In Chrome: View > Developer > Allow JavaScript from Apple Events. Then retry."
218
323
  };
219
- return null;
220
324
  }
325
+ return null;
326
+ }
221
327
 
222
- return { token, cookie };
223
- } catch (e) {
224
- lastExtractionError = normalizeExtractionError(e);
328
+ if (!token) {
329
+ lastExtractionError = {
330
+ code: "token_not_found",
331
+ message: "Could not extract Slack token from Chrome.",
332
+ detail: "Ensure a Slack workspace is open in Chrome (not just the landing page). If Chrome blocks AppleScript, enable View > Developer > Allow JavaScript from Apple Events."
333
+ };
225
334
  return null;
226
335
  }
336
+
337
+ return { token, cookie };
227
338
  }
228
339
 
229
340
  /**
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.5",
5
- "description": "Claude-first Slack MCP for self-host or managed Cloud, with Gemini CLI, deployment review, and secure-default HTTP.",
4
+ "version": "4.1.0",
5
+ "description": "Slack MCP without OAuth no app registration, no admin approval. Works with Claude Code, Cursor, Copilot (where the official server doesn't). 16 tools, one command.",
6
6
  "type": "module",
7
7
  "main": "src/server.js",
8
8
  "bin": {
@@ -30,11 +30,8 @@
30
30
  "build:demo-mobile:gif": "node scripts/build-mobile-demo.js --gif",
31
31
  "record-demo": "node scripts/record-demo.js",
32
32
  "social-preview:update": "node scripts/update-github-social-preview.js --headed",
33
+ "bookmarklet": "node scripts/generate-bookmarklet.js",
33
34
  "cf:browser": "node scripts/cloudflare-browser-tool.js",
34
- "metrics:release-health": "node scripts/collect-release-health.js",
35
- "metrics:release-health:delta": "node scripts/build-release-health-delta.js",
36
- "impact:push:v3": "node scripts/impact-push-v3.js --dry-run",
37
- "impact:push:v3:apply": "node scripts/impact-push-v3.js --apply",
38
35
  "verify:attribution-guardrail": "node scripts/verify-attribution-guardrail.js",
39
36
  "verify:public-pages": "node scripts/verify-generated-public-pages.js",
40
37
  "verify:version-parity": "node scripts/check-version-parity.js",
@@ -65,7 +62,14 @@
65
62
  "slack-integration",
66
63
  "ai-agents",
67
64
  "automation",
68
- "productivity"
65
+ "productivity",
66
+ "oauth-free",
67
+ "no-admin-approval",
68
+ "cursor",
69
+ "copilot",
70
+ "windsurf",
71
+ "codex-cli",
72
+ "stealth-mode"
69
73
  ],
70
74
  "author": {
71
75
  "name": "Revasser",
package/public/index.html CHANGED
@@ -255,7 +255,7 @@
255
255
  <div class="container">
256
256
  <h1>Slack Web API <span id="status" class="status"></span></h1>
257
257
  <div style="background:rgba(240,194,70,0.08);border:1px solid rgba(240,194,70,0.2);border-radius:8px;padding:8px 14px;margin-bottom:16px;display:flex;align-items:center;justify-content:space-between;flex-wrap:wrap;gap:8px;font-size:13px;color:#d4c48a">
258
- <span>Skip local setup? <strong style="color:#f0c246">Slack MCP Cloud</strong> gives you 15 managed tools with one URL. Team adds 3 AI workflows.</span>
258
+ <span>Hosted version coming soon — <strong style="color:#f0c246">permanent OAuth, semantic search, AI summaries</strong></span>
259
259
  <a href="https://mcp.revasserlabs.com" style="color:#f0c246;font-weight:600;text-decoration:none;white-space:nowrap" target="_blank">Learn more &rarr;</a>
260
260
  </div>
261
261
  <div class="grid">
package/public/share.html CHANGED
@@ -4,17 +4,17 @@
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. Self-host locally or use the managed Cloud path with Gemini CLI support, pricing, security/procurement review, deployment review, and hosted credentials.">
7
+ <meta name="description" content="No OAuth. No admin. 16 Slack tools for Claude, Cursor, Copilot, Gemini, and any MCP client. One command: npx -y @jtalk22/slack-mcp --setup">
8
8
  <meta property="og:type" content="website">
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, Gemini CLI support, security/procurement review, deployment review, and support.">
9
+ <meta property="og:title" content="Slack MCP Server — No OAuth, no admin, just your browser session">
10
+ <meta property="og:description" content="Slack's official MCP needs OAuth + admin. This one uses your browser session. 16 tools, works with Claude, Cursor, Copilot, Gemini.">
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
- <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, Gemini CLI support, security/procurement review, deployment review, and support.">
16
+ <meta name="twitter:title" content="Slack MCP Server — No OAuth, no admin, just your browser session">
17
+ <meta name="twitter:description" content="16 tools for Claude, Cursor, Copilot, Gemini. npx -y @jtalk22/slack-mcp --setup">
18
18
  <meta name="twitter:image" content="https://jtalk22.github.io/slack-mcp-server/docs/images/social-preview-v3.png">
19
19
  <link rel="icon" href="https://jtalk22.github.io/slack-mcp-server/docs/assets/icon-512.png" type="image/png">
20
20
  <style>
@@ -107,7 +107,7 @@
107
107
  <body>
108
108
  <main class="wrap">
109
109
  <h1>Slack MCP Server</h1>
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
+ <p class="sub">Give Claude full access to your Slack. Self-host 16 tools for free. Hosted version with semantic search, AI summaries, and permanent OAuth coming soon.</p>
111
111
 
112
112
  <a class="preview" href="https://github.com/jtalk22/slack-mcp-server" rel="noopener">
113
113
  <img src="https://jtalk22.github.io/slack-mcp-server/docs/images/social-preview-v3.png" alt="Slack MCP Server social preview card">
@@ -117,22 +117,13 @@
117
117
  <a href="https://github.com/jtalk22/slack-mcp-server/blob/main/docs/SETUP.md" rel="noopener">Install (`--setup`)</a>
118
118
  <a href="https://github.com/jtalk22/slack-mcp-server/blob/main/docs/SETUP.md" rel="noopener">Verify (`--version/--doctor/--status`)</a>
119
119
  <a href="https://github.com/jtalk22/slack-mcp-server/releases/latest" rel="noopener">Latest Release</a>
120
- <a href="https://mcp.revasserlabs.com/pricing?utm_source=github&utm_medium=pages&utm_campaign=slack_mcp_cloud" rel="noopener">Pricing</a>
121
- <a href="https://mcp.revasserlabs.com/workflows?utm_source=github&utm_medium=pages&utm_campaign=slack_mcp_cloud" rel="noopener">Workflows</a>
122
- <a href="https://mcp.revasserlabs.com/gemini-cli?utm_source=github&utm_medium=pages&utm_campaign=slack_mcp_cloud" rel="noopener">Gemini CLI</a>
123
- <a href="https://mcp.revasserlabs.com/docs?utm_source=github&utm_medium=pages&utm_campaign=slack_mcp_cloud" rel="noopener">Cloud Docs</a>
124
- <a href="https://mcp.revasserlabs.com/security?utm_source=github&utm_medium=pages&utm_campaign=slack_mcp_cloud" rel="noopener">Security</a>
125
- <a href="https://mcp.revasserlabs.com/procurement?utm_source=github&utm_medium=pages&utm_campaign=slack_mcp_cloud" rel="noopener">Procurement</a>
126
- <a href="https://mcp.revasserlabs.com/deployment?utm_source=github&utm_medium=pages&utm_campaign=slack_mcp_cloud" rel="noopener">Deployment Review</a>
127
- <a href="https://mcp.revasserlabs.com/support?utm_source=github&utm_medium=pages&utm_campaign=slack_mcp_cloud" rel="noopener">Cloud Support</a>
128
- <a href="https://mcp.revasserlabs.com/use-cases/support-triage?utm_source=github&utm_medium=pages&utm_campaign=slack_mcp_cloud" rel="noopener">Support Triage Use Case</a>
129
120
  <a href="https://jtalk22.github.io/slack-mcp-server/" rel="noopener">Autoplay Demo Landing</a>
130
- <a href="https://jtalk22.github.io/slack-mcp-server/docs/videos/demo-claude-mobile-20s.mp4" rel="noopener">20s Mobile Clip</a>
121
+ <a href="https://jtalk22.github.io/slack-mcp-server/docs/videos/demo-slack-mcp-mobile-20s.mp4" rel="noopener">20s Mobile Clip</a>
131
122
  <a href="https://www.npmjs.com/package/@jtalk22/slack-mcp" rel="noopener">npm Package</a>
132
- <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>
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">Hosted</a>
133
124
  </div>
134
125
 
135
- <p class="note"><strong>Verify in 30 seconds:</strong> <code>--version</code>, <code>--doctor</code>, <code>--status</code>. Self-host gives 16 tools and full operator control. Cloud starts at $19/mo for 15 managed tools, deployment review, procurement-ready security, readiness guidance, and support. Team at $49/mo adds 3 AI workflows. Claude is the primary path; Gemini CLI is supported on the hosted endpoint.</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. Hosted version coming soon at <a href="https://mcp.revasserlabs.com">mcp.revasserlabs.com</a>.</p>
136
127
  </main>
137
128
  </body>
138
129
  </html>