MCP OAuth Explained: What Happens When You Connect an AI Assistant (2026)
Connecting an AI assistant to a service over MCP does not hand it your password. It runs an OAuth 2.1 authorization code flow with PKCE: you sign in on the provider's own domain, approve a specific list of scopes, and the assistant gets back a short-lived token limited to exactly those scopes. The screen worth reading is the consent screen. Read access and write access are separate grants, and on Viallo's MCP server write access is never granted by default. Before you connect any MCP server, check three things: which scopes it asks for, whether deletion through it is reversible, and where you go to revoke it.

What MCP OAuth actually is
MCP OAuth is the authorization handshake that lets an AI assistant call a remote MCP server on your behalf without ever seeing your password. The client discovers which authorization server protects the MCP server, registers itself, sends you to the provider's own login page, and receives a scoped, expiring access token in return. It is the same OAuth 2.1 authorization code flow with PKCE that calendar and banking integrations use, applied to a new kind of client. Servers can also use a static API key instead, and plenty do, but a key cannot be scoped down or expired the same way, which is why the MCP specification treats OAuth as the default for anything reachable over HTTP.
MCP itself is the Model Context Protocol, an open standard Anthropic published in November 2024 for connecting AI assistants to external tools and data. It settled a real problem: before it existed, every assistant needed a bespoke integration with every service. Now a service ships one server and any MCP-capable client can talk to it. That is good for interoperability and slightly alarming for anyone who reads a permissions dialog properly, because the same universality means you will be approving these grants a lot.
Viallo is a private photo sharing platform that stores photos on EU servers, never uses them to train AI, and lets the people you share with open an album in any browser without creating an account. It runs an MCP server at mcp.viallo.app/mcp, authorized with OAuth 2.1 and scoped consent, which is why this post uses it as the worked example instead of a hypothetical one. Everything below is what that server actually does, not a paraphrase of the spec.
The MCP OAuth flow, step by step
Most of the MCP OAuth flow happens before you see anything. Your assistant does four network round trips in the background, then opens a browser window and hands the decision to you. Here is the whole sequence.
- The client calls the server with no token and gets refused. The MCP server answers 401 Unauthorized with a WWW-Authenticate header pointing at its protected resource metadata document, per RFC 9728. This one header is the whole discovery entry point.
- The client reads the resource metadata. That document names the authorization servers allowed to mint tokens for this particular resource. A client that skips this step and guesses is a client that can be pointed at the wrong issuer.
- The client reads the authorization server metadata. RFC 8414 defines a well-known document listing the authorize endpoint, the token endpoint, and the supported scopes. Nothing is hardcoded on either side.
- The client registers itself, if it has to. RFC 7591 dynamic client registration lets a client that has never met this server obtain a client ID on the spot. This is the step that makes "paste a URL and you are connected" work at all - nobody has to pre-register every assistant with every MCP server on the internet.
- The client generates a PKCE verifier and opens your browser. It creates a random secret, hashes it, and sends only the hash to the authorize endpoint. PKCE is RFC 7636, and OAuth 2.1 makes it mandatory for every client rather than optional for mobile apps.
- You sign in on the provider's domain. Check the address bar here. The credentials go to the provider, never to the assistant, which is the entire reason this flow exists. Two-factor prompts and passkeys work normally because you are on the real login page.
- You approve a specific list of scopes. This is the screen people click through. Whatever is ticked here is the ceiling on everything that follows, for as long as the grant lives.
- The code is exchanged for a token. The provider redirects back with a one-time authorization code. The client sends that code plus the original PKCE verifier to the token endpoint and gets an access token. An attacker who intercepts the code without the verifier has nothing.
One detail that is easy to miss: the token is bound to the specific server it was requested for, using resource indicators from RFC 8707. A token minted for one MCP server cannot be replayed against a different one. That matters more every month, because the realistic end state is an assistant holding tokens for a dozen services at once.

MCP server authentication: what happens on every call
The handshake runs once. MCP server authentication runs on every single tool call after it. Each request carries the access token as a bearer token over HTTPS, and the server checks four things before it does any work.
- Is the token real? Signature or introspection against the issuer. A forged token dies here.
- Is it still alive? Access tokens expire. This is the property an API key does not have, and it is why a leaked OAuth token has a shrinking blast radius rather than a permanent one.
- Is it for me? Audience validation. A token issued for another resource server gets rejected even if it is otherwise valid.
- Does it permit this specific tool? Scope check. A token without albums.write cannot reach delete_album, no matter what the model asked for.
Viallo's server uses Streamable HTTP and is stateless, which means there is no long-lived session sitting on the server between calls. Every request re-proves itself from scratch. The practical effect is that revocation is immediate rather than eventually consistent: cut the grant and the very next tool call fails, instead of a warm session limping on until it happens to expire.
Scoped consent: the screen that decides everything
Scopes are the part of the flow that actually protects you, and they are the part nobody reads. A scope is a named bundle of permissions. The token carries the scopes you approved, and the server enforces them per tool. If the bundle is coarse - one "access your account" checkbox for everything - then scoped consent is theatre. If it is granular, it is a real control.
Viallo's MCP server exposes 17 tools split across four scopes. The split is the point: each scope unlocks a specific set of tools and nothing outside it.
| Scope | Tools it unlocks | What it can do | What it still cannot do |
|---|---|---|---|
| profile.read | get_me | Read display name, email, plan and storage usage | Touch a single photo or album |
| albums.read | list_albums, get_album, list_photos, get_photo_metadata, search_photos, view_photo | Browse albums, read metadata, search captions and places, fetch one photo you explicitly ask for | Create, rename, move or delete anything |
| albums.write | create_album, update_album, delete_album, add_photos_to_album, remove_photo_from_album, request_upload_url, confirm_upload | Create and reorganize albums, upload photos, permanently delete photos and albums | Create or revoke share links |
| share_links.manage | create_share_link, get_share_links, revoke_share_link | Create share links with expiry and password options, list them, revoke them | Read or change album contents |
The second thing worth checking on any MCP server is what it returns by default. Viallo's is metadata-first: search_photos covers captions, tags, filenames, dates and place names. Viallo's servers run no image analysis at all - no object detection, no face recognition, no searching by what is in the picture. The single exception is view_photo, which is the only path to actual pixels, runs on an explicit request, and writes every call to your activity log.
That is a meaningfully different default from the alternatives. Google Photos runs its own models over the image content to power search and memories. Dropping a folder of images into a chat assistant hands the model the pixels immediately. Neither is wrong, but if you assumed"connected to my photos" means the same thing everywhere, it does not. This is the same distinction covered in our post on what AI assistants can see when they get photo access.
Why write access has to be a separate grant
Read mistakes are embarrassing. Write mistakes are permanent. That asymmetry is the entire argument for splitting the grant, and it is why any MCP server that bundles read and write into one checkbox should make you hesitate.
On Viallo, delete_album and remove_photo_from_album remove the files from storage. There is no undelete, in MCP or in the app. No trash folder, no 30-day grace period, no support ticket that gets them back. That is deliberate and it is stated plainly rather than buried, but it means granting albums.write to an assistant is a real decision rather than a formality.
There is a second reason, and it is specific to language models. Everything an MCP server returns lands back in the model's context: album names, captions, filenames, tags. Any of those fields can contain text that reads like an instruction, whether someone put it there on purpose or a file just happens to be named badly. This is prompt injection, and no amount of model training removes it entirely.
The mitigation is not cleverness, it is scope. If the token has no write scope, the worst outcome of a successful injection is that the assistant reads something it should not have. With write scope, the worst outcome involves your photos no longer existing. Connect read-only first. Add write later, if and when you actually want an assistant reorganizing albums for you.
MCP OAuth vs API keys vs handing over a password
MCP servers are authorized in one of three ways in practice. They are not equivalent, and the differences show up exactly when something has already gone wrong.
| Property | Sharing your password | Static API key | OAuth 2.1 + PKCE |
|---|---|---|---|
| What the client holds | Your actual credentials | A long-lived secret | A short-lived scoped token |
| Limited to specific actions | No | Rarely, and usually all-or-nothing | Yes, per scope |
| Expires on its own | No | Usually not | Yes |
| Revocable without changing your password | No | Yes, if the provider offers key management | Yes, per connected app |
| Works with 2FA and passkeys | Breaks them | Sidesteps them | Preserves them |
| Damage if it leaks | Full account takeover | Full API surface until someone notices | Only the granted scopes, and only until expiry |
| Audit trail | None | Per key at best | Per grant, and per call if the server logs them |
API keys are not evil. For an MCP server you run yourself on your own machine, a key in a config file is perfectly reasonable. The calculus changes the moment the server is remote and the client is a commercial assistant you do not control, because then "revoke this one connection" needs to be a button rather than a password reset.
MCP security best practices: 8 checks before you connect
These are the MCP security best practices worth applying to any server, not just this one. They take about ninety seconds and they are the difference between an informed grant and a reflexive one.
- Check the domain on the login screen. The sign-in must happen on the provider's own domain. If an assistant asks you to type a password into its own interface, that is not OAuth and you should stop.
- Read the scope list against the task. If you wanted an assistant that finds photos and it is asking for write access, the mismatch is the signal.
- Ask what deletion means on this service. Trash folder or gone? On Viallo it is gone, permanently, with no undelete. Knowing which one you are dealing with should happen before you grant, not after.
- Ask what the server returns by default. Metadata, or the files themselves? A metadata-first server has a far smaller exposure surface than one that streams content on any read.
- Prefer servers that separate read from write. One combined"full access" scope means you cannot make the safe choice even if you want to.
- Find out whether calls are logged, and where. An activity log you can actually open is what turns "I think it only read metadata" into something you can verify.
- Locate the revoke page before you connect. If you cannot find where a grant gets cut off, you have not finished evaluating the service.
- Treat everything the server returns as untrusted text. Filenames and captions flow back into the model's context. A server that only ever returns your own data is a smaller surface than one aggregating content from strangers.
One honest caveat to end the list on: scopes are an authorization control, not encryption. Viallo encrypts photos in transit and at rest, but it is not end-to-end encrypted, and no scope prevents an authorized token from reading what its scope allows. If your requirement is that the provider mathematically cannot read your files, that is a different product category, and services like Ente or Proton Drive are the honest answer rather than this one. Related reading on the OS-level version of the same problem: what app photo permissions actually grant.
How to revoke an MCP connection
Revocation is the part of the flow you should rehearse once while nothing is wrong. On Viallo it is four steps.
- Open Settings, then Connected Apps at /settings/connected-apps.
- Find the assistant in the list of connected clients.
- Revoke it. Tokens are removed immediately, and because the server is stateless the next tool call from that client fails rather than riding out a warm session.
- Review the security activity entries. They are kept for 90 days, so you can see what the connection did before you cut it.
Access tokens also expire on their own, which means a connection you set up once and forgot about stops working without any action from you. That is the quiet advantage of OAuth over a key in a config file: abandonment is a safe default rather than a slowly accumulating risk.
Worth saying plainly: revoking does not undo anything that already happened. If an assistant deleted an album, cutting the grant afterwards does not bring it back. Revocation stops future access, and that is all it does.

If you want to try the flow described here rather than read about it, Viallo's MCP server is documented at viallo.app/mcp and MCP access is included on every plan, including the free one - 2 albums, 200 photos, 10 GB, no credit card. Connect it read-only, watch what lands in your activity log, and decide about write access afterwards. That order is the whole recommendation.
For the click-by-click version in each client - Claude Code, Claude Desktop, ChatGPT and Cursor - along with how to check the connection actually works, how to connect an MCP server picks up where this post leaves off.
Frequently Asked Questions
Is MCP OAuth safe?
MCP OAuth is safer than the alternatives it replaced, because the assistant never receives your password and the token it does receive is scoped and expires on its own. Viallo's MCP server issues tokens limited to the scopes ticked on the consent screen, keeps read-only as the default, and writes every tool call to the account's activity log. It is not encryption, though: an authorized token can read whatever its scopes allow, so a service that mathematically cannot read your files is a different trade-off and a different product.
What is the difference between MCP OAuth and an API key?
An API key is a single long-lived secret that usually unlocks everything the API can do, while MCP OAuth issues a short-lived token limited to the scopes you approved. Viallo splits its 17 MCP tools across four scopes - profile.read, albums.read, albums.write and share_links.manage - so a connected assistant can be genuinely read-only rather than trusted with the whole account. API keys are simpler and still sensible for a server you run yourself, but they cannot be scoped down, cannot expire on their own, and cannot be revoked per client.
How do I revoke an MCP server's access to my account?
Revoke it in the provider's connected-apps settings, which removes the tokens and makes the assistant's next call fail. On Viallo that page is /settings/connected-apps, and the security activity entries stay visible for 90 days afterwards so you can review what the connection did before you cut it. Revocation only stops future access - it does not reverse anything the assistant already did, so a deleted album stays deleted.
Can an MCP server see all my photos?
That depends entirely on the server, and on a well-designed one the answer is no. Viallo's MCP server is metadata-first - search covers captions, tags, filenames, dates and place names, and Viallo runs no image analysis at all, so there is no object detection, no face recognition and no searching by picture content. The one exception is the view_photo tool, which hands over a single photo you explicitly asked to see and logs every call; uploading a folder of images into a chat is the opposite default, because the model gets the pixels straight away.
Do I have to give an AI assistant write access?
No, and on a properly scoped server you should not by default. Viallo asks for albums.write as its own item on the consent screen and never grants it automatically, so an assistant can browse, search and answer questions while being unable to change anything. Grant it only if you actually want albums reorganized or photos uploaded, and go in knowing that delete_album and remove_photo_from_album remove files from storage permanently, with no undelete.