@jtalk22/slack-mcp 1.2.2 → 1.2.3
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 +53 -4
- package/docs/COMMUNICATION-STYLE.md +14 -7
- package/docs/HN-LAUNCH.md +61 -0
- package/docs/INDEX.md +28 -0
- package/docs/SETUP.md +20 -2
- package/docs/TROUBLESHOOTING.md +27 -0
- package/package.json +1 -1
- package/public/demo-claude.html +10 -10
- package/public/demo-video.html +9 -9
- package/public/demo.html +8 -8
- package/scripts/setup-wizard.js +139 -33
- package/scripts/verify-install-flow.js +87 -0
- package/src/cli.js +1 -0
- package/src/server-http.js +1 -1
- package/src/server.js +2 -2
- package/src/web-server.js +3 -3
package/README.md
CHANGED
|
@@ -37,6 +37,8 @@
|
|
|
37
37
|
|
|
38
38
|
---
|
|
39
39
|
|
|
40
|
+
> Free local-first path: install with `npx -y @jtalk22/slack-mcp`, run on your own machine, and keep full control of session tokens.
|
|
41
|
+
|
|
40
42
|
### Why This Exists
|
|
41
43
|
|
|
42
44
|
I built this because I was working with someone to help me manage a complex workload, and we kept hitting walls. They needed context from my messages—"what did X say about Y?"—and standard app/OAuth flows were too constrained for that workflow.
|
|
@@ -100,15 +102,24 @@ Instead of authenticating as a bot, this server leverages your existing Chrome s
|
|
|
100
102
|
|
|
101
103
|
**Runtime:** Node.js 20+
|
|
102
104
|
|
|
103
|
-
###
|
|
105
|
+
### 30-Second Proof
|
|
104
106
|
|
|
105
107
|
```bash
|
|
106
108
|
npx -y @jtalk22/slack-mcp --version
|
|
109
|
+
npx -y @jtalk22/slack-mcp --doctor
|
|
107
110
|
npx -y @jtalk22/slack-mcp --setup
|
|
108
|
-
npx -y @jtalk22/slack-mcp --status
|
|
109
111
|
```
|
|
110
112
|
|
|
111
|
-
|
|
113
|
+
Expected:
|
|
114
|
+
- `--version` prints `slack-mcp-server v1.2.x`
|
|
115
|
+
- `--doctor` returns one clear next action with exit code:
|
|
116
|
+
- `0` ready
|
|
117
|
+
- `1` missing credentials
|
|
118
|
+
- `2` invalid/expired credentials
|
|
119
|
+
- `3` connectivity/runtime issue
|
|
120
|
+
- `--setup` launches the interactive wizard
|
|
121
|
+
|
|
122
|
+
Proof script for launch threads: [docs/HN-LAUNCH.md](docs/HN-LAUNCH.md)
|
|
112
123
|
|
|
113
124
|
### Option A: npm (Recommended)
|
|
114
125
|
|
|
@@ -130,6 +141,20 @@ npm install
|
|
|
130
141
|
docker pull ghcr.io/jtalk22/slack-mcp-server:latest
|
|
131
142
|
```
|
|
132
143
|
|
|
144
|
+
### Install Sanity (Clean Temp Directory)
|
|
145
|
+
|
|
146
|
+
```bash
|
|
147
|
+
tmpdir="$(mktemp -d)"
|
|
148
|
+
cd "$tmpdir"
|
|
149
|
+
npx -y @jtalk22/slack-mcp --version
|
|
150
|
+
npx -y @jtalk22/slack-mcp --help
|
|
151
|
+
npx -y @jtalk22/slack-mcp --status
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
Expected:
|
|
155
|
+
- `--version` and `--help` exit `0`
|
|
156
|
+
- `--status` exits non-zero until credentials are configured
|
|
157
|
+
|
|
133
158
|
---
|
|
134
159
|
|
|
135
160
|
## Configuration
|
|
@@ -300,7 +325,7 @@ npm run web
|
|
|
300
325
|
|
|
301
326
|
```
|
|
302
327
|
════════════════════════════════════════════════════════════
|
|
303
|
-
Slack Web API Server v1.2.
|
|
328
|
+
Slack Web API Server v1.2.3
|
|
304
329
|
════════════════════════════════════════════════════════════
|
|
305
330
|
|
|
306
331
|
Dashboard: http://localhost:3000/?key=smcp_xxxxxxxxxxxx
|
|
@@ -321,6 +346,7 @@ Just click the link - no copy-paste needed. The key is saved to your browser and
|
|
|
321
346
|
|
|
322
347
|
## Operations Guides
|
|
323
348
|
|
|
349
|
+
- [Docs Index](docs/INDEX.md) - One-click index for setup, API, troubleshooting, deployment, and support docs
|
|
324
350
|
- [Deployment Modes](docs/DEPLOYMENT-MODES.md) - Choose the right operating model (`stdio`, `web`, hosted HTTP, Smithery/Worker)
|
|
325
351
|
- [Use Case Recipes](docs/USE_CASE_RECIPES.md) - 12 copy/paste prompts mapped to current tool contracts
|
|
326
352
|
- [Support Boundaries](docs/SUPPORT-BOUNDARIES.md) - Scope, response targets, and solo-maintainer capacity limits
|
|
@@ -329,6 +355,26 @@ If you're evaluating team rollout, start with [Deployment Modes](docs/DEPLOYMENT
|
|
|
329
355
|
|
|
330
356
|
---
|
|
331
357
|
|
|
358
|
+
## Getting Help Fast
|
|
359
|
+
|
|
360
|
+
1. Run:
|
|
361
|
+
```bash
|
|
362
|
+
npx -y @jtalk22/slack-mcp --version
|
|
363
|
+
npx -y @jtalk22/slack-mcp --doctor
|
|
364
|
+
```
|
|
365
|
+
2. If setup fails, run:
|
|
366
|
+
```bash
|
|
367
|
+
npx -y @jtalk22/slack-mcp --setup
|
|
368
|
+
```
|
|
369
|
+
3. Open an issue with full environment details:
|
|
370
|
+
- [Bug Report Template](.github/ISSUE_TEMPLATE/bug_report.md)
|
|
371
|
+
- [Deployment Intake Template](.github/ISSUE_TEMPLATE/deployment-intake.md)
|
|
372
|
+
4. Check scope and response targets:
|
|
373
|
+
- [Support Boundaries](docs/SUPPORT-BOUNDARIES.md)
|
|
374
|
+
- [Troubleshooting Guide](docs/TROUBLESHOOTING.md)
|
|
375
|
+
|
|
376
|
+
---
|
|
377
|
+
|
|
332
378
|
## Troubleshooting
|
|
333
379
|
|
|
334
380
|
### Tokens Expired
|
|
@@ -337,6 +383,9 @@ If you're evaluating team rollout, start with [Deployment Modes](docs/DEPLOYMENT
|
|
|
337
383
|
slack_refresh_tokens # In Claude
|
|
338
384
|
# Or: npm run tokens:auto
|
|
339
385
|
|
|
386
|
+
# Package setup wizard
|
|
387
|
+
npx -y @jtalk22/slack-mcp --setup
|
|
388
|
+
|
|
340
389
|
# Linux/Windows: Manual update
|
|
341
390
|
# Edit ~/.slack-mcp-tokens.json with fresh values
|
|
342
391
|
```
|
|
@@ -18,12 +18,18 @@ Thanks for reporting this.
|
|
|
18
18
|
Status: fixed in `<version>`.
|
|
19
19
|
|
|
20
20
|
Included:
|
|
21
|
-
- `<
|
|
22
|
-
- `<
|
|
21
|
+
- `<fix 1>`
|
|
22
|
+
- `<fix 2>`
|
|
23
|
+
|
|
24
|
+
Verify:
|
|
25
|
+
- `npx -y @jtalk22/slack-mcp --version`
|
|
26
|
+
- `npx -y @jtalk22/slack-mcp --status`
|
|
23
27
|
|
|
24
28
|
Install/update:
|
|
25
29
|
- `npx -y @jtalk22/slack-mcp`
|
|
26
30
|
- `npm i -g @jtalk22/slack-mcp@<version>`
|
|
31
|
+
|
|
32
|
+
If it still reproduces, reply with OS, Node version, runtime mode (`stdio|web|http|worker`), and exact error output.
|
|
27
33
|
```
|
|
28
34
|
|
|
29
35
|
## Release Notes Template
|
|
@@ -31,17 +37,18 @@ Install/update:
|
|
|
31
37
|
````md
|
|
32
38
|
## <version> — <short title>
|
|
33
39
|
|
|
34
|
-
###
|
|
40
|
+
### Improved
|
|
35
41
|
- <item>
|
|
36
42
|
- <item>
|
|
37
43
|
|
|
38
|
-
###
|
|
39
|
-
-
|
|
44
|
+
### Compatibility
|
|
45
|
+
- No API/tool schema changes.
|
|
40
46
|
|
|
41
47
|
### Verify
|
|
42
48
|
```bash
|
|
43
|
-
|
|
44
|
-
|
|
49
|
+
npx -y @jtalk22/slack-mcp --version
|
|
50
|
+
npx -y @jtalk22/slack-mcp --setup
|
|
51
|
+
npx -y @jtalk22/slack-mcp --status
|
|
45
52
|
```
|
|
46
53
|
````
|
|
47
54
|
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# HN Launch Kit
|
|
2
|
+
|
|
3
|
+
Use this file as copy/paste launch material with no extra edits.
|
|
4
|
+
|
|
5
|
+
## Launch Post Draft
|
|
6
|
+
|
|
7
|
+
Title idea: `Show HN: Slack MCP Server (local-first, session-based Slack access for Claude)`
|
|
8
|
+
|
|
9
|
+
Post body:
|
|
10
|
+
|
|
11
|
+
```md
|
|
12
|
+
Built a local-first Slack MCP server so Claude can use the same Slack access already available in your browser session.
|
|
13
|
+
|
|
14
|
+
What it does:
|
|
15
|
+
- DMs/channels/thread reads
|
|
16
|
+
- workspace search
|
|
17
|
+
- message send + user lookups
|
|
18
|
+
- local web mode when MCP is unavailable
|
|
19
|
+
|
|
20
|
+
Quick proof:
|
|
21
|
+
- `npx -y @jtalk22/slack-mcp --version`
|
|
22
|
+
- `npx -y @jtalk22/slack-mcp --status`
|
|
23
|
+
- `npx -y @jtalk22/slack-mcp --setup`
|
|
24
|
+
|
|
25
|
+
Repo: https://github.com/jtalk22/slack-mcp-server
|
|
26
|
+
npm: https://www.npmjs.com/package/@jtalk22/slack-mcp
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## First Comment Draft
|
|
30
|
+
|
|
31
|
+
```md
|
|
32
|
+
Notes up front:
|
|
33
|
+
- Local-first usage is fully supported.
|
|
34
|
+
- Tokens are your Slack session credentials and are stored locally by default.
|
|
35
|
+
- macOS supports automatic Chrome extraction; Linux/Windows use guided manual setup.
|
|
36
|
+
- Current docs include deployment modes, support boundaries, and troubleshooting.
|
|
37
|
+
|
|
38
|
+
If install fails, include OS, Node version, runtime mode (`stdio|web|http|worker`), and exact error output.
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## Rebuttal Mini-FAQ
|
|
42
|
+
|
|
43
|
+
### Is this bypassing Slack app scopes?
|
|
44
|
+
It does not use Slack app OAuth scopes. It uses your existing signed-in session permissions.
|
|
45
|
+
|
|
46
|
+
### What about token expiry?
|
|
47
|
+
Session tokens expire. `--setup` refreshes credentials, and macOS supports automatic extraction from Chrome.
|
|
48
|
+
|
|
49
|
+
### Is this intended as a stealth hosted proxy?
|
|
50
|
+
No. The free path is local/self-hosted first. Deployment docs describe tradeoffs and support boundaries before team rollout.
|
|
51
|
+
|
|
52
|
+
### Is this safe for production teams?
|
|
53
|
+
Treat as operator-managed infrastructure. Validate with your own risk/compliance controls before broader rollout.
|
|
54
|
+
|
|
55
|
+
## Install Proof Block
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
npx -y @jtalk22/slack-mcp --version
|
|
59
|
+
npx -y @jtalk22/slack-mcp --status
|
|
60
|
+
npx -y @jtalk22/slack-mcp --setup
|
|
61
|
+
```
|
package/docs/INDEX.md
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# Documentation Index
|
|
2
|
+
|
|
3
|
+
Start here for setup, validation, operations, and support.
|
|
4
|
+
|
|
5
|
+
## Core
|
|
6
|
+
|
|
7
|
+
- [Setup Guide](SETUP.md)
|
|
8
|
+
- [API Reference](API.md)
|
|
9
|
+
- [Web API Reference](WEB-API.md)
|
|
10
|
+
- [Troubleshooting](TROUBLESHOOTING.md)
|
|
11
|
+
|
|
12
|
+
## Operations
|
|
13
|
+
|
|
14
|
+
- [Deployment Modes](DEPLOYMENT-MODES.md)
|
|
15
|
+
- [Use Case Recipes](USE_CASE_RECIPES.md)
|
|
16
|
+
- [Support Boundaries](SUPPORT-BOUNDARIES.md)
|
|
17
|
+
|
|
18
|
+
## Launch and Communication
|
|
19
|
+
|
|
20
|
+
- [HN Launch Kit](HN-LAUNCH.md)
|
|
21
|
+
- [Communication Style](COMMUNICATION-STYLE.md)
|
|
22
|
+
- [Changelog](../CHANGELOG.md)
|
|
23
|
+
|
|
24
|
+
## Issue Intake
|
|
25
|
+
|
|
26
|
+
- [Bug Report Template](../.github/ISSUE_TEMPLATE/bug_report.md)
|
|
27
|
+
- [Feature Request Template](../.github/ISSUE_TEMPLATE/feature_request.md)
|
|
28
|
+
- [Deployment Intake Template](../.github/ISSUE_TEMPLATE/deployment-intake.md)
|
package/docs/SETUP.md
CHANGED
|
@@ -23,6 +23,24 @@ cd ~/slack-mcp-server
|
|
|
23
23
|
npm install
|
|
24
24
|
```
|
|
25
25
|
|
|
26
|
+
### 2.5 Verify Install Path in a Clean Directory
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
tmpdir="$(mktemp -d)"
|
|
30
|
+
cd "$tmpdir"
|
|
31
|
+
npx -y @jtalk22/slack-mcp --version
|
|
32
|
+
npx -y @jtalk22/slack-mcp --help
|
|
33
|
+
npx -y @jtalk22/slack-mcp --doctor
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Expected:
|
|
37
|
+
- `--version` and `--help` succeed.
|
|
38
|
+
- `--doctor` exits with one clear status:
|
|
39
|
+
- `0` ready
|
|
40
|
+
- `1` missing credentials
|
|
41
|
+
- `2` invalid/expired credentials
|
|
42
|
+
- `3` connectivity/runtime issue
|
|
43
|
+
|
|
26
44
|
### 3. Get Slack Tokens
|
|
27
45
|
|
|
28
46
|
**Option A: Setup Wizard (recommended)**
|
|
@@ -120,11 +138,11 @@ You should see your username and team name.
|
|
|
120
138
|
|
|
121
139
|
### "No credentials found"
|
|
122
140
|
|
|
123
|
-
Run `npx -y @jtalk22/slack-mcp --
|
|
141
|
+
Run `npx -y @jtalk22/slack-mcp --doctor` to confirm diagnostic code, then `npx -y @jtalk22/slack-mcp --setup` with Slack open in Chrome.
|
|
124
142
|
|
|
125
143
|
### "invalid_auth" Error
|
|
126
144
|
|
|
127
|
-
Tokens have expired.
|
|
145
|
+
Tokens have expired. Run `npx -y @jtalk22/slack-mcp --doctor` and follow the suggested next action.
|
|
128
146
|
|
|
129
147
|
### MCP Server Not Loading
|
|
130
148
|
|
package/docs/TROUBLESHOOTING.md
CHANGED
|
@@ -4,6 +4,30 @@ Common issues and their solutions.
|
|
|
4
4
|
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
+
## Install Flow Sanity Check
|
|
8
|
+
|
|
9
|
+
If first-run setup is failing, validate command resolution in a clean directory:
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
tmpdir="$(mktemp -d)"
|
|
13
|
+
cd "$tmpdir"
|
|
14
|
+
npx -y @jtalk22/slack-mcp --version
|
|
15
|
+
npx -y @jtalk22/slack-mcp --help
|
|
16
|
+
npx -y @jtalk22/slack-mcp --doctor
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Expected:
|
|
20
|
+
- `--version` and `--help` exit `0`
|
|
21
|
+
- `--doctor` exits with one of:
|
|
22
|
+
- `0` ready
|
|
23
|
+
- `1` missing credentials
|
|
24
|
+
- `2` invalid/expired credentials
|
|
25
|
+
- `3` connectivity/runtime issue
|
|
26
|
+
|
|
27
|
+
If `--version` fails here, the issue is install/runtime path, not Slack credentials.
|
|
28
|
+
|
|
29
|
+
---
|
|
30
|
+
|
|
7
31
|
## DMs Not Showing Up
|
|
8
32
|
|
|
9
33
|
**Symptom:** `slack_list_conversations` returns channels but no DMs.
|
|
@@ -57,6 +81,9 @@ slack_refresh_tokens
|
|
|
57
81
|
# Option 2: Package setup wizard
|
|
58
82
|
npx -y @jtalk22/slack-mcp --setup
|
|
59
83
|
|
|
84
|
+
# Option 3: Doctor diagnostics
|
|
85
|
+
npx -y @jtalk22/slack-mcp --doctor
|
|
86
|
+
|
|
60
87
|
# Option 3: Repo CLI
|
|
61
88
|
npm run tokens:auto
|
|
62
89
|
|
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": "1.2.
|
|
4
|
+
"version": "1.2.3",
|
|
5
5
|
"description": "Session-based Slack access for Claude - DMs, channels, search, and threads. Local-first with your existing Slack session.",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"main": "src/server.js",
|
package/public/demo-claude.html
CHANGED
|
@@ -12,19 +12,19 @@
|
|
|
12
12
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
13
13
|
<meta name="author" content="@jtalk22">
|
|
14
14
|
<title>Slack MCP Server - Claude Desktop Demo</title>
|
|
15
|
-
<meta name="description" content="Session-based Slack access
|
|
15
|
+
<meta name="description" content="Session-based Slack access for Claude with your existing workspace permissions. Demo workflows for DMs, channels, search, and threads.">
|
|
16
16
|
|
|
17
17
|
<!-- Open Graph -->
|
|
18
|
-
<meta property="og:title" content="Slack MCP Server - Session Access Demo">
|
|
19
|
-
<meta property="og:description" content="
|
|
18
|
+
<meta property="og:title" content="Slack MCP Server - Session-Based Slack Access Demo">
|
|
19
|
+
<meta property="og:description" content="Session-based Slack access for Claude with your existing workspace permissions. Search, thread, and messaging workflows.">
|
|
20
20
|
<meta property="og:type" content="website">
|
|
21
21
|
<meta property="og:url" content="https://jtalk22.github.io/slack-mcp-server/public/demo-claude.html">
|
|
22
22
|
<meta property="og:image" content="https://assets-worker.james-20a.workers.dev/projects/slack-mcp-server/demo-claude.gif">
|
|
23
23
|
|
|
24
24
|
<!-- Twitter Card -->
|
|
25
25
|
<meta name="twitter:card" content="summary_large_image">
|
|
26
|
-
<meta name="twitter:title" content="Slack MCP Server - Session Access Demo">
|
|
27
|
-
<meta name="twitter:description" content="
|
|
26
|
+
<meta name="twitter:title" content="Slack MCP Server - Session-Based Slack Access Demo">
|
|
27
|
+
<meta name="twitter:description" content="Session-based Slack access for Claude with your existing workspace permissions. Search, thread, and messaging workflows.">
|
|
28
28
|
<meta name="twitter:image" content="https://assets-worker.james-20a.workers.dev/projects/slack-mcp-server/demo-claude.gif">
|
|
29
29
|
|
|
30
30
|
<!-- Theme -->
|
|
@@ -1151,17 +1151,17 @@
|
|
|
1151
1151
|
<div class="cta-strip">
|
|
1152
1152
|
<div class="links">
|
|
1153
1153
|
<a href="https://www.npmjs.com/package/@jtalk22/slack-mcp" target="_blank" rel="noopener noreferrer">Install</a>
|
|
1154
|
-
<a href="https://github.com/jtalk22/slack-mcp-server/blob/main/docs/
|
|
1155
|
-
<a href="https://github.com/jtalk22/slack-mcp-server
|
|
1154
|
+
<a href="https://github.com/jtalk22/slack-mcp-server/blob/main/docs/SETUP.md" target="_blank" rel="noopener noreferrer">Setup Guide</a>
|
|
1155
|
+
<a href="https://github.com/jtalk22/slack-mcp-server#30-second-proof" target="_blank" rel="noopener noreferrer">30-Second Proof</a>
|
|
1156
1156
|
</div>
|
|
1157
1157
|
<div class="note">
|
|
1158
|
-
|
|
1158
|
+
Free local-first path with optional team rollout guidance in <a href="https://github.com/jtalk22/slack-mcp-server/blob/main/docs/DEPLOYMENT-MODES.md" target="_blank" rel="noopener noreferrer">Deployment Modes</a>.
|
|
1159
1159
|
</div>
|
|
1160
1160
|
</div>
|
|
1161
1161
|
<header class="page-header">
|
|
1162
1162
|
<h1>
|
|
1163
1163
|
<span>Slack MCP Server</span>
|
|
1164
|
-
<span class="badge">🔧 MCP Demo v1.2.
|
|
1164
|
+
<span class="badge">🔧 MCP Demo v1.2.3</span>
|
|
1165
1165
|
</h1>
|
|
1166
1166
|
<p>See how Claude uses MCP tools to access your Slack workspace</p>
|
|
1167
1167
|
</header>
|
|
@@ -1233,7 +1233,7 @@
|
|
|
1233
1233
|
<div class="title-logo">💬</div>
|
|
1234
1234
|
<h1>Slack MCP Server</h1>
|
|
1235
1235
|
<p class="title-tagline">Full Slack access for Claude Desktop</p>
|
|
1236
|
-
<p class="title-version">v1.2.
|
|
1236
|
+
<p class="title-version">v1.2.3 • @jtalk22</p>
|
|
1237
1237
|
</div>
|
|
1238
1238
|
|
|
1239
1239
|
<!-- Scenario Caption Overlay -->
|
package/public/demo-video.html
CHANGED
|
@@ -4,15 +4,15 @@
|
|
|
4
4
|
<meta charset="UTF-8">
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
6
|
<title>Slack MCP Server Demo</title>
|
|
7
|
-
<meta name="description" content="
|
|
7
|
+
<meta name="description" content="Session-based Slack access for Claude with your existing workspace permissions. Video demo for DMs, channels, search, and threads.">
|
|
8
8
|
<meta property="og:type" content="website">
|
|
9
|
-
<meta property="og:title" content="Slack MCP Server - Session Access Demo">
|
|
10
|
-
<meta property="og:description" content="
|
|
9
|
+
<meta property="og:title" content="Slack MCP Server - Session-Based Slack Access Demo">
|
|
10
|
+
<meta property="og:description" content="Session-based Slack access for Claude with your existing workspace permissions. Video walkthrough for Slack workflows.">
|
|
11
11
|
<meta property="og:url" content="https://jtalk22.github.io/slack-mcp-server/public/demo-video.html">
|
|
12
12
|
<meta property="og:image" content="https://raw.githubusercontent.com/jtalk22/slack-mcp-server/main/docs/images/demo-poster.png">
|
|
13
13
|
<meta name="twitter:card" content="summary_large_image">
|
|
14
|
-
<meta name="twitter:title" content="Slack MCP Server - Session Access Demo">
|
|
15
|
-
<meta name="twitter:description" content="
|
|
14
|
+
<meta name="twitter:title" content="Slack MCP Server - Session-Based Slack Access Demo">
|
|
15
|
+
<meta name="twitter:description" content="Session-based Slack access for Claude with your existing workspace permissions. Video walkthrough for Slack workflows.">
|
|
16
16
|
<meta name="twitter:image" content="https://raw.githubusercontent.com/jtalk22/slack-mcp-server/main/docs/images/demo-poster.png">
|
|
17
17
|
<style>
|
|
18
18
|
* {
|
|
@@ -142,15 +142,15 @@
|
|
|
142
142
|
<body>
|
|
143
143
|
<div class="container">
|
|
144
144
|
<h1>Slack MCP Server</h1>
|
|
145
|
-
<p class="subtitle">
|
|
145
|
+
<p class="subtitle">Free local-first Slack access using your existing session permissions</p>
|
|
146
146
|
<div class="cta-strip">
|
|
147
147
|
<div class="links">
|
|
148
148
|
<a href="https://www.npmjs.com/package/@jtalk22/slack-mcp" target="_blank" rel="noopener noreferrer">Install</a>
|
|
149
|
-
<a href="https://github.com/jtalk22/slack-mcp-server/blob/main/docs/
|
|
150
|
-
<a href="https://github.com/jtalk22/slack-mcp-server
|
|
149
|
+
<a href="https://github.com/jtalk22/slack-mcp-server/blob/main/docs/SETUP.md" target="_blank" rel="noopener noreferrer">Setup Guide</a>
|
|
150
|
+
<a href="https://github.com/jtalk22/slack-mcp-server#30-second-proof" target="_blank" rel="noopener noreferrer">30-Second Proof</a>
|
|
151
151
|
</div>
|
|
152
152
|
<div class="note">
|
|
153
|
-
|
|
153
|
+
Free local-first path with optional team rollout guidance in <a href="https://github.com/jtalk22/slack-mcp-server/blob/main/docs/DEPLOYMENT-MODES.md" target="_blank" rel="noopener noreferrer">Deployment Modes</a>.
|
|
154
154
|
</div>
|
|
155
155
|
</div>
|
|
156
156
|
|
package/public/demo.html
CHANGED
|
@@ -8,18 +8,18 @@
|
|
|
8
8
|
<!-- Open Graph / Social Sharing -->
|
|
9
9
|
<meta property="og:type" content="website">
|
|
10
10
|
<meta property="og:url" content="https://jtalk22.github.io/slack-mcp-server/public/demo.html">
|
|
11
|
-
<meta property="og:title" content="Slack MCP Server - Session Access Demo">
|
|
12
|
-
<meta property="og:description" content="
|
|
11
|
+
<meta property="og:title" content="Slack MCP Server - Session-Based Slack Access Demo">
|
|
12
|
+
<meta property="og:description" content="Session-based Slack access for Claude with your existing workspace permissions. DMs, channels, search, and threads.">
|
|
13
13
|
<meta property="og:image" content="https://raw.githubusercontent.com/jtalk22/slack-mcp-server/main/docs/images/demo-main.png">
|
|
14
14
|
|
|
15
15
|
<!-- Twitter Card -->
|
|
16
16
|
<meta name="twitter:card" content="summary_large_image">
|
|
17
|
-
<meta name="twitter:title" content="Slack MCP Server - Session Access Demo">
|
|
18
|
-
<meta name="twitter:description" content="
|
|
17
|
+
<meta name="twitter:title" content="Slack MCP Server - Session-Based Slack Access Demo">
|
|
18
|
+
<meta name="twitter:description" content="Session-based Slack access for Claude with your existing workspace permissions. DMs, channels, search, and threads.">
|
|
19
19
|
<meta name="twitter:image" content="https://raw.githubusercontent.com/jtalk22/slack-mcp-server/main/docs/images/demo-main.png">
|
|
20
20
|
|
|
21
21
|
<!-- SEO -->
|
|
22
|
-
<meta name="description" content="Session-based Slack access for Claude. Interactive demo for DMs, channels, search, and thread workflows.">
|
|
22
|
+
<meta name="description" content="Session-based Slack access for Claude with your existing workspace permissions. Interactive demo for DMs, channels, search, and thread workflows.">
|
|
23
23
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
24
24
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
25
25
|
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
|
|
@@ -636,11 +636,11 @@
|
|
|
636
636
|
<div class="cta-strip">
|
|
637
637
|
<div class="cta-links">
|
|
638
638
|
<a href="https://www.npmjs.com/package/@jtalk22/slack-mcp" target="_blank" rel="noopener noreferrer">Install</a>
|
|
639
|
-
<a href="https://github.com/jtalk22/slack-mcp-server/blob/main/docs/
|
|
640
|
-
<a href="https://github.com/jtalk22/slack-mcp-server
|
|
639
|
+
<a href="https://github.com/jtalk22/slack-mcp-server/blob/main/docs/SETUP.md" target="_blank" rel="noopener noreferrer">Setup Guide</a>
|
|
640
|
+
<a href="https://github.com/jtalk22/slack-mcp-server#30-second-proof" target="_blank" rel="noopener noreferrer">30-Second Proof</a>
|
|
641
641
|
</div>
|
|
642
642
|
<div class="cta-note">
|
|
643
|
-
|
|
643
|
+
Free local-first path with optional team rollout guidance in <a href="https://github.com/jtalk22/slack-mcp-server/blob/main/docs/DEPLOYMENT-MODES.md" target="_blank" rel="noopener noreferrer">Deployment Modes</a>.
|
|
644
644
|
</div>
|
|
645
645
|
</div>
|
|
646
646
|
|
package/scripts/setup-wizard.js
CHANGED
|
@@ -12,11 +12,19 @@
|
|
|
12
12
|
|
|
13
13
|
import { platform } from "os";
|
|
14
14
|
import * as readline from "readline";
|
|
15
|
-
import {
|
|
16
|
-
|
|
15
|
+
import {
|
|
16
|
+
loadTokens,
|
|
17
|
+
saveTokens,
|
|
18
|
+
extractFromChrome,
|
|
19
|
+
isAutoRefreshAvailable,
|
|
20
|
+
TOKEN_FILE,
|
|
21
|
+
getFromFile,
|
|
22
|
+
getFromKeychain,
|
|
23
|
+
} from "../lib/token-store.js";
|
|
17
24
|
|
|
18
25
|
const IS_MACOS = platform() === 'darwin';
|
|
19
|
-
const VERSION = "1.2.
|
|
26
|
+
const VERSION = "1.2.3";
|
|
27
|
+
const MIN_NODE_MAJOR = 20;
|
|
20
28
|
|
|
21
29
|
// ANSI colors
|
|
22
30
|
const colors = {
|
|
@@ -69,27 +77,25 @@ async function pressEnterToContinue(rl) {
|
|
|
69
77
|
}
|
|
70
78
|
|
|
71
79
|
async function validateTokens(token, cookie) {
|
|
72
|
-
const hadOldToken = Object.prototype.hasOwnProperty.call(process.env, "SLACK_TOKEN");
|
|
73
|
-
const hadOldCookie = Object.prototype.hasOwnProperty.call(process.env, "SLACK_COOKIE");
|
|
74
|
-
const oldToken = process.env.SLACK_TOKEN;
|
|
75
|
-
const oldCookie = process.env.SLACK_COOKIE;
|
|
76
|
-
|
|
77
|
-
// Temporarily set env vars for validation
|
|
78
|
-
process.env.SLACK_TOKEN = token;
|
|
79
|
-
process.env.SLACK_COOKIE = cookie;
|
|
80
|
-
|
|
81
80
|
try {
|
|
82
|
-
const
|
|
83
|
-
|
|
81
|
+
const response = await fetch("https://slack.com/api/auth.test", {
|
|
82
|
+
method: "POST",
|
|
83
|
+
headers: {
|
|
84
|
+
"Authorization": `Bearer ${token}`,
|
|
85
|
+
"Cookie": `d=${cookie}`,
|
|
86
|
+
"Content-Type": "application/json; charset=utf-8",
|
|
87
|
+
},
|
|
88
|
+
body: JSON.stringify({}),
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
const result = await response.json();
|
|
92
|
+
if (!result.ok) {
|
|
93
|
+
return { valid: false, error: result.error || "auth.test_failed" };
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
return { valid: true, user: result.user, team: result.team, userId: result.user_id };
|
|
84
97
|
} catch (e) {
|
|
85
98
|
return { valid: false, error: e.message };
|
|
86
|
-
} finally {
|
|
87
|
-
// Always restore prior process env state
|
|
88
|
-
if (hadOldToken) process.env.SLACK_TOKEN = oldToken;
|
|
89
|
-
else delete process.env.SLACK_TOKEN;
|
|
90
|
-
|
|
91
|
-
if (hadOldCookie) process.env.SLACK_COOKIE = oldCookie;
|
|
92
|
-
else delete process.env.SLACK_COOKIE;
|
|
93
99
|
}
|
|
94
100
|
}
|
|
95
101
|
|
|
@@ -243,23 +249,118 @@ async function showStatus() {
|
|
|
243
249
|
}
|
|
244
250
|
print();
|
|
245
251
|
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
process.env.SLACK_TOKEN = creds.token;
|
|
249
|
-
process.env.SLACK_COOKIE = creds.cookie;
|
|
250
|
-
|
|
251
|
-
const result = await slackAPI("auth.test", {});
|
|
252
|
-
success("Status: VALID");
|
|
253
|
-
print(`User: ${result.user}`);
|
|
254
|
-
print(`Team: ${result.team}`);
|
|
255
|
-
print(`User ID: ${result.user_id}`);
|
|
256
|
-
} catch (e) {
|
|
252
|
+
const result = await validateTokens(creds.token, creds.cookie);
|
|
253
|
+
if (!result.valid) {
|
|
257
254
|
error("Status: INVALID");
|
|
258
|
-
print(`Error: ${
|
|
255
|
+
print(`Error: ${result.error}`);
|
|
259
256
|
print();
|
|
260
257
|
print("Run setup wizard to refresh: npx -y @jtalk22/slack-mcp --setup");
|
|
261
258
|
process.exit(1);
|
|
262
259
|
}
|
|
260
|
+
|
|
261
|
+
success("Status: VALID");
|
|
262
|
+
print(`User: ${result.user}`);
|
|
263
|
+
print(`Team: ${result.team}`);
|
|
264
|
+
print(`User ID: ${result.userId}`);
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
function getDoctorCredentials() {
|
|
268
|
+
if (process.env.SLACK_TOKEN && process.env.SLACK_COOKIE) {
|
|
269
|
+
return { token: process.env.SLACK_TOKEN, cookie: process.env.SLACK_COOKIE, source: "environment" };
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
const fileTokens = getFromFile();
|
|
273
|
+
if (fileTokens?.token && fileTokens?.cookie) {
|
|
274
|
+
return {
|
|
275
|
+
token: fileTokens.token,
|
|
276
|
+
cookie: fileTokens.cookie,
|
|
277
|
+
source: "file",
|
|
278
|
+
path: TOKEN_FILE,
|
|
279
|
+
updatedAt: fileTokens.updatedAt,
|
|
280
|
+
};
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
if (IS_MACOS) {
|
|
284
|
+
const keychainToken = getFromKeychain("token");
|
|
285
|
+
const keychainCookie = getFromKeychain("cookie");
|
|
286
|
+
if (keychainToken && keychainCookie) {
|
|
287
|
+
return { token: keychainToken, cookie: keychainCookie, source: "keychain" };
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
return null;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
function classifyAuthError(rawError) {
|
|
295
|
+
const msg = String(rawError || "").toLowerCase();
|
|
296
|
+
if (
|
|
297
|
+
msg.includes("invalid_auth") ||
|
|
298
|
+
msg.includes("token_expired") ||
|
|
299
|
+
msg.includes("not_authed") ||
|
|
300
|
+
msg.includes("account_inactive")
|
|
301
|
+
) {
|
|
302
|
+
return 2;
|
|
303
|
+
}
|
|
304
|
+
return 3;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
function parseNodeMajor() {
|
|
308
|
+
return Number.parseInt(process.versions.node.split(".")[0], 10);
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
async function runDoctor() {
|
|
312
|
+
print(`${colors.bold}slack-mcp-server doctor${colors.reset}`);
|
|
313
|
+
print();
|
|
314
|
+
|
|
315
|
+
const nodeMajor = parseNodeMajor();
|
|
316
|
+
if (Number.isNaN(nodeMajor) || nodeMajor < MIN_NODE_MAJOR) {
|
|
317
|
+
error(`Node.js ${process.versions.node} detected (requires Node ${MIN_NODE_MAJOR}+)`);
|
|
318
|
+
print();
|
|
319
|
+
print("Next action:");
|
|
320
|
+
print(` npx -y @jtalk22/slack-mcp --doctor # rerun after upgrading Node ${MIN_NODE_MAJOR}+`);
|
|
321
|
+
process.exit(3);
|
|
322
|
+
}
|
|
323
|
+
success(`Node.js ${process.versions.node} (supported)`);
|
|
324
|
+
|
|
325
|
+
const creds = getDoctorCredentials();
|
|
326
|
+
if (!creds) {
|
|
327
|
+
error("Credentials: not found");
|
|
328
|
+
print();
|
|
329
|
+
print("Next action:");
|
|
330
|
+
print(" npx -y @jtalk22/slack-mcp --setup");
|
|
331
|
+
process.exit(1);
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
success(`Credentials loaded from: ${creds.source}`);
|
|
335
|
+
if (creds.path) {
|
|
336
|
+
print(`Path: ${creds.path}`);
|
|
337
|
+
}
|
|
338
|
+
if (creds.updatedAt) {
|
|
339
|
+
print(`Last updated: ${creds.updatedAt}`);
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
print();
|
|
343
|
+
print("Validating Slack auth...");
|
|
344
|
+
const validation = await validateTokens(creds.token, creds.cookie);
|
|
345
|
+
if (!validation.valid) {
|
|
346
|
+
const exitCode = classifyAuthError(validation.error);
|
|
347
|
+
error(`Slack auth failed: ${validation.error}`);
|
|
348
|
+
print();
|
|
349
|
+
print("Next action:");
|
|
350
|
+
if (exitCode === 2) {
|
|
351
|
+
print(" npx -y @jtalk22/slack-mcp --setup");
|
|
352
|
+
} else {
|
|
353
|
+
print(" Check network connectivity and retry:");
|
|
354
|
+
print(" npx -y @jtalk22/slack-mcp --doctor");
|
|
355
|
+
}
|
|
356
|
+
process.exit(exitCode);
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
success(`Slack auth valid for ${validation.user} @ ${validation.team}`);
|
|
360
|
+
print();
|
|
361
|
+
print("Ready. Next command:");
|
|
362
|
+
print(" npx -y @jtalk22/slack-mcp");
|
|
363
|
+
process.exit(0);
|
|
263
364
|
}
|
|
264
365
|
|
|
265
366
|
async function showHelp() {
|
|
@@ -271,6 +372,7 @@ async function showHelp() {
|
|
|
271
372
|
print(" npx -y @jtalk22/slack-mcp Start MCP server (stdio)");
|
|
272
373
|
print(" npx -y @jtalk22/slack-mcp --setup Interactive token setup wizard");
|
|
273
374
|
print(" npx -y @jtalk22/slack-mcp --status Check token health");
|
|
375
|
+
print(" npx -y @jtalk22/slack-mcp --doctor Run runtime and auth diagnostics");
|
|
274
376
|
print(" npx -y @jtalk22/slack-mcp --version Print version");
|
|
275
377
|
print(" npx -y @jtalk22/slack-mcp --help Show this help");
|
|
276
378
|
print();
|
|
@@ -296,6 +398,10 @@ async function main() {
|
|
|
296
398
|
case 'status':
|
|
297
399
|
await showStatus();
|
|
298
400
|
return;
|
|
401
|
+
case '--doctor':
|
|
402
|
+
case 'doctor':
|
|
403
|
+
await runDoctor();
|
|
404
|
+
return;
|
|
299
405
|
case '--version':
|
|
300
406
|
case '-v':
|
|
301
407
|
print(`slack-mcp-server v${VERSION}`);
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { spawnSync } from "node:child_process";
|
|
4
|
+
import { mkdtempSync, rmSync } from "node:fs";
|
|
5
|
+
import { tmpdir } from "node:os";
|
|
6
|
+
import { join } from "node:path";
|
|
7
|
+
|
|
8
|
+
const PKG = "@jtalk22/slack-mcp";
|
|
9
|
+
|
|
10
|
+
function runNpx(args, options = {}) {
|
|
11
|
+
const cmdArgs = ["-y", PKG, ...args];
|
|
12
|
+
const result = spawnSync("npx", cmdArgs, {
|
|
13
|
+
cwd: options.cwd,
|
|
14
|
+
env: options.env,
|
|
15
|
+
encoding: "utf8",
|
|
16
|
+
timeout: 120000,
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
return {
|
|
20
|
+
args: cmdArgs.join(" "),
|
|
21
|
+
status: result.status,
|
|
22
|
+
stdout: (result.stdout || "").trim(),
|
|
23
|
+
stderr: (result.stderr || "").trim(),
|
|
24
|
+
error: result.error,
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function assert(condition, message, details = "") {
|
|
29
|
+
if (!condition) {
|
|
30
|
+
const suffix = details ? `\n${details}` : "";
|
|
31
|
+
throw new Error(`${message}${suffix}`);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function printResult(label, result) {
|
|
36
|
+
console.log(`\n[${label}] npx ${result.args}`);
|
|
37
|
+
console.log(`exit=${result.status}`);
|
|
38
|
+
if (result.stdout) {
|
|
39
|
+
console.log("stdout:");
|
|
40
|
+
console.log(result.stdout);
|
|
41
|
+
}
|
|
42
|
+
if (result.stderr) {
|
|
43
|
+
console.log("stderr:");
|
|
44
|
+
console.log(result.stderr);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function main() {
|
|
49
|
+
const testHome = mkdtempSync(join(tmpdir(), "slack-mcp-install-check-"));
|
|
50
|
+
|
|
51
|
+
// Force a clean environment so --status reflects missing credentials.
|
|
52
|
+
const env = { ...process.env, HOME: testHome, USERPROFILE: testHome };
|
|
53
|
+
delete env.SLACK_TOKEN;
|
|
54
|
+
delete env.SLACK_COOKIE;
|
|
55
|
+
|
|
56
|
+
try {
|
|
57
|
+
const versionResult = runNpx(["--version"], { cwd: testHome, env });
|
|
58
|
+
printResult("version", versionResult);
|
|
59
|
+
assert(
|
|
60
|
+
versionResult.status === 0,
|
|
61
|
+
"Expected --version to exit 0",
|
|
62
|
+
versionResult.stderr || versionResult.stdout,
|
|
63
|
+
);
|
|
64
|
+
|
|
65
|
+
const helpResult = runNpx(["--help"], { cwd: testHome, env });
|
|
66
|
+
printResult("help", helpResult);
|
|
67
|
+
assert(
|
|
68
|
+
helpResult.status === 0,
|
|
69
|
+
"Expected --help to exit 0",
|
|
70
|
+
helpResult.stderr || helpResult.stdout,
|
|
71
|
+
);
|
|
72
|
+
|
|
73
|
+
const statusResult = runNpx(["--status"], { cwd: testHome, env });
|
|
74
|
+
printResult("status", statusResult);
|
|
75
|
+
assert(
|
|
76
|
+
statusResult.status !== 0,
|
|
77
|
+
"Expected --status to exit non-zero when credentials are missing",
|
|
78
|
+
statusResult.stderr || statusResult.stdout,
|
|
79
|
+
);
|
|
80
|
+
|
|
81
|
+
console.log("\nInstall flow verification passed.");
|
|
82
|
+
} finally {
|
|
83
|
+
rmSync(testHome, { recursive: true, force: true });
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
main();
|
package/src/cli.js
CHANGED
package/src/server-http.js
CHANGED
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.2.
|
|
14
|
+
* @version 1.2.3
|
|
15
15
|
*/
|
|
16
16
|
|
|
17
17
|
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
@@ -47,7 +47,7 @@ const BACKGROUND_REFRESH_INTERVAL = 4 * 60 * 60 * 1000;
|
|
|
47
47
|
|
|
48
48
|
// Package info
|
|
49
49
|
const SERVER_NAME = "slack-mcp-server";
|
|
50
|
-
const SERVER_VERSION = "1.2.
|
|
50
|
+
const SERVER_VERSION = "1.2.3";
|
|
51
51
|
|
|
52
52
|
// MCP Prompts - predefined prompt templates for common Slack operations
|
|
53
53
|
const PROMPTS = [
|
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.2.
|
|
8
|
+
* @version 1.2.3
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
11
|
import express from "express";
|
|
@@ -116,7 +116,7 @@ function extractContent(result) {
|
|
|
116
116
|
app.get("/", (req, res) => {
|
|
117
117
|
res.json({
|
|
118
118
|
name: "Slack Web API Server",
|
|
119
|
-
version: "1.2.
|
|
119
|
+
version: "1.2.3",
|
|
120
120
|
status: "running",
|
|
121
121
|
endpoints: [
|
|
122
122
|
"GET /health",
|
|
@@ -295,7 +295,7 @@ async function main() {
|
|
|
295
295
|
app.listen(PORT, '127.0.0.1', () => {
|
|
296
296
|
// Print to stderr to keep logs clean (stdout reserved for JSON in some setups)
|
|
297
297
|
console.error(`\n${"═".repeat(60)}`);
|
|
298
|
-
console.error(` Slack Web API Server v1.2.
|
|
298
|
+
console.error(` Slack Web API Server v1.2.3`);
|
|
299
299
|
console.error(`${"═".repeat(60)}`);
|
|
300
300
|
console.error(`\n Dashboard: http://localhost:${PORT}/?key=${API_KEY}`);
|
|
301
301
|
console.error(`\n API Key: ${API_KEY}`);
|