@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 +5 -16
- package/docs/API.md +31 -1
- package/docs/SETUP.md +1 -1
- package/docs/TROUBLESHOOTING.md +10 -3
- package/docs/WEB-API.md +6 -2
- package/lib/handlers.js +17 -7
- package/lib/tools.js +1 -1
- package/package.json +1 -1
- package/scripts/token-cli.js +3 -3
- package/scripts/{verify-v106.js → verify-core.js} +3 -3
- package/scripts/verify-web.js +1 -1
- package/src/server.js +2 -2
- package/src/web-server.js +18 -5
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
|
|
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
|
|
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.
|
|
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
|
-
|
|
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 | - |
|
|
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
package/docs/TROUBLESHOOTING.md
CHANGED
|
@@ -98,9 +98,15 @@ launchctl load ~/Library/LaunchAgents/com.slack-web-api.plist
|
|
|
98
98
|
|
|
99
99
|
### API Key Invalid
|
|
100
100
|
|
|
101
|
-
|
|
101
|
+
The web server generates a unique API key on first run, stored in `~/.slack-mcp-api-key`.
|
|
102
102
|
|
|
103
|
-
|
|
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
|
-
|
|
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.
|
|
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
|
-
|
|
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 {
|
|
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:
|
|
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
|
|
410
|
-
|
|
411
|
-
|
|
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: "
|
|
112
|
+
description: "Filename to save export (saved to ~/.slack-mcp-exports/)"
|
|
113
113
|
}
|
|
114
114
|
},
|
|
115
115
|
required: ["channel_id"]
|
package/package.json
CHANGED
package/scripts/token-cli.js
CHANGED
|
@@ -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(
|
|
145
|
-
execSync(
|
|
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
|
-
*
|
|
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("║
|
|
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("\
|
|
148
|
+
console.log("\nCore stability features verified");
|
|
149
149
|
process.exit(0);
|
|
150
150
|
} else {
|
|
151
151
|
console.log(`\n✗ TESTS FAILED (${passed}/${total})`);
|
package/scripts/verify-web.js
CHANGED
|
@@ -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("\
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
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
|
}
|