@jtalk22/slack-mcp 1.2.1 → 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 +75 -6
- package/docs/COMMUNICATION-STYLE.md +65 -0
- package/docs/DEPLOYMENT-MODES.md +48 -0
- package/docs/HN-LAUNCH.md +61 -0
- package/docs/INDEX.md +28 -0
- package/docs/SETUP.md +29 -5
- package/docs/SUPPORT-BOUNDARIES.md +49 -0
- package/docs/TROUBLESHOOTING.md +32 -2
- package/docs/USE_CASE_RECIPES.md +69 -0
- package/docs/WEB-API.md +2 -0
- package/lib/handlers.js +1 -1
- package/lib/slack-client.js +1 -1
- package/package.json +4 -2
- package/public/demo-claude.html +56 -9
- package/public/demo-video.html +56 -1
- package/public/demo.html +49 -6
- package/scripts/check-owner-attribution.sh +80 -0
- package/scripts/setup-git-hooks.sh +15 -0
- package/scripts/setup-wizard.js +148 -42
- 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/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="
|
|
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 -
|
|
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 -
|
|
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 -->
|
|
@@ -124,6 +124,43 @@
|
|
|
124
124
|
color: var(--text-secondary);
|
|
125
125
|
font-size: 16px;
|
|
126
126
|
}
|
|
127
|
+
.cta-strip {
|
|
128
|
+
width: 100%;
|
|
129
|
+
max-width: 960px;
|
|
130
|
+
margin-bottom: 16px;
|
|
131
|
+
background: rgba(15, 52, 96, 0.72);
|
|
132
|
+
border: 1px solid rgba(255, 255, 255, 0.15);
|
|
133
|
+
border-radius: 12px;
|
|
134
|
+
padding: 10px 14px;
|
|
135
|
+
display: flex;
|
|
136
|
+
justify-content: space-between;
|
|
137
|
+
align-items: center;
|
|
138
|
+
gap: 10px;
|
|
139
|
+
flex-wrap: wrap;
|
|
140
|
+
font-size: 13px;
|
|
141
|
+
}
|
|
142
|
+
.cta-strip .links {
|
|
143
|
+
display: flex;
|
|
144
|
+
gap: 8px;
|
|
145
|
+
flex-wrap: wrap;
|
|
146
|
+
}
|
|
147
|
+
.cta-strip .links a {
|
|
148
|
+
color: #d8efff;
|
|
149
|
+
text-decoration: none;
|
|
150
|
+
border: 1px solid rgba(255, 255, 255, 0.25);
|
|
151
|
+
border-radius: 999px;
|
|
152
|
+
padding: 4px 8px;
|
|
153
|
+
}
|
|
154
|
+
.cta-strip .links a:hover {
|
|
155
|
+
background: rgba(255, 255, 255, 0.08);
|
|
156
|
+
}
|
|
157
|
+
.cta-strip .note {
|
|
158
|
+
color: rgba(255, 255, 255, 0.78);
|
|
159
|
+
}
|
|
160
|
+
.cta-strip .note a {
|
|
161
|
+
color: #9ee7ff;
|
|
162
|
+
text-decoration: underline;
|
|
163
|
+
}
|
|
127
164
|
|
|
128
165
|
.badge {
|
|
129
166
|
display: inline-flex;
|
|
@@ -1111,10 +1148,20 @@
|
|
|
1111
1148
|
</style>
|
|
1112
1149
|
</head>
|
|
1113
1150
|
<body>
|
|
1151
|
+
<div class="cta-strip">
|
|
1152
|
+
<div class="links">
|
|
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/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
|
+
</div>
|
|
1157
|
+
<div class="note">
|
|
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
|
+
</div>
|
|
1160
|
+
</div>
|
|
1114
1161
|
<header class="page-header">
|
|
1115
1162
|
<h1>
|
|
1116
1163
|
<span>Slack MCP Server</span>
|
|
1117
|
-
<span class="badge">🔧 MCP Demo v1.2</span>
|
|
1164
|
+
<span class="badge">🔧 MCP Demo v1.2.3</span>
|
|
1118
1165
|
</h1>
|
|
1119
1166
|
<p>See how Claude uses MCP tools to access your Slack workspace</p>
|
|
1120
1167
|
</header>
|
|
@@ -1186,7 +1233,7 @@
|
|
|
1186
1233
|
<div class="title-logo">💬</div>
|
|
1187
1234
|
<h1>Slack MCP Server</h1>
|
|
1188
1235
|
<p class="title-tagline">Full Slack access for Claude Desktop</p>
|
|
1189
|
-
<p class="title-version">v1.2 • @jtalk22</p>
|
|
1236
|
+
<p class="title-version">v1.2.3 • @jtalk22</p>
|
|
1190
1237
|
</div>
|
|
1191
1238
|
|
|
1192
1239
|
<!-- Scenario Caption Overlay -->
|
|
@@ -1196,9 +1243,9 @@
|
|
|
1196
1243
|
<div class="closing-card" id="closingCard">
|
|
1197
1244
|
<div class="closing-check">✅</div>
|
|
1198
1245
|
<h2>Demo Complete</h2>
|
|
1199
|
-
<p class="closing-cta">
|
|
1246
|
+
<p class="closing-cta">Session-based Slack access aligned to your existing workspace permissions.</p>
|
|
1200
1247
|
<div class="closing-links">
|
|
1201
|
-
<code>
|
|
1248
|
+
<code>npx -y @jtalk22/slack-mcp --setup</code>
|
|
1202
1249
|
</div>
|
|
1203
1250
|
<p class="closing-github">github.com/jtalk22/slack-mcp-server</p>
|
|
1204
1251
|
<span class="easter-egg">ê</span>
|
package/public/demo-video.html
CHANGED
|
@@ -4,6 +4,16 @@
|
|
|
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="Session-based Slack access for Claude with your existing workspace permissions. Video demo for DMs, channels, search, and threads.">
|
|
8
|
+
<meta property="og:type" content="website">
|
|
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
|
+
<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-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
|
+
<meta name="twitter:image" content="https://raw.githubusercontent.com/jtalk22/slack-mcp-server/main/docs/images/demo-poster.png">
|
|
7
17
|
<style>
|
|
8
18
|
* {
|
|
9
19
|
margin: 0;
|
|
@@ -37,6 +47,41 @@
|
|
|
37
47
|
margin-bottom: 1.5rem;
|
|
38
48
|
font-size: 1rem;
|
|
39
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
|
+
}
|
|
40
85
|
.video-wrapper {
|
|
41
86
|
position: relative;
|
|
42
87
|
border-radius: 12px;
|
|
@@ -97,7 +142,17 @@
|
|
|
97
142
|
<body>
|
|
98
143
|
<div class="container">
|
|
99
144
|
<h1>Slack MCP Server</h1>
|
|
100
|
-
<p class="subtitle">
|
|
145
|
+
<p class="subtitle">Free local-first Slack access using your existing session permissions</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/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
|
+
</div>
|
|
152
|
+
<div class="note">
|
|
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
|
+
</div>
|
|
155
|
+
</div>
|
|
101
156
|
|
|
102
157
|
<div class="video-wrapper">
|
|
103
158
|
<video id="demo" poster="../docs/images/demo-poster.png" playsinline>
|
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-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 -
|
|
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="
|
|
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">
|
|
@@ -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/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
|
+
</div>
|
|
642
|
+
<div class="cta-note">
|
|
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
|
+
</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'."
|
|
@@ -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
|
@@ -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
|
|
|
@@ -232,7 +238,7 @@ async function showStatus() {
|
|
|
232
238
|
if (!creds) {
|
|
233
239
|
error("No tokens found");
|
|
234
240
|
print();
|
|
235
|
-
print("Run setup wizard: npx @jtalk22/slack-mcp --setup");
|
|
241
|
+
print("Run setup wizard: npx -y @jtalk22/slack-mcp --setup");
|
|
236
242
|
process.exit(1);
|
|
237
243
|
}
|
|
238
244
|
|
|
@@ -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}`);
|
|
256
|
+
print();
|
|
257
|
+
print("Run setup wizard to refresh: npx -y @jtalk22/slack-mcp --setup");
|
|
258
|
+
process.exit(1);
|
|
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}+)`);
|
|
259
318
|
print();
|
|
260
|
-
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");
|
|
261
331
|
process.exit(1);
|
|
262
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() {
|
|
@@ -268,11 +369,12 @@ async function showHelp() {
|
|
|
268
369
|
print("Full Slack access for Claude via MCP. Session mirroring bypasses OAuth.");
|
|
269
370
|
print();
|
|
270
371
|
print(`${colors.bold}Usage:${colors.reset}`);
|
|
271
|
-
print(" npx @jtalk22/slack-mcp Start MCP server (stdio)");
|
|
272
|
-
print(" npx @jtalk22/slack-mcp --setup Interactive token setup wizard");
|
|
273
|
-
print(" npx @jtalk22/slack-mcp --status Check token health");
|
|
274
|
-
print(" npx @jtalk22/slack-mcp --
|
|
275
|
-
print(" npx @jtalk22/slack-mcp --
|
|
372
|
+
print(" npx -y @jtalk22/slack-mcp Start MCP server (stdio)");
|
|
373
|
+
print(" npx -y @jtalk22/slack-mcp --setup Interactive token setup wizard");
|
|
374
|
+
print(" npx -y @jtalk22/slack-mcp --status Check token health");
|
|
375
|
+
print(" npx -y @jtalk22/slack-mcp --doctor Run runtime and auth diagnostics");
|
|
376
|
+
print(" npx -y @jtalk22/slack-mcp --version Print version");
|
|
377
|
+
print(" npx -y @jtalk22/slack-mcp --help Show this help");
|
|
276
378
|
print();
|
|
277
379
|
print(`${colors.bold}npm scripts:${colors.reset}`);
|
|
278
380
|
print(" npm start Start MCP server");
|
|
@@ -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}`);
|
|
@@ -345,8 +451,8 @@ async function main() {
|
|
|
345
451
|
print(`${colors.green}${colors.bold}Setup complete!${colors.reset}`);
|
|
346
452
|
print();
|
|
347
453
|
print("Next steps:");
|
|
348
|
-
print(" • Verify: npx @jtalk22/slack-mcp --status");
|
|
349
|
-
print(" • Start server: npx @jtalk22/slack-mcp");
|
|
454
|
+
print(" • Verify: npx -y @jtalk22/slack-mcp --status");
|
|
455
|
+
print(" • Start server: npx -y @jtalk22/slack-mcp");
|
|
350
456
|
print(" • Or add to Claude Desktop config");
|
|
351
457
|
} else {
|
|
352
458
|
print(`${colors.red}Setup failed.${colors.reset} See errors above.`);
|