OAuth clients for AI assistants (MCP)
Some MCP clients let you paste a token; others sign in with OAuth. Google
Gemini's Connected apps requires it ("Gemini requires standard OAuth for
server connections"), and ChatGPT and claude.ai connect a remote MCP server the
same way. OpenTodo therefore serves a small OAuth 2.1 authorization server next
to /mcp, so such a client signs in and works like any other MCP client
afterwards.
On this page
OpenTodo knows the redirect addresses of three apps, as their makers document them, and accepts nothing else unless the owner adds an address:
| App | Redirect addresses accepted | Source |
|---|---|---|
| Gemini | https://oauth-redirect.googleusercontent.com/r/… and /a/…, and the same on oauth-redirect-sandbox and oauth-redirect-test |
|
| ChatGPT | https://chatgpt.com/connector/oauth/… and https://chatgpt.com/connector_platform_oauth_redirect |
OpenAI |
| Claude | https://claude.ai/api/mcp/auth_callback and https://claude.com/api/mcp/auth_callback |
Anthropic |
… is one path segment the app chooses, such as ChatGPT's callback id; nothing
after it, no query and no other host is accepted.
It is off with the endpoint: enable AI assistants in Settings → Integrations → AI assistants first, or every address below answers 404.
Connecting Gemini#
- Open Settings → Integrations → AI assistants → Apps that sign in with OAuth.
- Choose App: Gemini, optionally give the client a name, and choose a permission: Read only, or Can make changes when the owner allows assistant writes.
- Create client. The client ID appears, and the secret is shown once. Copy both into Gemini's dialog.
- In Gemini, add
https://<your-server>/mcpas the MCP server URL. Its redirect address (https://oauth-redirect…googleusercontent.com/…) is already accepted — OpenTodo registers Google's six connector prefixes automatically, both for a client you create here and for one that registers itself. - Sign in to OpenTodo in that browser when Google sends you back. OpenTodo asks Connect Gemini to your account?, with what it will be able to do and where the answer goes; press Allow. Gemini is connected.
Connecting ChatGPT#
ChatGPT adds a remote MCP server as a custom plugin; your ChatGPT account or workspace has to allow custom MCP servers. OpenAI describes the steps in Connect and test your plugin.
- In OpenTodo, open Settings → Integrations → AI assistants → Apps that sign in with OAuth, choose App: ChatGPT and a permission, and press Create client. Keep the client ID and the secret (shown once) at hand.
- In ChatGPT, go to Plugins, select the plus button, then Add custom MCP server.
- Enter a name and a description, and under Connection enter
https://<your-server>/mcpas the MCP server URL. - Under authentication, choose OAuth. Where ChatGPT offers fields for an OAuth client ID and secret, enter the ones from step 1. Review the risk warning and select I understand and want to continue, then Create as a plugin.
- ChatGPT sends you to OpenTodo. Sign in if asked; OpenTodo asks Connect
ChatGPT to your account? and shows
chatgpt.comas where the answer goes. Press Allow.
ChatGPT's redirect address is https://chatgpt.com/connector/oauth/<callback id>
(or the stable https://chatgpt.com/connector_platform_oauth_redirect); a client
made for ChatGPT accepts both, so you don't need to copy it. Without a client ID,
ChatGPT registers itself (dynamic registration), with Read only access.
Connecting Claude (claude.ai)#
claude.ai on the web, Claude Desktop and the Claude mobile apps connect a remote
MCP server as a custom connector and share one redirect address,
https://claude.ai/api/mcp/auth_callback. Anthropic describes the dialog in
Add a custom connector.
Claude Code is different: connect it with a token through the
OpenTodo plugin.
- In OpenTodo, open Settings → Integrations → AI assistants → Apps that sign in with OAuth, choose App: Claude and a permission, and press Create client. Keep the client ID and the secret (shown once) at hand.
- In Claude, go to Customize → Connectors and click Add custom connector. On a Team or Enterprise plan, an owner adds it in Organization settings → Connectors instead.
- Enter
https://<your-server>/mcpas the server URL. - Enter the client ID and secret from step 1: under Advanced settings, or, when the dialog has two steps, with Use your own OAuth client. Click Add.
- Click Connect. Claude sends you to OpenTodo; sign in if asked, check that
the answer goes to
claude.ai, and press Allow.
Without step 1 and the client ID, Claude can register itself (Register
automatically), with Read only access. A client made for Claude also
accepts https://claude.com/api/mcp/auth_callback, which Anthropic has said the
address may become.
Other apps#
For an app that is not in the list, choose App: Other app and enter its
redirect address: the exact address the app returns to after sign-in, as its
documentation names it. It must start with https://, and it is matched exactly:
no wildcard, no other path, no query the app adds later. Only the owner can add
such an address; an app cannot register one itself.
What the assistant sees#
The assistant then sees only your own projects and the shared projects you are a member of, exactly like a token-based client. Can make changes additionally needs the owner's AI assistants may make changes switch and your own Allow AI assistants to make changes to my tasks switch; without them the client reads only.
How it works#
| Address | What it is |
|---|---|
/.well-known/oauth-protected-resource |
RFC 9728 document naming this server as the authorization server for /mcp |
/.well-known/oauth-authorization-server |
RFC 8414 metadata: endpoints, authorization_code and refresh_token, S256 only |
POST /oauth/register |
RFC 7591 dynamic registration, accepted only for the redirect addresses of Gemini, ChatGPT and Claude above |
GET /oauth/authorize |
The browser step; needs a signed-in session and sends it to the consent dialog |
POST /oauth/token |
Code exchange (client_secret_post or client_secret_basic) and refresh |
POST /oauth/revoke |
RFC 7009 revocation by the client of its own access or refresh token |
Every access token is an ordinary OpenTodo API token, so it appears in
Settings → Integrations → API tokens as MCP: <client name>, is refused on
everything except the MCP endpoint's tools, and dies with the account. Access
tokens stop working after an hour. The refresh token lasts 180 days from its last use.
Each refresh gives the app a new refresh token, and the one it sent stops working. If an
old refresh token is ever sent again, the server takes it as a sign that the token was copied:
it ends the connection (the app's refresh token and access token stop working) and records
mcp_oauth.refresh_reused in the audit log. Connect the app again to continue. An app has to
keep the refresh token from each answer, as OAuth 2.1 expects.
Gemini is the exception: a client whose redirect addresses are all Google's (a client made for Gemini, and every client Gemini registers itself) gets the same refresh token back, because Google's guide for connected apps requires it and Gemini would otherwise unlink. Its refresh token works until the connection is revoked, the client is deleted, or 180 days pass without a refresh.
A code is issued only when you press Allow in the consent dialog, so a link
someone sends you can't connect an app to your account on its own: only allow a
connection you started yourself. The audit log records
mcp_oauth.client_created, mcp_oauth.granted, mcp_oauth.revoked,
mcp_oauth.refresh_reused and mcp_oauth.client_deleted with the client id, never a secret
or token.
Ending access#
- Settings → Integrations → API tokens: delete
MCP: <client name>to kill the current access token immediately (the client mints a new one on its next refresh). - Settings → Integrations → AI assistants → Delete client: removes the client, every grant it held and their access tokens. This is the complete cut-off.
- The client itself may call
/oauth/revokewith its own credentials; Gemini does when you unlink the app.
Notes#
- OAuth is served on this server's own origin, so
OPENTODO_BASE_URLshould be the address clients use (cookies, passkeys and these redirects are host-bound). - Demo instances refuse registration, authorization and the token endpoints: a sandbox must not hand out credentials.
- A client that registers itself always gets Read only access and a client
secret (
client_secret_post); create the client in Settings for Can make changes. - Self-registration is limited to five a minute per address, and clients that registered themselves but were never authorized are removed after a day.
- The server never calls out during this flow; it only answers.