OAuth for MCP clients

AI assistants such as the Claude.ai and ChatGPT connectors cannot take a pasted key. They connect to a project’s MCP server with OAuth 2.1: the person signs in to Spatibase, sees a consent screen and chooses what the assistant may do. This page is for developers building or debugging an MCP client. People who just want to connect an assistant should read Connect an AI assistant.

On this page, $PROJECT_ID is the project’s UUID, and the MCP URL is https://mcp.spatibase.com/projects/$PROJECT_ID (copy the exact address from the project’s AI agents page, in Connect your assistant).

Which standards does Spatibase follow?

Spatibase is its own authorization server and follows the MCP authorization specification, revision 2026-07-28. That includes OAuth 2.1 with PKCE, Protected Resource Metadata (RFC 9728), Authorization Server Metadata (RFC 8414), Resource Indicators (RFC 8707), the iss authorization response parameter (RFC 9207), Client ID Metadata Documents, Dynamic Client Registration (RFC 7591) for older clients, and Token Revocation (RFC 7009).

Clients that can send a header (Claude Code, Cursor, your own agent) can use an API key instead. See MCP server.

How does a client connect?

A client goes through these steps:

  1. Call the MCP URL without a token. On a private project the answer is 401 with WWW-Authenticate: Bearer resource_metadata="…". On a public project an anonymous call succeeds with read access, so a client that wants more must start the flow itself.

  2. Read the protected resource metadata at GET https://mcp.spatibase.com/.well-known/oauth-protected-resource/projects/$PROJECT_ID. It returns resource (the project’s canonical MCP URL), authorization_servers, scopes_supported and bearer_methods_supported: ["header"].

  3. Read the authorization server metadata at GET https://api.spatibase.com/.well-known/oauth-authorization-server. It lists the endpoints, code_challenge_methods_supported: ["S256"], client_id_metadata_document_supported: true and authorization_response_iss_parameter_supported: true.

  4. Get a client id, in one of two ways (see the next section).

  5. Send the person’s browser to the authorization endpoint:

    http
    GET /oauth/authorize?response_type=code&client_id=…&redirect_uri=…&code_challenge=…&code_challenge_method=S256&state=…&scope=read%20edit&resource=<MCP URL>

    Spatibase asks the person to sign in if needed, then shows the consent screen. The request stays valid for 30 minutes, long enough to sign in by email link.

  6. The person selects Allow or Cancel. The browser returns to redirect_uri with code, state and iss, or with error=access_denied.

  7. Exchange the code with a form-encoded POST /oauth/token:

    http
    POST /oauth/token
    Content-Type: application/x-www-form-urlencoded
    
    grant_type=authorization_code&code=…&code_verifier=…&redirect_uri=…&client_id=…&resource=…

    The answer is {access_token: "sb_oat_…", token_type: "Bearer", expires_in: 3600, refresh_token: "sb_ort_…", scope}.

  8. Call the MCP URL with Authorization: Bearer sb_oat_…. Before the hour is up, refresh with grant_type=refresh_token&refresh_token=…&client_id=….

How does a client get a client id?

Spatibase accepts two kinds of client id:

  • Client ID Metadata Document (preferred). The client id is an https:// URL with a path, on the default port, that serves JSON with client_id (exactly that URL), client_name, redirect_uris and token_endpoint_auth_method: "none". Spatibase fetches it when a person starts connecting: public addresses only, no redirects, one 5-second deadline, at most 10 KB. It is cached according to its Cache-Control, between 5 minutes and 24 hours.
  • Dynamic client registration. POST /oauth/register with {client_name, redirect_uris, token_endpoint_auth_method?} answers 201 with {client_id: "sb_client_…", …}. The default none creates a public client. client_secret_post or client_secret_basic also returns a client_secret (sb_ocs_…), once.

The consent screen, Connect an app to Spatibase, shows the app’s name, the project and What may it do?. For a client with a metadata document, it shows the domain the app is published from; otherwise it says Spatibase cannot confirm who made the app, and a client registered through /oauth/register carries an unverified badge. If the redirect goes to the person’s own computer, it warns them to allow it only if they started connecting themselves.

Scope Label on the consent screen Pre-selected
read Read the project Always included
edit Edit drafts Yes
publish Publish No
delete Delete No
model Change the content model No

The person’s role caps the scope. An owner may allow all five, an editor read edit publish delete, a developer read only. The screen lists only the scopes the app asked for, and greys out those the role does not allow (“not available: your role is …”). Approving more than the role allows answers 422. Each token is also cut to the person’s current role on every request, so changing someone’s role narrows their assistants at once. What each scope unlocks is in the tool list.

Rules worth knowing

Topic Rule
Redirect URIs Matched exactly against the registered ones. https, or http only on localhost, 127.0.0.1 and [::1]. No fragments.
Errors before a redirect An unknown client, an unregistered redirect URI, a wrong response_type or missing PKCE show an error page and never redirect. invalid_target and invalid_scope go back to the app only once someone has connected it; before that they are an error page too.
PKCE Required, S256 only.
resource Required on /oauth/authorize and must be a project’s MCP URL. Optional on token and refresh requests, but must match when sent.
Scopes read edit publish delete model. No scope asks for all of them. Unknown values (for example offline_access) are ignored. read is implied by any scope and always granted; the person may leave out the others. A refresh may narrow the scope, never widen it.
Authorization codes sb_oac_…, valid 10 minutes, single use. Using a code twice fails and revokes the tokens the first use produced.
Access tokens sb_oat_…, valid 1 hour, for one project’s MCP URL only, and only while the person is still a member of the project.
Refresh tokens sb_ort_…, valid 30 days, rotated on every use. Presenting a used refresh token again closes the whole connection.
Revocation POST /oauth/revoke with token=…&client_id=… always answers 200. Revoking a refresh token also revokes its access tokens.
Error format /oauth/* endpoints answer {error, error_description}. Rate limits per IP answer 429 {error: "rate_limited"} with Retry-After; on /oauth/authorize a 429 page.
Storage Spatibase stores only SHA-256 hashes of codes, tokens and client secrets.

Connected apps

Every approval creates a connection for one person, one app and one project. Approving the same app for the same project again updates that connection.

People see their connections in the account menu under Connected apps, with the project, the permissions, when it was connected and when it was last used. Disconnect removes access straight away. Connecting, changing and disconnecting are recorded in the project’s Activity, and changes the assistant makes appear there as “person via app”.

Updated

Docs support

How can we help?

A person on our team reads every message. We include the page you are on, so you don't have to explain where.

Or write to hello@spatibase.com. For outages, check System status.

Talk to us

Not sure it’s a fit? Let’s find out

Tell us about your use case and what you need, from data model to security and deployment. In 30 minutes we’ll look at it together and tell you honestly if we can help.

Or write to hello@spatibase.com.