Skip to content

mcp-auth.json loses tokens for multiple servers — MCP servers keep requiring re-auth #42875

Closed

Description

@pratikwayal01

Bug: mcp-auth.json loses tokens for multiple servers — MCP servers suddenly need re-auth

Summary

OAuth tokens in ~/.local/share/opencode/mcp-auth.json get wiped for several MCP servers at once. Sessions that were authenticated start prompting for auth again, even though the OAuth server's refresh token is still valid. It is not server-specific — several unrelated servers lost tokens in the same rewrite.

Observed

  • File mcp-auth.json was rewritten (mtime changed) without a manual edit.
  • Before rewrite: several servers had a tokens object (accessToken, refreshToken, expiresAt, scope).
  • After rewrite: only servers whose entries contained NO leftover PKCE state (codeVerifier / oauthState) kept their tokens. Every entry that still carried codeVerifier + oauthState lost its tokens — reduced to clientInfo + codeVerifier + oauthState + serverUrl.
  • The affected servers use different OAuth providers (custom SDK OAuth, Keycloak, Jira, etc.), so this is not one provider's token format.

What this looks like per entry

{
  "clientInfo": { "clientId": "...", "clientIdIssuedAt": 1786703712 },
  "codeVerifier": "...",
  "oauthState": "...",
  "serverUrl": "https://..."
}

→ no tokens key at all. The server-side refresh token still exists and works (verified by calling the token endpoint with grant_type=refresh_token — returns a fresh access token), so the only thing lost is the client's stored copy.

Impact

Any MCP server that does OAuth (dynamic client registration) requires a full interactive re-auth every time the file is rewritten. This happened at least twice in our setup within days.

Hypothesis

  • Re-running opencode mcp add <server> overwrites the existing entry with a fresh clientInfo/PKCE state and drops the stored tokens.
  • Or: the auth file is rewritten wholesale (version upgrade / config change / another process), and only entries written with tokens survive; anything mid-flow or registered-but-not-completed loses its tokens.
  • Correlates with leftover PKCE state: entries that still hold codeVerifier/oauthState are the ones that lost tokens — clean entries (no leftover PKCE state) kept theirs.

Expected

  • opencode mcp add / re-config on an existing server should preserve existing tokens (or warn that re-auth is required instead of silently dropping them).
  • If the file is rewritten wholesale, tokens should be merged/retained per server.

Environment

  • opencode, Linux, latest as of 2026-08-16.
  • mcp-auth.json path: ~/.local/share/opencode/mcp-auth.json
  • Trigger not yet pinned to a single command — file mtime changed without a manual edit.

Activity

  1. github-actions commented on Aug 16, 2026

    @github-actions
    Contributor

    This issue might be a duplicate of existing issues. Please check:

  2. iamwoz commented on Aug 17, 2026

    @iamwoz

    Same issue here — opencode 1.18.18, Linux (headless host).

    Setup

    Remote MCP server at https://mcp.fibery.io/mcp (OAuth + dynamic client registration), configured as fibery-[redacted]. It was previously named Fibery and later renamed.

    Symptoms

    • Every new opencode session drops back to needs_auth and forces a full interactive re-auth.

    • The log has ~30 occurrences over the last few days:

      level=WARN message="server unavailable" key=fibery-[redacted] type=remote status=needs_auth

    • One long-lived session stayed authenticated for ~23 hours, so the refresh flow works within a process — it's the persisted token that's lost, not the server-side refresh token.

    mcp-auth.json state

    Matches the report's signature exactly: leftover PKCE state, no tokens key.

    {
    "fibery-[redacted]": {
      "clientInfo": { "clientId": "ccfac24a-…" },
      "codeVerifier": "…",
      "oauthState": "…",
      "serverUrl": "https://mcp.fibery.io/mcp"
    },
    "Fibery": {
      "clientInfo": { "clientId": "3a61e33e-…" },
      "codeVerifier": "…",
      "oauthState": "…",
      "serverUrl": "https://mcp.fibery.io/mcp"
    }
    }

    Two things stand out:

    1. Both entries still carry codeVerifier + oauthState and have no tokens key, so opencode requires full re-auth every time.
    2. Fibery is a stale duplicate from before the rename — two different clientIds registered against the same serverUrl, which looks like the "re-registers a new dynamic client every run" path from MCP OAuth: opencode mcp auth re-registers a new dynamic client every run instead of reusing the stored registration #35625.

    Confirmed not a provider issue

    Fibery's authorization server metadata (/.well-known/oauth-authorization-server) advertises
    grant_types_supported: ["authorization_code","refresh_token"] and scopes_supported: "openid","offline"]. Exercising the token endpoint directly with grant_type=refresh_token returns a fresh access token, so the server-side refresh token is still valid — only opencode's stored copy was dropped.

  3. big-andy-coates commented on Aug 19, 2026

    @big-andy-coates

    Hit this with the atlassian-rovo-mcp remote server (https://mcp.atlassian.com/v1/mcp/authv2) and traced it end to end — adding as a concrete data point since this issue doesn't currently mention Atlassian/Rovo.

    Symptom: despite having a 90-day refresh token (offline_access scope, rotating), the server needed a full interactive re-auth (opencode mcp auth atlassian-rovo-mcp) roughly every 8 hours — i.e. exactly the access token's lifetime, as if the refresh token were never being used at all.

    Server log (~/.local/share/opencode/log/opencode.log) shows the pattern clearly — repeated failures every 20-90 min for hours with no refresh attempt or error ever logged in between, just an immediate fall-through to needs_auth:

    level=WARN message="mcp connect failed" server=atlassian-rovo-mcp status.status=needs_auth
    level=WARN message="mcp connect failed" server=atlassian-rovo-mcp status.status=needs_auth
    ... (repeats every 20-90 min for ~12 hours)
    level=INFO message="cli starting" args=["mcp","auth","atlassian-rovo-mcp"]   <- manual re-auth required
    

    Root cause appears to be upstream in the MCP TypeScript SDK. Atlassian issues rotating refresh tokens ("https://id.atlassian.com/atl_token_type":"ROTATING_REFRESH" in the decoded JWT) — each refresh invalidates the old refresh token and issues a new one that must be persisted immediately. The SDK's own auth() helper had a bug where a provider.saveTokens() failure during that persistence step was silently swallowed and the client fell through to requesting a brand new authorization — exactly this symptom. Confirmed via the SDK's own changeset description:

    Against an authorization server that rotates refresh tokens... this loses credentials rather than merely hiding an error. The exchange has already succeeded server-side, so the old refresh token is invalidated at the moment the new one is issued; dropping the new token set leaves nothing usable on either side.

    Fixed upstream in modelcontextprotocol/typescript-sdk#2053, merged 2026-08-18 — but not yet in any published release (latest npm @modelcontextprotocol/sdk is still 1.30.0 from 2026-07-27, before the fix), and opencode itself currently pins 1.29.0 in packages/opencode/package.json, one minor behind even that.

    So right now there's no available fix at either layer for OAuth servers with rotating refresh tokens (Atlassian, Keycloak, and per the SDK changeset this is "the OAuth 2.1 default"). Once the SDK cuts a release containing #2053, opencode will also need to bump its @modelcontextprotocol/sdk dependency past 1.29.0/1.30.0 to pick it up.

  4. rekram1-node commented on Sep 22, 2026

    @rekram1-node
    Collaborator

    V2 no longer uses mcp-auth.json. OAuth tokens, the client registration, and the refresh state for each MCP server are stored as a credential in the local database and written atomically per server, so an in-progress login for one server cannot rewrite or drop another server's tokens. Closing as resolved on V2; if tokens go missing on the current release, please open a new issue with steps to reproduce.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions