@jtalk22/slack-mcp 3.0.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 +63 -28
- 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 +35 -36
- package/public/index.html +10 -6
- package/public/share.html +128 -0
- package/scripts/setup-wizard.js +1 -1
- package/server.json +10 -4
- package/src/server-http.js +16 -1
- package/src/server.js +31 -7
- package/src/web-server.js +119 -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 -40
- package/docs/INSTALL-PROOF.md +0 -18
- package/docs/LAUNCH-COPY-v3.0.0.md +0 -73
- package/docs/LAUNCH-MATRIX.md +0 -22
- package/docs/LAUNCH-OPS.md +0 -71
- package/docs/RELEASE-HEALTH.md +0 -90
- 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-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/web-api-mobile-360x800.png +0 -0
- package/docs/images/web-api-mobile-390x844.png +0 -0
- package/public/demo-claude.html +0 -1958
- package/public/demo-video.html +0 -235
- package/public/demo.html +0 -1196
- package/scripts/build-release-health-delta.js +0 -201
- package/scripts/capture-screenshots.js +0 -146
- package/scripts/check-owner-attribution.sh +0 -80
- package/scripts/check-public-language.sh +0 -25
- package/scripts/check-version-parity.js +0 -176
- package/scripts/cloudflare-browser-tool.js +0 -237
- package/scripts/collect-release-health.js +0 -150
- package/scripts/record-demo.js +0 -162
- package/scripts/release-preflight.js +0 -243
- package/scripts/setup-git-hooks.sh +0 -15
- package/scripts/verify-core.js +0 -159
- package/scripts/verify-install-flow.js +0 -193
- package/scripts/verify-web.js +0 -269
package/scripts/verify-web.js
DELETED
|
@@ -1,269 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
/**
|
|
3
|
-
* Web UI Verification Script
|
|
4
|
-
*
|
|
5
|
-
* Tests:
|
|
6
|
-
* 1. Server starts and prints Magic Link
|
|
7
|
-
* 2. /demo.html contains "STATIC PREVIEW" banner
|
|
8
|
-
* 3. /?key=... serves the dashboard (index.html)
|
|
9
|
-
* 4. /demo-video.html media assets are reachable
|
|
10
|
-
* 5. Server shuts down cleanly
|
|
11
|
-
*/
|
|
12
|
-
|
|
13
|
-
import { spawn } from "child_process";
|
|
14
|
-
import { dirname, join } from "path";
|
|
15
|
-
import { fileURLToPath } from "url";
|
|
16
|
-
|
|
17
|
-
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
18
|
-
const SERVER_PATH = join(__dirname, "../src/web-server.js");
|
|
19
|
-
const PORT = 3456; // Use non-standard port to avoid conflicts
|
|
20
|
-
const TIMEOUT = 15000;
|
|
21
|
-
|
|
22
|
-
let serverProc = null;
|
|
23
|
-
|
|
24
|
-
function log(msg) {
|
|
25
|
-
console.log(` ${msg}`);
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
function cleanup() {
|
|
29
|
-
if (serverProc) {
|
|
30
|
-
serverProc.kill("SIGTERM");
|
|
31
|
-
serverProc = null;
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
process.on("exit", cleanup);
|
|
36
|
-
process.on("SIGINT", () => { cleanup(); process.exit(1); });
|
|
37
|
-
process.on("SIGTERM", () => { cleanup(); process.exit(1); });
|
|
38
|
-
|
|
39
|
-
async function startServer() {
|
|
40
|
-
return new Promise((resolve, reject) => {
|
|
41
|
-
let magicLink = null;
|
|
42
|
-
let apiKey = null;
|
|
43
|
-
let output = "";
|
|
44
|
-
|
|
45
|
-
serverProc = spawn("node", [SERVER_PATH], {
|
|
46
|
-
env: { ...process.env, PORT: String(PORT) },
|
|
47
|
-
stdio: ["pipe", "pipe", "pipe"]
|
|
48
|
-
});
|
|
49
|
-
|
|
50
|
-
const timeout = setTimeout(() => {
|
|
51
|
-
reject(new Error("Server startup timeout - no magic link detected"));
|
|
52
|
-
}, TIMEOUT);
|
|
53
|
-
|
|
54
|
-
serverProc.stderr.on("data", (data) => {
|
|
55
|
-
const text = data.toString();
|
|
56
|
-
output += text;
|
|
57
|
-
|
|
58
|
-
// Look for magic link pattern
|
|
59
|
-
const match = text.match(/Dashboard:\s*(http:\/\/[^\s]+)/);
|
|
60
|
-
if (match) {
|
|
61
|
-
magicLink = match[1];
|
|
62
|
-
// Extract key from URL
|
|
63
|
-
const keyMatch = magicLink.match(/[?&]key=([^&\s]+)/);
|
|
64
|
-
if (keyMatch) {
|
|
65
|
-
apiKey = keyMatch[1];
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
// Server is ready when we see the full banner
|
|
70
|
-
if (output.includes("Dashboard:") && output.includes("API Key:")) {
|
|
71
|
-
clearTimeout(timeout);
|
|
72
|
-
resolve({ magicLink, apiKey });
|
|
73
|
-
}
|
|
74
|
-
});
|
|
75
|
-
|
|
76
|
-
serverProc.on("error", (err) => {
|
|
77
|
-
clearTimeout(timeout);
|
|
78
|
-
reject(err);
|
|
79
|
-
});
|
|
80
|
-
|
|
81
|
-
serverProc.on("exit", (code) => {
|
|
82
|
-
if (code !== null && code !== 0) {
|
|
83
|
-
clearTimeout(timeout);
|
|
84
|
-
reject(new Error(`Server exited with code ${code}`));
|
|
85
|
-
}
|
|
86
|
-
});
|
|
87
|
-
});
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
async function testDemoPage() {
|
|
91
|
-
const url = `http://localhost:${PORT}/demo.html`;
|
|
92
|
-
const res = await fetch(url);
|
|
93
|
-
|
|
94
|
-
if (!res.ok) {
|
|
95
|
-
throw new Error(`Failed to fetch demo.html: ${res.status}`);
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
const html = await res.text();
|
|
99
|
-
|
|
100
|
-
if (!html.includes("STATIC PREVIEW")) {
|
|
101
|
-
throw new Error("demo.html missing 'STATIC PREVIEW' banner");
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
if (!html.includes("Who is Alex?")) {
|
|
105
|
-
throw new Error("demo.html missing anonymized 'Who is Alex?' scenario");
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
return true;
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
async function testDashboard(apiKey) {
|
|
112
|
-
const url = `http://localhost:${PORT}/?key=${apiKey}`;
|
|
113
|
-
const res = await fetch(url);
|
|
114
|
-
|
|
115
|
-
if (!res.ok) {
|
|
116
|
-
throw new Error(`Failed to fetch dashboard: ${res.status}`);
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
const html = await res.text();
|
|
120
|
-
|
|
121
|
-
// Should serve index.html (the dashboard)
|
|
122
|
-
if (!html.includes("Slack Web API")) {
|
|
123
|
-
throw new Error("Dashboard page missing 'Slack Web API' title");
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
if (!html.includes("authModal")) {
|
|
127
|
-
throw new Error("Dashboard missing auth modal");
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
return true;
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
async function testApiWithKey(apiKey) {
|
|
134
|
-
// Test that API rejects bad key
|
|
135
|
-
const badRes = await fetch(`http://localhost:${PORT}/health`, {
|
|
136
|
-
headers: { "Authorization": "Bearer bad-key" }
|
|
137
|
-
});
|
|
138
|
-
|
|
139
|
-
if (badRes.status !== 401) {
|
|
140
|
-
throw new Error(`Expected 401 for bad key, got ${badRes.status}`);
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
return true;
|
|
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
|
-
|
|
185
|
-
async function main() {
|
|
186
|
-
console.log("╔════════════════════════════════════════╗");
|
|
187
|
-
console.log("║ Web UI Verification Tests ║");
|
|
188
|
-
console.log("╚════════════════════════════════════════╝");
|
|
189
|
-
|
|
190
|
-
const results = [];
|
|
191
|
-
|
|
192
|
-
try {
|
|
193
|
-
// Test 1: Server starts with magic link
|
|
194
|
-
console.log("\n[TEST 1] Server Startup & Magic Link");
|
|
195
|
-
console.log("─".repeat(40));
|
|
196
|
-
|
|
197
|
-
const { magicLink, apiKey } = await startServer();
|
|
198
|
-
|
|
199
|
-
if (!magicLink) {
|
|
200
|
-
throw new Error("No magic link found");
|
|
201
|
-
}
|
|
202
|
-
if (!apiKey) {
|
|
203
|
-
throw new Error("No API key found in magic link");
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
log(`Magic Link: ${magicLink}`);
|
|
207
|
-
log(`API Key: ${apiKey.substring(0, 20)}...`);
|
|
208
|
-
log("PASS: Server started with magic link");
|
|
209
|
-
results.push(true);
|
|
210
|
-
|
|
211
|
-
// Test 2: Demo page
|
|
212
|
-
console.log("\n[TEST 2] Demo Page (/demo.html)");
|
|
213
|
-
console.log("─".repeat(40));
|
|
214
|
-
|
|
215
|
-
await testDemoPage();
|
|
216
|
-
log("PASS: Demo page serves correctly with STATIC PREVIEW banner");
|
|
217
|
-
results.push(true);
|
|
218
|
-
|
|
219
|
-
// Test 3: Dashboard
|
|
220
|
-
console.log("\n[TEST 3] Dashboard (/?key=...)");
|
|
221
|
-
console.log("─".repeat(40));
|
|
222
|
-
|
|
223
|
-
await testDashboard(apiKey);
|
|
224
|
-
log("PASS: Dashboard serves with auth modal");
|
|
225
|
-
results.push(true);
|
|
226
|
-
|
|
227
|
-
// Test 4: API auth
|
|
228
|
-
console.log("\n[TEST 4] API Authentication");
|
|
229
|
-
console.log("─".repeat(40));
|
|
230
|
-
|
|
231
|
-
await testApiWithKey(apiKey);
|
|
232
|
-
log("PASS: API correctly rejects bad keys");
|
|
233
|
-
results.push(true);
|
|
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
|
-
|
|
243
|
-
} catch (err) {
|
|
244
|
-
console.log(` FAIL: ${err.message}`);
|
|
245
|
-
results.push(false);
|
|
246
|
-
} finally {
|
|
247
|
-
cleanup();
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
// Summary
|
|
251
|
-
console.log("\n" + "═".repeat(40));
|
|
252
|
-
const passed = results.filter(r => r).length;
|
|
253
|
-
const total = results.length;
|
|
254
|
-
|
|
255
|
-
if (passed === total) {
|
|
256
|
-
console.log(`\n✓ ALL TESTS PASSED (${passed}/${total})`);
|
|
257
|
-
console.log("\nWeb UI features verified");
|
|
258
|
-
process.exit(0);
|
|
259
|
-
} else {
|
|
260
|
-
console.log(`\n✗ TESTS FAILED (${passed}/${total})`);
|
|
261
|
-
process.exit(1);
|
|
262
|
-
}
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
main().catch(e => {
|
|
266
|
-
console.error("Test error:", e);
|
|
267
|
-
cleanup();
|
|
268
|
-
process.exit(1);
|
|
269
|
-
});
|