@jtalk22/slack-mcp 1.2.4 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. package/README.md +113 -61
  2. package/docs/CLOUDFLARE-BROWSER-TOOLKIT.md +67 -0
  3. package/docs/DEPLOYMENT-MODES.md +10 -3
  4. package/docs/HN-LAUNCH.md +45 -36
  5. package/docs/INDEX.md +9 -0
  6. package/docs/INSTALL-PROOF.md +18 -0
  7. package/docs/LAUNCH-COPY-v3.0.0.md +73 -0
  8. package/docs/LAUNCH-MATRIX.md +22 -0
  9. package/docs/LAUNCH-OPS.md +71 -0
  10. package/docs/RELEASE-HEALTH.md +24 -0
  11. package/docs/TROUBLESHOOTING.md +27 -0
  12. package/docs/WEB-API.md +13 -4
  13. package/docs/images/demo-channel-messages.png +0 -0
  14. package/docs/images/demo-channels.png +0 -0
  15. package/docs/images/demo-claude-mobile-360x800.png +0 -0
  16. package/docs/images/demo-claude-mobile-390x844.png +0 -0
  17. package/docs/images/demo-main-mobile-360x800.png +0 -0
  18. package/docs/images/demo-main-mobile-390x844.png +0 -0
  19. package/docs/images/demo-main.png +0 -0
  20. package/docs/images/demo-poster.png +0 -0
  21. package/docs/images/demo-sidebar.png +0 -0
  22. package/docs/images/web-api-mobile-360x800.png +0 -0
  23. package/docs/images/web-api-mobile-390x844.png +0 -0
  24. package/lib/handlers.js +14 -6
  25. package/lib/slack-client.js +17 -1
  26. package/package.json +28 -12
  27. package/public/demo-claude.html +83 -10
  28. package/public/demo-video.html +33 -4
  29. package/public/demo.html +136 -2
  30. package/public/index.html +132 -69
  31. package/scripts/capture-screenshots.js +103 -53
  32. package/scripts/check-version-parity.js +176 -0
  33. package/scripts/cloudflare-browser-tool.js +237 -0
  34. package/scripts/collect-release-health.js +1 -1
  35. package/scripts/record-demo.js +22 -9
  36. package/scripts/release-preflight.js +243 -0
  37. package/scripts/setup-wizard.js +12 -2
  38. package/scripts/verify-install-flow.js +38 -2
  39. package/scripts/verify-web.js +49 -1
  40. package/server.json +47 -0
  41. package/smithery.yaml +34 -0
  42. package/src/server-http.js +123 -8
  43. package/src/server.js +36 -8
  44. package/src/web-server.js +60 -20
  45. package/docs/images/demo-claude-v1.2.gif +0 -0
  46. package/docs/images/demo-readme.gif +0 -0
  47. package/docs/release-health/2026-02-25.md +0 -33
  48. package/docs/release-health/2026-02-26.md +0 -33
  49. package/docs/release-health/24h-delta.md +0 -21
  50. package/docs/release-health/24h-end.md +0 -33
  51. package/docs/release-health/24h-start.md +0 -33
  52. package/docs/release-health/latest.md +0 -33
  53. package/docs/videos/.gitkeep +0 -0
  54. package/docs/videos/demo-claude-v1.2.webm +0 -0
@@ -0,0 +1,243 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { spawnSync } from "child_process";
4
+ import { mkdirSync, writeFileSync } from "fs";
5
+ import { dirname, resolve } from "path";
6
+ import { fileURLToPath } from "url";
7
+
8
+ const __dirname = dirname(fileURLToPath(import.meta.url));
9
+ const ROOT = resolve(__dirname, "..");
10
+ const REPORT_PATH = resolve(ROOT, "docs", "release-health", "prepublish-dry-run.md");
11
+
12
+ const EXPECTED_NAME = process.env.EXPECTED_GIT_NAME || "jtalk22";
13
+ const EXPECTED_EMAIL = process.env.EXPECTED_GIT_EMAIL || "james@revasser.nyc";
14
+ const OWNER_RANGE = process.env.OWNER_CHECK_RANGE || "origin/main..HEAD";
15
+
16
+ function run(command, args = [], options = {}) {
17
+ return spawnSync(command, args, {
18
+ cwd: ROOT,
19
+ encoding: "utf8",
20
+ env: process.env,
21
+ maxBuffer: 20 * 1024 * 1024,
22
+ ...options
23
+ });
24
+ }
25
+
26
+ function trimOutput(text = "", maxChars = 1200) {
27
+ const normalized = String(text || "").trim();
28
+ if (!normalized) return "";
29
+ if (normalized.length <= maxChars) return normalized;
30
+ return `${normalized.slice(0, maxChars)}... [truncated]`;
31
+ }
32
+
33
+ function stepResult(name, command, ok, details = "", commandOutput = "") {
34
+ return { name, command, ok, details, commandOutput };
35
+ }
36
+
37
+ function gitIdentityStep() {
38
+ const nameResult = run("git", ["config", "--get", "user.name"]);
39
+ const emailResult = run("git", ["config", "--get", "user.email"]);
40
+
41
+ const actualName = nameResult.stdout.trim();
42
+ const actualEmail = emailResult.stdout.trim();
43
+ const ok =
44
+ nameResult.status === 0 &&
45
+ emailResult.status === 0 &&
46
+ actualName === EXPECTED_NAME &&
47
+ actualEmail === EXPECTED_EMAIL;
48
+
49
+ const details = ok
50
+ ? `Configured as ${actualName} <${actualEmail}>`
51
+ : `Expected ${EXPECTED_NAME} <${EXPECTED_EMAIL}>, found ${actualName || "(missing)"} <${actualEmail || "(missing)"}>`;
52
+
53
+ return stepResult(
54
+ "Git identity",
55
+ "git config --get user.name && git config --get user.email",
56
+ ok,
57
+ details,
58
+ `${nameResult.stdout}${nameResult.stderr}${emailResult.stdout}${emailResult.stderr}`
59
+ );
60
+ }
61
+
62
+ function ownerAttributionStep() {
63
+ const result = run("bash", ["scripts/check-owner-attribution.sh", OWNER_RANGE]);
64
+ return stepResult(
65
+ "Owner attribution",
66
+ `bash scripts/check-owner-attribution.sh ${OWNER_RANGE}`,
67
+ result.status === 0,
68
+ result.status === 0 ? "All commits in range are owner-attributed." : "Owner attribution check failed.",
69
+ `${result.stdout}${result.stderr}`
70
+ );
71
+ }
72
+
73
+ function publicLanguageStep() {
74
+ const result = run("bash", ["scripts/check-public-language.sh"]);
75
+ return stepResult(
76
+ "Public language",
77
+ "bash scripts/check-public-language.sh",
78
+ result.status === 0,
79
+ result.status === 0 ? "Public wording guardrail passed." : "Disallowed wording found.",
80
+ `${result.stdout}${result.stderr}`
81
+ );
82
+ }
83
+
84
+ function markerScanStep() {
85
+ const pattern = "Co-authored-by|co-authored-by|Generated with|generated with";
86
+ const scanPaths = [
87
+ "README.md",
88
+ "docs",
89
+ "public",
90
+ ".github/RELEASE_NOTES_TEMPLATE.md",
91
+ ".github/ISSUE_REPLY_TEMPLATE.md"
92
+ ];
93
+ const result = run("rg", [
94
+ "-n",
95
+ pattern,
96
+ "--glob",
97
+ "!docs/release-health/**",
98
+ ...scanPaths
99
+ ]);
100
+
101
+ if (result.status === 1) {
102
+ return stepResult(
103
+ "Public attribution markers",
104
+ "marker-scan",
105
+ true,
106
+ "No non-owner attribution markers found in public surfaces."
107
+ );
108
+ }
109
+
110
+ return stepResult(
111
+ "Public attribution markers",
112
+ "marker-scan",
113
+ false,
114
+ result.status === 0
115
+ ? "Found disallowed markers on public surfaces."
116
+ : "Marker scan failed.",
117
+ `${result.stdout}${result.stderr}`
118
+ );
119
+ }
120
+
121
+ function runNodeStep(name, scriptPath, extraArgs = []) {
122
+ const result = run("node", [scriptPath, ...extraArgs]);
123
+ return stepResult(
124
+ name,
125
+ `node ${scriptPath}${extraArgs.length ? ` ${extraArgs.join(" ")}` : ""}`,
126
+ result.status === 0,
127
+ result.status === 0 ? "Passed." : "Failed.",
128
+ `${result.stdout}${result.stderr}`
129
+ );
130
+ }
131
+
132
+ function npmPackSnapshot() {
133
+ const result = run("npm", ["pack", "--dry-run", "--json"]);
134
+ if (result.status !== 0) {
135
+ return {
136
+ ok: false,
137
+ details: "Unable to generate npm pack snapshot.",
138
+ output: `${result.stdout}${result.stderr}`
139
+ };
140
+ }
141
+
142
+ try {
143
+ const parsed = JSON.parse(result.stdout);
144
+ const entry = Array.isArray(parsed) ? parsed[0] : parsed;
145
+ const fileCount = Array.isArray(entry.files) ? entry.files.length : 0;
146
+ const details = `package size ${entry.size} bytes, unpacked ${entry.unpackedSize} bytes, files ${fileCount}`;
147
+ return { ok: true, details, output: result.stdout };
148
+ } catch (error) {
149
+ return {
150
+ ok: false,
151
+ details: "npm pack output was not valid JSON.",
152
+ output: `${result.stdout}\n${String(error)}`
153
+ };
154
+ }
155
+ }
156
+
157
+ function buildReport(results, packSnapshot) {
158
+ const generated = new Date().toISOString();
159
+ const failed = results.filter((step) => !step.ok);
160
+ const lines = [];
161
+ lines.push("# Prepublish Dry Run");
162
+ lines.push("");
163
+ lines.push(`- Generated: ${generated}`);
164
+ lines.push(`- Expected owner: \`${EXPECTED_NAME} <${EXPECTED_EMAIL}>\``);
165
+ lines.push(`- Owner range: \`${OWNER_RANGE}\``);
166
+ lines.push("");
167
+ lines.push("## Step Matrix");
168
+ lines.push("");
169
+ lines.push("| Step | Status | Command | Details |");
170
+ lines.push("|---|---|---|---|");
171
+ for (const step of results) {
172
+ lines.push(`| ${step.name} | ${step.ok ? "pass" : "fail"} | \`${step.command}\` | ${step.details} |`);
173
+ }
174
+ lines.push("");
175
+ lines.push("## npm Pack Snapshot");
176
+ lines.push("");
177
+ lines.push(`- Status: ${packSnapshot.ok ? "pass" : "fail"}`);
178
+ lines.push(`- Details: ${packSnapshot.details}`);
179
+ lines.push("");
180
+
181
+ if (failed.length === 0 && packSnapshot.ok) {
182
+ lines.push("## Result");
183
+ lines.push("");
184
+ lines.push("Prepublish dry run passed.");
185
+ } else {
186
+ lines.push("## Result");
187
+ lines.push("");
188
+ lines.push("Prepublish dry run failed.");
189
+ lines.push("");
190
+ lines.push("### Failing checks");
191
+ for (const step of failed) {
192
+ lines.push(`- ${step.name}`);
193
+ }
194
+ if (!packSnapshot.ok) lines.push("- npm pack snapshot");
195
+ }
196
+
197
+ lines.push("");
198
+ lines.push("## Command Output (Truncated)");
199
+ lines.push("");
200
+ for (const step of results) {
201
+ lines.push(`### ${step.name}`);
202
+ lines.push("");
203
+ lines.push("```text");
204
+ lines.push(trimOutput(step.commandOutput) || "(no output)");
205
+ lines.push("```");
206
+ lines.push("");
207
+ }
208
+ lines.push("### npm Pack Snapshot");
209
+ lines.push("");
210
+ lines.push("```text");
211
+ lines.push(trimOutput(packSnapshot.output, 4000) || "(no output)");
212
+ lines.push("```");
213
+ lines.push("");
214
+
215
+ return lines.join("\n");
216
+ }
217
+
218
+ function main() {
219
+ const steps = [
220
+ gitIdentityStep(),
221
+ ownerAttributionStep(),
222
+ publicLanguageStep(),
223
+ markerScanStep(),
224
+ runNodeStep("Core verification", "scripts/verify-core.js"),
225
+ runNodeStep("Web verification", "scripts/verify-web.js"),
226
+ runNodeStep("Install-flow verification", "scripts/verify-install-flow.js"),
227
+ runNodeStep("Version parity", "scripts/check-version-parity.js", ["--allow-propagation"])
228
+ ];
229
+
230
+ const packSnapshot = npmPackSnapshot();
231
+ const report = buildReport(steps, packSnapshot);
232
+
233
+ mkdirSync(resolve(ROOT, "docs", "release-health"), { recursive: true });
234
+ writeFileSync(REPORT_PATH, `${report}\n`);
235
+
236
+ console.log(`Wrote ${REPORT_PATH}`);
237
+ const failed = steps.some((step) => !step.ok) || !packSnapshot.ok;
238
+ if (failed) {
239
+ process.exitCode = 1;
240
+ }
241
+ }
242
+
243
+ main();
@@ -23,8 +23,9 @@ import {
23
23
  } from "../lib/token-store.js";
24
24
 
25
25
  const IS_MACOS = platform() === 'darwin';
26
- const VERSION = "1.2.4";
26
+ const VERSION = "3.0.0";
27
27
  const MIN_NODE_MAJOR = 20;
28
+ const AUTH_TEST_URL = process.env.SLACK_MCP_AUTH_TEST_URL || "https://slack.com/api/auth.test";
28
29
 
29
30
  // ANSI colors
30
31
  const colors = {
@@ -78,7 +79,7 @@ async function pressEnterToContinue(rl) {
78
79
 
79
80
  async function validateTokens(token, cookie) {
80
81
  try {
81
- const response = await fetch("https://slack.com/api/auth.test", {
82
+ const response = await fetch(AUTH_TEST_URL, {
82
83
  method: "POST",
83
84
  headers: {
84
85
  "Authorization": `Bearer ${token}`,
@@ -251,6 +252,8 @@ async function showStatus() {
251
252
 
252
253
  if (!creds) {
253
254
  error("No tokens found");
255
+ print("Code: missing_credentials");
256
+ print("Message: No credentials available from environment, file, or keychain.");
254
257
  print();
255
258
  print("Run setup wizard: npx -y @jtalk22/slack-mcp --setup");
256
259
  process.exit(1);
@@ -268,6 +271,7 @@ async function showStatus() {
268
271
  const result = await validateTokens(creds.token, creds.cookie);
269
272
  if (!result.valid) {
270
273
  error("Status: INVALID");
274
+ print("Code: auth_failed");
271
275
  print(`Error: ${result.error}`);
272
276
  print();
273
277
  print("Run setup wizard to refresh: npx -y @jtalk22/slack-mcp --setup");
@@ -275,6 +279,8 @@ async function showStatus() {
275
279
  }
276
280
 
277
281
  success("Status: VALID");
282
+ print("Code: ok");
283
+ print("Message: Slack auth valid.");
278
284
  print(`User: ${result.user}`);
279
285
  print(`Team: ${result.team}`);
280
286
  print(`User ID: ${result.userId}`);
@@ -331,6 +337,7 @@ async function runDoctor() {
331
337
  const nodeMajor = parseNodeMajor();
332
338
  if (Number.isNaN(nodeMajor) || nodeMajor < MIN_NODE_MAJOR) {
333
339
  error(`Node.js ${process.versions.node} detected (requires Node ${MIN_NODE_MAJOR}+)`);
340
+ print("Code: runtime_node_unsupported");
334
341
  print();
335
342
  print("Next action:");
336
343
  print(` npx -y @jtalk22/slack-mcp --doctor # rerun after upgrading Node ${MIN_NODE_MAJOR}+`);
@@ -341,6 +348,7 @@ async function runDoctor() {
341
348
  const creds = getDoctorCredentials();
342
349
  if (!creds) {
343
350
  error("Credentials: not found");
351
+ print("Code: missing_credentials");
344
352
  print();
345
353
  print("Next action:");
346
354
  print(" npx -y @jtalk22/slack-mcp --setup");
@@ -361,6 +369,7 @@ async function runDoctor() {
361
369
  if (!validation.valid) {
362
370
  const exitCode = classifyAuthError(validation.error);
363
371
  error(`Slack auth failed: ${validation.error}`);
372
+ print(`Code: ${exitCode === 2 ? "auth_invalid" : "runtime_auth_check_failed"}`);
364
373
  print();
365
374
  print("Next action:");
366
375
  if (exitCode === 2) {
@@ -373,6 +382,7 @@ async function runDoctor() {
373
382
  }
374
383
 
375
384
  success(`Slack auth valid for ${validation.user} @ ${validation.team}`);
385
+ print("Code: ok");
376
386
  print();
377
387
  print("Ready. Next command:");
378
388
  print(" npx -y @jtalk22/slack-mcp");
@@ -1,16 +1,19 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  import { spawnSync } from "node:child_process";
4
- import { mkdtempSync, rmSync } from "node:fs";
4
+ import { mkdtempSync, readFileSync, rmSync } from "node:fs";
5
5
  import { tmpdir } from "node:os";
6
6
  import { dirname, join } from "node:path";
7
7
  import { fileURLToPath } from "node:url";
8
8
 
9
9
  const PKG = "@jtalk22/slack-mcp";
10
10
  const repoRoot = join(dirname(fileURLToPath(import.meta.url)), "..");
11
+ const strictPublished = process.argv.includes("--strict-published");
12
+ const PUBLISHED_SPEC = `${PKG}@latest`;
13
+ const localVersion = JSON.parse(readFileSync(join(repoRoot, "package.json"), "utf8")).version;
11
14
 
12
15
  function runNpx(args, options = {}) {
13
- const cmdArgs = ["-y", PKG, ...args];
16
+ const cmdArgs = ["-y", PUBLISHED_SPEC, ...args];
14
17
  const result = spawnSync("npx", cmdArgs, {
15
18
  cwd: options.cwd,
16
19
  env: options.env,
@@ -97,6 +100,18 @@ function main() {
97
100
  "Expected --version to exit 0",
98
101
  versionResult.stderr || versionResult.stdout,
99
102
  );
103
+ const publishedMatchesLocal = versionResult.stdout.includes(localVersion);
104
+ if (strictPublished) {
105
+ assert(
106
+ publishedMatchesLocal,
107
+ `Expected published npx version to match local ${localVersion}`,
108
+ versionResult.stdout,
109
+ );
110
+ } else if (!publishedMatchesLocal) {
111
+ console.log(
112
+ `warning: npx resolved ${versionResult.stdout || "unknown"} while local version is ${localVersion}; strict published checks are deferred until publish.`
113
+ );
114
+ }
100
115
 
101
116
  const helpResult = runNpx(["--help"], { cwd: testHome, env });
102
117
  printResult("help", helpResult);
@@ -113,6 +128,15 @@ function main() {
113
128
  "Expected --status to exit non-zero when credentials are missing",
114
129
  statusResult.stderr || statusResult.stdout,
115
130
  );
131
+ if (strictPublished) {
132
+ assert(
133
+ !statusResult.stderr.includes("Attempting Chrome auto-extraction"),
134
+ "Expected npx --status to be read-only without auto-extraction side effects",
135
+ statusResult.stderr,
136
+ );
137
+ } else if (statusResult.stderr.includes("Attempting Chrome auto-extraction")) {
138
+ console.log("warning: published npx --status still has extraction side effects; re-run with --strict-published after publish.");
139
+ }
116
140
 
117
141
  const localStatusResult = runLocalSetupStatus({ env });
118
142
  printResult("local-status", localStatusResult);
@@ -148,6 +172,18 @@ function main() {
148
172
  localDoctorInvalidResult.stderr || localDoctorInvalidResult.stdout,
149
173
  );
150
174
 
175
+ const runtimeEnv = {
176
+ ...invalidEnv,
177
+ SLACK_MCP_AUTH_TEST_URL: "http://127.0.0.1:9/auth.test"
178
+ };
179
+ const localDoctorRuntimeResult = runLocalDoctor({ env: runtimeEnv });
180
+ printResult("local-doctor-runtime", localDoctorRuntimeResult);
181
+ assert(
182
+ localDoctorRuntimeResult.status === 3,
183
+ "Expected local --doctor to exit 3 when runtime connectivity fails",
184
+ localDoctorRuntimeResult.stderr || localDoctorRuntimeResult.stdout,
185
+ );
186
+
151
187
  console.log("\nInstall flow verification passed.");
152
188
  } finally {
153
189
  rmSync(testHome, { recursive: true, force: true });
@@ -6,7 +6,8 @@
6
6
  * 1. Server starts and prints Magic Link
7
7
  * 2. /demo.html contains "STATIC PREVIEW" banner
8
8
  * 3. /?key=... serves the dashboard (index.html)
9
- * 4. Server shuts down cleanly
9
+ * 4. /demo-video.html media assets are reachable
10
+ * 5. Server shuts down cleanly
10
11
  */
11
12
 
12
13
  import { spawn } from "child_process";
@@ -142,6 +143,45 @@ async function testApiWithKey(apiKey) {
142
143
  return true;
143
144
  }
144
145
 
146
+ async function testDemoVideoAssets() {
147
+ const demoVideoUrl = `http://localhost:${PORT}/demo-video.html`;
148
+ const demoVideoRes = await fetch(demoVideoUrl);
149
+
150
+ if (!demoVideoRes.ok) {
151
+ throw new Error(`Failed to fetch demo-video.html: ${demoVideoRes.status}`);
152
+ }
153
+
154
+ const demoVideoHtml = await demoVideoRes.text();
155
+ const requiredAssetCandidates = [
156
+ [
157
+ "/docs/images/demo-poster.png",
158
+ "https://jtalk22.github.io/slack-mcp-server/docs/images/demo-poster.png",
159
+ ],
160
+ [
161
+ "/docs/videos/demo-claude.webm",
162
+ "https://jtalk22.github.io/slack-mcp-server/docs/videos/demo-claude.webm",
163
+ ],
164
+ ];
165
+
166
+ for (const candidates of requiredAssetCandidates) {
167
+ const matched = candidates.find((candidate) => demoVideoHtml.includes(candidate));
168
+ if (!matched) {
169
+ throw new Error(`demo-video.html missing expected media reference: ${candidates.join(" OR ")}`);
170
+ }
171
+
172
+ const assetUrl = matched.startsWith("http")
173
+ ? matched
174
+ : `http://localhost:${PORT}${matched}`;
175
+
176
+ const assetRes = await fetch(assetUrl);
177
+ if (!assetRes.ok) {
178
+ throw new Error(`Demo media not reachable: ${assetUrl} (status ${assetRes.status})`);
179
+ }
180
+ }
181
+
182
+ return true;
183
+ }
184
+
145
185
  async function main() {
146
186
  console.log("╔════════════════════════════════════════╗");
147
187
  console.log("║ Web UI Verification Tests ║");
@@ -192,6 +232,14 @@ async function main() {
192
232
  log("PASS: API correctly rejects bad keys");
193
233
  results.push(true);
194
234
 
235
+ // Test 5: Demo video/media paths
236
+ console.log("\n[TEST 5] Demo Video Media Reachability");
237
+ console.log("─".repeat(40));
238
+
239
+ await testDemoVideoAssets();
240
+ log("PASS: demo-video media assets are reachable");
241
+ results.push(true);
242
+
195
243
  } catch (err) {
196
244
  console.log(` FAIL: ${err.message}`);
197
245
  results.push(false);
package/server.json ADDED
@@ -0,0 +1,47 @@
1
+ {
2
+ "$schema": "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json",
3
+ "name": "io.github.jtalk22/slack-mcp-server",
4
+ "title": "Slack MCP Server",
5
+ "description": "Session-based Slack access for Claude: DMs, channels, search, and threads via your Slack session.",
6
+ "websiteUrl": "https://jtalk22.github.io/slack-mcp-server/public/demo.html",
7
+ "icons": [
8
+ {
9
+ "src": "https://raw.githubusercontent.com/jtalk22/slack-mcp-server/main/docs/assets/icon-512.png",
10
+ "mimeType": "image/png",
11
+ "sizes": [
12
+ "512x512"
13
+ ]
14
+ }
15
+ ],
16
+ "repository": {
17
+ "url": "https://github.com/jtalk22/slack-mcp-server",
18
+ "source": "github"
19
+ },
20
+ "version": "3.0.0",
21
+ "packages": [
22
+ {
23
+ "registryType": "npm",
24
+ "identifier": "@jtalk22/slack-mcp",
25
+ "version": "3.0.0",
26
+ "transport": {
27
+ "type": "stdio"
28
+ },
29
+ "environmentVariables": [
30
+ {
31
+ "description": "Slack xoxc- token from browser session",
32
+ "isRequired": false,
33
+ "format": "string",
34
+ "isSecret": true,
35
+ "name": "SLACK_TOKEN"
36
+ },
37
+ {
38
+ "description": "Slack xoxd- cookie from browser session",
39
+ "isRequired": false,
40
+ "format": "string",
41
+ "isSecret": true,
42
+ "name": "SLACK_COOKIE"
43
+ }
44
+ ]
45
+ }
46
+ ]
47
+ }
package/smithery.yaml ADDED
@@ -0,0 +1,34 @@
1
+ # Smithery configuration for slack-mcp-server
2
+ # https://smithery.ai/docs/build/project-config/smithery-yaml
3
+
4
+ startCommand:
5
+ type: stdio
6
+ configSchema:
7
+ type: object
8
+ properties:
9
+ slackToken:
10
+ type: string
11
+ title: "Slack Token"
12
+ description: "Your xoxc- token from browser session. Optional on macOS (auto-extracted from Chrome)."
13
+ pattern: "^xoxc-.*$"
14
+ slackCookie:
15
+ type: string
16
+ title: "Slack Cookie"
17
+ description: "Your xoxd- cookie from browser session. Optional on macOS (auto-extracted from Chrome)."
18
+ pattern: "^xoxd-.*$"
19
+ autoRefresh:
20
+ type: boolean
21
+ title: "Auto Refresh"
22
+ description: "Enable automatic token refresh from Chrome (macOS only)"
23
+ default: true
24
+ additionalProperties: false
25
+ commandFunction: |-
26
+ (config) => ({
27
+ command: 'node',
28
+ args: ['src/server.js'],
29
+ env: {
30
+ ...(config.slackToken && { SLACK_TOKEN: config.slackToken }),
31
+ ...(config.slackCookie && { SLACK_COOKIE: config.slackCookie }),
32
+ ...(config.autoRefresh === false && { SLACK_NO_AUTO_REFRESH: 'true' })
33
+ }
34
+ })