Skip to main content

OAuth & Client Registration

This page explains what happens when an MCP client signs in to ABsmartly with OAuth: which endpoints it calls, how it gets registered, and why some clients are accepted while others are rejected. You don't need any of this to connect a supported client, because the client does it all for you. It is useful when you connect a client that isn't listed in Setup, or when you need to debug a failed connection.

Two ways to connect with OAuth​

You can reach the MCP server at two addresses. Both use OAuth 2.1 with the authorization code flow and PKCE, but the parties involved are different.

Hosted serverYour instance's /mcp endpoint
MCP URLhttps://mcp.absmartly.com/mcp (or /sse)https://your-instance.absmartly.com/mcp
TransportsStreamable HTTP and SSEStreamable HTTP
OAuth authorization servermcp.absmartly.com, which in turn signs you in through your instanceYour ABsmartly instance
Token the client holdsAn MCP access token (1 hour) plus a refresh token (30 days)Your ABsmartly session token, which expires after 24 hours without use (no refresh token)
Consent pageShown once per client and instance, then remembered in that browser for 30 daysShown on every authorization
Instance URLPassed as ?absmartly-endpoint=…, or entered in the browserImplied by the URL

In both cases you sign in with your normal ABsmartly login, whether that is SAML, SSO or email and password. The MCP client never sees your password.

Hosted server (mcp.absmartly.com)​

The hosted server runs two OAuth flows in a row. Toward your MCP client it is the authorization server: it registers the client, shows the consent page and issues the client's tokens. Toward your ABsmartly instance it is itself an OAuth client: it sends you to your instance to sign in, then keeps the resulting ABsmartly session token on the server side.

Sequence diagram of the hosted OAuth flow. The MCP client calls /mcp and receives a 401 with a WWW-Authenticate header, discovers the protected-resource and authorization-server metadata, registers with POST /register or uses a trusted client metadata URL, and opens /authorize in the browser. mcp.absmartly.com asks for the instance URL if needed and shows the consent page, then redirects the browser to the instance's /auth/oauth/authorize, where the user signs in. The instance redirects back to /oauth/callback; mcp.absmartly.com exchanges the code at /auth/oauth/token, reads /auth/oauth/userinfo, and redirects the browser to the client with its own code. The client exchanges that code at /token for an MCP access token and refresh token, then calls /mcp with a Bearer token, and mcp.absmartly.com calls the instance's /v1 API with the ABsmartly session token.

Select the diagram to open it full size.

Step by step:

  1. The client calls POST /mcp without a token and gets 401 Unauthorized with a WWW-Authenticate header that points to the protected-resource metadata and asks for the mcp:access scope.
  2. The client reads the discovery documents: /.well-known/oauth-protected-resource/mcp and /.well-known/oauth-authorization-server.
  3. The client registers, either through POST /register or by using a trusted metadata-document URL as its client_id.
  4. The client opens /authorize in your browser, with a PKCE S256 code challenge.
  5. If the URL didn't include absmartly-endpoint, a Connect to ABsmartly page asks for your instance URL. Then the consent page asks you to Approve or Deny the client.
  6. mcp.absmartly.com redirects your browser to your instance's /auth/oauth/authorize, where you sign in.
  7. Your instance redirects back to https://mcp.absmartly.com/oauth/callback. The hosted server exchanges the code at your instance's /auth/oauth/token and reads your name and email from /auth/oauth/userinfo.
  8. Your browser is redirected back to the MCP client with an authorization code, which the client exchanges at POST /token.
  9. The client calls /mcp with Authorization: Bearer <token>. The hosted server calls your instance's /v1 API on your behalf.

When the client refreshes its token, the hosted server first checks that your ABsmartly session is still valid. If it has ended, the refresh fails and the client asks you to sign in again.

Your instance's /mcp endpoint​

Your ABsmartly instance also serves MCP directly at https://your-instance.absmartly.com/mcp. Here the instance is both the MCP server and the OAuth authorization server, so there is only one flow.

Sequence diagram of the in-product OAuth flow. The MCP client calls the instance&#39;s /mcp and receives a 401, reads the discovery documents, registers with POST /auth/oauth/register or uses a trusted client metadata URL, and opens /auth/oauth/authorize in the browser. If the user isn&#39;t signed in, the instance shows its login page (SAML, SSO or password) and then returns to the authorization request. The consent page is shown, and on approval the browser is redirected to the client with a code. The client exchanges it at /auth/oauth/token for an access token and calls /mcp with a Bearer token.

Select the diagram to open it full size.

The token your MCP client receives is tied to the browser session you approved it from:

  • It expires after 24 hours without use. Each use extends it, and keeps that browser session alive.
  • Signing out of ABsmartly in that browser ends access for every MCP client you approved from it. Reconnect from the client to sign in again.
  • Revoking the token signs that browser out of ABsmartly. Because the token is the browser session, a revocation request (POST /auth/oauth/revoke) ends the session, and with it the access of every other MCP client you approved from that browser. Disconnecting a client without revoking the token leaves the session active until it expires or you sign out.
  • There is no refresh token. When the token expires, the client starts the flow again.

Endpoints​

All of these are standard OAuth endpoints. Your MCP client discovers them automatically from the .well-known documents. You only need to call one yourself when you register a client manually.

PurposeHosted (https://mcp.absmartly.com)Your instance (https://your-instance.absmartly.com)
MCP (Streamable HTTP)POST /mcpPOST /mcp
MCP (SSE, legacy)GET /sseNot available
Protected-resource metadata (RFC 9728)GET /.well-known/oauth-protected-resource/mcpGET /.well-known/oauth-protected-resource/mcp
Authorization-server metadata (RFC 8414)GET /.well-known/oauth-authorization-serverGET /.well-known/oauth-authorization-server
Dynamic client registration (RFC 7591)POST /registerPOST /auth/oauth/register
Authorization and consentGET /authorizeGET /auth/oauth/authorize
TokenPOST /tokenPOST /auth/oauth/token
Token revocation (RFC 7009)POST /tokenPOST /auth/oauth/revoke
User infoNot availableGET /auth/oauth/userinfo

Both servers support:

SettingValue
Grant typeauthorization_code (the hosted server also issues refresh_token)
PKCERequired, S256 only. plain and missing challenges are rejected.
Client authenticationnone (public clients), client_secret_basic, client_secret_post. A client that registers without choosing one gets client_secret_basic.
Scopesmcp:access (default) and user:info
Client ID metadata documentsSupported, for trusted clients only
note

The mcp:access scope grants the same access you have in the ABsmartly Web Console. The MCP client can do anything you can do, so only approve clients you trust.

How a client gets accepted​

An MCP client can be accepted in one of four ways. The client picks the first one it supports. Most clients need no setup from you.

Decision flowchart. A client that uses an API key needs no OAuth registration. A client whose client_id is a URL is accepted only if the URL is on the trusted list of client metadata documents, otherwise it is rejected with invalid_client. Any other client registers with POST /register and is accepted only if all its redirect URIs are on the allowlist, otherwise it is rejected with invalid_redirect_uri. A client that can&#39;t register itself can use a client ID and secret registered manually. Rejected clients can fall back to an API key or ask ABsmartly support to add the client. Accepted clients continue to the authorization code flow with PKCE and the consent page.

Select the diagram to open it full size.

Client ID metadata documents​

Some clients don't register at all. Instead their client_id is an HTTPS URL that points to a JSON document describing the client. ABsmartly fetches the document only if the URL is on this trusted list:

ClientMetadata document URL
Claude (claude.ai, Claude Desktop, mobile)https://claude.ai/oauth/mcp-oauth-client-metadata
Claude Codehttps://claude.ai/oauth/claude-code-client-metadata
VS Codehttps://vscode.dev/oauth/client-metadata.json
VS Code Insidershttps://insiders.vscode.dev/oauth/client-metadata.json
Goosehttps://goose-docs.ai/oauth/client-metadata.json
GitHub Copilot CLIhttps://github.com/copilot/cli/client-metadata.json

Any other URL used as a client_id is rejected with invalid_client, without being fetched. The hosted server returns the message "This MCP client is not supported by this server", and your instance returns "Unknown client". Redirect URIs listed in a trusted document are still checked against the redirect URI allowlist.

Dynamic client registration​

Clients that don't use a metadata document register themselves by calling the registration endpoint with their name and redirect URIs. This is how Cursor, ChatGPT, Windsurf, Gemini CLI, Zed, MCP Inspector and most other clients connect.

Registration succeeds only if every redirect URI is on the allowlist. Otherwise it fails with invalid_redirect_uri and the message "One or more redirect_uris are not allowed".

Registered clients expire after 90 days. On the hosted server the 90 days count from registration, whether or not the client is used. On your instance they count from the last use. If a client that used to work starts failing with invalid_client, remove and re-add the connector so it registers again.

Manual registration​

Some platforms can't register themselves and ask you for a client ID and client secret instead, for example Gemini Enterprise. In that case, register the client yourself once, then paste the result into the platform:

curl -X POST https://mcp.absmartly.com/register \
-H "Content-Type: application/json" \
-d '{
"client_name": "Gemini Enterprise",
"redirect_uris": ["https://vertexaisearch.cloud.google.com/oauth-redirect"],
"token_endpoint_auth_method": "client_secret_basic"
}'

The response contains a client_id and a client_secret. Use the platform's real callback URL as the redirect URI, and it must be on the allowlist. Keep the client secret private, and register again if the client expires.

The example registers client_secret_basic, which matches the default of most platforms (for example Use HTTP Basic Authentication in Gemini Enterprise). If your platform sends the secret in the request body instead, register client_secret_post. Keep the registered method and the platform's setting the same. The client must use PKCE with S256; enable PKCE in the platform if it's optional there.

Some platforms only generate their callback URL when you create the connection. Create the connection first with placeholder credentials, copy the callback URL it returns, register a client with that URL, then update the connection with the new client_id and client_secret.

API key​

If your client can't use OAuth at all, or isn't on the allowlist, connect with an API key instead. API key connections skip OAuth entirely, so no registration is needed. This works with any client that can send an HTTP header or connect to a URL with a query string.

Allowed redirect URIs​

A redirect URI is where your browser sends the authorization code after you approve the client. To stop a malicious client from receiving your access, ABsmartly only accepts redirect URIs from this list. URIs that contain a fragment (#…) or embedded credentials are always rejected.

KindAllowed valuesUsed by
Loopbackhttp://localhost, http://127.0.0.1 and http://[::1], on any port and path (RFC 8252)Claude Code, Gemini CLI, Goose, GitHub Copilot CLI, Windsurf, Zed, Kiro, MCP Inspector, the ABsmartly CLI and other local apps
Custom schemescursor://…, claude://…Cursor and Claude desktop apps
Claudehttps://claude.ai/api/mcp/auth_callback, https://claude.com/api/mcp/auth_callbackclaude.ai, Claude Desktop
ChatGPThttps://chatgpt.com/connector_platform_oauth_redirect, https://chatgpt.com/connector/oauth/…ChatGPT connectors
VS Codehttps://vscode.dev/redirect, https://insiders.vscode.dev/redirectVS Code, VS Code Insiders
Cursorhttps://www.cursor.com/agents/mcp/oauth/callbackCursor background agents
Cloudflarehttps://playground.ai.cloudflare.com/oauth/callbackCloudflare AI Playground
Productboardhttps://integrations.productboard.com/oauth2/callbackProductboard
Perplexityhttps://www.perplexity.ai/rest/connections/oauth_callback, https://enterprise.perplexity.ai/rest/connections/oauth_callbackPerplexity, Perplexity Enterprise
Googlehttps://vertexaisearch.cloud.google.com/oauth-redirectGemini Enterprise
Microsofthttps://global.consent.azure-apim.net/redirect and https://global.consent.azure-apim.net/redirect/<connector-id>Microsoft Copilot Studio, Azure AI Foundry
Amazonhttps://bedrock-agentcore.<region>.amazonaws.com/identities/oauth2/callback/<uuid>Amazon Bedrock AgentCore

HTTPS redirect URIs must match the listed scheme, host and path. The allowlist check ignores the query string. However, when the client starts sign-in, the redirect URI it sends must match the one it registered exactly, query string included, or authorization fails. Entries shown with …, <connector-id>, <region> or <uuid> accept any value in that position. Plain http:// is only accepted for the loopback addresses.

Cloud agent platforms

The Perplexity, Google, Microsoft and Amazon callbacks are shared by every customer of those platforms, so they don't identify whose connector will receive the access. The consent page shows a warning for these hosts. Only approve when you have just started the connection yourself.

These platforms connect from their own cloud. Connect them to the hosted server URL, as shown in Setup.

Your client isn't on the list?​

If your MCP client is rejected and isn't covered above:

  1. Use an API key. It works with any client and needs no registration. See Authentication.
  2. Ask for the client to be added. Contact ABsmartly support with the client's name and its redirect URI or metadata-document URL. You can also open an issue on github.com/absmartly/mcp.

Self-hosted clients with a callback on your own domain, for example LibreChat, Open WebUI or self-hosted n8n, can't be added to the shared allowlist. Use an API key for these.

ABsmartly shows an Authorize Access page before any client receives access. On the hosted server it appears before you sign in to your instance; on your instance's own /mcp endpoint it appears after you sign in. It shows:

  • the name of the client asking for access,
  • the ABsmartly account that will be shared (on the hosted server, the instance URL), and the site the access will be sent to,
  • what the client will be able to do, and
  • Approve and Deny buttons.

Extra warnings appear when the access goes to an app on your own computer (loopback redirect) or to a shared cloud platform. Only approve a request you started yourself from your own MCP client.

The consent request expires after 10 minutes, and it must be completed in the same browser that started it. If it expires, restart the connection from your MCP client.