OAuth redirect URI changed from cursor:// to http://localhost for Streamable HTTP MCP

Where does the bug appear (feature/product)?

Cursor IDE

Describe the Bug

I’m developing a Streamable HTTP MCP server with OAuth support and noticed a change in Cursor’s behavior.

Previously, Cursor sent the following redirect URI in the authorization request:

cursor://anysphere.cursor-mcp/oauth/callback

Recently (currently on Cursor 3.10.17), Cursor now sends:

http ://localhost:/…

“mcp-server”: {
“type”: “streamable-http”,
“url”: “”,
“auth”: {
“CLIENT_ID”: “”,
“CLIENT_SECRET”: “”,
“scopes”: “openid profile email”
}
}

I’m using Streamable HTTP (not stdio or mcp-remote) and testing with multiple OAuth providers (Okta, Auth0, and Keycloak). The behavior is the same across all of them.

Steps to Reproduce

  1. Set up a Streamable HTTP MCP server that requires OAuth authentication.
  2. Configure the server with a static OAuth client (tested with Okta, Auth0, and Keycloak).
  3. Connect to the MCP server using Cursor IDE 3.10.17.
  4. Initiate the OAuth authentication flow.
  5. Inspect the authorization request received by the OAuth provider.

Expected Behavior

The authorization request uses:

redirect_uri= cursor://anysphere.cursor-mcp/oauth/callback

(This was the behavior observed previously.)

Actual Behavior

The authorization request now uses:

redirect_uri=http ://localhost:8787

No changes were made to:

the MCP server,
the OAuth provider,
the OAuth client configuration,
or the OAuth metadata.

The only observed change is that Cursor now sends a localhost redirect URI instead of the previously used cursor:// callback.

Operating System

Windows 10/11

Version Information

Version: 3.10.17 (user setup)
VS Code Extension API: 1.125.0
Commit: c89f45b831621cdc5e951694db44fecd8fab1150
Date: 2026-07-05T06:39:45.228Z
Layout: IDE
Build Type: Stable
Release Track: Default
Electron: 40.10.3
Chromium: 144.0.7559.236
Node.js: 24.15.0
V8: 14.4.258.32-electron.0
xterm.js: 6.1.0-beta.256
OS: Windows_NT x64 10.0.26200

Additional Information

A few questions:

Is this an intentional change in Cursor’s OAuth implementation?
Is auth.redirect_uri in mcp.json no longer honored?
Should Streamable HTTP MCP servers now expect http ://localhost loopback callbacks instead of cursor://?

Any clarification would be appreciated.

Does this stop you from using Cursor

No - Cursor works, but with this issue

Hi Tejaswini,

We recently moved MCP OAuth in the IDE/CLI from the cursor:// custom-scheme deeplink to a loopback redirect, http://localhost:8787/callback. That’s the redirect style recommended for native apps in RFC 8252, and we switched because a lot of OAuth providers won’t accept a custom-scheme (cursor://) URI. This has been rolling out recently, which is why it changed without any edit on your end.

To your specific questions:

  1. Intentional? Yes. The loopback redirect is the new default for IDE/CLI MCP auth.
  2. auth.redirect_uri in mcp.json? There’s no redirect_uri field to honor. The auth block only supports CLIENT_ID, CLIENT_SECRET, and scopes — Cursor manages the redirect URI itself.
  3. Should static servers expect http://localhost now? Yes. With a static OAuth client (Client ID/Secret), Cursor doesn’t register redirect URIs with your provider the way it would with Dynamic Client Registration, so your provider’s allowlist has to include whatever Cursor sends.

The fix: in your OAuth app (Okta/Auth0/Keycloak), add http://localhost:8787/callback to the allowed redirect URIs. Keep cursor://anysphere.cursor-mcp/oauth/callback allowlisted too, since the desktop flow still falls back to it in some cases. The port is fixed at 8787.

One heads-up: the Static OAuth docs currently list only the web and desktop callbacks and don’t yet mention the loopback URL - so adding http://localhost:8787/callback alongside them is the missing piece. Let me know if the mismatch persists after you allowlist it.

Hey @mohitjain ,

I recently hit this while re-authenticating to the honeycomb mcp.

It caused a .. sort of related bug. Here’s the summary from cursor itself:

Symptom: OAuth consent on honeycomb.io succeeds, but the browser then fails to connect when redirected to http://127.0.0.1:8787/callback?... (connection refused / site can’t be reached). Auth never completes in Cursor.

Root cause (on my machine): Cursor’s MCP OAuth callback listener binds to IPv6 loopback only, while the OAuth provider redirects to IPv4 127.0.0.1.

Diagnostics during an active auth attempt

$ lsof -iTCP:8787 -sTCP:LISTEN -P -n
COMMAND  PID USER   FD   TYPE  NODE NAME
Cursor   ...  mp   43u  IPv6  ...  TCP [::1]:8787 (LISTEN)

$ curl -sS -o /dev/null -w "localhost: %{http_code}\n" http://localhost:8787/callback
localhost: 200

$ curl -sS -o /dev/null -w "127.0.0.1: %{http_code}\n" http://127.0.0.1:8787/callback
curl: (7) Failed to connect to 127.0.0.1 port 8787: Couldn't connect to server

So localhost works (resolves to ::1 on macOS), but 127.0.0.1 does not — even though both are semantically “loopback”. OAuth requires exact redirect URI string matching, and many providers (including Honeycomb via dynamic client registration) redirect to 127.0.0.1 rather than localhost.

Workaround that works: When the browser lands on the failed 127.0.0.1:8787 URL, manually change the host to localhost (keep port/path/query intact). Cursor then receives the callback and auth completes.

Suggested fix: The MCP OAuth callback server on port 8787 should listen on both 127.0.0.1 and ::1 (dual-stack loopback), or consistently use one host in both the registered redirect URI and the listener bind address. For reference, OpenAI Codex’s MCP OAuth implementation explicitly binds local callbacks to 127.0.0.1.

Environment:

  • Cursor 3.10.20 (arm64), macOS 25.5.0
  • MCP config: { "hcpServers": { "honeycomb": { "url": "https://mcp.honeycomb.io/mcp" } } }
  • Provider: Honeycomb MCP (OAuth via ui.honeycomb.io)

You’re right that this is a separate issue from the original thread: that one was a provider-side allowlist gap, whereas you’re hitting a genuine bug on our side — the MCP OAuth callback server binds to IPv6 loopback ([::1]:8787) only, so a provider that redirects the browser to IPv4 127.0.0.1:8787 gets a connection refused. I reproduced the same binding behavior on my end, so it’s on us to fix.

Since it’s a distinct root cause, could you open a new thread in Bug Reports for it? That keeps it trackable separately from this one. You’ve basically already got everything - please include:

  1. The symptom: OAuth consent succeeds, but the browser can’t reach http://127.0.0.1:8787/callback (connection refused) and auth never completes in Cursor.
  2. Your lsof -iTCP:8787 -sTCP:LISTEN -P -n output showing Cursor bound to [::1]:8787, plus the two curl checks (localhost → 200, 127.0.0.1 → refused).
  3. Cursor version and OS/arch (3.10.20, macOS arm64), the MCP server/provider (Honeycomb, OAuth via dynamic client registration), and your MCP logs from Output → MCP: honeycomb if you have them.

In the meantime, the workaround you found is the right one: when the browser lands on the failed http://127.0.0.1:8787/callback, change the host to localhost (keep the port, path, and query intact) and auth completes.

Re-raised here: MCP OAuth binds to IPv6 loopback, breaking 127.0.0.1 MCP redirect

Cursor’s OAuth redirect_uri is using a fixed localhost port (8787) that conflicts with quite a few things that might be running on a developer machine: Dask’s web interface, Cloudflare’s wrangler dev, probably some others. This breaks the OAuth flow if one of those apps happens to be running.

Cursor could use a random free high port. This is allowed by RFC 8252, and seems to be what most tools do:

The authorization server MUST allow any port to be specified at the
time of the request for loopback IP redirect URIs, to accommodate
clients that obtain an available ephemeral port from the operating
system at the time of the request.

Good callout, and you’re right on the spec. The loopback callback currently uses a fixed port (8787), so if another local process is already sitting on that port (Cloudflare wrangler dev defaults to 8787, and Dask and a few others can land there too), the connect fails - Cursor either can’t stand up the callback listener or the redirect hits the wrong server.

Quick workaround: free up 8787 while you run the MCP Connect flow (stop the other service, or start it on a different port). The listener only needs the port during the auth handshake, so once you’re connected the token is saved and you can put the other service back on 8787.

On the larger point: moving to an OS-assigned ephemeral port instead of a fixed 8787 is a reasonable change. Since it’s a separate issue from the redirect-URI change this thread started on, would you mind opening a new thread in Bug Reports so it can be tracked on its own? A few details would help: whether you’re hitting this in the IDE or the CLI, which tool was holding 8787, and what the failure looked like (an error, a hang, or nothing happening).

Where does the bug appear (feature/product)?

Cursor IDE

Describe the Bug

Cursor’s OAuth redirect_uri is using a fixed localhost port (8787) that conflicts with quite a few things that might be running on a developer machine: Dask’s web interface, Cloudflare’s wrangler dev, probably some others. This breaks the OAuth flow if one of those apps happens to be running.

as discussed here: OAuth redirect URI changed from cursor:// to http://localhost for Streamable HTTP MCP - #13 by mohitjain

Steps to Reproduce

Add a new MCP server that uses OAuth.

During the OAuth exchange, after consent is granted, the authorization server will redirect back to http://localhost:8787. If another app is running, it will typically display a 404 not found error in the browser.

Expected Behavior

Cursor could use a random free high port. This is allowed by RFC 8252, and seems to be what most tools do:

The authorization server MUST allow any port to be specified at the
time of the request for loopback IP redirect URIs, to accommodate
clients that obtain an available ephemeral port from the operating
system at the time of the request.

Operating System

MacOS

Version Information

Version: 3.11.13 (Universal)
VS Code Extension API: 1.125.0
Commit: 3f21b08f0b436a07be29fbfe00b304fa15553350
Date: 2026-07-10T01:45:28.254Z
Layout: Agent Window
Build Type: Stable
Release Track: Default
Electron: 40.10.3
Chromium: 144.0.7559.236
Node.js: 24.15.0
V8: 14.4.258.32-electron.0
xterm.js: 6.1.0-beta.256
OS: Darwin arm64 25.5.0

Does this stop you from using Cursor

No - Cursor works, but with this issue

@mohitjain

Hi,

We are experiencing an OAuth authentication issue with a remote MCP server in Cursor.

On some Cursor installations, the OAuth authorization request uses the previous callback:

cursor://anysphere.cursor-mcp/oauth/callback

On other installations, Cursor now generates:

http://localhost:8787/callback

The new callback URL has already been registered in the OAuth client configuration exactly as generated by Cursor:

http://localhost:8787/callback

However, authentication still fails and the browser displays a redirect URI warning or mismatch.

We also noticed a difference in the OAuth state generated by Cursor.

Installations using the previous callback contain:

{
  "surface": "exthost"
}

Installations using the new localhost callback contain:

{
  "surface": "mcp_process"
}

One of the affected Cursor installations is running version:

3.11.19

The authorization request generated by the affected installation contains values equivalent to:

response_type=code
code_challenge_method=S256
redirect_uri=http://localhost:8787/callback

We would like to confirm:

  1. Was the MCP OAuth callback intentionally changed from cursor://anysphere.cursor-mcp/oauth/callback to http://localhost:8787/callback?
  2. Is this change being enabled through a gradual rollout, remote feature flag, or a new MCP implementation?
  3. What is the difference between the exthost and mcp_process OAuth surfaces?
  4. Is there any supported Cursor setting, environment variable, command-line option, or mcp.json property that allows selecting the callback URL?
  5. Is it possible to temporarily force Cursor to use the previous callback?
  6. Are there any known issues involving the local callback listener on port 8787, IPv4 versus IPv6, multiple Cursor windows, or corporate proxies?
  7. Does Cursor require both callback URLs to remain registered during the transition?
cursor://anysphere.cursor-mcp/oauth/callback
http://localhost:8787/callback

Since the localhost callback is already registered exactly as generated, we are trying to determine whether this is a recent Cursor regression, an incomplete rollout, or an additional requirement in the new mcp_process OAuth flow.

Any official documentation, known issue, workaround, or confirmation from the Cursor team would be very helpful.

Thanks!

Quick answers:

  1. Yes, intentional. MCP OAuth moved to the http://localhost:8787/callback loopback (RFC 8252) because many providers reject the cursor:// scheme.

  2. Yes, a gradual server-side rollout, which is why installs differ with no change on your end.

  3. exthost is the older flow (uses cursor://); mcp_process is the newer one (uses the loopback). It just marks which side owns the callback. 4/5. No. There’s no mcp.json field, setting, env var, or CLI flag to pick or force the callback. auth supports only CLIENT_ID/CLIENT_SECRET/scopes; Cursor manages the redirect.

  4. Known 8787 gotchas: if the provider redirects to 127.0.0.1:8787 and it won’t load, change the host to localhost in the browser (keep port/path/query); and free 8787 if another local process (e.g. wrangler dev) is holding it.

  5. Yes. Allowlist all three callbacks Cursor may send, not just the loopback:

    cursor://anysphere.cursor-mcp/oauth/callback
    https://www.cursor.com/agents/mcp/oauth/callback
    http://localhost:8787/callback
    

That last point is almost certainly your mismatch: with Dynamic Client Registration, Cursor sends all three at once, and a provider that strictly validates the set rejects it unless it recognizes all three (localhost alone isn’t enough). Docs.

If it still fails, share whether the server is static (CLIENT_ID/CLIENT_SECRET) or DCR, plus the exact provider error and the Output → MCP: <server> log from a failed Connect.

This issue has broken Cursor’s ability to use the Algolia Productivity MCP - Get started with Algolia Productivity MCP - Algolia.

[Shared MCP process] Transient error connecting to streamableHttp server: Redirect URIs do not match

So I think is the thread to report this, but just trying to setup a new connection to Webull Agentic AI and I have already reported this to them. But it fails to authenticate. Cursor gave me this summary to put here.

Cursor 3.12.17 · macOS 26.5 · Safari default browser

Adding Webull Cloud MCP (https://api.webull.com/mcp) per Webull Agentic docs fails before any login page.

Log (mcp-server-user-webull.log):
HTTP 417
Raw body: {“msg”:“redirectUris only supports 1 URI”,“code”:“client.redirectUri.limit.exceeded”,“traceId”:“a167630b-62b2-4699-b00e-130198e8357d”}

Also: Cursor wraps this as “Invalid OAuth error response … path: [‘error’]” because Webull returns msg/code, not OAuth “error” string.

Ask: For streamable HTTP MCP, can Cursor’s Dynamic Client Registration send a single redirect URI (e.g. only localhost:8787/callback) for providers that reject multi-URI registration? Webull advertises Cursor support but their register endpoint allows only 1 redirect URI.

Reproduced after clean mcp.json, Safari as default browser, and Cursor reload. OAuth never reaches passport.webull.com — fails at client registration.

@Jake_Hall1 @Therigwin, you’re both hitting the same thing, and it’s on our side rather than your config.

@Therigwin- to your direct question: no, there’s currently no way to make Cursor register just one redirect URI during auto-registration. This is a known limitation we’ve flagged for providers that cap redirect URIs at one. The Invalid OAuth error response ... path: ['error'] is a separate, cosmetic issue: Webull returns a non-standard error body, so Cursor doesn’t parse it cleanly and falls back to that generic message. The real failure is still the one-URI registration cap, so reporting it to Webull was the right move.

The workaround that reliably sidesteps this, if your provider lets you create an OAuth client manually:

  1. In the provider (Webull / Algolia), create a static OAuth app and set its redirect URI to http://localhost:8787/callback.
  2. Put that client’s credentials in the auth block of your mcp.json:

"auth": {
"CLIENT_ID": "...",
"CLIENT_SECRET": "...",
"scopes": "..."
}

With a static client, Cursor skips auto-registration, so it no longer sends the multi-URL set. You only need http://localhost:8787/callback allowlisted. See the Static OAuth for remote servers section of the MCP docs.

If your provider only supports auto-registration (no way to pre-create a client), there isn’t a clean client-side fix yet, and reducing how many callbacks we register is on us.

If it still fails after that, share the Output → MCP: <server> log from a failed Connect and whether the server uses a manual Client ID or auto-registration, and I’ll dig in.

This issue has broken Cursor’s ability to use the SFMC MCE MCP. Does anyone know the work around for SFMC MCE MCP ? Thanks

Hey @mzs,

We’ve shipped some changes to how the local OAuth callback handles port conflicts since you posted. Sign-ins from multiple Cursor windows or the CLI now coordinate on the callback port instead of colliding, and if another app is holding the port during the handshake, the connect now fails immediately with a clear message instead of silently sending the callback to the other app.

The port itself is still fixed, meaning it has to be registered with the provider ahead of time, so an OS-assigned port isn’t an option. You’ll still need to briefly free port 8787 if something like wrangler dev is sitting on it while you connect.

Could you give it another try on the latest version and let me know how it behaves for your setup?

Hey @Sarada_Parasa, sorry this sat unanswered.

The most reliable way to get SFMC MCE connected today is to skip Cursor’s automatic OAuth registration and use a static client:

  1. In SFMC, create a manual OAuth app (an Installed Package with an API Integration component) and register both of these as allowed redirect URLs on it:
http://localhost:8787/callback
https://www.cursor.com/agents/mcp/oauth/callback
  1. Put that package’s Client ID and Secret in the auth block of your mcp.json:
{
  "mcpServers": {
    "sfmc": {
      "url": "https://<your-sfmc-mcp-endpoint>/mcp",
      "auth": {
        "CLIENT_ID": "your-client-id",
        "CLIENT_SECRET": "your-client-secret",
        "scopes": "..."
      }
    }
  }
}

With a static client, Cursor stops sending the multi-URL registration set that some providers reject, so only those two callbacks need to be allowlisted. Full steps are under “Static OAuth for remote servers” in the MCP docs.

If it still fails after that, grab the Output panel → MCP: <server> log from a failed Connect and share it here, and I’ll dig in.