Skip to main content
The Caret MCP server supports two authentication methods: OAuth 2.0 with PKCE (recommended) and API Key (legacy).

OAuth 2.0 with PKCE

Most MCP clients (Claude Desktop, Cursor, etc.) handle the OAuth flow automatically. This section is for developers building custom MCP clients.

1. Dynamic Client Registration

Register your client to get a client_id and client_secret.
curl -X POST "https://api.caret.so/mcp/oauth/register" \
  -H "Content-Type: application/json" \
  -d '{
    "client_name": "My MCP Client",
    "redirect_uris": ["http://localhost:3000/callback"],
    "grant_types": ["authorization_code", "refresh_token"],
    "response_types": ["code"],
    "token_endpoint_auth_method": "client_secret_post",
    "scope": "read offline_access"
  }'
Response:
{
  "client_id": "caret_abc123...",
  "client_secret": "secret_xyz...",
  "client_name": "My MCP Client",
  "redirect_uris": ["http://localhost:3000/callback"],
  "grant_types": ["authorization_code", "refresh_token"],
  "response_types": ["code"],
  "token_endpoint_auth_method": "client_secret_post",
  "scope": "read write offline_access openid profile email"
}

2. Authorization Request

Redirect the user to the authorization endpoint.
GET https://api.caret.so/mcp/oauth/authorize
  ?response_type=code
  &client_id={client_id}
  &redirect_uri={redirect_uri}
  &code_challenge={code_challenge}
  &code_challenge_method=S256
  &state={state}
  &scope=read+offline_access
ParameterRequiredDescription
response_typeYesMust be code
client_idYesFrom client registration
redirect_uriYesMust match a registered redirect URI
code_challengeRecommendedPKCE code challenge (S256)
code_challenge_methodWith challengeMust be S256
stateRecommendedOpaque value for CSRF protection
scopeOptionalSpace-separated scopes (defaults to read)
The user will be redirected to Google sign-in, then prompted to select a Caret workspace. After authorization, the user is redirected back to your redirect_uri with a code parameter.

3. Token Exchange

Exchange the authorization code for tokens.
curl -X POST "https://api.caret.so/mcp/oauth/token" \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "grant_type=authorization_code" \
  -d "code={authorization_code}" \
  -d "redirect_uri={redirect_uri}" \
  -d "client_id={client_id}" \
  -d "client_secret={client_secret}" \
  -d "code_verifier={code_verifier}"
Response:
{
  "access_token": "eyJhbGciOiJIUzI1...",
  "token_type": "Bearer",
  "expires_in": 3600,
  "refresh_token": "rt_abc123...",
  "scope": "read offline_access"
}

4. Token Refresh

When the access token expires, use the refresh token to get a new one.
curl -X POST "https://api.caret.so/mcp/oauth/token" \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "grant_type=refresh_token" \
  -d "refresh_token={refresh_token}" \
  -d "client_id={client_id}" \
  -d "client_secret={client_secret}"
The old refresh token is revoked and a new one is issued with each refresh.

Scopes

ScopeDescription
readRead access to notes, knowledge, workspace, and members
writeWrite access (reserved for future use)
offline_accessRequest a refresh token
openidOpenID Connect identity
profileUser profile info
emailUser email address

Token Lifetime

TokenLifetime
Access token (JWT)1 hour
Refresh token30 days
Authorization code10 minutes

API Key Authentication

As an alternative to OAuth, you can use an existing Caret API key.
curl -X POST "https://api.caret.so/mcp" \
  -H "Authorization: Bearer your_api_key_here" \
  -H "Content-Type: application/json" \
  -d '{"method": "tools/call", ...}'
Create API keys in Settings > Developer in the Caret app. See API Authentication for details.

Permission Mapping

PermissionMCP Tools
notes / readcaret_list_notes, caret_get_note, caret_search_notes, caret_search_knowledge
users / readcaret_list_members
(none required)caret_get_workspace

Errors

ErrorStatusDescription
invalid_client400/401Unknown client or authentication failed
invalid_grant400Authorization code expired, already used, or PKCE verification failed
invalid_scope400Requested scope not supported or not allowed
invalid_request400Missing or malformed parameters
server_error500Internal server error