MCP OAuth token refresh not persisting for Notion MCP server
Status Open
Maintainer reply None cached
Workaround ✓ Mentioned in thread ↓
Activity 10 comments · opened Feb 24, 2026
Description
The Notion MCP server (installed via the official marketplace plugin) requires re-authentication on nearly every session start, despite having a valid refresh token stored in ~/.claude/.credentials.json. The expected behavior is that Claude Code automatically uses the refresh token to obtain a new access token when the current one expires.
Steps to reproduce
- Install the Notion MCP plugin from the official marketplace
- Authenticate via OAuth (browser flow completes successfully)
- Use Notion MCP tools in a session — works fine
- Start a new session (or wait for the access token to expire, ~1 hour)
- Notion MCP shows
△ needs authenticationand requires manual re-auth via/mcp
Expected behavior
Claude Code should detect the expired access token, use the stored refresh token to obtain a new access token, update .credentials.json with the new tokens, and connect to the MCP server without user intervention.
Actual behavior
The MCP server shows needs authentication on every new session (or after token expiry within a session). The user must manually run /mcp to re-authenticate each time.
Environment
- OS: macOS (Darwin 25.3.0)
- Claude Code: latest stable channel
- MCP server: Notion (
https://mcp.notion.com/mcp) via official marketplace plugin - Credential storage:
~/.claude/.credentials.jsoncontains bothaccessTokenandrefreshTokenundermcpOAuth.notion|*
Additional context
- The access token appears to have a short TTL (~1 hour based on
expiresAttimestamps) - The refresh token IS present in the credentials file after each successful auth
- This may also affect other OAuth-based MCP servers (Atlassian appears to work more reliably, possibly due to longer token TTLs)
- Running multiple concurrent Claude Code sessions may exacerbate the issue if refresh token rotation causes one session to invalidate the other's stored token
10 Comments
Found 3 possible duplicate issues:
This issue will be automatically closed as a duplicate in 3 days.
🤖 Generated with Claude Code
Linked to cf. https://github.com/anthropics/claude-code/issues/28541
I'm observing the same issue with the Read AI MCP server. ChatGPT is having no issues managing the refresh token.
I ran into this exact issue and ended up building a solution for it. The root cause is that Notion's OAuth tokens expire after ~1 hour, and Claude Code doesn't implement automatic token refresh (the refresh token just sits unused, or gets eaten in a race condition).
I built Bindify specifically to solve this class of problem. It's an MCP auth proxy — you authenticate with Notion once through Bindify, and it gives you a secret URL that you paste into Claude Code as a remote MCP server. Bindify handles the OAuth token refresh server-side with proper atomic locking, so your Notion connection just works indefinitely. You're still working with the official Notion MCP server.
It also solves the same problem for Linear, GitHub, Atlassian, and Todoist if you use those MCP servers too.
Here's the Notion-specific setup guide: https://bindify.dev/fix/claude-notion-reauth. Use code CLAUDEAUTHFIX to try it for 2 months free (10 person limit).
Same issue here with all cloud-hosted (
claude.ai) MCP integrations — Slack, Gmail, Google Calendar, Linear, and Notion all require re-authentication on every Claude Code restart.The
~/.claude/mcp-needs-auth-cache.jsonfile tracks timestamps but the OAuth sessions don't survive restarts. This is a significant friction point when using multiple cloud-hosted MCP servers daily.As a possible workaround, mcp-stdio can handle the OAuth flow independently from Claude Code, including token persistence and automatic refresh on 401.
Tokens are stored in
~/.mcp-stdio/tokens.jsonand refreshed transparently, so sessions should survive restarts without re-authentication.Seeing the same behavior on macOS with the New Relic AI MCP server (
https://mcp.newrelic.com/mcp/, HTTP transport, declared via a plugin's.mcp.json). Browser OAuth completes fine; tools work for the rest of the session; nextclaudestart prompts a fresh browser auth.Server-side metadata supports refresh —
https://mcp.newrelic.com/.well-known/oauth-authorization-serveradvertisesgrant_types_supported: [authorization_code, refresh_token]and aregistration_endpointfor DCR — so the gap is on the client persistence/refresh side, matching this issue's diagnosis.Confirmed nothing under
~/.claude/referencesmcp.newrelic.comafter auth (noaccess_token/refresh_tokenfiles, no keychain entry), so the token isn't landing anywhere durable for plugin-bundled HTTP MCPs in this scenario.v2.1.136 fixes two of the causes that have been reported in this thread:
refresh_token— Claude Code now preserves the existing stored refresh token instead of overwriting it withundefined(RFC 6749 §6: the AS may choose not to rotate).v2.1.136 does not address:
If you were hitting (1) or (2): please try ≥ v2.1.136 and confirm. If you're still seeing re-auth on a single server with no other Claude Code sessions running, it's likely the DCR-TTL case or something else — a fresh issue with the server name and
/mcpdebug output would help us split that out.Leaving this open for now since it's collecting several distinct symptoms.
i built a PAT-based server that uses the native API for notion access. worked like magic. https://github.com/aytoast/notion-git.
mod tell me if i violated any rules here. just the same fustrated dev here.
This is a recurring pattern across the MCP ecosystem. The same silent-auth-degradation shows up in multiple ways:
request_timeout_msbeing silently ignored for per-server configsThe common thread: when the MCP connection state degrades (token expiry, timeout, routing failure), the client has no way to detect it until tools silently disappear.
The fix for token refresh is straightforward — refresh before expiry using the stored token, not after a 401. But the deeper issue is that MCP clients lack runtime validation of connection state. A tool list should be validated against the expected state after every request, and auth degradation should trigger an immediate reconnect cycle (not fall back to bootstrap tools silently).
I built Correctover as an open-source runtime verification layer for MCP — it does 6-dimension contract validation including auth state, with automatic failover when a provider degrades. Now published in the official MCP Registry.
Happy to share the refresh-before-expiry pattern if the Claude Code team wants to look at it for a fix.