@jtalk22/slack-mcp 3.1.0 → 3.2.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 +45 -13
- package/docs/SETUP.md +64 -29
- package/docs/TROUBLESHOOTING.md +28 -0
- package/lib/handlers.js +156 -0
- package/lib/slack-client.js +11 -3
- package/lib/token-store.js +6 -5
- package/lib/tools.js +131 -0
- package/package.json +15 -8
- package/public/index.html +10 -6
- package/public/share.html +6 -5
- package/scripts/setup-wizard.js +1 -1
- package/server.json +8 -2
- package/src/server-http.js +16 -1
- package/src/server.js +31 -7
- package/src/web-server.js +117 -4
- package/docs/CLOUDFLARE-BROWSER-TOOLKIT.md +0 -67
- package/docs/COMMUNICATION-STYLE.md +0 -66
- package/docs/COMPATIBILITY.md +0 -19
- package/docs/DEPLOYMENT-MODES.md +0 -55
- package/docs/HN-LAUNCH.md +0 -72
- package/docs/INDEX.md +0 -41
- package/docs/INSTALL-PROOF.md +0 -18
- package/docs/LAUNCH-COPY-v3.0.0.md +0 -101
- package/docs/LAUNCH-MATRIX.md +0 -22
- package/docs/LAUNCH-OPS.md +0 -71
- package/docs/RELEASE-HEALTH.md +0 -77
- package/docs/SUPPORT-BOUNDARIES.md +0 -49
- package/docs/USE_CASE_RECIPES.md +0 -69
- package/docs/WEB-API.md +0 -303
- package/docs/images/demo-channel-messages.png +0 -0
- package/docs/images/demo-channels.png +0 -0
- package/docs/images/demo-claude-mobile-360x800.png +0 -0
- package/docs/images/demo-claude-mobile-390x844.png +0 -0
- package/docs/images/demo-claude-mobile-poster.png +0 -0
- package/docs/images/demo-main-mobile-360x800.png +0 -0
- package/docs/images/demo-main-mobile-390x844.png +0 -0
- package/docs/images/demo-main.png +0 -0
- package/docs/images/demo-messages.png +0 -0
- package/docs/images/demo-poster.png +0 -0
- package/docs/images/demo-sidebar.png +0 -0
- package/docs/images/diagram-oauth-comparison.svg +0 -80
- package/docs/images/diagram-session-flow.svg +0 -105
- package/docs/images/social-preview-v3.png +0 -0
- package/docs/images/web-api-mobile-360x800.png +0 -0
- package/docs/images/web-api-mobile-390x844.png +0 -0
- package/public/demo-claude.html +0 -1974
- package/public/demo-video.html +0 -244
- package/public/demo.html +0 -1196
- package/scripts/build-mobile-demo.js +0 -168
- package/scripts/build-release-health-delta.js +0 -201
- package/scripts/build-social-preview.js +0 -189
- package/scripts/capture-screenshots.js +0 -152
- package/scripts/check-owner-attribution.sh +0 -131
- package/scripts/check-public-language.sh +0 -26
- package/scripts/check-version-parity.js +0 -218
- package/scripts/cloudflare-browser-tool.js +0 -237
- package/scripts/collect-release-health.js +0 -162
- package/scripts/impact-push-v3.js +0 -781
- package/scripts/record-demo.js +0 -163
- package/scripts/release-preflight.js +0 -247
- package/scripts/setup-git-hooks.sh +0 -15
- package/scripts/update-github-social-preview.js +0 -208
- package/scripts/verify-core.js +0 -159
- package/scripts/verify-install-flow.js +0 -193
- package/scripts/verify-web.js +0 -273
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jtalk22/slack-mcp",
|
|
3
3
|
"mcpName": "io.github.jtalk22/slack-mcp-server",
|
|
4
|
-
"version": "3.
|
|
4
|
+
"version": "3.2.0",
|
|
5
5
|
"description": "Session-based Slack MCP for Claude and MCP clients: local-first workflows, secure-default HTTP.",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"main": "src/server.js",
|
|
@@ -59,7 +59,11 @@
|
|
|
59
59
|
"automation",
|
|
60
60
|
"productivity"
|
|
61
61
|
],
|
|
62
|
-
"author":
|
|
62
|
+
"author": {
|
|
63
|
+
"name": "James Lambert",
|
|
64
|
+
"email": "james@revasser.nyc",
|
|
65
|
+
"url": "https://github.com/jtalk22"
|
|
66
|
+
},
|
|
63
67
|
"license": "MIT",
|
|
64
68
|
"repository": {
|
|
65
69
|
"type": "git",
|
|
@@ -79,12 +83,15 @@
|
|
|
79
83
|
"files": [
|
|
80
84
|
"src/",
|
|
81
85
|
"lib/",
|
|
82
|
-
"public/",
|
|
83
|
-
"
|
|
84
|
-
"
|
|
85
|
-
"
|
|
86
|
-
"docs/
|
|
87
|
-
"docs/
|
|
86
|
+
"public/index.html",
|
|
87
|
+
"public/share.html",
|
|
88
|
+
"scripts/setup-wizard.js",
|
|
89
|
+
"scripts/token-cli.js",
|
|
90
|
+
"docs/SETUP.md",
|
|
91
|
+
"docs/API.md",
|
|
92
|
+
"docs/TROUBLESHOOTING.md",
|
|
93
|
+
"docs/assets/icon.svg",
|
|
94
|
+
"docs/assets/icon-512.png",
|
|
88
95
|
"README.md",
|
|
89
96
|
"LICENSE",
|
|
90
97
|
"server.json",
|
package/public/index.html
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
<head>
|
|
4
4
|
<meta charset="UTF-8">
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
-
<title>Slack Web
|
|
6
|
+
<title>Slack MCP Server — Web Dashboard</title>
|
|
7
7
|
<style>
|
|
8
8
|
@import url('https://fonts.googleapis.com/css2?family=IBM+Plex+Sans:wght@400;500;600&family=Space+Grotesk:wght@500;600;700&display=swap');
|
|
9
9
|
|
|
@@ -253,7 +253,11 @@
|
|
|
253
253
|
</div>
|
|
254
254
|
|
|
255
255
|
<div class="container">
|
|
256
|
-
<h1>Slack Web API <span id="status" class="status"></span></h1>
|
|
256
|
+
<h1>Slack Web API <span style="font-size:0.55em;opacity:0.5;font-weight:400;vertical-align:middle">v3.2.0</span> <span id="status" class="status"></span></h1>
|
|
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>Skip local setup? <strong style="color:#f0c246">Slack MCP Cloud</strong> gives you 16 tools with one URL — no tokens to manage.</span>
|
|
259
|
+
<a href="https://jtalk22.github.io/slack-mcp-server/cloud.html" style="color:#f0c246;font-weight:600;text-decoration:none;white-space:nowrap" target="_blank">Learn more →</a>
|
|
260
|
+
</div>
|
|
257
261
|
<div class="grid">
|
|
258
262
|
<div class="sidebar">
|
|
259
263
|
<h3>CONVERSATIONS</h3>
|
|
@@ -267,7 +271,7 @@
|
|
|
267
271
|
</div>
|
|
268
272
|
<div class="main-panel">
|
|
269
273
|
<div class="search-section">
|
|
270
|
-
<input type="text" id="searchQuery" placeholder="Search messages...">
|
|
274
|
+
<input type="text" id="searchQuery" placeholder="Search messages..." onkeypress="if(event.key==='Enter')searchMessages()">
|
|
271
275
|
<button onclick="searchMessages()">Search</button>
|
|
272
276
|
</div>
|
|
273
277
|
<h2 id="channelName">Select a conversation</h2>
|
|
@@ -377,7 +381,7 @@
|
|
|
377
381
|
try {
|
|
378
382
|
const data = await api('/conversations?types=' + types);
|
|
379
383
|
list.innerHTML = data.conversations.map(c =>
|
|
380
|
-
'<li onclick="loadHistory(\'' + c.id + '\', \'' + c.name.replace(/'/g, "
|
|
384
|
+
'<li onclick="loadHistory(\'' + c.id + '\', \'' + escapeHtml(c.name).replace(/'/g, "'") + '\', this)"><div>' + escapeHtml(c.name) + '</div><div class="type">' + escapeHtml(c.type) + '</div></li>'
|
|
381
385
|
).join('');
|
|
382
386
|
} catch (e) { list.innerHTML = '<li class="error-msg">' + e.message + '</li>'; }
|
|
383
387
|
}
|
|
@@ -397,7 +401,7 @@
|
|
|
397
401
|
const container = document.getElementById('messages');
|
|
398
402
|
if (!messages || messages.length === 0) { container.innerHTML = '<div class="loading">No messages</div>'; return; }
|
|
399
403
|
container.innerHTML = messages.map(m =>
|
|
400
|
-
'<div class="message"><div class="header"><span class="user">' + (m.user || m.user_id || 'Unknown') + '</span><span class="time">' + new Date(m.datetime).toLocaleString() + '</span></div><div class="text">' + escapeHtml(m.text || '') + '</div></div>'
|
|
404
|
+
'<div class="message"><div class="header"><span class="user">' + escapeHtml(m.user || m.user_id || 'Unknown') + '</span><span class="time">' + new Date(m.datetime).toLocaleString() + '</span></div><div class="text">' + escapeHtml(m.text || '') + '</div></div>'
|
|
401
405
|
).join('');
|
|
402
406
|
container.scrollTop = container.scrollHeight;
|
|
403
407
|
}
|
|
@@ -423,7 +427,7 @@
|
|
|
423
427
|
const data = await api('/search?q=' + encodeURIComponent(query));
|
|
424
428
|
if (data.matches && data.matches.length > 0) {
|
|
425
429
|
container.innerHTML = data.matches.map(m =>
|
|
426
|
-
'<div class="message"><div class="header"><span class="user">' + (m.user || 'Unknown') + ' in #' + m.channel + '</span><span class="time">' + new Date(m.datetime).toLocaleString() + '</span></div><div class="text">' + escapeHtml(m.text || '') + '</div></div>'
|
|
430
|
+
'<div class="message"><div class="header"><span class="user">' + escapeHtml(m.user || 'Unknown') + ' in #' + escapeHtml(m.channel || '') + '</span><span class="time">' + new Date(m.datetime).toLocaleString() + '</span></div><div class="text">' + escapeHtml(m.text || '') + '</div></div>'
|
|
427
431
|
).join('');
|
|
428
432
|
} else { container.innerHTML = '<div class="loading">No results found</div>'; }
|
|
429
433
|
} catch (e) { container.innerHTML = '<div class="error-msg">' + e.message + '</div>'; }
|
package/public/share.html
CHANGED
|
@@ -3,17 +3,17 @@
|
|
|
3
3
|
<head>
|
|
4
4
|
<meta charset="utf-8">
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
6
|
-
<title>Slack MCP Server v3.
|
|
6
|
+
<title>Slack MCP Server v3.2.0</title>
|
|
7
7
|
<meta name="description" content="Session-based Slack MCP for Claude. Local-first stdio/web with secure-default hosted HTTP in v3.">
|
|
8
8
|
<meta property="og:type" content="website">
|
|
9
|
-
<meta property="og:title" content="Slack MCP Server v3.
|
|
9
|
+
<meta property="og:title" content="Slack MCP Server v3.2.0">
|
|
10
10
|
<meta property="og:description" content="Session-based Slack MCP for Claude. Local-first stdio/web with secure-default hosted HTTP in v3.">
|
|
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 v3.
|
|
16
|
+
<meta name="twitter:title" content="Slack MCP Server v3.2.0">
|
|
17
17
|
<meta name="twitter:description" content="Session-based Slack MCP for Claude. Local-first stdio/web with secure-default hosted HTTP in v3.">
|
|
18
18
|
<meta name="twitter:image" content="https://jtalk22.github.io/slack-mcp-server/docs/images/social-preview-v3.png">
|
|
19
19
|
<style>
|
|
@@ -105,8 +105,8 @@
|
|
|
105
105
|
</head>
|
|
106
106
|
<body>
|
|
107
107
|
<main class="wrap">
|
|
108
|
-
<h1>Slack MCP Server v3.
|
|
109
|
-
<p class="sub">
|
|
108
|
+
<h1>Slack MCP Server v3.2.0</h1>
|
|
109
|
+
<p class="sub">Give Claude full access to your Slack. Read channels, search messages, send replies, get AI summaries. Self-host free or Cloud from $19/mo.</p>
|
|
110
110
|
|
|
111
111
|
<a class="preview" href="https://github.com/jtalk22/slack-mcp-server" rel="noopener">
|
|
112
112
|
<img src="https://jtalk22.github.io/slack-mcp-server/docs/images/social-preview-v3.png" alt="Slack MCP Server social preview card">
|
|
@@ -119,6 +119,7 @@
|
|
|
119
119
|
<a href="https://jtalk22.github.io/slack-mcp-server/" rel="noopener">Autoplay Demo Landing</a>
|
|
120
120
|
<a href="https://jtalk22.github.io/slack-mcp-server/docs/videos/demo-claude-mobile-20s.mp4" rel="noopener">20s Mobile Clip</a>
|
|
121
121
|
<a href="https://www.npmjs.com/package/@jtalk22/slack-mcp" rel="noopener">npm Package</a>
|
|
122
|
+
<a href="https://jtalk22.github.io/slack-mcp-server/cloud.html" rel="noopener" style="background:rgba(240,194,70,0.18);border-color:rgba(240,194,70,0.45);color:#f0c246">Cloud ($19/mo)</a>
|
|
122
123
|
</div>
|
|
123
124
|
|
|
124
125
|
<p class="note"><strong>Verify in 30 seconds:</strong> <code>--version</code>, <code>--doctor</code>, <code>--status</code>. Maintainer/operator: <code>jtalk22</code> (<code>james@revasser.nyc</code>).</p>
|
package/scripts/setup-wizard.js
CHANGED
|
@@ -23,7 +23,7 @@ import {
|
|
|
23
23
|
} from "../lib/token-store.js";
|
|
24
24
|
|
|
25
25
|
const IS_MACOS = platform() === 'darwin';
|
|
26
|
-
const VERSION = "3.
|
|
26
|
+
const VERSION = "3.2.0";
|
|
27
27
|
const MIN_NODE_MAJOR = 20;
|
|
28
28
|
const AUTH_TEST_URL = process.env.SLACK_MCP_AUTH_TEST_URL || "https://slack.com/api/auth.test";
|
|
29
29
|
|
package/server.json
CHANGED
|
@@ -17,12 +17,18 @@
|
|
|
17
17
|
"url": "https://github.com/jtalk22/slack-mcp-server",
|
|
18
18
|
"source": "github"
|
|
19
19
|
},
|
|
20
|
-
"version": "3.
|
|
20
|
+
"version": "3.2.0",
|
|
21
|
+
"remotes": [
|
|
22
|
+
{
|
|
23
|
+
"type": "streamable-http",
|
|
24
|
+
"url": "https://mcp.revasserlabs.com/oauth/mcp"
|
|
25
|
+
}
|
|
26
|
+
],
|
|
21
27
|
"packages": [
|
|
22
28
|
{
|
|
23
29
|
"registryType": "npm",
|
|
24
30
|
"identifier": "@jtalk22/slack-mcp",
|
|
25
|
-
"version": "3.
|
|
31
|
+
"version": "3.2.0",
|
|
26
32
|
"transport": {
|
|
27
33
|
"type": "stdio"
|
|
28
34
|
},
|
package/src/server-http.js
CHANGED
|
@@ -27,10 +27,15 @@ import {
|
|
|
27
27
|
handleSendMessage,
|
|
28
28
|
handleGetThread,
|
|
29
29
|
handleListUsers,
|
|
30
|
+
handleAddReaction,
|
|
31
|
+
handleRemoveReaction,
|
|
32
|
+
handleConversationsMark,
|
|
33
|
+
handleConversationsUnreads,
|
|
34
|
+
handleUsersSearch,
|
|
30
35
|
} from "../lib/handlers.js";
|
|
31
36
|
|
|
32
37
|
const SERVER_NAME = "slack-mcp-server";
|
|
33
|
-
const SERVER_VERSION = "3.
|
|
38
|
+
const SERVER_VERSION = "3.2.0";
|
|
34
39
|
const PORT = process.env.PORT || 3000;
|
|
35
40
|
const HTTP_INSECURE = process.env.SLACK_MCP_HTTP_INSECURE === "1";
|
|
36
41
|
const HTTP_AUTH_TOKEN = process.env.SLACK_MCP_HTTP_AUTH_TOKEN || process.env.SLACK_API_KEY || null;
|
|
@@ -115,6 +120,16 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
115
120
|
return await handleGetThread(args);
|
|
116
121
|
case "slack_list_users":
|
|
117
122
|
return await handleListUsers(args);
|
|
123
|
+
case "slack_add_reaction":
|
|
124
|
+
return await handleAddReaction(args);
|
|
125
|
+
case "slack_remove_reaction":
|
|
126
|
+
return await handleRemoveReaction(args);
|
|
127
|
+
case "slack_conversations_mark":
|
|
128
|
+
return await handleConversationsMark(args);
|
|
129
|
+
case "slack_conversations_unreads":
|
|
130
|
+
return await handleConversationsUnreads(args);
|
|
131
|
+
case "slack_users_search":
|
|
132
|
+
return await handleUsersSearch(args);
|
|
118
133
|
default:
|
|
119
134
|
return {
|
|
120
135
|
content: [{
|
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 3.
|
|
14
|
+
* @version 3.2.0
|
|
15
15
|
*/
|
|
16
16
|
|
|
17
17
|
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
@@ -41,6 +41,11 @@ import {
|
|
|
41
41
|
handleSendMessage,
|
|
42
42
|
handleGetThread,
|
|
43
43
|
handleListUsers,
|
|
44
|
+
handleAddReaction,
|
|
45
|
+
handleRemoveReaction,
|
|
46
|
+
handleConversationsMark,
|
|
47
|
+
handleConversationsUnreads,
|
|
48
|
+
handleUsersSearch,
|
|
44
49
|
} from "../lib/handlers.js";
|
|
45
50
|
|
|
46
51
|
// Background refresh interval (4 hours)
|
|
@@ -48,7 +53,7 @@ const BACKGROUND_REFRESH_INTERVAL = 4 * 60 * 60 * 1000;
|
|
|
48
53
|
|
|
49
54
|
// Package info
|
|
50
55
|
const SERVER_NAME = "slack-mcp-server";
|
|
51
|
-
const SERVER_VERSION = "3.
|
|
56
|
+
const SERVER_VERSION = "3.2.0";
|
|
52
57
|
|
|
53
58
|
// MCP Prompts - predefined prompt templates for common Slack operations
|
|
54
59
|
const PROMPTS = [
|
|
@@ -255,6 +260,21 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
255
260
|
case "slack_list_users":
|
|
256
261
|
return await handleListUsers(args);
|
|
257
262
|
|
|
263
|
+
case "slack_add_reaction":
|
|
264
|
+
return await handleAddReaction(args);
|
|
265
|
+
|
|
266
|
+
case "slack_remove_reaction":
|
|
267
|
+
return await handleRemoveReaction(args);
|
|
268
|
+
|
|
269
|
+
case "slack_conversations_mark":
|
|
270
|
+
return await handleConversationsMark(args);
|
|
271
|
+
|
|
272
|
+
case "slack_conversations_unreads":
|
|
273
|
+
return await handleConversationsUnreads(args);
|
|
274
|
+
|
|
275
|
+
case "slack_users_search":
|
|
276
|
+
return await handleUsersSearch(args);
|
|
277
|
+
|
|
258
278
|
default:
|
|
259
279
|
return {
|
|
260
280
|
content: [{
|
|
@@ -306,11 +326,15 @@ async function main() {
|
|
|
306
326
|
// Use unref() so this timer doesn't prevent the process from exiting
|
|
307
327
|
// when the MCP transport closes (prevents zombie processes)
|
|
308
328
|
const backgroundTimer = setInterval(async () => {
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
329
|
+
try {
|
|
330
|
+
const health = await checkTokenHealth(console);
|
|
331
|
+
if (health.refreshed) {
|
|
332
|
+
console.error("Background: tokens refreshed successfully");
|
|
333
|
+
} else if (health.critical) {
|
|
334
|
+
console.error("Background: tokens critical - open Slack in Chrome");
|
|
335
|
+
}
|
|
336
|
+
} catch (err) {
|
|
337
|
+
console.error(`Background health check failed: ${err.message}`);
|
|
314
338
|
}
|
|
315
339
|
}, BACKGROUND_REFRESH_INTERVAL);
|
|
316
340
|
backgroundTimer.unref();
|
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 3.
|
|
8
|
+
* @version 3.2.0
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
11
|
import express from "express";
|
|
@@ -30,6 +30,11 @@ import {
|
|
|
30
30
|
handleSendMessage,
|
|
31
31
|
handleGetThread,
|
|
32
32
|
handleListUsers,
|
|
33
|
+
handleAddReaction,
|
|
34
|
+
handleRemoveReaction,
|
|
35
|
+
handleConversationsMark,
|
|
36
|
+
handleConversationsUnreads,
|
|
37
|
+
handleUsersSearch,
|
|
33
38
|
} from "../lib/handlers.js";
|
|
34
39
|
|
|
35
40
|
const app = express();
|
|
@@ -86,7 +91,7 @@ app.use((req, res, next) => {
|
|
|
86
91
|
res.header("Access-Control-Allow-Origin", origin);
|
|
87
92
|
}
|
|
88
93
|
res.header("Access-Control-Allow-Headers", "Content-Type, Authorization");
|
|
89
|
-
res.header("Access-Control-Allow-Methods", "GET, POST, OPTIONS");
|
|
94
|
+
res.header("Access-Control-Allow-Methods", "GET, POST, DELETE, OPTIONS");
|
|
90
95
|
if (req.method === "OPTIONS") {
|
|
91
96
|
return res.sendStatus(200);
|
|
92
97
|
}
|
|
@@ -136,7 +141,7 @@ function extractContent(result) {
|
|
|
136
141
|
app.get("/", (req, res) => {
|
|
137
142
|
res.json({
|
|
138
143
|
name: "Slack Web API Server",
|
|
139
|
-
version: "3.
|
|
144
|
+
version: "3.2.0",
|
|
140
145
|
status: "ok",
|
|
141
146
|
code: "ok",
|
|
142
147
|
message: "Web API server is running.",
|
|
@@ -151,6 +156,11 @@ app.get("/", (req, res) => {
|
|
|
151
156
|
"POST /messages",
|
|
152
157
|
"GET /users",
|
|
153
158
|
"GET /users/:id",
|
|
159
|
+
"POST /reactions",
|
|
160
|
+
"DELETE /reactions",
|
|
161
|
+
"POST /conversations/:id/mark",
|
|
162
|
+
"GET /conversations/unreads",
|
|
163
|
+
"GET /users/search",
|
|
154
164
|
],
|
|
155
165
|
docs: "Add Authorization: Bearer <api-key> header to all requests"
|
|
156
166
|
});
|
|
@@ -207,6 +217,19 @@ app.get("/conversations", authenticate, async (req, res) => {
|
|
|
207
217
|
}
|
|
208
218
|
});
|
|
209
219
|
|
|
220
|
+
// Get unread conversations
|
|
221
|
+
app.get("/conversations/unreads", authenticate, async (req, res) => {
|
|
222
|
+
try {
|
|
223
|
+
const result = await handleConversationsUnreads({
|
|
224
|
+
types: req.query.types || "im,mpim,public_channel,private_channel",
|
|
225
|
+
limit: parseInt(req.query.limit) || 50
|
|
226
|
+
});
|
|
227
|
+
res.json(extractContent(result));
|
|
228
|
+
} catch (e) {
|
|
229
|
+
sendStructuredError(res, 500, "unreads_failed", String(e?.message || e));
|
|
230
|
+
}
|
|
231
|
+
});
|
|
232
|
+
|
|
210
233
|
// Get conversation history
|
|
211
234
|
app.get("/conversations/:id/history", authenticate, async (req, res) => {
|
|
212
235
|
try {
|
|
@@ -253,6 +276,28 @@ app.get("/conversations/:id/thread/:ts", authenticate, async (req, res) => {
|
|
|
253
276
|
}
|
|
254
277
|
});
|
|
255
278
|
|
|
279
|
+
// Mark conversation as read
|
|
280
|
+
app.post("/conversations/:id/mark", authenticate, async (req, res) => {
|
|
281
|
+
try {
|
|
282
|
+
if (!req.body.timestamp) {
|
|
283
|
+
return sendStructuredError(
|
|
284
|
+
res,
|
|
285
|
+
400,
|
|
286
|
+
"invalid_request",
|
|
287
|
+
"timestamp is required",
|
|
288
|
+
"Include timestamp in request JSON."
|
|
289
|
+
);
|
|
290
|
+
}
|
|
291
|
+
const result = await handleConversationsMark({
|
|
292
|
+
channel_id: req.params.id,
|
|
293
|
+
timestamp: req.body.timestamp
|
|
294
|
+
});
|
|
295
|
+
res.json(extractContent(result));
|
|
296
|
+
} catch (e) {
|
|
297
|
+
sendStructuredError(res, 500, "mark_failed", String(e?.message || e));
|
|
298
|
+
}
|
|
299
|
+
});
|
|
300
|
+
|
|
256
301
|
// Search messages
|
|
257
302
|
app.get("/search", authenticate, async (req, res) => {
|
|
258
303
|
try {
|
|
@@ -310,6 +355,28 @@ app.get("/users", authenticate, async (req, res) => {
|
|
|
310
355
|
}
|
|
311
356
|
});
|
|
312
357
|
|
|
358
|
+
// Search users (must be registered before /users/:id to avoid Express matching "search" as an ID)
|
|
359
|
+
app.get("/users/search", authenticate, async (req, res) => {
|
|
360
|
+
try {
|
|
361
|
+
if (!req.query.q) {
|
|
362
|
+
return sendStructuredError(
|
|
363
|
+
res,
|
|
364
|
+
400,
|
|
365
|
+
"invalid_request",
|
|
366
|
+
"Query parameter 'q' is required",
|
|
367
|
+
"Set the q query string and retry."
|
|
368
|
+
);
|
|
369
|
+
}
|
|
370
|
+
const result = await handleUsersSearch({
|
|
371
|
+
query: req.query.q,
|
|
372
|
+
limit: parseInt(req.query.limit) || 20
|
|
373
|
+
});
|
|
374
|
+
res.json(extractContent(result));
|
|
375
|
+
} catch (e) {
|
|
376
|
+
sendStructuredError(res, 500, "user_search_failed", String(e?.message || e));
|
|
377
|
+
}
|
|
378
|
+
});
|
|
379
|
+
|
|
313
380
|
// Get user info
|
|
314
381
|
app.get("/users/:id", authenticate, async (req, res) => {
|
|
315
382
|
try {
|
|
@@ -322,6 +389,52 @@ app.get("/users/:id", authenticate, async (req, res) => {
|
|
|
322
389
|
}
|
|
323
390
|
});
|
|
324
391
|
|
|
392
|
+
// Add reaction
|
|
393
|
+
app.post("/reactions", authenticate, async (req, res) => {
|
|
394
|
+
try {
|
|
395
|
+
if (!req.body.channel_id || !req.body.timestamp || !req.body.reaction) {
|
|
396
|
+
return sendStructuredError(
|
|
397
|
+
res,
|
|
398
|
+
400,
|
|
399
|
+
"invalid_request",
|
|
400
|
+
"channel_id, timestamp, and reaction are required",
|
|
401
|
+
"Include channel_id, timestamp, and reaction in request JSON."
|
|
402
|
+
);
|
|
403
|
+
}
|
|
404
|
+
const result = await handleAddReaction({
|
|
405
|
+
channel_id: req.body.channel_id,
|
|
406
|
+
timestamp: req.body.timestamp,
|
|
407
|
+
reaction: req.body.reaction
|
|
408
|
+
});
|
|
409
|
+
res.json(extractContent(result));
|
|
410
|
+
} catch (e) {
|
|
411
|
+
sendStructuredError(res, 500, "add_reaction_failed", String(e?.message || e));
|
|
412
|
+
}
|
|
413
|
+
});
|
|
414
|
+
|
|
415
|
+
// Remove reaction
|
|
416
|
+
app.delete("/reactions", authenticate, async (req, res) => {
|
|
417
|
+
try {
|
|
418
|
+
if (!req.body.channel_id || !req.body.timestamp || !req.body.reaction) {
|
|
419
|
+
return sendStructuredError(
|
|
420
|
+
res,
|
|
421
|
+
400,
|
|
422
|
+
"invalid_request",
|
|
423
|
+
"channel_id, timestamp, and reaction are required",
|
|
424
|
+
"Include channel_id, timestamp, and reaction in request JSON."
|
|
425
|
+
);
|
|
426
|
+
}
|
|
427
|
+
const result = await handleRemoveReaction({
|
|
428
|
+
channel_id: req.body.channel_id,
|
|
429
|
+
timestamp: req.body.timestamp,
|
|
430
|
+
reaction: req.body.reaction
|
|
431
|
+
});
|
|
432
|
+
res.json(extractContent(result));
|
|
433
|
+
} catch (e) {
|
|
434
|
+
sendStructuredError(res, 500, "remove_reaction_failed", String(e?.message || e));
|
|
435
|
+
}
|
|
436
|
+
});
|
|
437
|
+
|
|
325
438
|
// Start server
|
|
326
439
|
async function main() {
|
|
327
440
|
// Check for credentials
|
|
@@ -337,7 +450,7 @@ async function main() {
|
|
|
337
450
|
app.listen(PORT, '127.0.0.1', () => {
|
|
338
451
|
// Print to stderr to keep logs clean (stdout reserved for JSON in some setups)
|
|
339
452
|
console.error(`\n${"═".repeat(60)}`);
|
|
340
|
-
console.error(` Slack Web API Server v3.
|
|
453
|
+
console.error(` Slack Web API Server v3.2.0`);
|
|
341
454
|
console.error(`${"═".repeat(60)}`);
|
|
342
455
|
console.error(`\n Dashboard: http://localhost:${PORT}/?key=${API_KEY}`);
|
|
343
456
|
console.error(`\n API Key: ${API_KEY}`);
|
|
@@ -1,67 +0,0 @@
|
|
|
1
|
-
# Cloudflare Browser Toolkit
|
|
2
|
-
|
|
3
|
-
Use Cloudflare Browser Rendering from this repo to run resilient page checks and captures when local browser automation is blocked.
|
|
4
|
-
|
|
5
|
-
## Prerequisites
|
|
6
|
-
|
|
7
|
-
- `CLOUDFLARE_ACCOUNT_ID` set
|
|
8
|
-
- `CLOUDFLARE_API_TOKEN` **or** `CF_TERRAFORM_TOKEN` set
|
|
9
|
-
- Token needs Browser Rendering access
|
|
10
|
-
- Account API tokens are supported (recommended for this workflow)
|
|
11
|
-
|
|
12
|
-
## Verify Access
|
|
13
|
-
|
|
14
|
-
```bash
|
|
15
|
-
npm run cf:browser -- verify
|
|
16
|
-
```
|
|
17
|
-
|
|
18
|
-
The verifier checks account-token auth (`/accounts/{account_id}/tokens/verify`) first and falls back to user-token verification automatically.
|
|
19
|
-
|
|
20
|
-
## Quick Commands
|
|
21
|
-
|
|
22
|
-
Rendered HTML:
|
|
23
|
-
|
|
24
|
-
```bash
|
|
25
|
-
npm run cf:browser -- content "https://example.com"
|
|
26
|
-
```
|
|
27
|
-
|
|
28
|
-
Markdown extract:
|
|
29
|
-
|
|
30
|
-
```bash
|
|
31
|
-
npm run cf:browser -- markdown "https://example.com"
|
|
32
|
-
```
|
|
33
|
-
|
|
34
|
-
Link extraction:
|
|
35
|
-
|
|
36
|
-
```bash
|
|
37
|
-
npm run cf:browser -- links "https://example.com"
|
|
38
|
-
```
|
|
39
|
-
|
|
40
|
-
Selector scrape:
|
|
41
|
-
|
|
42
|
-
```bash
|
|
43
|
-
npm run cf:browser -- scrape "https://example.com" --selectors "h1,.card a"
|
|
44
|
-
```
|
|
45
|
-
|
|
46
|
-
Screenshot:
|
|
47
|
-
|
|
48
|
-
```bash
|
|
49
|
-
npm run cf:browser -- screenshot "https://example.com" --out ./tmp/example.png --type png
|
|
50
|
-
```
|
|
51
|
-
|
|
52
|
-
PDF:
|
|
53
|
-
|
|
54
|
-
```bash
|
|
55
|
-
npm run cf:browser -- pdf "https://example.com" --out ./tmp/example.pdf
|
|
56
|
-
```
|
|
57
|
-
|
|
58
|
-
Structured JSON:
|
|
59
|
-
|
|
60
|
-
```bash
|
|
61
|
-
npm run cf:browser -- json "https://example.com" --schema '{"title":"string","links":["string"]}'
|
|
62
|
-
```
|
|
63
|
-
|
|
64
|
-
## Notes
|
|
65
|
-
|
|
66
|
-
- This toolkit is for page rendering, extraction, and evidence capture.
|
|
67
|
-
- It does not manage third-party account logins for posting workflows.
|
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
# Communication Style Guide
|
|
2
|
-
|
|
3
|
-
Use this guide for release notes, issue replies, and changelog entries.
|
|
4
|
-
|
|
5
|
-
## Rules
|
|
6
|
-
|
|
7
|
-
1. Keep text technical, concise, and factual.
|
|
8
|
-
2. Do not include model/tool credit lines.
|
|
9
|
-
3. Do not include co-author trailers from tooling.
|
|
10
|
-
4. State exact versions and commands when relevant.
|
|
11
|
-
5. Avoid speculative claims.
|
|
12
|
-
6. Release titles use `vX.Y.Z — <concrete operational outcome>`.
|
|
13
|
-
|
|
14
|
-
## Issue Reply Template
|
|
15
|
-
|
|
16
|
-
```md
|
|
17
|
-
Thanks for reporting this.
|
|
18
|
-
|
|
19
|
-
Status: fixed in `<version>`.
|
|
20
|
-
|
|
21
|
-
Included:
|
|
22
|
-
- `<fix 1>`
|
|
23
|
-
- `<fix 2>`
|
|
24
|
-
|
|
25
|
-
Verify:
|
|
26
|
-
- `npx -y @jtalk22/slack-mcp --version`
|
|
27
|
-
- `npx -y @jtalk22/slack-mcp --status`
|
|
28
|
-
|
|
29
|
-
Install/update:
|
|
30
|
-
- `npx -y @jtalk22/slack-mcp`
|
|
31
|
-
- `npm i -g @jtalk22/slack-mcp@<version>`
|
|
32
|
-
|
|
33
|
-
If it still reproduces, reply with OS, Node version, runtime mode (`stdio|web|http|worker`), and exact error output.
|
|
34
|
-
```
|
|
35
|
-
|
|
36
|
-
## Release Notes Template
|
|
37
|
-
|
|
38
|
-
````md
|
|
39
|
-
## <version> — <short title>
|
|
40
|
-
|
|
41
|
-
### Improved
|
|
42
|
-
- <item>
|
|
43
|
-
- <item>
|
|
44
|
-
|
|
45
|
-
### Compatibility
|
|
46
|
-
- No API/tool schema changes.
|
|
47
|
-
|
|
48
|
-
### Verify
|
|
49
|
-
```bash
|
|
50
|
-
npx -y @jtalk22/slack-mcp --version
|
|
51
|
-
npx -y @jtalk22/slack-mcp --setup
|
|
52
|
-
npx -y @jtalk22/slack-mcp --status
|
|
53
|
-
```
|
|
54
|
-
````
|
|
55
|
-
|
|
56
|
-
## Changelog Entry Template
|
|
57
|
-
|
|
58
|
-
```md
|
|
59
|
-
## [<version>] - YYYY-MM-DD
|
|
60
|
-
|
|
61
|
-
### Fixed
|
|
62
|
-
- <item>
|
|
63
|
-
|
|
64
|
-
### Changed
|
|
65
|
-
- <item>
|
|
66
|
-
```
|
package/docs/COMPATIBILITY.md
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
# Compatibility Matrix
|
|
2
|
-
|
|
3
|
-
Use this matrix to choose a known working client/runtime path before rollout.
|
|
4
|
-
|
|
5
|
-
| Client | Mode | Token path | Status | Quick verify command |
|
|
6
|
-
|---|---|---|---|---|
|
|
7
|
-
| Claude Desktop (macOS) | `stdio` | `~/.slack-mcp-tokens.json` via `--setup` auto-extract | Supported | `npx -y @jtalk22/slack-mcp --setup && npx -y @jtalk22/slack-mcp --status` |
|
|
8
|
-
| Claude Desktop (Windows) | `stdio` | `env` (`SLACK_TOKEN`, `SLACK_COOKIE`) in config | Supported | `npx -y @jtalk22/slack-mcp --status` |
|
|
9
|
-
| Claude Desktop (Linux) | `stdio` | `env` or token file via guided setup | Supported | `npx -y @jtalk22/slack-mcp --status` |
|
|
10
|
-
| Claude Code CLI | `stdio` | `~/.slack-mcp-tokens.json` or `env` | Supported | `npx -y @jtalk22/slack-mcp --version && npx -y @jtalk22/slack-mcp --status` |
|
|
11
|
-
| Local Browser UI | `web` | token file or `env` | Supported | `npx -y @jtalk22/slack-mcp web` |
|
|
12
|
-
| Hosted Node Runtime | `http` | `env` in host runtime | Supported with operator controls | `node src/server-http.js` then `curl -s http://localhost:8080/health` |
|
|
13
|
-
| Cloudflare Worker / Smithery transport | `worker` | runtime env/query handoff per deployment config | Supported with deployment validation | `wrangler deploy --config workers/wrangler.toml` and verify `/health` |
|
|
14
|
-
|
|
15
|
-
## Notes
|
|
16
|
-
|
|
17
|
-
1. Runtime baseline is Node 20+.
|
|
18
|
-
2. `--doctor` is the fastest first check when setup status is unknown.
|
|
19
|
-
3. For hosted/team deployment, use [DEPLOYMENT-MODES.md](DEPLOYMENT-MODES.md) before production rollout.
|