Letting users trust their own MCP clients, without opening a phishing hole A May 2026 arXiv study of 119 OAuth-enabled MCP servers found registration endpoints on 114 of them, or 95.8%, that accept arbitrary redirect_uri values from anonymous requesters, enabling one-click account takeover through a phishing link. The analysis cites a January 2026 Obsidian Security report that the same chain hit Square's MCP server, where self-registration accepted any redirect address and one click exposed a merchant's profile, transactions and bank details; Obsidian Security wrote that "any user who had previously authorized the Square MCP server was vulnerable." The piece argues that redirect-URI allowlists are what make a user's Approve click mean what they think it means, since PKCE and self-declared client names do not stop an attacker who starts the flow. Letting users trust their own MCP clients, without opening a phishing hole The problem You run your own agent: a Slack bot, a service on a VPS, an MCP client you wrote over a weekend. You want it on your files, which on Stream Vessel https://streamvessel.com means your own S3 bucket, where an agent can read, write and delete through the MCP server. The agent speaks MCP’s standard OAuth sign-in: it registers itself, opens a browser, you click Approve, it gets a token. It fails at step one. At registration the agent named its redirect URI , the address your browser returns to after you approve, carrying a one-time authorization code the agent swaps for a token. Yours is https://agent.yourhost.example/callback ; the answer is invalid redirect uri . The server only accepts addresses on its operator’s allowlist : Claude, ChatGPT, Cursor, VS Code and a few others. Getting yours added means asking the operator to change a config file. The server cannot simply accept any address. That list is the addresses the post office will deliver your house keys to. Without it, a stranger registers a fake “Claude” that has the keys sent to their place, and phishes your storage with one link. So the server is either closed to you or unsafe for everyone. Both failures are common. On the closed side, this is what people run into: “The redirect URI is not allowed, the URL is not part of Atlassian whitelisted domains for registered MCP Clients.” — an error users, Cursor’s among them, reported in the Atlassian developer community https://community.developer.atlassian.com/t/issue-with-oauth-redirect-uri-whitelisting-for-atlassian-remote-mcp-server/93721 “…each host application must have a registered GitHub App or OAuth App to authenticate on behalf of the user.” — GitHub MCP server, policies and governance https://github.com/github/github-mcp-server/blob/main/docs/policies-and-governance.md ; otherwise you fall back to a personal access token On the open side, a May 2026 study https://arxiv.org/html/2605.22333v1 that tested 119 OAuth-enabled MCP servers found registration endpoints on 114 of them, 95.8%, that “accept arbitrary redirect uri values from anonymous requesters.” The attack: one link, one click, someone else’s bucket Say your server accepts any registration and any redirect address. An attacker with no account on it can take over a user’s access in four steps: 1. Register a client named “Claude” whose redirect URI is https://evil.example/cb . Open registration is anonymous, by design. 2. Send a signed-in user a normal-looking /oauth/authorize link: “Reconnect your storage to Claude”. 3. The user sees your real consent screen, on your real domain, in their real session, and clicks Approve. 4. The code lands at evil.example . The attacker trades it for a token and is now the user. PKCE does not help: it lets only the client that started a flow finish it, and the attacker started this one. The client name is self-declared and proves nothing. The consent screen shows the redirect host, but claude-connect.app passes a glance. It is the same trick as the fake-app approval prompts sent to Microsoft 365 users: a real page and a real Approve; there the attacker uses an app the platform registered, here an address the server should never have accepted. The user does approve, but on false information. A redirect allowlist is what makes an approval mean what the user thinks it means: whatever the name says, the keys can only go where the list says. This is not hypothetical. In January 2026 Obsidian Security https://www.obsidiansecurity.com/blog/when-mcp-meets-oauth-common-pitfalls-leading-to-one-click-account-takeover reported this chain against Square’s MCP server: self-registration accepted any redirect address, and one click exposed a merchant’s profile, transactions and bank details. In the researchers’ words, “any user who had previously authorized the Square MCP server was vulnerable.” Square’s fix, they note, only applied to newly registered clients. Others found the same chain on an MCP proxy in front of a company’s single sign-on https://hackt.us/full-account-takeover-on-an-mcp-oauth-proxy-why-pkce-can-t-save-you , worth a 24-hour access token and a 14-day refresh token per click, and across bug-bounty programs https://blog.sicks3c.io/research/ato-via-open-mcp-dcr/ , where it was sometimes waved off as social engineering. SickSec’s answer: the victim never leaves the trusted domain. A quieter problem rides along. When a request fails a check after its redirect address was accepted, the error is sent to that address. So a server that accepts any address is an open redirector on its own domain: a crafted authorize link with a bad scope bounces visitors to any site, under your hostname. RFC 9700 describes exactly this case. What the spec says, and what servers actually do The MCP spec names the risk and leaves the policy to each server. Its security considerations https://modelcontextprotocol.io/specification/draft/basic/authorization/security-considerations say authorization servers MUST validate exact redirect URIs, SHOULD only redirect automatically to a URI they trust, and MAY instead inform the user and let them decide. The last two are RFC 9700’s sentences, word for word. What “trust” means is left open. Registration is shifting too. The 2026-07-28 client registration page https://modelcontextprotocol.io/specification/2026-07-28/basic/authorization/client-registration deprecates Dynamic Client Registration a client registering itself over an API, no human involved for Client ID Metadata Documents CIMD , where the client id is an HTTPS URL to a document listing its redirect URIs. CIMD says who publishes a client, not whether this user meant to connect it. The OAuth working group has been here. In a December 2025 thread on the IETF OAuth list https://mailarchive.ietf.org/arch/msg/oauth/zDCwUCE4 dzv73lG2 NXzEXYLME/ , Warren Parad argued that unauthenticated registration turns an MCP authorization server into an open redirector. Aaron Parecki’s quoted reply points to RFC 9700 §4.11.2, which starts from exactly that setup. In practice, servers sit at one of two poles: | Approach | Example | Who can connect | Phishing exposure | |---|---|---|---| | Fixed allowlist, set by the operator | pgEdge Postgres MCP server https://docs.pgedge.com/pgedge-postgres-mcp-server/development/guide/auth oauth/ : self-registration is on, but the registered URI must be on its list Claude’s callback and two loopback URIs by default | Only listed clients; anything else needs a config change | Low | | Whatever the client registered | 114 of the 119 servers tested in the May 2026 study | Any client | High: the attack above | Loopback means localhost or 127.0.0.1 : an app on the user’s own machine, waiting for the browser to come back. The first pole costs real users, and clients will not fix it for you. A Claude Code issue https://github.com/anthropics/claude-code/issues/66511 from June 2026 reports that organizations with exact-match redirect allowlists cannot sign in from Claude Code, because its http://localhost: