Putting a product inside ChatGPT or Claude sounds like an integration task. It is closer to becoming an identity provider’s customer and an authorization server’s operator at the same time. The tool call is the easy part; the hard part is proving, on every request, that the user in the chat window is the user who owns the data.
Building Trove — a bookmark platform whose MCP server exposes save and search as tools inside ChatGPT, Claude Desktop and Code, VS Code and Goose — that flow was the piece that took real design work. Here is what it looks like end to end.
Why a token in a config file is not enough
The tempting shortcut is a long-lived API key the user pastes into their client config. It works in a demo and fails as a product. The key is copied into a file in plain text, it does not expire, it cannot be scoped to a subset of the account, and revoking it means revoking everything. Worse, the user has no way to see what they granted, because they never granted anything — they pasted a secret.
OAuth 2.1 exists to answer exactly this. The user is sent to a consent screen on the service they already trust, approves a named scope, and the client receives a token it can refresh and the user can revoke. The MCP specification builds on that rather than inventing a parallel scheme, which is the right call.
The flow, end to end
Trove runs three participants: the web app, which owns the consent UI; the MCP server, which exposes the tools; and Supabase, which is the identity provider. The authorization-code flow ties them together.
- The MCP client discovers the server’s authorization metadata and starts an authorization-code request, with PKCE.
- The user is redirected into the Trove web app. If they are not signed in, Supabase handles that first — the MCP server never sees a credential.
- The web app renders a consent screen naming the client and what it is asking for. This is the only screen in the flow the user is asked to reason about, so it says what will happen in plain terms.
- On approval, the authorization code goes back to the client, which exchanges it — with its PKCE verifier — for an access token and a refresh token.
- Every subsequent tool call carries the access token. The MCP server validates it and resolves it to a user before any bookmark is read or written.
Nothing in that list is novel. That is the point: it is the standard flow, implemented properly, rather than a bespoke scheme that would need its own security review and its own explanation to every user.
Separating the identity provider from the authorization surface
The decision worth calling out is that Supabase is the identity provider, but the consent lives in the web app. Authentication and authorization are different questions — who are you, and what are you allowing this client to do — and pushing them into one screen produces a UI that answers neither clearly.
Keeping consent in the product also means it can be presented in the product’s own language, and revocation can live beside every other account setting rather than in an identity console the user has never opened.
One widget, every host
MCP hosts do not just call tools; they render inline widgets in the conversation. The obvious approach is to detect the host and branch — one rendering path for ChatGPT, another for Claude, another for whatever ships next quarter. That approach ages badly. Every new host is a new branch, and every branch is a place for behaviour to diverge.
We implemented the MCP Apps specification, SEP-1865, to the standard rather than to any particular host. One widget definition renders in every compliant host without per-vendor code. When a new client appears, the work is testing rather than porting.
Writing to the specification instead of to the two hosts you can test today is the difference between an integration and a surface you can keep.
What this buys the product
Trove is a pnpm monorepo where @trove/database and @trove/shared are consumed by every client. The MCP server is a thin surface over the same repository layer and the same AI enrichment pipeline the web app uses. A bookmark saved from a chat assistant gets the same title, summary, tags and suggested collection as one saved from the browser, because it is the same code path, not a reimplementation.
That is the underrated payoff of doing the auth properly. Once identity is resolved correctly at the edge, the MCP server is just another caller of the shared core — and the feature you ship once lands on every surface.
If you are building one
- Use the authorization-code flow with PKCE. Do not ship pasted API keys, even temporarily — they set an expectation you will have to migrate users off later.
- Keep the consent screen in your product, in your language, next to your revocation UI.
- Implement the current MCP Apps specification rather than branching per host. Standards compliance is cheaper than vendor detection.
- Make the MCP server a client of your existing service layer. If it needs its own business logic, the logic was in the wrong place already.