@jtalk22/slack-mcp 1.2.0 → 1.2.2
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/LICENSE +1 -1
- package/README.md +61 -61
- package/docs/COMMUNICATION-STYLE.md +58 -0
- package/docs/DEPLOYMENT-MODES.md +48 -0
- package/docs/SETUP.md +11 -5
- package/docs/SUPPORT-BOUNDARIES.md +49 -0
- package/docs/TROUBLESHOOTING.md +5 -2
- package/docs/USE_CASE_RECIPES.md +69 -0
- package/docs/WEB-API.md +2 -0
- package/docs/assets/icon-512.png +0 -0
- package/docs/assets/icon.svg +27 -0
- package/docs/images/demo-claude-v1.2.gif +0 -0
- package/docs/images/demo-poster.png +0 -0
- package/docs/images/demo-readme.gif +0 -0
- package/docs/images/diagram-oauth-comparison.svg +80 -0
- package/docs/images/diagram-session-flow.svg +105 -0
- package/lib/handlers.js +1 -1
- package/lib/slack-client.js +36 -7
- package/lib/tools.js +69 -0
- package/package.json +6 -3
- package/public/demo-claude.html +56 -9
- package/public/demo-video.html +206 -0
- package/public/demo.html +49 -6
- package/scripts/check-owner-attribution.sh +80 -0
- package/scripts/record-demo.js +4 -0
- package/scripts/setup-git-hooks.sh +15 -0
- package/scripts/setup-wizard.js +28 -25
- package/src/cli.js +57 -0
- package/src/server-http.js +1 -1
- package/src/server.js +170 -3
- package/src/web-server.js +3 -3
- package/docs/images/demo-session.gif +0 -0
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
+
<title>Slack MCP Server Demo</title>
|
|
7
|
+
<meta name="description" content="Video demo of session-based Slack access for Claude with DMs, channels, search, and threads.">
|
|
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="Video walkthrough of session-based Slack workflows in Claude.">
|
|
11
|
+
<meta property="og:url" content="https://jtalk22.github.io/slack-mcp-server/public/demo-video.html">
|
|
12
|
+
<meta property="og:image" content="https://raw.githubusercontent.com/jtalk22/slack-mcp-server/main/docs/images/demo-poster.png">
|
|
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="Video walkthrough of session-based Slack workflows in Claude.">
|
|
16
|
+
<meta name="twitter:image" content="https://raw.githubusercontent.com/jtalk22/slack-mcp-server/main/docs/images/demo-poster.png">
|
|
17
|
+
<style>
|
|
18
|
+
* {
|
|
19
|
+
margin: 0;
|
|
20
|
+
padding: 0;
|
|
21
|
+
box-sizing: border-box;
|
|
22
|
+
}
|
|
23
|
+
body {
|
|
24
|
+
font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
|
|
25
|
+
background: linear-gradient(135deg, #1a1a2e 0%, #16213e 100%);
|
|
26
|
+
min-height: 100vh;
|
|
27
|
+
display: flex;
|
|
28
|
+
flex-direction: column;
|
|
29
|
+
align-items: center;
|
|
30
|
+
justify-content: center;
|
|
31
|
+
padding: 2rem;
|
|
32
|
+
}
|
|
33
|
+
.container {
|
|
34
|
+
max-width: 900px;
|
|
35
|
+
width: 100%;
|
|
36
|
+
}
|
|
37
|
+
h1 {
|
|
38
|
+
color: #ffffff;
|
|
39
|
+
font-size: 1.75rem;
|
|
40
|
+
font-weight: 600;
|
|
41
|
+
text-align: center;
|
|
42
|
+
margin-bottom: 0.5rem;
|
|
43
|
+
}
|
|
44
|
+
.subtitle {
|
|
45
|
+
color: #94a3b8;
|
|
46
|
+
text-align: center;
|
|
47
|
+
margin-bottom: 1.5rem;
|
|
48
|
+
font-size: 1rem;
|
|
49
|
+
}
|
|
50
|
+
.cta-strip {
|
|
51
|
+
margin: 0 auto 1rem;
|
|
52
|
+
background: rgba(15, 52, 96, 0.72);
|
|
53
|
+
border: 1px solid rgba(255, 255, 255, 0.16);
|
|
54
|
+
border-radius: 12px;
|
|
55
|
+
padding: 10px 14px;
|
|
56
|
+
display: flex;
|
|
57
|
+
justify-content: space-between;
|
|
58
|
+
align-items: center;
|
|
59
|
+
gap: 10px;
|
|
60
|
+
flex-wrap: wrap;
|
|
61
|
+
font-size: 0.8125rem;
|
|
62
|
+
}
|
|
63
|
+
.cta-strip .links {
|
|
64
|
+
display: flex;
|
|
65
|
+
gap: 8px;
|
|
66
|
+
flex-wrap: wrap;
|
|
67
|
+
}
|
|
68
|
+
.cta-strip .links a {
|
|
69
|
+
color: #d8efff;
|
|
70
|
+
text-decoration: none;
|
|
71
|
+
border: 1px solid rgba(255, 255, 255, 0.24);
|
|
72
|
+
border-radius: 999px;
|
|
73
|
+
padding: 4px 8px;
|
|
74
|
+
}
|
|
75
|
+
.cta-strip .links a:hover {
|
|
76
|
+
background: rgba(255, 255, 255, 0.08);
|
|
77
|
+
}
|
|
78
|
+
.cta-strip .note {
|
|
79
|
+
color: rgba(255, 255, 255, 0.82);
|
|
80
|
+
}
|
|
81
|
+
.cta-strip .note a {
|
|
82
|
+
color: #9ee7ff;
|
|
83
|
+
text-decoration: underline;
|
|
84
|
+
}
|
|
85
|
+
.video-wrapper {
|
|
86
|
+
position: relative;
|
|
87
|
+
border-radius: 12px;
|
|
88
|
+
overflow: hidden;
|
|
89
|
+
box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.5);
|
|
90
|
+
background: #0f0f1a;
|
|
91
|
+
}
|
|
92
|
+
video {
|
|
93
|
+
width: 100%;
|
|
94
|
+
display: block;
|
|
95
|
+
border-radius: 12px;
|
|
96
|
+
}
|
|
97
|
+
.controls {
|
|
98
|
+
display: flex;
|
|
99
|
+
justify-content: center;
|
|
100
|
+
gap: 1rem;
|
|
101
|
+
margin-top: 1.5rem;
|
|
102
|
+
}
|
|
103
|
+
.btn {
|
|
104
|
+
padding: 0.75rem 1.5rem;
|
|
105
|
+
border-radius: 8px;
|
|
106
|
+
border: none;
|
|
107
|
+
font-size: 0.875rem;
|
|
108
|
+
font-weight: 500;
|
|
109
|
+
cursor: pointer;
|
|
110
|
+
transition: all 0.2s;
|
|
111
|
+
}
|
|
112
|
+
.btn-primary {
|
|
113
|
+
background: #4ecdc4;
|
|
114
|
+
color: #1a1a2e;
|
|
115
|
+
}
|
|
116
|
+
.btn-primary:hover {
|
|
117
|
+
background: #5eead4;
|
|
118
|
+
transform: translateY(-1px);
|
|
119
|
+
}
|
|
120
|
+
.btn-secondary {
|
|
121
|
+
background: rgba(255, 255, 255, 0.1);
|
|
122
|
+
color: #ffffff;
|
|
123
|
+
border: 1px solid rgba(255, 255, 255, 0.2);
|
|
124
|
+
}
|
|
125
|
+
.btn-secondary:hover {
|
|
126
|
+
background: rgba(255, 255, 255, 0.15);
|
|
127
|
+
}
|
|
128
|
+
.back-link {
|
|
129
|
+
margin-top: 2rem;
|
|
130
|
+
text-align: center;
|
|
131
|
+
}
|
|
132
|
+
.back-link a {
|
|
133
|
+
color: #94a3b8;
|
|
134
|
+
text-decoration: none;
|
|
135
|
+
font-size: 0.875rem;
|
|
136
|
+
}
|
|
137
|
+
.back-link a:hover {
|
|
138
|
+
color: #ffffff;
|
|
139
|
+
}
|
|
140
|
+
</style>
|
|
141
|
+
</head>
|
|
142
|
+
<body>
|
|
143
|
+
<div class="container">
|
|
144
|
+
<h1>Slack MCP Server</h1>
|
|
145
|
+
<p class="subtitle">Full workspace access via local session mirroring</p>
|
|
146
|
+
<div class="cta-strip">
|
|
147
|
+
<div class="links">
|
|
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/API.md" target="_blank" rel="noopener noreferrer">API Docs</a>
|
|
150
|
+
<a href="https://github.com/jtalk22/slack-mcp-server/blob/main/CHANGELOG.md" target="_blank" rel="noopener noreferrer">Changelog</a>
|
|
151
|
+
</div>
|
|
152
|
+
<div class="note">
|
|
153
|
+
Team rollout? Review <a href="https://github.com/jtalk22/slack-mcp-server/blob/main/docs/DEPLOYMENT-MODES.md" target="_blank" rel="noopener noreferrer">Deployment Modes</a>.
|
|
154
|
+
</div>
|
|
155
|
+
</div>
|
|
156
|
+
|
|
157
|
+
<div class="video-wrapper">
|
|
158
|
+
<video id="demo" poster="../docs/images/demo-poster.png" playsinline>
|
|
159
|
+
<source src="../docs/videos/demo-claude-v1.2.webm" type="video/webm">
|
|
160
|
+
<source src="../docs/images/demo-claude-v1.2.gif" type="image/gif">
|
|
161
|
+
Your browser does not support the video tag.
|
|
162
|
+
</video>
|
|
163
|
+
</div>
|
|
164
|
+
|
|
165
|
+
<div class="controls">
|
|
166
|
+
<button class="btn btn-primary" onclick="togglePlay()">Play / Pause</button>
|
|
167
|
+
<button class="btn btn-secondary" onclick="restart()">Restart</button>
|
|
168
|
+
</div>
|
|
169
|
+
|
|
170
|
+
<div class="back-link">
|
|
171
|
+
<a href="https://github.com/jtalk22/slack-mcp-server">← Back to Repository</a>
|
|
172
|
+
</div>
|
|
173
|
+
</div>
|
|
174
|
+
|
|
175
|
+
<script>
|
|
176
|
+
const video = document.getElementById('demo');
|
|
177
|
+
|
|
178
|
+
// Autoplay with 1 second delay
|
|
179
|
+
setTimeout(() => {
|
|
180
|
+
video.play().catch(() => {
|
|
181
|
+
// Autoplay blocked, user will need to click
|
|
182
|
+
console.log('Autoplay blocked, click to play');
|
|
183
|
+
});
|
|
184
|
+
}, 1000);
|
|
185
|
+
|
|
186
|
+
function togglePlay() {
|
|
187
|
+
if (video.paused) {
|
|
188
|
+
video.play();
|
|
189
|
+
} else {
|
|
190
|
+
video.pause();
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
function restart() {
|
|
195
|
+
video.currentTime = 0;
|
|
196
|
+
video.play();
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// Loop the video
|
|
200
|
+
video.addEventListener('ended', () => {
|
|
201
|
+
video.currentTime = 0;
|
|
202
|
+
video.play();
|
|
203
|
+
});
|
|
204
|
+
</script>
|
|
205
|
+
</body>
|
|
206
|
+
</html>
|
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 -
|
|
12
|
-
<meta property="og:description" content="
|
|
11
|
+
<meta property="og:title" content="Slack MCP Server - Session Access Demo">
|
|
12
|
+
<meta property="og:description" content="Interactive demo for session-based Slack access in Claude: 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 -
|
|
18
|
-
<meta name="twitter:description" content="
|
|
17
|
+
<meta name="twitter:title" content="Slack MCP Server - Session Access Demo">
|
|
18
|
+
<meta name="twitter:description" content="Interactive demo for session-based Slack access in Claude: 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="
|
|
22
|
+
<meta name="description" content="Session-based Slack access for Claude. 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">
|
|
@@ -74,12 +74,45 @@
|
|
|
74
74
|
color: white;
|
|
75
75
|
text-decoration: underline;
|
|
76
76
|
}
|
|
77
|
+
.cta-strip {
|
|
78
|
+
background: rgba(15, 52, 96, 0.9);
|
|
79
|
+
border-bottom: 1px solid rgba(255, 255, 255, 0.12);
|
|
80
|
+
padding: 10px 16px;
|
|
81
|
+
display: flex;
|
|
82
|
+
justify-content: space-between;
|
|
83
|
+
align-items: center;
|
|
84
|
+
gap: 12px;
|
|
85
|
+
flex-wrap: wrap;
|
|
86
|
+
font-size: 13px;
|
|
87
|
+
}
|
|
88
|
+
.cta-links {
|
|
89
|
+
display: flex;
|
|
90
|
+
gap: 10px;
|
|
91
|
+
flex-wrap: wrap;
|
|
92
|
+
}
|
|
93
|
+
.cta-links a {
|
|
94
|
+
color: #d8efff;
|
|
95
|
+
text-decoration: none;
|
|
96
|
+
padding: 4px 8px;
|
|
97
|
+
border: 1px solid rgba(255, 255, 255, 0.25);
|
|
98
|
+
border-radius: 999px;
|
|
99
|
+
}
|
|
100
|
+
.cta-links a:hover {
|
|
101
|
+
background: rgba(255, 255, 255, 0.1);
|
|
102
|
+
}
|
|
103
|
+
.cta-note {
|
|
104
|
+
color: rgba(255, 255, 255, 0.75);
|
|
105
|
+
}
|
|
106
|
+
.cta-note a {
|
|
107
|
+
color: #9ee7ff;
|
|
108
|
+
text-decoration: underline;
|
|
109
|
+
}
|
|
77
110
|
|
|
78
111
|
/* Main Layout */
|
|
79
112
|
.split-container {
|
|
80
113
|
display: grid;
|
|
81
114
|
grid-template-columns: 420px 1fr;
|
|
82
|
-
height: calc(100vh -
|
|
115
|
+
height: calc(100vh - 88px);
|
|
83
116
|
overflow: hidden;
|
|
84
117
|
}
|
|
85
118
|
|
|
@@ -600,6 +633,16 @@
|
|
|
600
633
|
STATIC PREVIEW - No real data. Run <code>npm run web</code> for live dashboard.
|
|
601
634
|
<a href="https://github.com/jtalk22/slack-mcp-server">View on GitHub</a>
|
|
602
635
|
</div>
|
|
636
|
+
<div class="cta-strip">
|
|
637
|
+
<div class="cta-links">
|
|
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/API.md" target="_blank" rel="noopener noreferrer">API Docs</a>
|
|
640
|
+
<a href="https://github.com/jtalk22/slack-mcp-server/blob/main/CHANGELOG.md" target="_blank" rel="noopener noreferrer">Changelog</a>
|
|
641
|
+
</div>
|
|
642
|
+
<div class="cta-note">
|
|
643
|
+
Team rollout? Review <a href="https://github.com/jtalk22/slack-mcp-server/blob/main/docs/DEPLOYMENT-MODES.md" target="_blank" rel="noopener noreferrer">Deployment Modes</a>.
|
|
644
|
+
</div>
|
|
645
|
+
</div>
|
|
603
646
|
|
|
604
647
|
<div class="split-container">
|
|
605
648
|
<!-- LEFT: Claude Chat Panel -->
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
EXPECTED_NAME="${EXPECTED_GIT_NAME:-jtalk22}"
|
|
5
|
+
EXPECTED_EMAIL="${EXPECTED_GIT_EMAIL:-james@revasser.nyc}"
|
|
6
|
+
BANNED_REGEX='(?i)(co-authored-by|generated with|\bclaude\b|\bgpt\b|\bcopilot\b|\bgemini\b|\bai\b)'
|
|
7
|
+
|
|
8
|
+
die() {
|
|
9
|
+
echo "ERROR: $*" >&2
|
|
10
|
+
exit 1
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
contains_banned_markers() {
|
|
14
|
+
local text="$1"
|
|
15
|
+
if command -v rg >/dev/null 2>&1; then
|
|
16
|
+
rg -Niq "$BANNED_REGEX" <<<"$text"
|
|
17
|
+
else
|
|
18
|
+
grep -Eiq '(Co-authored-by|Generated with|Claude|GPT|Copilot|Gemini)' <<<"$text" \
|
|
19
|
+
|| grep -Eiq '(^|[^[:alnum:]_])[Aa][Ii]([^[:alnum:]_]|$)' <<<"$text"
|
|
20
|
+
fi
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
if [[ "${SKIP_LOCAL_CONFIG_CHECK:-0}" != "1" ]]; then
|
|
24
|
+
local_name="$(git config --get user.name || true)"
|
|
25
|
+
local_email="$(git config --get user.email || true)"
|
|
26
|
+
|
|
27
|
+
[[ -n "$local_name" ]] || die "Missing repo-local git user.name"
|
|
28
|
+
[[ -n "$local_email" ]] || die "Missing repo-local git user.email"
|
|
29
|
+
|
|
30
|
+
[[ "$local_name" == "$EXPECTED_NAME" ]] \
|
|
31
|
+
|| die "Repo-local user.name is '$local_name' (expected '$EXPECTED_NAME')"
|
|
32
|
+
[[ "$local_email" == "$EXPECTED_EMAIL" ]] \
|
|
33
|
+
|| die "Repo-local user.email is '$local_email' (expected '$EXPECTED_EMAIL')"
|
|
34
|
+
fi
|
|
35
|
+
|
|
36
|
+
default_range="HEAD"
|
|
37
|
+
if git rev-parse --verify origin/main >/dev/null 2>&1; then
|
|
38
|
+
default_range="origin/main..HEAD"
|
|
39
|
+
fi
|
|
40
|
+
|
|
41
|
+
range="${1:-${GIT_CHECK_RANGE:-$default_range}}"
|
|
42
|
+
|
|
43
|
+
git rev-list --count "$range" >/dev/null 2>&1 || die "Invalid commit range: $range"
|
|
44
|
+
commit_count="$(git rev-list --count "$range")"
|
|
45
|
+
|
|
46
|
+
if [[ "$commit_count" -eq 0 ]]; then
|
|
47
|
+
echo "No commits to validate in range '$range'."
|
|
48
|
+
exit 0
|
|
49
|
+
fi
|
|
50
|
+
|
|
51
|
+
errors=0
|
|
52
|
+
|
|
53
|
+
while IFS= read -r sha; do
|
|
54
|
+
author_name="$(git show -s --format=%an "$sha")"
|
|
55
|
+
author_email="$(git show -s --format=%ae "$sha")"
|
|
56
|
+
committer_name="$(git show -s --format=%cn "$sha")"
|
|
57
|
+
committer_email="$(git show -s --format=%ce "$sha")"
|
|
58
|
+
body="$(git show -s --format=%B "$sha")"
|
|
59
|
+
|
|
60
|
+
if [[ "$author_name" != "$EXPECTED_NAME" || "$author_email" != "$EXPECTED_EMAIL" ]]; then
|
|
61
|
+
echo "Commit $sha has author '$author_name <$author_email>' (expected '$EXPECTED_NAME <$EXPECTED_EMAIL>')." >&2
|
|
62
|
+
errors=1
|
|
63
|
+
fi
|
|
64
|
+
|
|
65
|
+
if [[ "$committer_name" != "$EXPECTED_NAME" || "$committer_email" != "$EXPECTED_EMAIL" ]]; then
|
|
66
|
+
echo "Commit $sha has committer '$committer_name <$committer_email>' (expected '$EXPECTED_NAME <$EXPECTED_EMAIL>')." >&2
|
|
67
|
+
errors=1
|
|
68
|
+
fi
|
|
69
|
+
|
|
70
|
+
if contains_banned_markers "$body"; then
|
|
71
|
+
echo "Commit $sha contains disallowed attribution markers in commit message." >&2
|
|
72
|
+
errors=1
|
|
73
|
+
fi
|
|
74
|
+
done < <(git rev-list "$range")
|
|
75
|
+
|
|
76
|
+
if [[ "$errors" -ne 0 ]]; then
|
|
77
|
+
exit 1
|
|
78
|
+
fi
|
|
79
|
+
|
|
80
|
+
echo "Owner-only attribution check passed for $commit_count commit(s) in '$range'."
|
package/scripts/record-demo.js
CHANGED
|
@@ -73,6 +73,10 @@ async function recordDemo() {
|
|
|
73
73
|
await page.goto(`file://${demoPath}`);
|
|
74
74
|
await page.waitForTimeout(1000);
|
|
75
75
|
|
|
76
|
+
// Hold on initial frame for a few seconds (visible first frame in GIF)
|
|
77
|
+
console.log('⏸️ Holding initial frame (3s)...');
|
|
78
|
+
await page.waitForTimeout(3000);
|
|
79
|
+
|
|
76
80
|
// Set slow speed for video recording
|
|
77
81
|
console.log(`⏱️ Setting speed to ${CONFIG.speed}x...`);
|
|
78
82
|
await page.selectOption('#speedSelect', CONFIG.speed);
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
repo_root="$(git rev-parse --show-toplevel)"
|
|
5
|
+
cd "$repo_root"
|
|
6
|
+
|
|
7
|
+
[[ -d .githooks ]] || {
|
|
8
|
+
echo "Missing .githooks directory." >&2
|
|
9
|
+
exit 1
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
git config core.hooksPath .githooks
|
|
13
|
+
find .githooks -maxdepth 1 -type f -exec chmod +x {} +
|
|
14
|
+
|
|
15
|
+
echo "Configured git hooks path: .githooks"
|
package/scripts/setup-wizard.js
CHANGED
|
@@ -16,7 +16,7 @@ import { loadTokens, saveTokens, extractFromChrome, isAutoRefreshAvailable, TOKE
|
|
|
16
16
|
import { slackAPI } from "../lib/slack-client.js";
|
|
17
17
|
|
|
18
18
|
const IS_MACOS = platform() === 'darwin';
|
|
19
|
-
const VERSION = "1.2.
|
|
19
|
+
const VERSION = "1.2.2";
|
|
20
20
|
|
|
21
21
|
// ANSI colors
|
|
22
22
|
const colors = {
|
|
@@ -69,24 +69,27 @@ async function pressEnterToContinue(rl) {
|
|
|
69
69
|
}
|
|
70
70
|
|
|
71
71
|
async function validateTokens(token, cookie) {
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
process.env.SLACK_TOKEN = token;
|
|
77
|
-
process.env.SLACK_COOKIE = cookie;
|
|
78
|
-
|
|
79
|
-
const result = await slackAPI("auth.test", {});
|
|
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;
|
|
80
76
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
if (oldCookie) process.env.SLACK_COOKIE = oldCookie;
|
|
85
|
-
else delete process.env.SLACK_COOKIE;
|
|
77
|
+
// Temporarily set env vars for validation
|
|
78
|
+
process.env.SLACK_TOKEN = token;
|
|
79
|
+
process.env.SLACK_COOKIE = cookie;
|
|
86
80
|
|
|
81
|
+
try {
|
|
82
|
+
const result = await slackAPI("auth.test", {});
|
|
87
83
|
return { valid: true, user: result.user, team: result.team };
|
|
88
84
|
} catch (e) {
|
|
89
85
|
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;
|
|
90
93
|
}
|
|
91
94
|
}
|
|
92
95
|
|
|
@@ -175,7 +178,7 @@ async function runManualSetup(rl) {
|
|
|
175
178
|
" .teams)[0]].token",
|
|
176
179
|
], 55);
|
|
177
180
|
print();
|
|
178
|
-
print(
|
|
181
|
+
print(` Copy the token (starts with ${colors.cyan}xoxc-${colors.reset})`);
|
|
179
182
|
print();
|
|
180
183
|
|
|
181
184
|
const token = await question(rl, `${colors.bold}Paste your token:${colors.reset} `);
|
|
@@ -187,7 +190,7 @@ async function runManualSetup(rl) {
|
|
|
187
190
|
|
|
188
191
|
print();
|
|
189
192
|
print(`${colors.bold}Step 4:${colors.reset} Go to ${colors.cyan}Application${colors.reset} tab → ${colors.cyan}Cookies${colors.reset} → slack.com`);
|
|
190
|
-
print(
|
|
193
|
+
print(` Find the '${colors.cyan}d${colors.reset}' cookie and copy its value`);
|
|
191
194
|
print();
|
|
192
195
|
|
|
193
196
|
const cookie = await question(rl, `${colors.bold}Paste your cookie:${colors.reset} `);
|
|
@@ -229,7 +232,7 @@ async function showStatus() {
|
|
|
229
232
|
if (!creds) {
|
|
230
233
|
error("No tokens found");
|
|
231
234
|
print();
|
|
232
|
-
print("Run setup wizard: npx @jtalk22/slack-mcp --setup");
|
|
235
|
+
print("Run setup wizard: npx -y @jtalk22/slack-mcp --setup");
|
|
233
236
|
process.exit(1);
|
|
234
237
|
}
|
|
235
238
|
|
|
@@ -254,7 +257,7 @@ async function showStatus() {
|
|
|
254
257
|
error("Status: INVALID");
|
|
255
258
|
print(`Error: ${e.message}`);
|
|
256
259
|
print();
|
|
257
|
-
print("Run setup wizard to refresh: npx @jtalk22/slack-mcp --setup");
|
|
260
|
+
print("Run setup wizard to refresh: npx -y @jtalk22/slack-mcp --setup");
|
|
258
261
|
process.exit(1);
|
|
259
262
|
}
|
|
260
263
|
}
|
|
@@ -265,11 +268,11 @@ async function showHelp() {
|
|
|
265
268
|
print("Full Slack access for Claude via MCP. Session mirroring bypasses OAuth.");
|
|
266
269
|
print();
|
|
267
270
|
print(`${colors.bold}Usage:${colors.reset}`);
|
|
268
|
-
print(" npx @jtalk22/slack-mcp Start MCP server (stdio)");
|
|
269
|
-
print(" npx @jtalk22/slack-mcp --setup Interactive token setup wizard");
|
|
270
|
-
print(" npx @jtalk22/slack-mcp --status Check token health");
|
|
271
|
-
print(" npx @jtalk22/slack-mcp --version Print version");
|
|
272
|
-
print(" npx @jtalk22/slack-mcp --help Show this help");
|
|
271
|
+
print(" npx -y @jtalk22/slack-mcp Start MCP server (stdio)");
|
|
272
|
+
print(" npx -y @jtalk22/slack-mcp --setup Interactive token setup wizard");
|
|
273
|
+
print(" npx -y @jtalk22/slack-mcp --status Check token health");
|
|
274
|
+
print(" npx -y @jtalk22/slack-mcp --version Print version");
|
|
275
|
+
print(" npx -y @jtalk22/slack-mcp --help Show this help");
|
|
273
276
|
print();
|
|
274
277
|
print(`${colors.bold}npm scripts:${colors.reset}`);
|
|
275
278
|
print(" npm start Start MCP server");
|
|
@@ -342,8 +345,8 @@ async function main() {
|
|
|
342
345
|
print(`${colors.green}${colors.bold}Setup complete!${colors.reset}`);
|
|
343
346
|
print();
|
|
344
347
|
print("Next steps:");
|
|
345
|
-
print(" • Verify: npx @jtalk22/slack-mcp --status");
|
|
346
|
-
print(" • Start server: npx @jtalk22/slack-mcp");
|
|
348
|
+
print(" • Verify: npx -y @jtalk22/slack-mcp --status");
|
|
349
|
+
print(" • Start server: npx -y @jtalk22/slack-mcp");
|
|
347
350
|
print(" • Or add to Claude Desktop config");
|
|
348
351
|
} else {
|
|
349
352
|
print(`${colors.red}Setup failed.${colors.reset} See errors above.`);
|
package/src/cli.js
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* slack-mcp package entrypoint dispatcher.
|
|
4
|
+
*
|
|
5
|
+
* Supports:
|
|
6
|
+
* - default stdio server startup
|
|
7
|
+
* - web/http server modes
|
|
8
|
+
* - setup wizard and its status/help/version flags
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { spawn } from "node:child_process";
|
|
12
|
+
import { dirname, join } from "node:path";
|
|
13
|
+
import { fileURLToPath } from "node:url";
|
|
14
|
+
|
|
15
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
16
|
+
|
|
17
|
+
const args = process.argv.slice(2);
|
|
18
|
+
const firstArg = args[0];
|
|
19
|
+
|
|
20
|
+
const WIZARD_ARGS = new Set([
|
|
21
|
+
"--setup", "setup",
|
|
22
|
+
"--status", "status",
|
|
23
|
+
"--version", "-v",
|
|
24
|
+
"--help", "-h", "help",
|
|
25
|
+
]);
|
|
26
|
+
|
|
27
|
+
let scriptPath = join(__dirname, "server.js");
|
|
28
|
+
let scriptArgs = args;
|
|
29
|
+
|
|
30
|
+
if (firstArg === "web") {
|
|
31
|
+
scriptPath = join(__dirname, "web-server.js");
|
|
32
|
+
scriptArgs = args.slice(1);
|
|
33
|
+
} else if (firstArg === "http") {
|
|
34
|
+
scriptPath = join(__dirname, "server-http.js");
|
|
35
|
+
scriptArgs = args.slice(1);
|
|
36
|
+
} else if (WIZARD_ARGS.has(firstArg)) {
|
|
37
|
+
scriptPath = join(__dirname, "../scripts/setup-wizard.js");
|
|
38
|
+
scriptArgs = args;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const child = spawn(process.execPath, [scriptPath, ...scriptArgs], {
|
|
42
|
+
stdio: "inherit",
|
|
43
|
+
env: process.env,
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
child.on("error", (error) => {
|
|
47
|
+
console.error(`Failed to start ${scriptPath}: ${error.message}`);
|
|
48
|
+
process.exit(1);
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
child.on("exit", (code, signal) => {
|
|
52
|
+
if (signal) {
|
|
53
|
+
process.kill(process.pid, signal);
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
process.exit(code ?? 0);
|
|
57
|
+
});
|
package/src/server-http.js
CHANGED