@jtalk22/slack-mcp 1.1.5 → 1.1.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -87,7 +87,7 @@ flowchart LR
87
87
  - **Send Messages** - DMs or channels, with thread support
88
88
  - **User Directory** - List and search 500+ users with pagination
89
89
 
90
- ### Stability (v1.0.6+)
90
+ ### Stability
91
91
  - **Auto Token Refresh** - Extracts fresh tokens from Chrome automatically *(macOS only)*
92
92
  - **Atomic Writes** - File operations use temp-file-then-rename to prevent corruption
93
93
  - **Zombie Protection** - Background timers use `unref()` for clean process exit
@@ -294,11 +294,11 @@ npm run web
294
294
  # Or: npx @jtalk22/slack-mcp web
295
295
  ```
296
296
 
297
- **Magic Link (v1.1.0+):** The console prints a one-click URL with the API key embedded:
297
+ **Magic Link:** The console prints a one-click URL with the API key embedded:
298
298
 
299
299
  ```
300
300
  ════════════════════════════════════════════════════════════
301
- Slack Web API Server v1.1.0
301
+ Slack Web API Server v1.1.7
302
302
  ════════════════════════════════════════════════════════════
303
303
 
304
304
  Dashboard: http://localhost:3000/?key=smcp_xxxxxxxxxxxx
@@ -393,20 +393,9 @@ slack-mcp-server/
393
393
 
394
394
  ## Contributing
395
395
 
396
- 1. Fork the repository
397
- 2. Create a feature branch
398
- 3. Make your changes
399
- 4. Run `node --check` on modified files
400
- 5. Submit a pull request
396
+ PRs welcome. Run `node --check` on modified files before submitting.
401
397
 
402
- ---
403
-
404
- ## Support
405
-
406
- If this saved you from OAuth hell, consider:
407
-
408
- - [GitHub Sponsors](https://github.com/sponsors/jtalk22)
409
- - Star the repo
398
+ If you find this project useful, consider [sponsoring](https://github.com/sponsors/jtalk22) or starring the repo.
410
399
 
411
400
  ---
412
401
 
package/docs/API.md CHANGED
@@ -21,6 +21,36 @@ Check if Slack tokens are valid.
21
21
 
22
22
  ---
23
23
 
24
+ ### slack_token_status
25
+
26
+ Get detailed token health, age, and cache statistics.
27
+
28
+ **Parameters:** None
29
+
30
+ **Returns:**
31
+ ```json
32
+ {
33
+ "token": {
34
+ "status": "healthy",
35
+ "age_hours": 2.5,
36
+ "source": "file",
37
+ "updated_at": "2026-01-08T12:00:00Z",
38
+ "message": "Token is healthy"
39
+ },
40
+ "auto_refresh": {
41
+ "enabled": true,
42
+ "interval": "4 hours",
43
+ "requires": "Slack tab open in Chrome"
44
+ },
45
+ "cache": {
46
+ "users": { "size": 25, "maxSize": 500, "ttlMs": 3600000 },
47
+ "dms": { "count": 10, "age_hours": 1.2 }
48
+ }
49
+ }
50
+ ```
51
+
52
+ ---
53
+
24
54
  ### slack_refresh_tokens
25
55
 
26
56
  Force refresh tokens from Chrome.
@@ -119,7 +149,7 @@ Export full conversation with threads.
119
149
  | latest | string | - | Unix timestamp end |
120
150
  | max_messages | number | 2000 | Max messages (up to 10000) |
121
151
  | include_threads | boolean | true | Fetch thread replies |
122
- | output_file | string | - | Save to file path |
152
+ | output_file | string | - | Filename (saved to ~/.slack-mcp-exports/) |
123
153
 
124
154
  **Timestamps:**
125
155
  - Dec 1, 2025 = `1733011200`
package/docs/SETUP.md CHANGED
@@ -12,7 +12,7 @@
12
12
 
13
13
  ```bash
14
14
  cd ~
15
- git clone https://github.com/yourusername/slack-mcp-server.git
15
+ git clone https://github.com/jtalk22/slack-mcp-server.git
16
16
  # or if already exists:
17
17
  cd ~/slack-mcp-server
18
18
  ```
@@ -98,9 +98,15 @@ launchctl load ~/Library/LaunchAgents/com.slack-web-api.plist
98
98
 
99
99
  ### API Key Invalid
100
100
 
101
- **Default API Key:** `slack-mcp-local`
101
+ The web server generates a unique API key on first run, stored in `~/.slack-mcp-api-key`.
102
102
 
103
- If you set a custom key, make sure it matches:
103
+ The key is printed to the console when you start the server:
104
+ ```
105
+ Dashboard: http://localhost:3000/?key=smcp_xxxxxxxxxxxx
106
+ API Key: smcp_xxxxxxxxxxxx
107
+ ```
108
+
109
+ You can also set a custom key:
104
110
  ```bash
105
111
  SLACK_API_KEY=your-custom-key npm run web
106
112
  ```
@@ -109,7 +115,8 @@ SLACK_API_KEY=your-custom-key npm run web
109
115
 
110
116
  Check if the server is running:
111
117
  ```bash
112
- curl http://localhost:3000/health -H "Authorization: Bearer slack-mcp-local"
118
+ # Get your API key from ~/.slack-mcp-api-key
119
+ curl http://localhost:3000/health -H "Authorization: Bearer $(cat ~/.slack-mcp-api-key)"
113
120
  ```
114
121
 
115
122
  Check LaunchAgent status:
package/docs/WEB-API.md CHANGED
@@ -12,7 +12,7 @@ npm run web
12
12
  The server will:
13
13
  1. Start on port 3000
14
14
  2. Serve the web UI at http://localhost:3000
15
- 3. Use default API key `slack-mcp-local` (or set `SLACK_API_KEY` env var)
15
+ 3. Generate a secure API key (saved to `~/.slack-mcp-api-key`)
16
16
 
17
17
  ## Auto-Start on Login (Recommended)
18
18
 
@@ -69,7 +69,11 @@ All API requests require the API key in the Authorization header:
69
69
  Authorization: Bearer <your-api-key>
70
70
  ```
71
71
 
72
- **Default API Key:** `slack-mcp-local`
72
+ The server generates a cryptographically secure API key on first run, saved to `~/.slack-mcp-api-key`. The key is printed to the console:
73
+
74
+ ```
75
+ Dashboard: http://localhost:3000/?key=smcp_xxxxxxxxxxxx
76
+ ```
73
77
 
74
78
  To use a custom key:
75
79
  ```bash
package/lib/handlers.js CHANGED
@@ -4,7 +4,8 @@
4
4
  * Implementation of all MCP tool handlers.
5
5
  */
6
6
 
7
- import { writeFileSync, readFileSync, existsSync, renameSync, unlinkSync } from "fs";
7
+ import { writeFileSync, readFileSync, existsSync, renameSync, unlinkSync, mkdirSync } from "fs";
8
+ import { execSync } from "child_process";
8
9
  import { homedir, platform } from "os";
9
10
  import { join } from "path";
10
11
  import { loadTokens, saveTokens, extractFromChrome, isAutoRefreshAvailable } from "./token-store.js";
@@ -33,7 +34,7 @@ function atomicWriteSync(filePath, content) {
33
34
  try {
34
35
  writeFileSync(tempPath, content);
35
36
  if (platform() === 'darwin' || platform() === 'linux') {
36
- try { require('child_process').execSync(`chmod 600 "${tempPath}"`); } catch {}
37
+ try { execSync(`chmod 600 "${tempPath}"`); } catch {}
37
38
  }
38
39
  renameSync(tempPath, filePath);
39
40
  } catch (e) {
@@ -115,7 +116,7 @@ export async function handleHealthCheck() {
115
116
  return {
116
117
  content: [{
117
118
  type: "text",
118
- text: "NO CREDENTIALS\n\nOptions:\n1. Open Slack in Chrome, then use slack_refresh_tokens\n2. Run: ~/slack-mcp-server/scripts/refresh-tokens.sh"
119
+ text: "NO CREDENTIALS\n\nOptions:\n1. Open Slack in Chrome, then use slack_refresh_tokens\n2. Run: npm run tokens:auto (or npx @jtalk22/slack-mcp tokens:auto)"
119
120
  }],
120
121
  isError: true
121
122
  };
@@ -404,11 +405,20 @@ export async function handleGetFullConversation(args) {
404
405
  messages: allMessages
405
406
  };
406
407
 
407
- // Save to file if requested
408
+ // Save to file if requested (restricted to ~/.slack-mcp-exports/ for security)
408
409
  if (args.output_file) {
409
- const outputPath = args.output_file.startsWith('/')
410
- ? args.output_file
411
- : join(homedir(), args.output_file);
410
+ const exportDir = join(homedir(), '.slack-mcp-exports');
411
+ // Ensure export directory exists
412
+ try { mkdirSync(exportDir, { recursive: true }); } catch {}
413
+
414
+ // Sanitize filename - remove any path traversal attempts
415
+ const sanitizedName = args.output_file
416
+ .replace(/^.*[\\\/]/, '') // Remove any path components
417
+ .replace(/\.\./g, '') // Remove .. sequences
418
+ .replace(/[<>:"|?*]/g, '') // Remove invalid chars
419
+ || 'export.json';
420
+
421
+ const outputPath = join(exportDir, sanitizedName);
412
422
  writeFileSync(outputPath, JSON.stringify(output, null, 2));
413
423
  output.saved_to = outputPath;
414
424
  }
package/lib/tools.js CHANGED
@@ -109,7 +109,7 @@ export const TOOLS = [
109
109
  },
110
110
  output_file: {
111
111
  type: "string",
112
- description: "Save to file path (optional, relative to home directory)"
112
+ description: "Filename to save export (saved to ~/.slack-mcp-exports/)"
113
113
  }
114
114
  },
115
115
  required: ["channel_id"]
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jtalk22/slack-mcp",
3
- "version": "1.1.5",
3
+ "version": "1.1.7",
4
4
  "description": "Full Slack access for Claude - DMs, channels, search. No OAuth. No admin approval. Just works.",
5
5
  "type": "module",
6
6
  "main": "src/server.js",
@@ -3,7 +3,7 @@
3
3
  * Token CLI - Manage Slack tokens
4
4
  */
5
5
 
6
- import { loadTokens, saveTokens, extractFromChrome, getFromFile, TOKEN_FILE } from "../lib/token-store.js";
6
+ import { loadTokens, 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
 
@@ -141,8 +141,8 @@ async function clearTokens() {
141
141
  }
142
142
 
143
143
  try {
144
- execSync('security delete-generic-password -s "slack-mcp-server" -a "token" 2>/dev/null');
145
- execSync('security delete-generic-password -s "slack-mcp-server" -a "cookie" 2>/dev/null');
144
+ execSync(`security delete-generic-password -s "${KEYCHAIN_SERVICE}" -a "token" 2>/dev/null`);
145
+ execSync(`security delete-generic-password -s "${KEYCHAIN_SERVICE}" -a "cookie" 2>/dev/null`);
146
146
  console.log("Deleted keychain entries");
147
147
  } catch (e) {
148
148
  console.log("No keychain entries to delete");
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  /**
3
- * v1.0.6 Verification Script
3
+ * Core Stability Verification Script
4
4
  *
5
5
  * Tests:
6
6
  * 1. Atomic write - no .tmp artifacts remain after write
@@ -131,7 +131,7 @@ async function testServerExit() {
131
131
 
132
132
  async function main() {
133
133
  console.log("╔════════════════════════════════════════╗");
134
- console.log("║ v1.0.6 Verification Tests ║");
134
+ console.log("║ Core Stability Verification Tests ║");
135
135
  console.log("╚════════════════════════════════════════╝");
136
136
 
137
137
  const results = [];
@@ -145,7 +145,7 @@ async function main() {
145
145
 
146
146
  if (passed === total) {
147
147
  console.log(`\n✓ ALL TESTS PASSED (${passed}/${total})`);
148
- console.log("\nReady to deploy v1.0.6");
148
+ console.log("\nCore stability features verified");
149
149
  process.exit(0);
150
150
  } else {
151
151
  console.log(`\n✗ TESTS FAILED (${passed}/${total})`);
@@ -206,7 +206,7 @@ async function main() {
206
206
 
207
207
  if (passed === total) {
208
208
  console.log(`\n✓ ALL TESTS PASSED (${passed}/${total})`);
209
- console.log("\nReady to bump version to 1.1.0");
209
+ console.log("\nWeb UI features verified");
210
210
  process.exit(0);
211
211
  } else {
212
212
  console.log(`\n✗ TESTS FAILED (${passed}/${total})`);
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.1.0
14
+ * @version 1.1.7
15
15
  */
16
16
 
17
17
  import { Server } from "@modelcontextprotocol/sdk/server/index.js";
@@ -43,7 +43,7 @@ const BACKGROUND_REFRESH_INTERVAL = 4 * 60 * 60 * 1000;
43
43
 
44
44
  // Package info
45
45
  const SERVER_NAME = "slack-mcp-server";
46
- const SERVER_VERSION = "1.1.0";
46
+ const SERVER_VERSION = "1.1.7";
47
47
 
48
48
  // Initialize server
49
49
  const server = new Server(
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.1.0
8
+ * @version 1.1.7
9
9
  */
10
10
 
11
11
  import express from "express";
@@ -67,9 +67,20 @@ const API_KEY = getOrCreateAPIKey();
67
67
  app.use(express.json());
68
68
  app.use(express.static(join(__dirname, "../public")));
69
69
 
70
- // CORS for local development
70
+ // CORS - restricted to localhost for security
71
+ // Using * would allow any website to make requests to your local server
72
+ const ALLOWED_ORIGINS = [
73
+ `http://localhost:${PORT}`,
74
+ `http://127.0.0.1:${PORT}`,
75
+ 'http://localhost:3000',
76
+ 'http://127.0.0.1:3000'
77
+ ];
78
+
71
79
  app.use((req, res, next) => {
72
- res.header("Access-Control-Allow-Origin", "*");
80
+ const origin = req.headers.origin;
81
+ if (ALLOWED_ORIGINS.includes(origin)) {
82
+ res.header("Access-Control-Allow-Origin", origin);
83
+ }
73
84
  res.header("Access-Control-Allow-Headers", "Content-Type, Authorization");
74
85
  res.header("Access-Control-Allow-Methods", "GET, POST, OPTIONS");
75
86
  if (req.method === "OPTIONS") {
@@ -280,14 +291,16 @@ async function main() {
280
291
  console.log(`Credentials loaded from: ${credentials.source}`);
281
292
  }
282
293
 
283
- app.listen(PORT, () => {
294
+ // Bind to localhost only - prevents access from other devices on the network
295
+ app.listen(PORT, '127.0.0.1', () => {
284
296
  // Print to stderr to keep logs clean (stdout reserved for JSON in some setups)
285
297
  console.error(`\n${"═".repeat(60)}`);
286
- console.error(` Slack Web API Server v1.1.0`);
298
+ console.error(` Slack Web API Server v1.1.7`);
287
299
  console.error(`${"═".repeat(60)}`);
288
300
  console.error(`\n Dashboard: http://localhost:${PORT}/?key=${API_KEY}`);
289
301
  console.error(`\n API Key: ${API_KEY}`);
290
302
  console.error(`\n curl -H "Authorization: Bearer ${API_KEY}" http://localhost:${PORT}/health`);
303
+ console.error(`\n Security: Bound to localhost only (127.0.0.1)`);
291
304
  console.error(`\n${"═".repeat(60)}\n`);
292
305
  });
293
306
  }