Skip to content

fix(core): refresh MCP OAuth token usage after re-auth#26312

Merged
scidomino merged 9 commits into
google-gemini:mainfrom
sahilkirad:feature-sahil-mcp-oauth-issue-fix
May 13, 2026
Merged

fix(core): refresh MCP OAuth token usage after re-auth#26312
scidomino merged 9 commits into
google-gemini:mainfrom
sahilkirad:feature-sahil-mcp-oauth-issue-fix

Conversation

@sahilkirad

Copy link
Copy Markdown
Contributor

Summary

Fixes MCP OAuth token reuse after token refresh/re-auth.

Previously, transport auth could continue using a stale access token until CLI restart.
This change makes token retrieval dynamic so refreshed/stored tokens are used without restarting Gemini CLI.

Closes #18895

Details

  • Added a dynamic OAuth auth provider path in MCP transport creation.
  • For oauth.enabled servers, token is fetched via getValidToken(...) at auth time.
  • For stored-token flow (without explicit oauth.enabled), token is also resolved dynamically.
  • Added/updated regression coverage in MCP client transport OAuth tests to verify dynamic token behavior.

Related issues

Validation

Ran successfully:

  • npm run build --workspace @google/gemini-cli-core
  • npm run test --workspace @google/gemini-cli-core -- src/tools/mcp-client.test.ts
    • Result: all tests passed (including OAuth transport cases)

Notes

  • No user-facing command/flag changes.
  • No docs update required for this fix.

@sahilkirad sahilkirad requested a review from a team as a code owner May 1, 2026 04:34
@gemini-code-assist

Copy link
Copy Markdown
Contributor

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request addresses an issue where MCP OAuth tokens would become stale, requiring a full CLI restart to refresh. By introducing a dynamic token provider, the system now resolves tokens at the time of authentication, ensuring that refreshed or updated credentials are used immediately. This change improves the robustness of the Gemini CLI's authentication flow for MCP servers.

Highlights

  • Dynamic OAuth Token Retrieval: Implemented a dynamic token provider for MCP transports, ensuring that OAuth tokens are refreshed or retrieved at request time rather than being cached at transport creation.
  • Improved Auth Handling: Updated the transport creation logic to support both explicit OAuth-enabled servers and stored-token flows, allowing for seamless re-authentication without requiring a CLI restart.
  • Regression Testing: Added comprehensive test cases to verify that the MCP client correctly utilizes the dynamic auth provider for both new and stored OAuth tokens.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request implements dynamic OAuth token retrieval for MCP clients by introducing the DynamicStoredOAuthProvider class. This allows tokens to be looked up or refreshed at request time rather than being fixed at transport creation. The createTransport function was updated to utilize this dynamic provider when OAuth is enabled or stored credentials are detected. Feedback was provided regarding the efficiency of the tokens() method in DynamicStoredOAuthProvider, specifically noting that instantiating storage and provider classes within the method leads to redundant disk I/O and should be refactored to use instance-scoped fields.

Comment thread packages/core/src/tools/mcp-client.ts Outdated
@gemini-cli gemini-cli Bot added priority/p2 Important but can be addressed in a future release. area/core Issues related to User Interface, OS Support, Core Functionality help wanted We will accept PRs from all issues marked as "help wanted". Thanks for your support! labels May 1, 2026
@sahilkirad sahilkirad requested review from a team as code owners May 1, 2026 05:31
@sahilkirad

Copy link
Copy Markdown
Contributor Author

Hi maintainers, all requested MCP OAuth fixes are pushed and tests were updated accordingly.
Evaluate Steering & Regressions is currently waiting for eval-gate deployment approval.
Could you please approve the eval-gate environment for this PR? Thank you.

@scidomino scidomino left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Gemini spotted two issues:

  1. Severe Performance Hit on HTTP Transports: The MCP SDK’s StreamableHTTPClientTransport calls authProvider.tokens() for every single RPC request (inside _commonHeaders()).

    • DynamicStoredOAuthProvider.tokens() delegates to oauthProvider.getValidToken().
    • getValidToken() calls tokenStorage.getCredentials(), which reads and parses the JSON file from the disk (or queries the system Keychain if using encrypted storage) synchronously/asynchronously.
    • Impact: This means every single MCP message sent via HTTP will perform a disk read or a slow Keychain lookup.
    • Recommendation: DynamicStoredOAuthProvider should cache the accessToken and expiresAt in memory, and only re-fetch/refresh when the token is expired (e.g., adding a 5-minute buffer).
  2. Redundant Storage Reads on Fallback: When oauth.enabled is falsy in the config, the tokens() method manually retrieves the clientId by calling this.tokenStorage.getCredentials(this.serverName). It then passes this clientId to this.oauthProvider.getValidToken(...), which internally calls getCredentials(...) again. This results in two back-to-back disk/keychain reads for a single request.

    • Recommendation: Can be fixed by reusing the retrieved credentials or by caching in memory (as per issue #1).

@sahilkirad

Copy link
Copy Markdown
Contributor Author

Okay @scidomino sir will do those changes

@sahilkirad

Copy link
Copy Markdown
Contributor Author

Hello @scidomino sir i have fixed the issues:

  • Added in-memory token caching in DynamicStoredOAuthProvider with a 5-minute buffer.
  • Avoided redundant storage reads in fallback flow.
  • Updated/added tests for dynamic authProvider behavior and caching path.

Validated locally:

  • npm run test --workspace @google/gemini-cli-core -- src/tools/mcp-client.test.ts
  • npm run build --workspace @google/gemini-cli-core

@sahilkirad sahilkirad requested a review from scidomino May 5, 2026 09:46
@gemini-cli gemini-cli Bot added the area/extensions Issues related to Gemini CLI extensions capability label May 10, 2026
@scidomino

Copy link
Copy Markdown
Collaborator

The architectural shift to a dynamic McpAuthProvider is the correct, "SDK-native" way to solve the stale token problem. However, the current implementation is overengineered in its complexity but inefficient in its execution.

  1. Move Logic to a Dedicated Service:

    • DynamicStoredOAuthProvider adds ~130 lines to mcp-client.ts, which is already too large.
    • Recommendation: Move this class to its own file (e.g., packages/core/src/mcp/stored-token-provider.ts). This aligns with how GoogleCredentialProvider and ServiceAccountImpersonationProvider are structured.
  2. Eliminate Redundant Storage Reads:

    • The current implementation reads from the disk up to three times for a single token retrieval: once to check existence, once in getValidToken, and once more to manually extract expiration metadata.
    • Recommendation: Refactor MCPOAuthProvider to return the full token metadata (including expiresAt) in one go. Additionally, in createTransport, just check for the existence of credentials rather than performing a full token validation before instantiating the provider.
  3. Simplify tokens() Logic:

    • The logic in the tokens() method regarding the // return fresh token comment is confusing. If a token doesn't have an expiry, it should still be handled cleanly without resetting the cache in a way that implies an error.

@sahilkirad

Copy link
Copy Markdown
Contributor Author

The architectural shift to a dynamic McpAuthProvider is the correct, "SDK-native" way to solve the stale token problem. However, the current implementation is overengineered in its complexity but inefficient in its execution.

  1. Move Logic to a Dedicated Service:

    • DynamicStoredOAuthProvider adds ~130 lines to mcp-client.ts, which is already too large.
    • Recommendation: Move this class to its own file (e.g., packages/core/src/mcp/stored-token-provider.ts). This aligns with how GoogleCredentialProvider and ServiceAccountImpersonationProvider are structured.
  2. Eliminate Redundant Storage Reads:

    • The current implementation reads from the disk up to three times for a single token retrieval: once to check existence, once in getValidToken, and once more to manually extract expiration metadata.
    • Recommendation: Refactor MCPOAuthProvider to return the full token metadata (including expiresAt) in one go. Additionally, in createTransport, just check for the existence of credentials rather than performing a full token validation before instantiating the provider.
  3. Simplify tokens() Logic:

    • The logic in the tokens() method regarding the // return fresh token comment is confusing. If a token doesn't have an expiry, it should still be handled cleanly without resetting the cache in a way that implies an error.

Surely @scidomino sir I'll implement these changes

@sahilkirad

Copy link
Copy Markdown
Contributor Author

The architectural shift to a dynamic McpAuthProvider is the correct, "SDK-native" way to solve the stale token problem. However, the current implementation is overengineered in its complexity but inefficient in its execution.

  1. Move Logic to a Dedicated Service:

    • DynamicStoredOAuthProvider adds ~130 lines to mcp-client.ts, which is already too large.
    • Recommendation: Move this class to its own file (e.g., packages/core/src/mcp/stored-token-provider.ts). This aligns with how GoogleCredentialProvider and ServiceAccountImpersonationProvider are structured.
  2. Eliminate Redundant Storage Reads:

    • The current implementation reads from the disk up to three times for a single token retrieval: once to check existence, once in getValidToken, and once more to manually extract expiration metadata.
    • Recommendation: Refactor MCPOAuthProvider to return the full token metadata (including expiresAt) in one go. Additionally, in createTransport, just check for the existence of credentials rather than performing a full token validation before instantiating the provider.
  3. Simplify tokens() Logic:

    • The logic in the tokens() method regarding the // return fresh token comment is confusing. If a token doesn't have an expiry, it should still be handled cleanly without resetting the cache in a way that implies an error.

hello @scidomino sir i am done with the changes ,
Implemented the requested changes for MCP OAuth token handling.

Moved DynamicStoredOAuthProvider out of mcp-client.ts into packages/core/src/mcp/stored-token-provider.ts.
Refactored token retrieval to use MCPOAuthProvider.getValidTokenWithMetadata(...) so token + expiry metadata come from one provider path.
Removed redundant follow-up storage reads in dynamic token flow.
Kept createTransport on credential-existence gating only (no eager full token validation).
Simplified tokens() behavior for no-expiry tokens: return token cleanly without long fallback caching semantics.
Updated tests in mcp-client.test.ts to match new call behavior and metadata path; targeted suite passes locally.

@scidomino scidomino enabled auto-merge May 12, 2026 21:34
@scidomino

Copy link
Copy Markdown
Collaborator

You have broken E2E tests. please fix.

@sahilkirad

Copy link
Copy Markdown
Contributor Author

You have broken E2E tests. please fix.

Sure @scidomino sir I'll do it

auto-merge was automatically disabled May 13, 2026 03:54

Head branch was pushed to by a user without write access

@sahilkirad sahilkirad requested a review from scidomino May 13, 2026 03:56
@sahilkirad

Copy link
Copy Markdown
Contributor Author

Hello @scidomino sir i have fixed the issue please could you grant the github actions workflow execution

@scidomino scidomino enabled auto-merge May 13, 2026 16:11
@sahilkirad

Copy link
Copy Markdown
Contributor Author

Hello @scidomino sir ,I fixed the deterministic E2E failure in integration-tests/file-system.test.ts (newline normalization). Current remaining failures are in test:always_passing_evals with repeated 503 retries and cross-suite timeouts (not tied to changed files). Could you please re-run eval gates?”

@github-actions

Copy link
Copy Markdown

🚨 Action Required: Eval Regressions Detected

Model: gemini-3-flash-preview

The following trustworthy evaluations passed on main and in recent Nightly runs, but failed in this PR. These regressions must be addressed before merging.

Test Name Nightly PR Result Status
should create a plan in plan mode and implement it for a refactoring task 94% 0% Regression

The check passed or was cleared for 79 other trustworthy evaluations.

🛠️ Troubleshooting & Fix Instructions

1. Ask Gemini CLI to fix it (Recommended)

Copy and paste this prompt to the agent:

The eval "should create a plan in plan mode and implement it for a refactoring task" in evals/plan_mode.eval.ts is failing. Investigate and fix it using the behavioral-evals skill.

2. Reproduce Locally

Run the following command to see the failure trajectory:

GEMINI_MODEL=gemini-3-flash-preview npm run test:all_evals -- evals/plan_mode.eval.ts --testNamePattern="should create a plan in plan mode and implement it for a refactoring task"

3. Manual Fix

See the Fixing Guide for detailed troubleshooting steps.

### 🧠 Model Steering Guidance

This PR modifies files that affect the model's behavior (prompts, tools, or instructions).

  • ⚠️ Consider adding Evals: No behavioral evaluations (evals/*.eval.ts) were added or updated in this PR. Consider adding a test case to verify the new behavior and prevent regressions.

This is an automated guidance message triggered by steering logic signatures.

@scidomino scidomino disabled auto-merge May 13, 2026 19:01
@scidomino scidomino merged commit fd01cc0 into google-gemini:main May 13, 2026
29 checks passed
@sripasg sripasg added the size/l A large sized PR label Jun 2, 2026
software-0ficial pushed a commit to software-0ficial/gemini-cli that referenced this pull request Jul 9, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/core Issues related to User Interface, OS Support, Core Functionality area/extensions Issues related to Gemini CLI extensions capability help wanted We will accept PRs from all issues marked as "help wanted". Thanks for your support! priority/p2 Important but can be addressed in a future release. size/l A large sized PR

Projects

None yet

Development

Successfully merging this pull request may close these issues.

CLI cannot use fresh token in MCP OAuth

3 participants