MCP OAuth token refresh not persisting for Notion MCP server

Status Open
Maintainer reply None cached
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

  1. Install the Notion MCP plugin from the official marketplace
  2. Authenticate via OAuth (browser flow completes successfully)
  3. Use Notion MCP tools in a session — works fine
  4. Start a new session (or wait for the access token to expire, ~1 hour)
  5. Notion MCP shows △ needs authentication and 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.json contains both accessToken and refreshToken under mcpOAuth.notion|*

Additional context

  • The access token appears to have a short TTL (~1 hour based on expiresAt timestamps)
  • 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

View original on GitHub ↗

10 Comments

github-actions[bot] · 6 months ago

Found 3 possible duplicate issues:

  1. https://github.com/anthropics/claude-code/issues/5706
  2. https://github.com/anthropics/claude-code/issues/19481
  3. https://github.com/anthropics/claude-code/issues/12447

This issue will be automatically closed as a duplicate in 3 days.

  • If your issue is a duplicate, please close it and 👍 the existing issue instead
  • To prevent auto-closure, add a comment or 👎 this comment

🤖 Generated with Claude Code

jean-humann · 6 months ago
rohitmusti · 5 months ago

I'm observing the same issue with the Read AI MCP server. ChatGPT is having no issues managing the refresh token.

dylancwood · 5 months ago

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).

demisx · 5 months ago

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.json file tracks timestamps but the OAuth sessions don't survive restarts. This is a significant friction point when using multiple cloud-hosted MCP servers daily.

shigechika · 4 months ago

As a possible workaround, mcp-stdio can handle the OAuth flow independently from Claude Code, including token persistence and automatic refresh on 401.

claude mcp add notion -- uvx mcp-stdio --oauth https://mcp.notion.com/mcp

Tokens are stored in ~/.mcp-stdio/tokens.json and refreshed transparently, so sessions should survive restarts without re-authentication.

willie · 4 months ago

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; next claude start prompts a fresh browser auth.

Server-side metadata supports refresh — https://mcp.newrelic.com/.well-known/oauth-authorization-server advertises grant_types_supported: [authorization_code, refresh_token] and a registration_endpoint for DCR — so the gap is on the client persistence/refresh side, matching this issue's diagnosis.

Confirmed nothing under ~/.claude/ references mcp.newrelic.com after auth (no access_token/refresh_token files, no keychain entry), so the token isn't landing anywhere durable for plugin-bundled HTTP MCPs in this scenario.

bhosmer-ant · 3 months ago

v2.1.136 fixes two of the causes that have been reported in this thread:

  1. Multi-server / multi-session refresh-token race — all MCP credential writes now go through a single cross-process lock with a fresh read under the lock, so concurrent refreshes no longer overwrite each other's rotated refresh tokens (the "one session invalidates another's stored token" case).
  2. Refresh response omits refresh_token — Claude Code now preserves the existing stored refresh token instead of overwriting it with undefined (RFC 6749 §6: the AS may choose not to rotate).

v2.1.136 does not address:

  • claude.ai-hosted connectors needing re-auth on every restart (@demisx's report — Slack/Gmail/Calendar/Linear via the claude.ai Connectors path). That's a different code path from user-configured MCP servers and is tracked separately.
  • Dynamic-client-registration TTL expiry — some providers expire the registered OAuth client (not the tokens) after a period, which forces a fresh browser auth regardless of refresh-token validity. This is a likely contributor to the Notion-only / single-server reports here.

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 /mcp debug output would help us split that out.

Leaving this open for now since it's collecting several distinct symptoms.

aytoast · 1 month ago

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.

Correctover · 1 month ago

This is a recurring pattern across the MCP ecosystem. The same silent-auth-degradation shows up in multiple ways:

  • Claude Code v2.1.206 (July 10) fixed request_timeout_ms being silently ignored for per-server configs
  • Claude Code v0.171.0 (July 14) fixed stdio MCP servers hanging indefinitely on connect
  • OpenAI Codex had a report of the agent bypassing MCP routing entirely and burning 281 tool executions

The 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.