[BUG] Slack MCP (HTTP OAuth): tokens cleared on every 401 instead of using refresh token — forces full re-auth ~each session
Preflight Checklist
- [x] I have searched existing issues and this hasn't been reported yet
- [x] This is a single bug report
- [x] I am using the latest version of Claude Code (bug observed on
2.1.141; temporarily downgraded to2.1.140as a workaround — see below)
What's Wrong?
The HTTP-OAuth-based Slack MCP server (https://mcp.slack.com/mcp, configured via /mcp) requires a full interactive OAuth re-authentication every time a 401 Unauthorized is received from the server — including transient cases like a dropped HTTP transport — even though a valid refresh token is present in the on-disk credential store.
On any 401, the CLI immediately logs Cleared stored tokens, opens a new authorisation URL in the browser, and prompts re-authentication. The refresh token in ~/.claude/.credentials.json is destroyed before any refresh attempt is made. A grant_type=refresh_token exchange is never attempted, even though the SDK logs Has refresh token: true repeatedly on the preceding successful tool calls.
In practice this means re-authenticating Slack approximately once per work-day (Slack issues access tokens with expires_in: 43200 / 12 h), and — more disruptively — it breaks any headless claude -p workflow that depends on the Slack MCP, because such workflows cannot complete the interactive OAuth flow.
What Should Happen?
On a 401 from an MCP HTTP server, the CLI should:
- If a
refreshTokenis present in storage for that server, attempt agrant_type=refresh_tokenexchange first. - Persist the new access/refresh token pair on success and retry the original tool call.
- Only clear stored tokens and fall back to the full authorisation-code flow if the refresh exchange itself fails (e.g. 400/401 from the token endpoint).
A debug log line on the refresh attempt path would also help diagnose this from mcp-logs-slack/.
Error Messages/Logs
The relevant transition during a single session that had previously authed successfully (extracted from ~/Library/Caches/claude-cli-nodejs/-<cwd>/mcp-logs-slack/):
2026-05-13T13:30:37.918Z Channel notifications skipped: server did not declare claude/channel capability
2026-05-13T13:31:07.872Z Calling MCP tool: slack_search_users
2026-05-13T13:31:07.872Z No token data found
2026-05-13T13:31:08.390Z Saving discovery state (authServer: https://mcp.slack.com)
2026-05-13T13:31:08.468Z No access token in storage
2026-05-13T13:31:08.469Z HTTP connection dropped after 2760s uptime
2026-05-13T13:31:08.469Z Connection error: Unauthorized
2026-05-13T13:31:08.469Z Tool 'slack_search_users' failed after 0s: Unauthorized
2026-05-13T13:31:08.469Z Tool call returned 401 Unauthorized - token may have expired
2026-05-13T13:31:18.834Z No access token in storage
2026-05-13T13:31:24.846Z Cleared stored tokens
2026-05-13T13:31:24.846Z Using redirect port: <PORT> (from config)
2026-05-13T13:31:25.086Z Fetched OAuth metadata with scope: …
2026-05-13T13:31:25.421Z Authorization URL: https://slack.com/oauth/v2_user/authorize?…
2026-05-13T13:31:25.421Z Opening authorization URL: …
The sequence:
- HTTP connection drops after 46 min of uptime (well within the 12 h token life).
- Server returns 401 on the next tool call.
- CLI logs
Cleared stored tokens, throwing away the still-valid refresh token. - CLI generates a fresh code verifier and opens the full OAuth URL in the browser.
After a successful re-auth on 2026-05-14T10:38, a fresh expires_in: 43200 token was issued and has_refresh_token: true was reported again — Slack is issuing refresh tokens consistently; the CLI just isn't using them on 401.
Steps to Reproduce
- Configure the Slack MCP via
/mcpand complete the OAuth flow. - Use the Slack MCP normally for a while so the access token is cached in
~/.claude/.credentials.json(the entry sits undermcpOAuth.slack|<hash>and includes a validaccessToken,refreshToken, andexpiresAt). - Either (a) leave the session idle long enough for the HTTP transport to drop, or (b) wait for the access token to expire (12 h).
- Make any Slack MCP tool call (e.g.
slack_search_users,slack_read_channel).
Expected: the CLI exchanges the refresh token for a new access token silently and the tool call succeeds.
Actual: the CLI clears the tokens, opens the authorisation URL in the browser, and fails the tool call with Unauthorized. Headless claude -p invocations fail outright because no human is present to complete the interactive flow.
Claude Model
Not sure / Multiple models (this is a CLI/MCP behaviour, model-independent)
Is this a regression?
Yes, this worked in a previous version
Last Working Version
2.1.140 (anecdotal — the symptom appeared after updating to 2.1.141)
Claude Code Version
2.1.141 (Claude Code) — bug observed here. Currently running 2.1.140 (Claude Code) as a temporary workaround.
Platform
Anthropic API
Operating System
macOS
Terminal/Shell
Terminal.app (macOS)
Additional Information
Workaround applied locally: pinned the CLI to 2.1.140 (the install is versioned at ~/.local/share/claude/versions/):
ln -sfn ~/.local/share/claude/versions/2.1.140 ~/.local/bin/claude
This is a temporary mitigation, not a fix — I haven't confirmed exactly where the regression was introduced.
Other context:
- Other HTTP-OAuth MCPs configured under
mcpOAuthin~/.claude/.credentials.jsonappear to be working fine — but those are used more intermittently than Slack and the symptom may not have surfaced yet. - Sanitised log excerpts can be supplied on request from
~/Library/Caches/claude-cli-nodejs/-<cwd>/mcp-logs-slack/.
This issue has 3 comments on GitHub. Read the full discussion on GitHub ↗