Field notes

MCP clients stopped following your redirects

Python MCP SDK 2.2.0 refuses any redirect that leaves your endpoint's origin. We probed 1,221 registry endpoints; at least six servers break on it.

Mark

Head of Marketing at MCPOrbit

Published
Updated
· Updated
Read time
· 9 min read
Diagram of an MCP client sending a request to an endpoint that answers with a redirect. The same-origin redirect is followed and the cross-origin redirect to a second host is refused.

The Python Model Context Protocol (MCP) SDK no longer follows a redirect that leaves your endpoint's origin. If your server answers with a 301 to a different host, clients running mcp 2.2.0 or later will not follow it. They treat the redirect as a failed response and tell the user where it pointed.

The change is pull request 3397, merged on 2026-09-04 and released in mcp 2.2.0 on 2026-09-07. Before it, the SDK built every HTTP client with follow_redirects=True. A redirect to anywhere was followed. Now the transport follows a redirect itself, and only while the target stays on the same origin.

This matters because a redirect is how most teams move a service. You rename the company, you point the old host at the new one, and every client keeps working. For MCP servers, that stopped being true in early September 2026.

What counts as the same origin?

An origin is the scheme, the host and the port together. All three have to match. The SDK adds one exception, borrowed from the rule httpx2 already used: an http to https upgrade on the same host counts as the same origin when both sides are on default ports.

So these two are followed:

307 https://api.example.com/mcp  -> https://api.example.com/mcp/   followed (trailing slash)
301 http://api.example.com/mcp   -> https://api.example.com/mcp    followed (https upgrade)

And these are not:

301 https://example.com/mcp      -> https://www.example.com/mcp    refused (different host)
307 https://api.example.com/mcp  -> http://api.example.com/mcp     refused (scheme downgrade)
301 https://api.example.com/mcp  -> https://api.example.com:8443/mcp  refused (different port)
302 https://api.example.com/mcp  -> https://api.example.com/mcp/   refused on a POST (method would change)

That last line is the one people miss. A 301, 302 or 303 tells the client it may switch to GET. httpx2 does exactly that, and a GET carries no body, so the JSON-RPC message would be thrown away. The SDK refuses rather than send a request that cannot work. Use 307 or 308 if you need a POST redirect to survive.

What the error looks like

There is no special exception class. The refused redirect comes back as the 3xx response it is, and the transport logs a warning naming the destination. If the message was a request, it fails with a JSON-RPC invalid-request error carrying the same text.

Redirect to https://app.rise2.ai/mcp not followed; use that URL as the endpoint if it is the intended server

The HTTPS to HTTP case gets its own message, because the cause is almost always the same one:

Redirect to http://mcp.example.com/mcp/v2 not followed: it would downgrade this HTTPS endpoint to plain HTTP.
The server is likely behind a TLS-terminating proxy whose forwarded headers it does not trust,
often combined with a trailing-slash difference. Try https://mcp.example.com/mcp/v2 instead, or fix the proxy settings.

OAuth failures get a shorter suffix. If a discovery, registration or token request was redirected and not followed, the error string ends with (redirected to <url>; not followed). That suffix is the fastest way to tell a genuine auth failure apart from a redirect you did not know you had.

How many real servers does this break?

We wanted a number rather than a guess, so we measured it. This is an ecosystem measurement across the public registry, not a failure we hit running our own server. Stating that is the point: it is what makes the number checkable by anyone with the same list.

We paged registry.modelcontextprotocol.io/v0/servers at 100 records a page, stopping at 40 pages. That is 4,000 records, 3,383 rows with a remote endpoint, and 1,221 distinct endpoint URLs. The 40-page stop is ours, not the end of the list: the cursor was still live when we stopped. So every figure below is about 1,221 endpoints sampled, never about the registry as a whole.

Each endpoint got one GET and one correctly formed initialize POST, in the same session, with redirects switched off. 1,140 of them answered both. Running both verbs mattered: a server that redirects on POST but refuses GET would be invisible to a GET-only probe.

The controls, and the one that failed

We fixed the controls before reading any result. A known cross-origin redirector had to classify as cross-origin, a known http to https host as an upgrade, and a known 200 as no redirect.

The first attempt used google.com, which 301s a GET. It returns 405 to a POST. It refuses the method before it ever redirects, so the run aborted and we swapped in bit.ly, which redirects under both verbs. That abort is worth reporting, because it proves the risk we were testing for is real: of nine general web hosts we checked, five redirect on GET and refuse POST outright.

A second signal fell out of the run unplanned. 405 Method Not Allowed drops from 410 responses on GET to 5 on POST. A POST-only MCP server rejecting a GET and accepting a POST is exactly that shape, which is independent evidence the probe was speaking the protocol rather than knocking on doors.

The answer: six

Six, and we checked both GET and POST, on the 1,140 endpoints that answered both. Zero of those 1,140 classified differently by verb, so the front door and the real door agree.

308 https://figlime.rise2.ai/mcp  -> https://app.rise2.ai/mcp         host migration
301 https://jobero.ai/mcp         -> https://jobherder.com/mcp        host migration (rebrand)
301 https://mcp.marketcore.ai/    -> https://mcp.marcora.ai/          host migration (rebrand)
308 https://mino.ai/mcp           -> https://agent.tinyfish.ai/mcp    host migration
307 https://parceled.ai/api/mcp   -> https://www.parceled.ai/api/mcp  bare host to www
307 https://mcp.fiber.ai/mcp/v2/  -> http://mcp.fiber.ai/mcp/v2       https downgrade, same host

Six out of 1,140 is 0.53%. The small number is the finding. This pattern is rare enough that an SDK could tighten it in a minor release without writing a migration guide, and common enough that it is now somebody's outage.

Two of the six are not migrations at all

Four of them are what you would expect: a company moved hosts or changed its name. The other two are the ones worth reading twice.

parceled.ai redirects to www.parceled.ai. Nobody files that under migration. It is a default that ships with most web stacks, set once and forgotten. But example.com and www.example.com are different hosts, so they are different origins, and the redirect is refused. If your MCP endpoint sits on a domain that normalizes to www, this is already your problem.

mcp.fiber.ai redirects from HTTPS to HTTP on the same host. Scheme is part of an origin, so that is cross-origin too. This is the one case in the set where the SDK refusing is protecting the user rather than inconveniencing them. No client should follow an endpoint downgrading itself to plain HTTP.

A bare host to www redirect is a different origin. That is a config nobody thinks of as a migration, and it now fails.

— MCPOrbit registry probe, 2026-09-21

How to fix it on your server

There are three real options, in order of how much you should prefer them.

  • Update your registry entry and your docs to the final URL. A redirect was always a courtesy, not an address. This is the only fix that works for every client, including ones that never followed redirects.
  • Serve the MCP endpoint on both origins for a transition window. Two live endpoints cost more than one redirect, but nothing breaks while listings catch up.
  • If you only redirect for path or slash normalization on the same host, switch 301 and 302 to 307 or 308. That keeps the method and the body, and same-origin redirects are still followed.

If you are behind a TLS-terminating proxy and seeing the downgrade message, the fix is in the proxy, not the SDK. Your application thinks the request arrived over HTTP because it does not trust the forwarded headers, so it builds its redirect with an http scheme. Configure the trusted proxy settings for your framework and the redirect becomes same-origin again.

This is a direction, not one SDK's opinion

Python is the SDK that breaks live servers today, but it is not alone in narrowing what a client will trust off its configured origin.

  • The Go SDK's OAuth discovery client now refuses redirects that downgrade to HTTP, and refuses redirects to localhost or a private IP. That is pull request 1220, merged 2026-09-02 and shipped in v1.8.0 on 2026-09-14. It is scoped to discovery, not the main transport.
  • The Rust SDK now lets the URL origin decide protected resource metadata discovery, in pull request 1264, shipped in rmcp 3.4.0 on 2026-09-15. A pointer found in a guessed document is treated as a guess, not as something the resource advertised.

Those two are narrower than the Python change and we are not claiming otherwise. The shared idea is what matters: an MCP client is configured for one endpoint, and the SDKs are getting stricter about what it will chase off that endpoint.

One more thing the probe found

While we had both verbs running, we checked what a 404 actually means at a published registry URL. 199 of the 1,140 endpoints that answered both, or 17.5%, return 404 to a correctly formed initialize at the URL their listing publishes.

We checked that figure rather than trusting it, and it moved. A GET-only probe found 226 endpoints returning 404. Of those, 14 answer a proper initialize fine, and 12 more return 401, meaning they are alive and just want a token. So a GET 404 overstates the problem by about 6%, and 17.5% is the corrected number.

Frequently asked questions

Frequently asked questions

Why does my MCP client say "Redirect not followed"?
Your server answered with a redirect that leaves its own origin, and the Python MCP SDK 2.2.0 or later refuses to follow it. Point the client at the URL named in the message. That URL is where your server actually lives now.
Which redirects will an MCP client still follow?
Only ones that stay on the same scheme, host and port, plus an http to https upgrade on the same host with default ports. On a POST the status also has to be 307 or 308, because 301, 302 and 303 let the client switch to GET and drop the message body.
Can I turn redirect following back on in the Python MCP SDK?
No. The transport sends each request with redirect following off and applies the origin rule itself, so the follow_redirects setting on a client you pass in is not consulted. The supported fix is to use the final URL as your endpoint.
Is a redirect from example.com to www.example.com cross-origin?
Yes. The host strings differ, so the origins differ, and the redirect is refused. This catches teams who never thought of their www normalization as a migration. It was one of six cross-origin redirects we found across 1,140 registry endpoints.
How do I move an MCP server to a new domain without breaking clients?
Serve the endpoint on both origins during the move, update your registry entry and docs to the new URL, and keep the old one alive until the listings have caught up. A redirect alone will not carry clients across, because they no longer follow it.
Which SDK version made this change?
Python mcp 2.2.0, released 2026-09-07, from pull request 3397 merged on 2026-09-04. Earlier versions built their HTTP client with follow_redirects=True and followed a redirect to any destination.

If you maintain an MCP server, the check takes a minute. Send one request to your published endpoint with redirects switched off and look at the status. Anything in the 300s means some client is already failing, and you will not hear about it from them. MCPOrbit connects to remote MCP servers and shows you the raw response, so you can see a redirect instead of guessing at a timeout.

Related reading: how MCP transports differ, publishing a server to the registry, and finding out what an MCP client supports.

About the author

Mark

Head of Marketing at MCPOrbit

Mark writes the MCPOrbit field notes. He measures things about the Model Context Protocol ecosystem and publishes what the numbers actually say.

Share this post

MCPOrbit

Test an MCP server in 60 seconds.

Download MCPOrbit for free — no account, no telemetry. Hear about a server and test it before the curiosity wears off.

macOS 14+ · Apple silicon · No account needed