@jtalk22/slack-mcp 1.2.2 → 1.2.4
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 +68 -7
- package/docs/API.md +11 -4
- package/docs/COMMUNICATION-STYLE.md +15 -7
- package/docs/COMPATIBILITY.md +19 -0
- package/docs/HN-LAUNCH.md +63 -0
- package/docs/INDEX.md +31 -0
- package/docs/RELEASE-HEALTH.md +66 -0
- package/docs/SETUP.md +22 -2
- package/docs/TROUBLESHOOTING.md +32 -3
- package/docs/WEB-API.md +12 -1
- package/docs/release-health/2026-02-25.md +33 -0
- package/docs/release-health/2026-02-26.md +33 -0
- package/docs/release-health/24h-delta.md +21 -0
- package/docs/release-health/24h-end.md +33 -0
- package/docs/release-health/24h-start.md +33 -0
- package/docs/release-health/latest.md +33 -0
- package/lib/handlers.js +113 -85
- package/lib/slack-client.js +20 -16
- package/lib/token-store.js +103 -30
- package/package.json +12 -33
- package/public/demo-claude.html +10 -10
- package/public/demo-video.html +9 -9
- package/public/demo.html +8 -8
- package/scripts/build-release-health-delta.js +201 -0
- package/scripts/check-public-language.sh +25 -0
- package/scripts/collect-release-health.js +150 -0
- package/scripts/setup-wizard.js +161 -39
- package/scripts/token-cli.js +6 -4
- package/scripts/verify-install-flow.js +157 -0
- package/src/cli.js +1 -0
- package/src/server-http.js +1 -1
- package/src/server.js +5 -5
- package/src/web-server.js +6 -6
package/scripts/setup-wizard.js
CHANGED
|
@@ -12,11 +12,19 @@
|
|
|
12
12
|
|
|
13
13
|
import { platform } from "os";
|
|
14
14
|
import * as readline from "readline";
|
|
15
|
-
import {
|
|
16
|
-
|
|
15
|
+
import {
|
|
16
|
+
saveTokens,
|
|
17
|
+
extractFromChrome,
|
|
18
|
+
getLastExtractionError,
|
|
19
|
+
isAutoRefreshAvailable,
|
|
20
|
+
TOKEN_FILE,
|
|
21
|
+
getFromFile,
|
|
22
|
+
getFromKeychain,
|
|
23
|
+
} from "../lib/token-store.js";
|
|
17
24
|
|
|
18
25
|
const IS_MACOS = platform() === 'darwin';
|
|
19
|
-
const VERSION = "1.2.
|
|
26
|
+
const VERSION = "1.2.4";
|
|
27
|
+
const MIN_NODE_MAJOR = 20;
|
|
20
28
|
|
|
21
29
|
// ANSI colors
|
|
22
30
|
const colors = {
|
|
@@ -69,27 +77,25 @@ async function pressEnterToContinue(rl) {
|
|
|
69
77
|
}
|
|
70
78
|
|
|
71
79
|
async function validateTokens(token, cookie) {
|
|
72
|
-
const hadOldToken = Object.prototype.hasOwnProperty.call(process.env, "SLACK_TOKEN");
|
|
73
|
-
const hadOldCookie = Object.prototype.hasOwnProperty.call(process.env, "SLACK_COOKIE");
|
|
74
|
-
const oldToken = process.env.SLACK_TOKEN;
|
|
75
|
-
const oldCookie = process.env.SLACK_COOKIE;
|
|
76
|
-
|
|
77
|
-
// Temporarily set env vars for validation
|
|
78
|
-
process.env.SLACK_TOKEN = token;
|
|
79
|
-
process.env.SLACK_COOKIE = cookie;
|
|
80
|
-
|
|
81
80
|
try {
|
|
82
|
-
const
|
|
83
|
-
|
|
81
|
+
const response = await fetch("https://slack.com/api/auth.test", {
|
|
82
|
+
method: "POST",
|
|
83
|
+
headers: {
|
|
84
|
+
"Authorization": `Bearer ${token}`,
|
|
85
|
+
"Cookie": `d=${cookie}`,
|
|
86
|
+
"Content-Type": "application/json; charset=utf-8",
|
|
87
|
+
},
|
|
88
|
+
body: JSON.stringify({}),
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
const result = await response.json();
|
|
92
|
+
if (!result.ok) {
|
|
93
|
+
return { valid: false, error: result.error || "auth.test_failed" };
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
return { valid: true, user: result.user, team: result.team, userId: result.user_id };
|
|
84
97
|
} catch (e) {
|
|
85
98
|
return { valid: false, error: e.message };
|
|
86
|
-
} finally {
|
|
87
|
-
// Always restore prior process env state
|
|
88
|
-
if (hadOldToken) process.env.SLACK_TOKEN = oldToken;
|
|
89
|
-
else delete process.env.SLACK_TOKEN;
|
|
90
|
-
|
|
91
|
-
if (hadOldCookie) process.env.SLACK_COOKIE = oldCookie;
|
|
92
|
-
else delete process.env.SLACK_COOKIE;
|
|
93
99
|
}
|
|
94
100
|
}
|
|
95
101
|
|
|
@@ -112,13 +118,27 @@ async function runMacOSSetup(rl) {
|
|
|
112
118
|
const tokens = extractFromChrome();
|
|
113
119
|
|
|
114
120
|
if (!tokens) {
|
|
121
|
+
const extractionError = getLastExtractionError();
|
|
115
122
|
print();
|
|
116
123
|
error("Could not extract tokens from Chrome.");
|
|
124
|
+
if (extractionError) {
|
|
125
|
+
print(`Reason: ${extractionError.message}`);
|
|
126
|
+
if (extractionError.detail) {
|
|
127
|
+
print(`Detail: ${extractionError.detail}`);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
117
130
|
print();
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
131
|
+
if (extractionError?.code === "apple_events_javascript_disabled") {
|
|
132
|
+
print("Fix and retry:");
|
|
133
|
+
print(" 1. In Chrome menu: View > Developer > Allow JavaScript from Apple Events");
|
|
134
|
+
print(" 2. Keep Slack open in a Chrome tab (app.slack.com)");
|
|
135
|
+
print(" 3. Re-run: npx -y @jtalk22/slack-mcp --setup");
|
|
136
|
+
} else {
|
|
137
|
+
print("Make sure:");
|
|
138
|
+
print(" 1. Chrome is running");
|
|
139
|
+
print(" 2. You have a Slack tab open (app.slack.com)");
|
|
140
|
+
print(" 3. You're logged into that workspace");
|
|
141
|
+
}
|
|
122
142
|
print();
|
|
123
143
|
|
|
124
144
|
const retry = await question(rl, "Try manual entry instead? (y/n): ");
|
|
@@ -227,7 +247,7 @@ async function runManualSetup(rl) {
|
|
|
227
247
|
}
|
|
228
248
|
|
|
229
249
|
async function showStatus() {
|
|
230
|
-
const creds =
|
|
250
|
+
const creds = getDoctorCredentials();
|
|
231
251
|
|
|
232
252
|
if (!creds) {
|
|
233
253
|
error("No tokens found");
|
|
@@ -237,29 +257,126 @@ async function showStatus() {
|
|
|
237
257
|
}
|
|
238
258
|
|
|
239
259
|
print(`Token source: ${creds.source}`);
|
|
240
|
-
|
|
260
|
+
if (creds.path) {
|
|
261
|
+
print(`Token file: ${creds.path}`);
|
|
262
|
+
}
|
|
241
263
|
if (creds.updatedAt) {
|
|
242
264
|
print(`Last updated: ${creds.updatedAt}`);
|
|
243
265
|
}
|
|
244
266
|
print();
|
|
245
267
|
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
process.env.SLACK_TOKEN = creds.token;
|
|
249
|
-
process.env.SLACK_COOKIE = creds.cookie;
|
|
250
|
-
|
|
251
|
-
const result = await slackAPI("auth.test", {});
|
|
252
|
-
success("Status: VALID");
|
|
253
|
-
print(`User: ${result.user}`);
|
|
254
|
-
print(`Team: ${result.team}`);
|
|
255
|
-
print(`User ID: ${result.user_id}`);
|
|
256
|
-
} catch (e) {
|
|
268
|
+
const result = await validateTokens(creds.token, creds.cookie);
|
|
269
|
+
if (!result.valid) {
|
|
257
270
|
error("Status: INVALID");
|
|
258
|
-
print(`Error: ${
|
|
271
|
+
print(`Error: ${result.error}`);
|
|
259
272
|
print();
|
|
260
273
|
print("Run setup wizard to refresh: npx -y @jtalk22/slack-mcp --setup");
|
|
261
274
|
process.exit(1);
|
|
262
275
|
}
|
|
276
|
+
|
|
277
|
+
success("Status: VALID");
|
|
278
|
+
print(`User: ${result.user}`);
|
|
279
|
+
print(`Team: ${result.team}`);
|
|
280
|
+
print(`User ID: ${result.userId}`);
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
function getDoctorCredentials() {
|
|
284
|
+
if (process.env.SLACK_TOKEN && process.env.SLACK_COOKIE) {
|
|
285
|
+
return { token: process.env.SLACK_TOKEN, cookie: process.env.SLACK_COOKIE, source: "environment" };
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
const fileTokens = getFromFile();
|
|
289
|
+
if (fileTokens?.token && fileTokens?.cookie) {
|
|
290
|
+
return {
|
|
291
|
+
token: fileTokens.token,
|
|
292
|
+
cookie: fileTokens.cookie,
|
|
293
|
+
source: "file",
|
|
294
|
+
path: TOKEN_FILE,
|
|
295
|
+
updatedAt: fileTokens.updatedAt,
|
|
296
|
+
};
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
if (IS_MACOS) {
|
|
300
|
+
const keychainToken = getFromKeychain("token");
|
|
301
|
+
const keychainCookie = getFromKeychain("cookie");
|
|
302
|
+
if (keychainToken && keychainCookie) {
|
|
303
|
+
return { token: keychainToken, cookie: keychainCookie, source: "keychain" };
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
return null;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
function classifyAuthError(rawError) {
|
|
311
|
+
const msg = String(rawError || "").toLowerCase();
|
|
312
|
+
if (
|
|
313
|
+
msg.includes("invalid_auth") ||
|
|
314
|
+
msg.includes("token_expired") ||
|
|
315
|
+
msg.includes("not_authed") ||
|
|
316
|
+
msg.includes("account_inactive")
|
|
317
|
+
) {
|
|
318
|
+
return 2;
|
|
319
|
+
}
|
|
320
|
+
return 3;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
function parseNodeMajor() {
|
|
324
|
+
return Number.parseInt(process.versions.node.split(".")[0], 10);
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
async function runDoctor() {
|
|
328
|
+
print(`${colors.bold}slack-mcp-server doctor${colors.reset}`);
|
|
329
|
+
print();
|
|
330
|
+
|
|
331
|
+
const nodeMajor = parseNodeMajor();
|
|
332
|
+
if (Number.isNaN(nodeMajor) || nodeMajor < MIN_NODE_MAJOR) {
|
|
333
|
+
error(`Node.js ${process.versions.node} detected (requires Node ${MIN_NODE_MAJOR}+)`);
|
|
334
|
+
print();
|
|
335
|
+
print("Next action:");
|
|
336
|
+
print(` npx -y @jtalk22/slack-mcp --doctor # rerun after upgrading Node ${MIN_NODE_MAJOR}+`);
|
|
337
|
+
process.exit(3);
|
|
338
|
+
}
|
|
339
|
+
success(`Node.js ${process.versions.node} (supported)`);
|
|
340
|
+
|
|
341
|
+
const creds = getDoctorCredentials();
|
|
342
|
+
if (!creds) {
|
|
343
|
+
error("Credentials: not found");
|
|
344
|
+
print();
|
|
345
|
+
print("Next action:");
|
|
346
|
+
print(" npx -y @jtalk22/slack-mcp --setup");
|
|
347
|
+
process.exit(1);
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
success(`Credentials loaded from: ${creds.source}`);
|
|
351
|
+
if (creds.path) {
|
|
352
|
+
print(`Path: ${creds.path}`);
|
|
353
|
+
}
|
|
354
|
+
if (creds.updatedAt) {
|
|
355
|
+
print(`Last updated: ${creds.updatedAt}`);
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
print();
|
|
359
|
+
print("Validating Slack auth...");
|
|
360
|
+
const validation = await validateTokens(creds.token, creds.cookie);
|
|
361
|
+
if (!validation.valid) {
|
|
362
|
+
const exitCode = classifyAuthError(validation.error);
|
|
363
|
+
error(`Slack auth failed: ${validation.error}`);
|
|
364
|
+
print();
|
|
365
|
+
print("Next action:");
|
|
366
|
+
if (exitCode === 2) {
|
|
367
|
+
print(" npx -y @jtalk22/slack-mcp --setup");
|
|
368
|
+
} else {
|
|
369
|
+
print(" Check network connectivity and retry:");
|
|
370
|
+
print(" npx -y @jtalk22/slack-mcp --doctor");
|
|
371
|
+
}
|
|
372
|
+
process.exit(exitCode);
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
success(`Slack auth valid for ${validation.user} @ ${validation.team}`);
|
|
376
|
+
print();
|
|
377
|
+
print("Ready. Next command:");
|
|
378
|
+
print(" npx -y @jtalk22/slack-mcp");
|
|
379
|
+
process.exit(0);
|
|
263
380
|
}
|
|
264
381
|
|
|
265
382
|
async function showHelp() {
|
|
@@ -271,6 +388,7 @@ async function showHelp() {
|
|
|
271
388
|
print(" npx -y @jtalk22/slack-mcp Start MCP server (stdio)");
|
|
272
389
|
print(" npx -y @jtalk22/slack-mcp --setup Interactive token setup wizard");
|
|
273
390
|
print(" npx -y @jtalk22/slack-mcp --status Check token health");
|
|
391
|
+
print(" npx -y @jtalk22/slack-mcp --doctor Run runtime and auth diagnostics");
|
|
274
392
|
print(" npx -y @jtalk22/slack-mcp --version Print version");
|
|
275
393
|
print(" npx -y @jtalk22/slack-mcp --help Show this help");
|
|
276
394
|
print();
|
|
@@ -296,6 +414,10 @@ async function main() {
|
|
|
296
414
|
case 'status':
|
|
297
415
|
await showStatus();
|
|
298
416
|
return;
|
|
417
|
+
case '--doctor':
|
|
418
|
+
case 'doctor':
|
|
419
|
+
await runDoctor();
|
|
420
|
+
return;
|
|
299
421
|
case '--version':
|
|
300
422
|
case '-v':
|
|
301
423
|
print(`slack-mcp-server v${VERSION}`);
|
package/scripts/token-cli.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* Token CLI - Manage Slack tokens
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import {
|
|
6
|
+
import { loadTokensReadOnly, saveTokens, extractFromChrome, getFromFile, TOKEN_FILE, KEYCHAIN_SERVICE } from "../lib/token-store.js";
|
|
7
7
|
import { slackAPI } from "../lib/slack-client.js";
|
|
8
8
|
import * as readline from "readline";
|
|
9
9
|
|
|
@@ -35,7 +35,7 @@ async function main() {
|
|
|
35
35
|
}
|
|
36
36
|
|
|
37
37
|
async function showStatus() {
|
|
38
|
-
const creds =
|
|
38
|
+
const creds = loadTokensReadOnly();
|
|
39
39
|
if (!creds) {
|
|
40
40
|
console.log("No tokens found");
|
|
41
41
|
console.log("");
|
|
@@ -46,11 +46,13 @@ async function showStatus() {
|
|
|
46
46
|
}
|
|
47
47
|
|
|
48
48
|
console.log("Token source:", creds.source);
|
|
49
|
-
|
|
49
|
+
if (creds.source === "file") {
|
|
50
|
+
console.log("Token file:", TOKEN_FILE);
|
|
51
|
+
}
|
|
50
52
|
console.log("");
|
|
51
53
|
|
|
52
54
|
try {
|
|
53
|
-
const result = await slackAPI("auth.test", {});
|
|
55
|
+
const result = await slackAPI("auth.test", {}, { retryOnAuthFail: false });
|
|
54
56
|
console.log("Status: VALID");
|
|
55
57
|
console.log("User:", result.user);
|
|
56
58
|
console.log("Team:", result.team);
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { spawnSync } from "node:child_process";
|
|
4
|
+
import { mkdtempSync, rmSync } from "node:fs";
|
|
5
|
+
import { tmpdir } from "node:os";
|
|
6
|
+
import { dirname, join } from "node:path";
|
|
7
|
+
import { fileURLToPath } from "node:url";
|
|
8
|
+
|
|
9
|
+
const PKG = "@jtalk22/slack-mcp";
|
|
10
|
+
const repoRoot = join(dirname(fileURLToPath(import.meta.url)), "..");
|
|
11
|
+
|
|
12
|
+
function runNpx(args, options = {}) {
|
|
13
|
+
const cmdArgs = ["-y", PKG, ...args];
|
|
14
|
+
const result = spawnSync("npx", cmdArgs, {
|
|
15
|
+
cwd: options.cwd,
|
|
16
|
+
env: options.env,
|
|
17
|
+
encoding: "utf8",
|
|
18
|
+
timeout: 120000,
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
return {
|
|
22
|
+
args: cmdArgs.join(" "),
|
|
23
|
+
status: result.status,
|
|
24
|
+
stdout: (result.stdout || "").trim(),
|
|
25
|
+
stderr: (result.stderr || "").trim(),
|
|
26
|
+
error: result.error,
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function assert(condition, message, details = "") {
|
|
31
|
+
if (!condition) {
|
|
32
|
+
const suffix = details ? `\n${details}` : "";
|
|
33
|
+
throw new Error(`${message}${suffix}`);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function printResult(label, result) {
|
|
38
|
+
console.log(`\n[${label}] npx ${result.args}`);
|
|
39
|
+
console.log(`exit=${result.status}`);
|
|
40
|
+
if (result.stdout) {
|
|
41
|
+
console.log("stdout:");
|
|
42
|
+
console.log(result.stdout);
|
|
43
|
+
}
|
|
44
|
+
if (result.stderr) {
|
|
45
|
+
console.log("stderr:");
|
|
46
|
+
console.log(result.stderr);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function runLocalSetupStatus(options = {}) {
|
|
51
|
+
const result = spawnSync("node", [join(repoRoot, "scripts/setup-wizard.js"), "--status"], {
|
|
52
|
+
cwd: repoRoot,
|
|
53
|
+
env: options.env,
|
|
54
|
+
encoding: "utf8",
|
|
55
|
+
timeout: 120000,
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
return {
|
|
59
|
+
args: "node scripts/setup-wizard.js --status",
|
|
60
|
+
status: result.status,
|
|
61
|
+
stdout: (result.stdout || "").trim(),
|
|
62
|
+
stderr: (result.stderr || "").trim(),
|
|
63
|
+
error: result.error,
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function runLocalDoctor(options = {}) {
|
|
68
|
+
const result = spawnSync("node", [join(repoRoot, "scripts/setup-wizard.js"), "--doctor"], {
|
|
69
|
+
cwd: repoRoot,
|
|
70
|
+
env: options.env,
|
|
71
|
+
encoding: "utf8",
|
|
72
|
+
timeout: 120000,
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
return {
|
|
76
|
+
args: "node scripts/setup-wizard.js --doctor",
|
|
77
|
+
status: result.status,
|
|
78
|
+
stdout: (result.stdout || "").trim(),
|
|
79
|
+
stderr: (result.stderr || "").trim(),
|
|
80
|
+
error: result.error,
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function main() {
|
|
85
|
+
const testHome = mkdtempSync(join(tmpdir(), "slack-mcp-install-check-"));
|
|
86
|
+
|
|
87
|
+
// Force a clean environment so --status reflects missing credentials.
|
|
88
|
+
const env = { ...process.env, HOME: testHome, USERPROFILE: testHome };
|
|
89
|
+
delete env.SLACK_TOKEN;
|
|
90
|
+
delete env.SLACK_COOKIE;
|
|
91
|
+
|
|
92
|
+
try {
|
|
93
|
+
const versionResult = runNpx(["--version"], { cwd: testHome, env });
|
|
94
|
+
printResult("version", versionResult);
|
|
95
|
+
assert(
|
|
96
|
+
versionResult.status === 0,
|
|
97
|
+
"Expected --version to exit 0",
|
|
98
|
+
versionResult.stderr || versionResult.stdout,
|
|
99
|
+
);
|
|
100
|
+
|
|
101
|
+
const helpResult = runNpx(["--help"], { cwd: testHome, env });
|
|
102
|
+
printResult("help", helpResult);
|
|
103
|
+
assert(
|
|
104
|
+
helpResult.status === 0,
|
|
105
|
+
"Expected --help to exit 0",
|
|
106
|
+
helpResult.stderr || helpResult.stdout,
|
|
107
|
+
);
|
|
108
|
+
|
|
109
|
+
const statusResult = runNpx(["--status"], { cwd: testHome, env });
|
|
110
|
+
printResult("status", statusResult);
|
|
111
|
+
assert(
|
|
112
|
+
statusResult.status !== 0,
|
|
113
|
+
"Expected --status to exit non-zero when credentials are missing",
|
|
114
|
+
statusResult.stderr || statusResult.stdout,
|
|
115
|
+
);
|
|
116
|
+
|
|
117
|
+
const localStatusResult = runLocalSetupStatus({ env });
|
|
118
|
+
printResult("local-status", localStatusResult);
|
|
119
|
+
assert(
|
|
120
|
+
localStatusResult.status !== 0,
|
|
121
|
+
"Expected local --status to exit non-zero when credentials are missing",
|
|
122
|
+
localStatusResult.stderr || localStatusResult.stdout,
|
|
123
|
+
);
|
|
124
|
+
assert(
|
|
125
|
+
!localStatusResult.stderr.includes("Attempting Chrome auto-extraction"),
|
|
126
|
+
"Expected local --status to be read-only without auto-extraction side effects",
|
|
127
|
+
localStatusResult.stderr,
|
|
128
|
+
);
|
|
129
|
+
|
|
130
|
+
const localDoctorMissingResult = runLocalDoctor({ env });
|
|
131
|
+
printResult("local-doctor-missing", localDoctorMissingResult);
|
|
132
|
+
assert(
|
|
133
|
+
localDoctorMissingResult.status === 1,
|
|
134
|
+
"Expected local --doctor to exit 1 when credentials are missing",
|
|
135
|
+
localDoctorMissingResult.stderr || localDoctorMissingResult.stdout,
|
|
136
|
+
);
|
|
137
|
+
|
|
138
|
+
const invalidEnv = {
|
|
139
|
+
...env,
|
|
140
|
+
SLACK_TOKEN: "xoxc-invalid-token",
|
|
141
|
+
SLACK_COOKIE: "xoxd-invalid-cookie",
|
|
142
|
+
};
|
|
143
|
+
const localDoctorInvalidResult = runLocalDoctor({ env: invalidEnv });
|
|
144
|
+
printResult("local-doctor-invalid", localDoctorInvalidResult);
|
|
145
|
+
assert(
|
|
146
|
+
localDoctorInvalidResult.status === 2,
|
|
147
|
+
"Expected local --doctor to exit 2 when credentials are invalid",
|
|
148
|
+
localDoctorInvalidResult.stderr || localDoctorInvalidResult.stdout,
|
|
149
|
+
);
|
|
150
|
+
|
|
151
|
+
console.log("\nInstall flow verification passed.");
|
|
152
|
+
} finally {
|
|
153
|
+
rmSync(testHome, { recursive: true, force: true });
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
main();
|
package/src/cli.js
CHANGED
package/src/server-http.js
CHANGED
package/src/server.js
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
* - Network error retry with exponential backoff
|
|
12
12
|
* - Background token health monitoring
|
|
13
13
|
*
|
|
14
|
-
* @version 1.2.
|
|
14
|
+
* @version 1.2.4
|
|
15
15
|
*/
|
|
16
16
|
|
|
17
17
|
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
@@ -25,7 +25,7 @@ import {
|
|
|
25
25
|
ReadResourceRequestSchema,
|
|
26
26
|
} from "@modelcontextprotocol/sdk/types.js";
|
|
27
27
|
|
|
28
|
-
import {
|
|
28
|
+
import { loadTokensReadOnly } from "../lib/token-store.js";
|
|
29
29
|
import { checkTokenHealth } from "../lib/slack-client.js";
|
|
30
30
|
import { TOOLS } from "../lib/tools.js";
|
|
31
31
|
import {
|
|
@@ -47,7 +47,7 @@ const BACKGROUND_REFRESH_INTERVAL = 4 * 60 * 60 * 1000;
|
|
|
47
47
|
|
|
48
48
|
// Package info
|
|
49
49
|
const SERVER_NAME = "slack-mcp-server";
|
|
50
|
-
const SERVER_VERSION = "1.2.
|
|
50
|
+
const SERVER_VERSION = "1.2.4";
|
|
51
51
|
|
|
52
52
|
// MCP Prompts - predefined prompt templates for common Slack operations
|
|
53
53
|
const PROMPTS = [
|
|
@@ -271,10 +271,10 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
271
271
|
// Main entry point
|
|
272
272
|
async function main() {
|
|
273
273
|
// Check for credentials at startup
|
|
274
|
-
const credentials =
|
|
274
|
+
const credentials = loadTokensReadOnly();
|
|
275
275
|
if (!credentials) {
|
|
276
276
|
console.error("WARNING: No Slack credentials found at startup");
|
|
277
|
-
console.error("
|
|
277
|
+
console.error("Use npx -y @jtalk22/slack-mcp --setup to configure credentials");
|
|
278
278
|
} else {
|
|
279
279
|
console.error(`Credentials loaded from: ${credentials.source}`);
|
|
280
280
|
|
package/src/web-server.js
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* Exposes Slack MCP tools as REST endpoints for browser access.
|
|
6
6
|
* Run alongside or instead of the MCP server for web-based access.
|
|
7
7
|
*
|
|
8
|
-
* @version 1.2.
|
|
8
|
+
* @version 1.2.4
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
11
|
import express from "express";
|
|
@@ -15,7 +15,7 @@ import { dirname, join } from "path";
|
|
|
15
15
|
import { existsSync, readFileSync, writeFileSync } from "fs";
|
|
16
16
|
import { execSync } from "child_process";
|
|
17
17
|
import { homedir } from "os";
|
|
18
|
-
import {
|
|
18
|
+
import { loadTokensReadOnly } from "../lib/token-store.js";
|
|
19
19
|
|
|
20
20
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
21
21
|
import {
|
|
@@ -116,7 +116,7 @@ function extractContent(result) {
|
|
|
116
116
|
app.get("/", (req, res) => {
|
|
117
117
|
res.json({
|
|
118
118
|
name: "Slack Web API Server",
|
|
119
|
-
version: "1.2.
|
|
119
|
+
version: "1.2.4",
|
|
120
120
|
status: "running",
|
|
121
121
|
endpoints: [
|
|
122
122
|
"GET /health",
|
|
@@ -283,10 +283,10 @@ app.get("/users/:id", authenticate, async (req, res) => {
|
|
|
283
283
|
// Start server
|
|
284
284
|
async function main() {
|
|
285
285
|
// Check for credentials
|
|
286
|
-
const credentials =
|
|
286
|
+
const credentials = loadTokensReadOnly();
|
|
287
287
|
if (!credentials) {
|
|
288
288
|
console.error("WARNING: No Slack credentials found");
|
|
289
|
-
console.error("Run:
|
|
289
|
+
console.error("Run: npx -y @jtalk22/slack-mcp --setup");
|
|
290
290
|
} else {
|
|
291
291
|
console.log(`Credentials loaded from: ${credentials.source}`);
|
|
292
292
|
}
|
|
@@ -295,7 +295,7 @@ async function main() {
|
|
|
295
295
|
app.listen(PORT, '127.0.0.1', () => {
|
|
296
296
|
// Print to stderr to keep logs clean (stdout reserved for JSON in some setups)
|
|
297
297
|
console.error(`\n${"═".repeat(60)}`);
|
|
298
|
-
console.error(` Slack Web API Server v1.2.
|
|
298
|
+
console.error(` Slack Web API Server v1.2.4`);
|
|
299
299
|
console.error(`${"═".repeat(60)}`);
|
|
300
300
|
console.error(`\n Dashboard: http://localhost:${PORT}/?key=${API_KEY}`);
|
|
301
301
|
console.error(`\n API Key: ${API_KEY}`);
|