Hosted Gmail MCP (user-gmail): Tools UI shows connected after logout/toggle, but Agent MCP tool calls return Unauthorized; OAuth reconnect never prompts

Title
Hosted Gmail MCP (user-gmail): Tools UI shows connected after logout/toggle, but Agent MCP tool calls return Unauthorized; OAuth reconnect never prompts

Product / version (from your About block)
Cursor: 3.3.30 (user setup), Stable, VS Code 1.105.1
Commit: 3dc559280adc5f931ade8e25c7b85393842acf30
OS: Windows 11 arm64 (Windows_NT arm64 10.0.26200)
Summary
Google hosted Gmail MCP is configured per Google’s Remote MCP docs (https://gmailmcp.googleapis.com/mcp/v1) with OAuth (CLIENT_ID / CLIENT_SECRET via env, scopes gmail.readonly + gmail.compose). GCP project has Gmail API and Gmail MCP API enabled; OAuth consent includes restricted Gmail scopes.

Observed:

Tools & MCPs shows gmail as healthy (green, tools enumerated, Logout visible).
After Logout or disabling/enabling the server, Cursor reconnects in milliseconds with no Google consent browser flow.
Composer / Agent-invoked MCP tools (e.g. user-gmail-search_threads) consistently fail with Unauthorized.
Expected:
After logout (or when no valid token exists), user should be guided through interactive OAuth until Gmail MCP calls succeed; connected status should correlate with successful authenticated tool calls from Agent.

Reproduction steps
Configure user-level MCP server gmail pointing at Google hosted Gmail MCP with OAuth env vars (same shape as Configure the Gmail MCP server  |  Google for Developers ).
Open Cursor → Settings → Tools & MCPs → enable gmail — observe green / tools list.
From Agent/chat, invoke search_threads (or ask assistant to call Gmail MCP tools).
Observe Unauthorized.
In Tools & MCPs, click Logout on gmail or toggle gmail Off → On.
Observe immediate connected state without Google OAuth UI.
Repeat Agent tool call → still Unauthorized.
Evidence (local logs — no secrets)
Paths:

%AppData%\Roaming\Cursor\logs\window*\workbench.mcp.oauth.log
%AppData%\Roaming\Cursor\logs\window*\exthost\anysphere.cursor-mcp\MCP user-gmail*.log
Excerpt — workbench.mcp.oauth.log: repeats user-gmail connected → error, and after user_logout clears OAuth, initializing → connected resumes quickly:

[MCPService] State transition: user-gmail connected → error
[MCPService] OAuth clear candidate for server: user-gmail (cause=user_logout)
[MCPService] Cleared OAuth state for server: user-gmail (cause=user_logout)
[MCPService] State transition: user-gmail initializing → connected
Excerpt — MCP user-gmail*.log: after LogoutServer, Successfully cleared OAuth tokens, then ReloadClient completes connected: true with conn=connected,auth=unknown — reconnect to streamableHttp without an intervening user OAuth completion logged:

[V2] Handling LogoutServer action
Successfully cleared OAuth tokens
[V2] Handling ReloadClient action
Successfully connected to streamableHttp server
ReloadClient completed, connected: true, statusType: connected
Impact
Blocks Agent/automation use of Google-hosted Gmail MCP despite healthy-looking MCP UI — high friction for any workflow relying on user-gmail-* tools.

Hi there!

We detected that this may be a bug report, so we’ve moved your post to the Bug Reports category.

To help us investigate and fix this faster, could you edit your original post to include the details from the template below?

Bug Report Template - Click to expand

Where does the bug appear (feature/product)?

  • Cursor IDE
  • Cursor CLI
  • Background Agent (GitHub, Slack, Web, Linear)
  • BugBot
  • Somewhere else…

Describe the Bug
A clear and concise description of what the bug is.


Steps to Reproduce
How can you reproduce this bug? We have a much better chance at fixing issues if we can reproduce them!


Expected Behavior
What is meant to happen here that isn’t working correctly?


Screenshots / Screen Recordings
If applicable, attach images or videos (.jpg, .png, .gif, .mp4, .mov)


Operating System

  • Windows 10/11
  • MacOS
  • Linux

Version Information

  • For Cursor IDE: Menu → About Cursor → Copy
  • For Cursor CLI: Run agent about in your terminal
IDE:
Version: 2.xx.x
VSCode Version: 1.105.1
Commit: ......

CLI:
CLI Version 2026.01.17-d239e66

For AI issues: which model did you use?
Model name (e.g., Sonnet 4, Tab…)


For AI issues: add Request ID with privacy disabled
Request ID: f9a7046a-279b-47e5-ab48-6e8dc12daba1
For Background Agent issues, also post the ID: bc-…


Additional Information
Add any other context about the problem here.


Does this stop you from using Cursor?

  • Yes - Cursor is unusable
  • Sometimes - I can sometimes use Cursor
  • No - Cursor works, but with this issue

The more details you provide, the easier it is for us to reproduce and fix the issue. Thanks!

Where does the bug appear (feature/product)?

Cursor IDE

Describe the Bug

Gmail MCP OAuth Browser Not Opening - Stuck in Auth Loop

Gmail MCP server OAuth flow initiates but fails to open browser for authorization, causing the server to remain in an unusable “Unauthorized” state. The “Logout” button clears tokens but does not trigger re-authentication.

Steps to Reproduce

Add Gmail MCP to ~/.cursor/mcp.json:

{
“mcpServers”: {
“gmail-mcp”: {
“url”: “gmailmcp.googleapis/mcp/v1”,
“auth”: {
“type”: “oauth”,
“CLIENT_ID”: “.apps.googleusercontent.com”,
“CLIENT_SECRET”: “<redacted — do not paste real secrets in bug reports>”,
“scopes”: [
“auth/gmail.readonly”,
“auth/gmail.compose”
]
}
}
}
}

Restart Cursor
Gmail MCP appears as “Connected” with “12 tools enabled” and a “Logout” button
Try to use a Gmail tool (e.g., “List my recent emails”)
Server returns “Unauthorized” error

Expected Behavior

When the Gmail tool is called without valid OAuth tokens:

Cursor should detect the authentication is required
The MCP server state should transition to needsAuth (like Amplitude/Miro plugins do)
A browser window should open to Google’s OAuth consent page
After user authorizes, the callback should return to Cursor
Gmail tools should work

Screenshots / Screen Recordings

Operating System

MacOS

Version Information

Cursor Version: 3.4.20 (arm64)

OS: macOS 14.4 (Darwin 25.4.0)

MCP Server: Gmail MCP (https://gmailmcp.googleapis.com/mcp/v1)

OAuth Config: Standard Google OAuth2 with PKCE

For AI issues: which model did you use?

Composer 2

Additional Information

Actual behavior:
Server state transitions from connected → error (not needsAuth)
Browser window never opens
No user notification or prompt to authorize
Server immediately returns “Unauthorized” error
Clicking “Logout” clears tokens but doesn’t trigger re-auth

Workaround Attempts (All Failed):
✗ Clicking “Logout” multiple times
✗ Removing and re-adding server config
✗ Clearing OAuth state from SQLite database
✗ Restarting Cursor
✗ Clearing OAuth attempt files

Additional Details:
No macOS permission prompts or notifications appeared
System can open browsers normally (tested with open command)
Gmail MCP API endpoint is accessible (returns tool list without auth)
Logout button works (clears tokens from database) but doesn’t trigger re-auth flow
OAuth attempts are logged but browser launch never happens

Does this stop you from using Cursor

No - Cursor works, but with this issue

I am also having the same issue.

What’s worse is that the official google mcp instructions for Cursor are misleading, the oauth client id doesn’t let us create a Web Application with cursor://anysphere.cursor-mcp/oauth/callback as the redirect URI

Hey, thanks for the detailed report with logs, it really helps.

This symptom is a known one: the UI marks the server as connected as soon as the transport (streamableHttp) is up, but that doesn’t guarantee the OAuth tokens are valid. After logout the transport reconnects instantly, and the consent flow doesn’t get triggered, which is why you see auth=unknown in the logs and Unauthorized on tool calls. We’ve confirmed this issue on our side, but I can’t share an ETA for the fix yet.

A couple things that might help for now:

  1. Authorization URL in the logs. In %AppData%\Roaming\Cursor\logs\<session>\window*\workbench.mcp.oauth.log, OAuth discovery sometimes generates the full authorization URL, but the browser doesn’t open. Look for a line with accounts.google.com/o/oauth2/... (it includes client_id, code_challenge, etc). Open it manually in your browser, complete the consent flow, and after the redirect to cursor://... Cursor should pick up the tokens.

  2. GCP OAuth client type. Gaurav is right above: for the redirect URI cursor://anysphere.cursor-mcp/oauth/callback, the OAuth client in Google Cloud Console must be a Desktop application, not a Web application. Web application clients don’t accept custom-scheme URIs. If you originally created a Web application client, recreate it as a Desktop application and update CLIENT_ID and CLIENT_SECRET in your env vars.

If none of this helps and the URL never shows up in the logs, reply and I’ll take a deeper look. When I have an update on the fix, I’ll post it here.

Creating a desktop oauth application also doesn’t work

Logs:

2026-05-23 17:10:06.682 [debug] Calling tool 'list_calendars' with toolCallId: tool_2e1cc16e-8984-4e62-abe3-6705eede956
2026-05-23 17:10:06.682 [debug] Tool arguments: {}
2026-05-23 17:10:06.688 [debug] No stored tokens found
2026-05-23 17:10:08.110 [warning] MCP HTTP exchange completed
2026-05-23 17:10:08.556 [info] Using enriched static OAuth client information from storage
2026-05-23 17:10:08.556 [info] Using redirect URL
2026-05-23 17:10:08.558 [debug] No stored tokens found
2026-05-23 17:10:08.558 [info] Using redirect URL
2026-05-23 17:10:08.558 [info] Saving PKCE code verifier
2026-05-23 17:10:08.563 [info] Using redirect URL
2026-05-23 17:10:08.563 [info] MCP OAuth redirect to authorization
2026-05-23 17:10:08.570 [info] Stored server URL for OAuth flow
2026-05-23 17:10:08.570 [info] OAuth provider needs auth callback during connection
2026-05-23 17:10:08.570 [error] Client error: Unauthorized Unauthorized
2026-05-23 17:10:08.570 [debug] [V2 FSM] auth:operation_auth_failed: conn=connected,auth=unknown -> conn=connected,auth=expired
2026-05-23 17:10:08.571 [error] Error calling tool 'list_calendars': Unauthorized Unauthorized

There is no URL being printed in the logs, these logs are taken from the Output pane of the mcp server

Moreover, due a bug in mcp-remote, that is also not working. Even if somehow mcp-remote works, this still needs to be fixed because this is currently unusable in Cursor’s web agents (https://cursor.com/agents)

Thanks for the logs. They confirm you’re hitting the same issue as in this thread: MCP HTTP/SSE OAuth: SDK silently fails to open browser after "MCP OAuth redirect to authorization" (3.2.11 → latest)

The SDK successfully builds the authorize URL and reaches the MCP OAuth redirect to authorization line, but the openExternal(url) step silently doesn’t run. So the browser doesn’t open, and the URL also doesn’t get printed in the Output pane. We reproduced this and it’s a known bug. I can’t share an ETA for the fix yet.

For now, there’s a working workaround: use mcp-remote as a stdio proxy. It opens the browser via Node open, avoiding the broken path in the Cursor SDK. Config looks like this:

{
  "mcpServers": {
    "gmail": {
      "command": "npx",
      "args": ["-y", "mcp-remote@latest", "https://gmailmcp.googleapis.com/mcp/v1", "8787"]
    }
  }
}

A couple notes:

  1. The callback port 8787 in the example must be in the redirect_uri allowlist of your OAuth client in GCP, otherwise DCR will return invalid_redirect_uri. For a Desktop client this is http://localhost:8787/oauth/callback or whatever port you pick.
  2. If you have multiple Cursor windows open, they’ll compete for that port. Keep just one window open during the initial authorization.

You mentioned mcp-remote also doesn’t work. Can you share its logs, what npx ... mcp-remote ... prints in the terminal? It might be a different issue, and we should debug that separately.

About cloud agents at cursor.com/agents. That’s a separate scope. There’s no UI there for an interactive OAuth consent flow, so a hosted Gmail MCP that uses user OAuth can’t run there the same way. You’d need either to authorize in the IDE first and reuse tokens, or use service account auth if the provider supports it. That’s an architectural limitation, not a Cursor bug. If you describe the exact workflow you want to run in a cloud agent, we can think through what’s possible.

Thanks

  1. For mcp-remote, the bug is that Google MCP servers return Unauthorized before it has a chance to listen on the port and likely it is a mcp-remote bug. The PR which is supposed to fix this fix: bind OAuth callback listener before browser auth by todor-roi · Pull Request #260 · geelen/mcp-remote · GitHub sadly also doesn’t fix the issue. URL opens in the browser, i’m able to authenticate and when it redirects back to the localhost, I see a connection refused error because mcp-remote stopped listening on the port before the auth was successful.
  2. Regarding the web based cursor agents, I’m curious to know how the Slack/Atlassian OAuth consent flow works and Google MCP doesn’t. If I add slack or atlassian or even mixpanel mcp, I’m able to see a Login button in the MCP list and it does per-user auth perfectly, why can’t a similar flow work in cloud agents (before the agent is started, at the time of adding the MCP entry)?

Thanks for looking into mcp-remote, that’s a useful data point.

On the mcp-remote race: yep, that matches what you described. Google MCP returns Unauthorized right away on initialize before mcp-remote has the loopback listener up. Then when the browser redirects to http://localhost:8787/oauth/callback the listener is already gone, so you get connection refused. That’s a bug in geelen/mcp-remote, not something we can patch from the Cursor side. PR #260 looks like it covers a nearby race, but not the redirect-after-auth window you’re hitting. Realistically, the fix needs to land upstream. The port is already pinned positionally in the config I shared, the 8787 after the URL, so there’s nothing to tighten there. If you want more detail on where it tears down, --debug on mcp-remote helps a bit, but I don’t have a workaround that actually closes that race. We’re tracking the broken openExternal path on our side separately, and once that’s fixed you won’t need mcp-remote for this at all.

On Slack, Atlassian, Mixpanel Login working in cloud agents but Google not: that’s the right question, and the difference is architectural, not a bug.

The connectors you see with a Login button in the Tools list are first-party integrations that Cursor manages, the same set you see at cursor.com/dashboard/integrations. Cursor owns the OAuth client for each provider, the consent flow runs once in the IDE or dashboard, and the tokens are stored in Cursor’s backend on your account. When a cloud agent starts, the backend loads those tokens into the run, so there’s no consent UI needed inside the cloud agent.

A hosted Gmail MCP that you set up yourself with your own CLIENT_ID and CLIENT_SECRET env vars doesn’t go through that path. The OAuth tokens only live in your local IDE’s MCP store. The backend doesn’t have a copy, and there’s no interactive consent UI in a cloud agent VM to get them on demand. So even if local IDE OAuth worked perfectly, the cloud agent flow still wouldn’t match Slack or Atlassian. It would need the same managed integration plumbing on our side.

Two paths that work today for cloud agents:

  • Static auth via the SDK. If you’re using Agent.create with mcpServers, you can pass headers: { Authorization: 'Bearer ...' } or an auth block with CLIENT_ID, CLIENT_SECRET, and scopes. Docs: Capabilities | Cursor Docs. This works if you can mint a token outside the interactive consent flow, like a long-lived token your backend issues, or a Google service account if your workflow supports domain-wide delegation.
  • Manage Gmail auth out of band, like service account plus impersonation, or your own token broker, then inject the bearer token via headers.

For interactive per-user Google consent inside a cloud agent run, the answer right now is it’s not supported. It would need Gmail MCP to be added as a managed integration, not a user-configured one. I’ll pass this along as feedback, but I don’t want to set expectations on timing.

Thanks for the detailed explanation. This sums up my experience also.

Please relay the feedback to the team with higher weightage, the number 1 blocker I am having to get cursor cloud agents/automations adopted within my company is the lack of some connectors (lesser than what Rovo or what Claude supports today, other examples are Zoom/Databricks).