You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
OAuth MCP server requires reauthentication after refresh-token rotation #65216
On a clean Zed install, configure one OAuth-protected HTTP MCP server in user-level settings.json (not in either project's settings). The server's OAuth provider must rotate refresh tokens and reject a token after it has been used for refhresh (in compliance with rfc6749 "The authorization server MAY issue a new refresh token, in which case the client MUST discard the old refresh token and replace it with the new refresh token.")
Replace the example URL with an OAuth-enabled MCP endpoint. Authenticate once in Zed so the OAuth session is saved.
Open several projects in Zed. All should inherit that one user-level MCP server entry and connect to it. Restoring several projects at startup may make the problem more likely.
Ensure setting "Restore On Startup" is set to "Last Session".
Close Zed and wait until the access token has expired, the refrehs token must still be valid.
Re-open Zed and let all projects connect to the MCP server. The first project to make a request will refresh its access token and rotate the refresh token.
Other projects will still have the old refresh token in their in-memory OAuth provider. When they make a request that requires a refresh, the MCP server will reject it with invalid_grant and Zed will require reauthentication.
Here is what has been analyzed so far:
The two projects inherit one user setting but load separate in-memory copies of the saved session:
sequenceDiagram
participant A as Project A
participant K as Shared keychain
participant B as Project B
participant M as MCP server
participant O as OAuth server
K-->>A: Load refresh-0
K-->>B: Load refresh-0
A->>M: Request with access-0
M-->>A: 401
A->>O: Refresh with refresh-0
O-->>A: access-1 and refresh-1
A->>K: Persist refresh-1
B->>M: Request with access-0
M-->>B: 401
B->>O: Refresh with stale refresh-0
O-->>B: invalid_grant
Note over B: Reauthentication required
The test uses two real project stores, one user setting, one shared in-memory credentials provider, and a mock MCP/OAuth server with rotating refresh tokens. Both connections initialize with the same saved session. Project A refreshes successfully and the new token is confirmed persisted before project B sends its request. The test still fails at the intended assertion:
Additional evidence are logs of a real world MCP protected by GitHubs OAuth that show regularly bursts of refresh-token exchange failures:
The MCP is private, though, so I cannot offer it for testing.
Current vs. Expected behavior
Current behavior: Each project independently connects to the same globally configured MCP server. After project A rotates the refresh token, project B can retain the old refresh token in its own in-memory OAuth provider. Its later refresh is rejected, and the MCP connection requires reauthentication even though the new session has already been persisted.
Expected behavior: Projects using the same OAuth-protected MCP URL should reuse or coordinate the current session. Refreshing in one project should not invalidate another project's connection or prompt the user to authenticate again. This should hold for sequential requests, not only simultaneous refreshes.
Reproduction steps
On a clean Zed install, configure one OAuth-protected HTTP MCP server in user-level
settings.json(not in either project's settings). The server's OAuth provider must rotate refresh tokens and reject a token after it has been used for refhresh (in compliance with rfc6749 "The authorization server MAY issue a new refresh token, in which case the client MUST discard the old refresh token and replace it with the new refresh token."){ "context_servers": { "example-mcp": { "enabled": true, "url": "https://mcp.example.com/mcp" } } }Replace the example URL with an OAuth-enabled MCP endpoint. Authenticate once in Zed so the OAuth session is saved.
Open several projects in Zed. All should inherit that one user-level MCP server entry and connect to it. Restoring several projects at startup may make the problem more likely.
Ensure setting "Restore On Startup" is set to "Last Session".
Close Zed and wait until the access token has expired, the refrehs token must still be valid.
Re-open Zed and let all projects connect to the MCP server. The first project to make a request will refresh its access token and rotate the refresh token.
Other projects will still have the old refresh token in their in-memory OAuth provider. When they make a request that requires a refresh, the MCP server will reject it with
invalid_grantand Zed will require reauthentication.Here is what has been analyzed so far:
The two projects inherit one user setting but load separate in-memory copies of the saved session:
sequenceDiagram participant A as Project A participant K as Shared keychain participant B as Project B participant M as MCP server participant O as OAuth server K-->>A: Load refresh-0 K-->>B: Load refresh-0 A->>M: Request with access-0 M-->>A: 401 A->>O: Refresh with refresh-0 O-->>A: access-1 and refresh-1 A->>K: Persist refresh-1 B->>M: Request with access-0 M-->>B: 401 B->>O: Refresh with stale refresh-0 O-->>B: invalid_grant Note over B: Reauthentication requiredI tried to let an AI agent craft a test to reproduce this in zed, which can be found here:
feature/reproduce-mcp-oauth-refresh-race.The test uses two real project stores, one user setting, one shared in-memory credentials provider, and a mock MCP/OAuth server with rotating refresh tokens. Both connections initialize with the same saved session. Project A refreshes successfully and the new token is confirmed persisted before project B sends its request. The test still fails at the intended assertion:
Additional evidence are logs of a real world MCP protected by GitHubs OAuth that show regularly bursts of refresh-token exchange failures:
The MCP is private, though, so I cannot offer it for testing.
Current vs. Expected behavior
Current behavior: Each project independently connects to the same globally configured MCP server. After project A rotates the refresh token, project B can retain the old refresh token in its own in-memory OAuth provider. Its later refresh is rejected, and the MCP connection requires reauthentication even though the new session has already been persisted.
Expected behavior: Projects using the same OAuth-protected MCP URL should reuse or coordinate the current session. Refreshing in one project should not invalidate another project's connection or prompt the user to authenticate again. This should hold for sequential requests, not only simultaneous refreshes.
Zed version and system specs
Zed: v1.21.0+stable.362.33c95853ed2b6956f339733c63a8220964ecbeb6 (Zed)
OS: macOS 26.7.1
Memory: 64 GiB
Architecture: aarch64
Attach Zed log file
no relevant logs found.
Relevant Zed settings
settings.json
{ "restore_on_startup": "last_session", "context_servers": { "example-mcp": { "enabled": true, "url": "https://mcp.example.com/mcp" } } }Relevant Keymap
No response
(for AI issues) Model provider details
No response
If you are using WSL on Windows, what flavor of Linux are you using?
None