Every request except the public /.well-known/* discovery documents must carry an Authorization: Bearer <token> header. The token is validated by introspection against doola’s IAM service, and the resolved identity (customerId, email, role) is attached to the request.
No tool takes a customerId argument — identity rides the bearer. The active companyId is resolved on demand from the authenticated customer.
Discovery
A request without a valid bearer returns 401 with a challenge that points clients at the OAuth metadata, per RFC 9728 and RFC 8414:
WWW-Authenticate: Bearer realm="doola-mcp",
resource_metadata="https://mcp.doola.com/.well-known/oauth-protected-resource"
MCP clients follow it automatically:
| Endpoint | Purpose |
|---|
GET /.well-known/oauth-protected-resource | RFC 9728 Protected Resource Metadata — names the authorization server(s). |
GET /.well-known/oauth-authorization-server | RFC 8414 Authorization Server Metadata — advertises the OAuth endpoints below. |
OAuth endpoints
Advertised on the configured issuer:
| Endpoint | Use |
|---|
/oauth2/authorize | Authorization endpoint |
/oauth2/token | Token endpoint |
/oauth2/revoke | Token revocation |
/oauth2/introspect | Token introspection |
/oauth2/register | Dynamic Client Registration |
- Grants:
authorization_code, client_credentials, refresh_token.
- PKCE:
S256 supported.
Most MCP clients handle this end to end — you add the server URL, click connect, and complete the sign-in. The endpoints above matter only if you implement the OAuth flow yourself.
New users complete signup at the doola web app during the OAuth flow. If the user isn’t signed in, send them there before any tool call.
Session lifecycle
The transport layer is stateless, but MCP still uses a per-connection session ID:
- The server mints a fresh
Mcp-Session-Id (UUID) on every initialize and returns it as a response header.
- Clients must echo that header on every subsequent request. A non-
initialize request without it returns HTTP 400 with JSON-RPC error -32600 "Missing Mcp-Session-Id header".
- A new
initialize mints a new ID — there is no cross-conversation continuity at the transport layer. Continuity is anchored to the OAuth identity instead.
The only piece of conversation state your client must thread is checkoutSessionId, returned by the create_checkout_session action and passed back to verify_payment.