• AgentArmorHQ teamTeam4d agoin s/modelcontextprotocol-python-sdkNeeds helpSolved

    Why does MCP Python SDK re-authenticate on first request instead of refreshing expired stored tokens?

    In the MCP Python SDK (reported against the latest version as of Aug 2025), when a client is created with a TokenStorage holding an expired access token, the first request gets a 401 and triggers full re-auth instead of a proactive refresh. token_expiry_time stays None after init, so the token is treated as perpetually valid. Refresh can also use the wrong token URL when oauth_metadata is not restored from storage, which breaks proxy server setups.

    Originally asked on GitHub: https://github.com/modelcontextprotocol/python-sdk/issues/1318

    4 answers

    • Best answerAgentArmorHQ teamTeam4d ago

      Status: open. As of the last comments in the thread, the fixes are not merged; two PRs target the two bugs.

      Root cause: in mcp/client/auth.py, OAuthContext.is_token_valid treats a None token_expiry_time as valid forever, and _initialize loads tokens from storage without restoring their expiry. A stored, expired token therefore looks perpetual, proactive refresh never runs, the first request gets a 401, and the client falls back to full re-auth. A second bug: when a client is re-instantiated from storage, oauth_metadata is not restored, so _refresh_token builds the token URL as server_url + /token, which is wrong for proxy servers that serve third-party OAuth endpoints.

      Two PRs address these: #1784 (restores token_expiry_time on _initialize, a small low-risk fix) and #2492 (adds optional get/set_oauth_metadata to TokenStorage, backward compatible, so refresh uses the real token endpoint).

      Workaround suggested by a contributor: after loading tokens in _initialize, set the expiry:

      python
      async def _initialize(self):
          self.context.current_tokens = await self.context.storage.get_tokens()
          self.context.client_info = await self.context.storage.get_client_info()
          if self.context.current_tokens:
              self.context.update_token_expiry(self.context.current_tokens)
          self._initialized = True
      

      Note this is a suggested patch, not a verified fix. update_token_expiry relies on expires_in, so the stored token must retain it; the thread notes the TokenStorage protocol does not explicitly require persisting expiry timestamps. For proxy setups, persist OAuth metadata alongside tokens so refresh hits the correct endpoint. Until the fix lands, one user switched to the requests-oauth2client library to bypass the SDK's OAuth flow.

      Did this work for you?
    • AgentArmorHQ teamTeam4d ago

      What @keurcien tried on GitHub (Outcome unclear)

      A contributor suggested restoring the expiry inside _initialize by calling self.context.update_token_expiry(self.context.current_tokens) after loading tokens from storage. The thread does not report a test result for this patch.

      Source: https://github.com/modelcontextprotocol/python-sdk/issues/1318#issuecomment-3376064128 (opens external site)

      Did this work for you?
    • AgentArmorHQ teamTeam4d ago

      What @Norcim133 tried on GitHub (Outcome unclear)

      A commenter suggested forcing OAuth discovery during _initialize so oauth_metadata is populated before refresh, since a proxy server's well-known endpoint otherwise leads to a wrong /token URL (404 and full re-auth). No test result is reported.

      Source: https://github.com/modelcontextprotocol/python-sdk/issues/1318#issuecomment-3235841894 (opens external site)

      Did this work for you?
    • AgentArmorHQ teamTeam4d ago

      What @levderquantori tried on GitHub (Outcome unclear)

      One user reported deciding to use the requests-oauth2client library to implement OAuth2 instead of the SDK's flow. The thread does not state whether the switch succeeded.

      Source: https://github.com/modelcontextprotocol/python-sdk/issues/1318#issuecomment-4903006524 (opens external site)

      Did this work for you?

    Add your answer or comment