[BUG] Slack MCP: http protocol-negotiation probe hangs -> 30s CONNECT_TIMEOUT in every session except the one that most recently minted a token
Summary
Since 2026-08-31, plugin:slack:slack (https://mcp.slack.com/mcp) fails to connect in every concurrent Claude Code session except whichever one most recently minted an OAuth token. It surfaces as CONNECT_TIMEOUT after 30000ms, which points at the network — but the endpoint is healthy and the stored token is valid.
The real sequence is: the http-transport version-negotiation probe gets no answer (fixed 5s), falls back to pinned legacy, and that exhausts the remaining 30s budget.
No client update was involved. The probe code is present in 2.1.248, 2.1.250 and 2.1.251 alike — what differs is the server-side flag tengu_mcp_protocol_negotiation_http = true on this account.
Environment
- Claude Code 2.1.251 (native install,
~/.local/share/claude/versions/2.1.251) - macOS Darwin 25.6.0, Node v26.3.0
- Plugin
claude-plugins-official/slack1.2.0 →https://mcp.slack.com/mcp,type: http, OAuthcallbackPort: 3118 - No
HTTP_PROXY/HTTPS_PROXYset - 6–8 concurrent interactive sessions — an unchanged workflow that worked for months
Relevant cached feature flags
From ~/.claude.json → .cachedGrowthBookFeatures:
tengu_mcp_protocol_negotiation_http = true <-- gates the failing probe
tengu_mcp_protocol_negotiation_stdio = true
tengu_mcp_protocol_negotiation_claudeai = true
tengu_mcp_protocol_negotiation_ccr = false
tengu_mcp_retry_failed_remote = false <-- see #48993
Failure sequence
From ~/Library/Caches/claude-cli-nodejs/<cwd>/mcp-logs-plugin-slack-slack/*.jsonl:
11:56:41 Initializing HTTP transport to https://mcp.slack.com/mcp
11:56:41 HTTP transport created successfully
11:56:41 Starting connection with timeout of 30000ms
11:56:41 Testing basic HTTP connectivity to https://mcp.slack.com/mcp
11:56:41 Returning tokens / Token length: 176 / Has refresh token: true / Expires in: 43055s
11:56:46 version negotiation probe timed out on the http transport;
reconnecting pinned legacy within the remaining budget
11:57:11 HTTP Connection failed after 30002ms: connection timed out after 30000ms (CONNECT_TIMEOUT)
11:57:11 Transient CONNECT_TIMEOUT on initial connect — retry 3/3 in 4000ms
The probe consumes a consistent 5s, then the legacy fallback consumes the remaining ~25s. This loops indefinitely.
The probe timeout has never occurred before today
234 log files spanning 2026-04-28 → 2026-08-31. First occurrence of version negotiation probe timed out is 2026-08-31T10:34:54.509Z; 53 occurrences, all today, none earlier.
The string is present in every installed binary, so this is not new code:
cd ~/.local/share/claude/versions
for v in 2.1.248 2.1.250 2.1.251; do
printf "%s: " "$v"; grep -a -o -m1 'version negotiation probe timed out' "$v"
done
# 2.1.248: version negotiation probe timed out
# 2.1.250: version negotiation probe timed out
# 2.1.251: version negotiation probe timed out
2.1.251 was installed 2026-08-28 21:22 and Slack worked normally afterwards, which rules out the version bump as the trigger.
Concurrency used to work, and token age was irrelevant
Parsing the token lifetime in effect at each successful Connection established:
| Date | Successful connects | Token age at connect |
|---|---|---|
| 2026-08-21 | 11 | 1s – 14,542s |
| 2026-08-24 | 13 | 1s – 26,907s |
| 2026-08-26 | 10 | 1s – 25,652s |
| 2026-08-31 | 4 | 1–2s only |
Many concurrent sessions previously connected with tokens hours old. Today, only a token that is 1–2 seconds old works.
Only the most recently minted token works
Three token re-mints today, each followed within one second by that same session connecting — and every other session holding the previous token then fails:
10:44:41 session 047630ef expires_in=43198 -> 10:44:42 Connection established
11:31:32 session af9535c3 expires_in=43199 -> 11:31:33 Connection established
11:54:18 session b35777c5 expires_in=43198 -> 11:54:18 Connection established
All sessions read one shared credential entry (the Slack plugin's fixed callbackPort: 3118 makes the storage key stable, unlike #43000), so there is only ever one live token.
Overlapping proof that the token and endpoint are fine — session b35777c5 was making successful calls at the exact moment session 40a9b36f could not complete initialize:
b35777c5 11:56:31 Tool 'slack_read_thread' completed successfully in 248ms
b35777c5 11:56:54 Tool 'slack_read_thread' completed successfully in 258ms
40a9b36f 11:56:46 version negotiation probe timed out ...
40a9b36f 11:57:11 CONNECT_TIMEOUT after 30002ms
The endpoint is healthy
curl -s -o /dev/null -w "%{http_code} dns=%{time_namelookup}s tls=%{time_appconnect}s total=%{time_total}s\n" \
-X POST https://mcp.slack.com/mcp \
-H 'Content-Type: application/json' -H 'Accept: application/json, text/event-stream' \
-d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"probe","version":"1"}}}'
# 401 dns=0.004838s tls=0.444653s total=0.646538s
Why the reported error is misleading
CONNECT_TIMEOUTimplicates the network. The actual cause is aninitializethat is never answered, misattributed to version negotiation.- When the stale token is cleanly rejected it is fast —
Error POSTing to endpoint: unauthorized: AuthenticateToken authentication failedin 0.4–10s — butslackis never written to~/.claude/mcp-needs-auth-cache.json(which does containlinear,google-workspace-*,databricks,figma). So no re-auth prompt is ever shown; the session silently retries a dead token into the 30s timeout. - 15 distinct session IDs hit this on 2026-08-31 with no actionable error.
Requested
- Don't attribute an unanswered
initializeto version negotiation — surface the underlying auth failure instead. - Bound the
pinned legacyfallback so it cannot consume the entire 30s connect budget. - Per-session credential handles, or a refresh coordinator that re-broadcasts a rotated token to live sessions, so one session's re-mint doesn't invalidate every other session (same underlying ask as #77130, which is open with no response).
- A documented local override for
tengu_mcp_protocol_negotiation_http— #48993 asked for the same fortengu_mcp_retry_failed_remoteand was closed without one. - Add
slackto the needs-auth cache onAuthenticateTokenfailure so users get a re-auth prompt rather than a timeout.
Related
- #77130 — one session's
/login/refresh invalidates connectors in all other concurrent sessions (open, no maintainer response). Same mechanism, but for claude.ai cloud connectors and without the timeout/hang symptom. - #48993 —
tengu_mcp_retry_failed_remote=falsecohort forces re-OAuth for Slack (closed, no maintainer response). - #43000 — MCP OAuth keyed on ephemeral callback port causes re-auth with multiple terminals (closed as not planned). Does not apply here because Slack pins port 3118.
- #51319 and slackapi/slack-mcp-plugin#46 — hardcoded callback port 3118 blocks parallel OAuth. Related but distinct: port 3118 was free in this case and the failure is at
initialize, not the browser flow.
Reproduce
cd ~/Library/Caches/claude-cli-nodejs
# the real error, per session
grep -rh "AuthenticateToken" --include="*.jsonl" ./*/mcp-logs-plugin-slack-slack/*.jsonl | grep 2026-08-31
# first-ever occurrence of the probe timeout
grep -rh "version negotiation probe" --include="*.jsonl" ./*/mcp-logs-plugin-slack-slack/*.jsonl | head -1
# token age at every successful connect, all history
grep -rh "Expires in\|Connection established" --include="*.jsonl" ./*/mcp-logs-plugin-slack-slack/*.jsonl
# the gating flag
python3 -c "import json;f=json.load(open('$HOME/.claude.json'))['cachedGrowthBookFeatures'];print({k:v for k,v in f.items() if 'protocol_negotiation' in k or k=='tengu_mcp_retry_failed_remote'})"
# slack absent from the needs-auth cache
tr ',' '\n' < ~/.claude/mcp-needs-auth-cache.json | grep -i slack # no outputThis issue has 1 comment on GitHub. Read the full discussion on GitHub ↗