/

MCP Guides

The July 2026 MCP release changes how AI assistants sign users into MCP apps

MCP 2026-07-28 replaces Dynamic Client Registration with Client ID Metadata Documents. Here is what changed, in plain terms, and why apps built on Skybridge barely notice.

Why sign-in is the hard part

An MCP app running inside ChatGPT or Claude starts out anonymous. A tool call arrives and nothing in it tells you who is asking, which is fine for looking up a train timetable and useless for showing someone their own order history. Closing that gap means letting the user log in, and MCP does it with Oauth, the same mechanism behind every "Continue with Google" button you have ever clicked.

What makes it awkward is that the assistant and your login provider are strangers. Login providers only serve software they recognize. "Continue with Google" works on a website because the site's developer registered the site with Google once and pasted a client ID into the code. In the case of an MCP app, the software asking for the login is ChatGPT or Claude, and the provider is whichever one sits behind your server. Nobody registered that pairing, and nobody could have, since neither side knows the other exists until a user connects. Everything that follows is about how MCP fills that gap.

The assistant used to ask for credentials on the spot

Until the July 2026 release, MCP solved that with Dynamic Client Registration (RFC 7591). The assistant would call your login provider and ask it to create a client, right then, unprompted, and the provider would hand back a client ID. Think of a contractor arriving at a building, signing in at the front desk, and being issued a visitor badge that the desk then has to file and keep track of.

It worked, but it came with a catch that tripped up most people building their first MCP app: the login provider had to support dynamic client registration. Plenty do not. Google and GitHub, for instance, do not, which meant you could not point at them directly and had to put a provider that does support it in front of them. The reason is what it asks of them: an endpoint open to the internet that mints and stores a client record for anyone who asks, which is an operational cost and an abuse surface for records that mostly go unused.

2026-07-28 deprecates this mechanism and recommends Client ID Metadata Documents (CIMD) in its place. Instead of asking to be issued credentials, the assistant publishes a small file on the web that describes itself, and the address of that file becomes its identity. Back to the building: rather than collecting a badge in advance, the contractor hands over a business card, and the front desk verifies it by visiting the address printed on it to check that everything lines up with what the contractor publishes there.

The file is genuinely small:

{
  "client_id": "https://app.example.com/oauth/client-metadata.json",
  "client_name": "Example MCP Client",
  "redirect_uris": ["http://127.0.0.1:3000/callback"],
  "grant_types": ["authorization_code"],
  "response_types": ["code"]

{
  "client_id": "https://app.example.com/oauth/client-metadata.json",
  "client_name": "Example MCP Client",
  "redirect_uris": ["http://127.0.0.1:3000/callback"],
  "grant_types": ["authorization_code"],
  "response_types": ["code"]

{
  "client_id": "https://app.example.com/oauth/client-metadata.json",
  "client_name": "Example MCP Client",
  "redirect_uris": ["http://127.0.0.1:3000/callback"],
  "grant_types": ["authorization_code"],
  "response_types": ["code"]

When a login request turns up identifying itself by URL, the login provider fetches that address, confirms the file says the same URL back, checks that the page the user will be sent to afterwards is one the file lists, and displays client_name on the consent screen the user sees. Nothing gets stored, and nothing has to be cleaned up later.

Old servers keep working. Dynamic Client Registration is still in the spec as a fallback, and assistants try the options in order: credentials they already hold, then metadata documents if your provider supports them, then registration on the spot, then asking the user to paste something in by hand.

There are two smaller changes in the release for the people writing the assistants rather than the apps. First, login providers must now identify themselves in their responses so an assistant can confirm the reply came from the provider it asked. This blocks attacks whereby the login is quietly routed through a different provider than the one the assistant asked. Second, assistants that still register on the spot have to declare what kind of app they are. Providers used to assume "web app" by default and reject the localhost redirect addresses that desktop apps and command-line tools depend on, which broke their logins entirely.

Almost none of this is an app builder’s problem

Almost all of that work happens somewhere other than your MCP server. Registration is between the assistant and your login provider. Verifying the response is the assistant's job. Support for metadata documents is your login provider's job.

Your server has a different role. It owns the resource the user is trying to reach. That role had three parts before this release and it still has the same three:

  • Tell the assistant where your login provider lives

  • Check the token that arrives with each request

  • Read the signed-in user in your handlers

One thing does change how you write handlers. The same release dropped sessions from the protocol, so requests no longer arrive as part of a conversation your server is tracking. Each one arrives on its own, carrying its own token, and that token is the only link between one call and the next. That means that you need to check it on every request instead of once at the start.

For MCP app builders, the deprecation changes the question to ask when picking a login provider. "Does it support registration on the fly" used to rule out most of the market, Google and GitHub included. That question is being replaced by "does it serve metadata documents," a much lower bar for providers to clear, since a static file is cheaper to operate than a public registration endpoint with storage to clean up. Adoption is early, though, so nothing changes this week: assistants fall back to Dynamic Client Registration, which stays in the spec for at least twelve months.

Skybridge

Wire any of six login providers in one line

Integrating CIMD by hand means publishing the discovery information, writing the code that checks tokens, wiring it into your server, and then finding out that your identity provider has an opinion documented nowhere near the code you were reading.

That’s why we decided to ship six providers—Auth0, AuthPlane, Clerk, Descope, Stytch and WorkOS—in Skybridge out of the box, so what was once a custom integration becomes a single option on your server:




Whatever extra information your provider puts in a token, your handlers can read it on extra.authInfo, and you can type it as you go: await workosProvider<{ tenant: string }>({ domain, audience }). One thing to plan for: no provider includes the user's email address by default. WorkOS, Clerk, Descope and Stytch can add one through a template, and Auth0 needs it namespaced.

Many apps want some tools open and some behind sign-in

Many app builders opt for a mixed auth model, where some tools are open to anyone and others sit behind sign-in. A shopping app could let anyone browse the catalogue and only ask for an account at checkout; a travel app could quote flights to anyone but only hold the seat at sign-in. The same split works for B2B tools, where the token carries the user's organization and your handler scopes results to it.

In Skybridge, one line per tool covers the possibility of having certain functionality open and others behind a signin:

server
  .registerTool({ name: "search-products", auth: { allowsAnonymous: true }, /* … */ }, handler)
  .registerTool({ name: "create-checkout", auth: { scopes: ["checkout"] }, /*

server
  .registerTool({ name: "search-products", auth: { allowsAnonymous: true }, /* … */ }, handler)
  .registerTool({ name: "create-checkout", auth: { scopes: ["checkout"] }, /*

server
  .registerTool({ name: "search-products", auth: { allowsAnonymous: true }, /* … */ }, handler)
  .registerTool({ name: "create-checkout", auth: { scopes: ["checkout"] }, /*

Marking a single tool as anonymous is enough for the server to start accepting signed-out callers. Every other tool stays behind sign-in, and permissions are checked before your code runs. Inside a handler, extra.authInfo is either filled in or empty depending on who called.

That's the whole surface area the new release leaves you: pick a provider, mark which tools need sign-in, and read the user in your handlers. The rest of the authentication machinery that changed in July belongs to the assistants and the providers.

To get started, Authenticate Users covers how it works and Connect an Identity Provider covers the setup for each one, with a runnable example per provider in the Skybridge repo.

Liked what you read here?

Get our newsletter!