Proxy authentication dialog never appears in multi-workbench windows, and one missed challenge latches proxy auth for the entire session

Where does the bug appear (feature/product)?

Cursor IDE

Describe the Bug

Streamable-HTTP MCP servers behind an authenticating proxy can never connect, because the proxy
credentials dialog is never shown. Two separate problems combine:

  1. The dialog is not delivered to windows that host more than one workbench.
  2. When the dialog is not answered, the pending-auth entry is never cleaned up, so proxy
    authentication stays broken for the rest of the session, for every server.

Launching with --classic makes the dialog appear immediately and everything works, which is
what points at frame delivery as the cause.

Steps to Reproduce

  1. Configure macOS to use a PAC file that routes some host through a proxy requiring Basic
    proxy authentication.
  2. Configure a streamableHttp MCP server pointing at a host behind that proxy.
  3. Start Cursor normally (default UI) with several MCP servers so they connect at startup.

Expected Behavior

The proxy credentials dialog appears so the challenge can be answered.

Actual

No dialog. The server retries and fails with a 60 second timeout each time:

connecting streamableHttp for "<server>"
Transient error connecting to streamableHttp server: MCP error -32001: Request timed out
MCP HTTP exchange failed

Toggling the server off and on, or reloading the window, does not help. Only a full restart
changes anything, and if the server connects at startup it fails again the same way.

Operating System

MacOS

Version Information

  • Cursor 3.19.19
  • macOS 15 (arm64)

For AI issues: which model did you use?

Opus 5

Additional Information

With Developer: Set Log Level… → Trace, main.log shows the two distinct states.

Latched session — every attempt, including manual toggles many minutes later, hits this and then
silently waits forever. asking window N to handle proxy login never appears:

17:52:09 [trace] auth#onLogin (proxy) - pending proxy handling found
17:53:15 [trace] auth#onLogin (proxy) - pending proxy handling found

Clean session, after a restart in which no server contacted the proxy during startup. The dialog
is dispatched but still does not render, and the main process waits on the reply indefinitely:

18:05:35 [trace] auth#onLogin (proxy) - no pending proxy handling found, starting new
18:05:35 [trace] auth#resolveProxyCredentials (proxy) - enter
18:05:35 [trace] auth#doResolveProxyCredentials - enter {"scheme":"basic","host":"<proxy-host>",
                 "port":0,"realm":"<realm>","isProxy":true,"attempt":1}
18:05:35 [trace] auth#doResolveProxyCredentials (proxy) - asking window 1 to handle proxy login

In that session the log directory contained window1_wb0, window1_wb1 and window2 — window 1
hosted two workbenches. There were no renderer-side errors.

Same action after relaunching with open -na "Cursor" --args --classic: the dialog appeared
immediately, and the server connected.

18:14:57 connecting streamableHttp for "<server>"
18:15:17 Successfully connected to streamableHttp server

Root cause hypothesis

I read through the bundled source to narrow this down, so the first point is inference rather
than something I verified directly — but --classic fixing it is consistent with it.

Frame delivery. ProxyAuthService.showProxyCredentialsDialog sends
vscode:openProxyAuthenticationDialog to the window’s webContents, and listens for the reply
on webContents.on('ipc-message', …). webContents.send only reaches a window’s top-level
frame. The handler for that channel is registered by the workbench, and in a multi-workbench
window the workbenches appear to run in child frames, so the message lands in a frame with no
listener and nothing renders.

No cleanup on an unanswered dialog. The reply is awaited with no timeout, so the promise
never settles and the finally that removes the entry from pendingProxyResolves never runs.
Every later onLogin for the same proxy finds that dead promise and awaits it, which is the
pending proxy handling found state above. This is why the symptom is a hang rather than an
error, and why it is session-wide rather than per-server: all servers behind the same proxy
share one auth key, so one unanswered challenge disables proxy auth for all of them.

Startup timing makes this near-deterministic. With many MCP servers connecting at once a few
seconds after launch, a proxy-backed server requests credentials before any window can render a
modal, so the very first challenge of the session is the one that gets lost.

Also minor, visible in the trace above: port is reported as 0 rather than the proxy’s actual
port in the authInfo passed from the MCP process.

Workaround

  1. Quit Cursor fully — nothing else clears the latched entry.
  2. Relaunch with --classic.
  3. Keep proxy-backed MCP servers "disabled": true in mcp.json so they do not fire during the
    startup burst, then enable them from Settings → MCP once the window is up.

Note that editing mcp.json externally does not trigger a reconnect; it has to be the UI toggle.

Separately, “remember” must be left unchecked when the dialog does appear. The auth state
object is recreated per request, so attempt is always 1, which means a stored credential is
returned on every subsequent challenge and the dialog is never shown again. For proxies using
one-time passwords (OTP / hardware token) this permanently reproduces the silent failure, since
the stored credential can never succeed.

Suggested fixes

  • Target the workbench frame rather than the window’s top-level frame, or fall back to a window
    that has a workbench in its main frame.
  • Add a timeout to the dialog reply, and clear the pendingProxyResolves entry in a finally
    so a single failure cannot poison the session.
  • Do not reuse stored credentials unconditionally when attempt === 1; the per-request auth
    state makes the attempt counter meaningless for these callers, which breaks OTP-based proxies.
  • Pass the real proxy port through in authInfo.

Generated with Cursor (AI-assisted).

Does this stop you from using Cursor

No - Cursor works, but with this issue

Hey @Natasha_Veselova, this isn’t anything in your setup. You’ve already found the right fix: launch with --classic, or keep proxy-backed MCP servers "disabled": true at startup and enable them from Settings → MCP once the window is up (the UI toggle is what triggers the reconnect).

If your proxy takes a fixed username/password (not a one-time code), you can also set http.proxy to http://username:password@proxy-host:port - Cursor uses those directly and never shows the prompt. Downside: it routes all traffic through that proxy instead of your PAC file, and for OTP proxies it won’t help, so --classic is the move there.

This is an issue we’re tracking. I’ll post here when there’s an update.