@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.
- package/README.md +165 -57
- package/docs/SETUP.md +2 -34
- package/docs/TROUBLESHOOTING.md +2 -24
- package/lib/handlers.js +1 -1
- package/lib/public-metadata.js +0 -65
- package/lib/public-pages.js +35 -75
- package/lib/token-store.js +180 -69
- package/package.json +11 -7
- package/public/index.html +1 -1
- package/public/share.html +9 -18
- package/scripts/setup-wizard.js +74 -25
- package/server.json +3 -3
- package/src/server.js +1 -1
- package/src/web-server.js +18 -13
package/lib/public-pages.js
CHANGED
|
@@ -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
|
|
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="
|
|
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
|
|
43
|
-
<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>
|
|
47
|
-
<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="${
|
|
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">
|
|
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
|
|
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">
|
|
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
|
|
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="${
|
|
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.
|
|
131
|
-
CLOUD_WORKFLOWS_URL: PUBLIC_METADATA.
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
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.
|
|
141
|
-
CLOUD_ACCOUNT_URL: PUBLIC_METADATA.
|
|
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:
|
|
155
|
-
TEAM_AI_WORKFLOW_COUNT:
|
|
156
|
-
CLOUD_SOLO_PRICE:
|
|
157
|
-
CLOUD_TEAM_PRICE:
|
|
158
|
-
CLOUD_TURNKEY_LAUNCH_PRICE:
|
|
159
|
-
CLOUD_MANAGED_RELIABILITY_PRICE:
|
|
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-
|
|
137
|
+
"public/demo-slack-mcp.html": replaceTokens(template("demo-slack-mcp.html.tpl"), tokens),
|
|
178
138
|
};
|
|
179
139
|
}
|
package/lib/token-store.js
CHANGED
|
@@ -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 {
|
|
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 {
|
|
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
|
|
122
|
-
detail: "
|
|
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
|
|
130
|
-
detail: "Open Google Chrome with
|
|
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
|
|
151
|
-
*
|
|
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
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
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
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
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: "
|
|
216
|
-
message: "
|
|
217
|
-
detail: "
|
|
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
|
-
|
|
223
|
-
|
|
224
|
-
|
|
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": "
|
|
5
|
-
"description": "
|
|
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>
|
|
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 →</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="
|
|
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="
|
|
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="
|
|
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
|
|
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-
|
|
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">
|
|
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
|
|
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>
|