MCP and OAuth API reference
CalmLens runs a remote MCP server at https://api.calmlens.com/mcp, with OAuth 2.1 sign-in, so AI assistants can work in a project for the person who approves them. Standard MCP clients discover everything here on their own; this page is for developers building a client or debugging one. To connect an existing assistant, see AI assistants.
MCP endpoint
| Property | Value |
|---|---|
| URL | https://api.calmlens.com/mcp |
| Transport | Streamable HTTP. Send each JSON-RPC message with POST; responses are JSON, never an event stream. GET returns 405. |
| Sessions | None: each request stands alone and no Mcp-Session-Id is issued. |
| Protocol versions | 2025-11-25, 2025-06-18, 2025-03-26 and 2024-11-05. An unsupported MCP-Protocol-Version header returns 400. |
| Batches | A JSON array of up to 20 messages, as protocol 2025-03-26 allows, returns an array of replies. |
| Authentication | Authorization: Bearer <key>, where the key comes from the OAuth flow below. A project API key also works. |
A request without a valid key returns 401 with a WWW-Authenticate header that points at the protected resource metadata, which is how clients find the sign-in:
WWW-Authenticate: Bearer resource_metadata="https://api.calmlens.com/.well-known/oauth-protected-resource/mcp"The server answers initialize, ping, tools/list and tools/call; notifications get 202 with no body.
Tools
Each tool runs one API endpoint with the caller's key, so the endpoint's validation, permissions and errors apply. The AI assistants guide lists the tools and their endpoints.
- Arguments. Path and query parameters are top-level arguments, named as in the API. A request body goes in
body.projectIddefaults to the key's project. - Results. A tool returns the API's JSON response as text content. When the API returns an error, the result has
isError: trueand the error body, with itscode. - Annotations. Read-only tools set
readOnlyHintand deleting tools setdestructiveHint, so clients can ask before running them. Keys approved for reading only aren't offered tools that change things.
Discovery
| Endpoint | Returns |
|---|---|
GET https://api.calmlens.com/.well-known/oauth-protected-resource/mcp | Protected resource metadata (RFC 9728): resource, authorization_servers and the read and write scopes. |
GET https://api.calmlens.com/.well-known/oauth-authorization-server | Authorization server metadata (RFC 8414): the endpoints below, S256 PKCE, the authorization_code grant and public clients (token_endpoint_auth_method: none). |
Register a client
POST https://api.calmlens.com/oauth/register registers a public client (RFC 7591). Clients have no secret: they prove themselves with PKCE.
{
"client_name": "My assistant",
"redirect_uris": ["https://assistant.example/oauth/callback"]
}| Field | Rules |
|---|---|
redirect_uris | One to ten. HTTPS on any host except CalmLens's own domain, HTTP on localhost, 127.0.0.1 or [::1] (any port), or an app's own scheme such as cursor://. No fragments, credentials, or script and data URLs. |
client_name | Shown on the consent page, reduced to plain letters, digits, spaces and simple punctuation. Names that read as CalmLens are replaced with the first redirect URI's host. |
The response is 201 with client_id and the registered values. Clients that never receive a key are deleted after a week; register again if yours was.
Authorize
Send the person's browser to https://calmlens.com/oauth/authorize. They sign in, see the app's name and where approving sends them, and choose a project and access.
| Parameter | Value |
|---|---|
response_type | code |
client_id | From registration. |
redirect_uri | Exactly one you registered. |
code_challenge | Base64url SHA-256 of your code verifier. |
code_challenge_method | S256 |
state | Optional, returned unchanged. |
scope | Optional. read alone offers read-only access; otherwise the person chooses read only or read and change. |
resource | Optional (RFC 8707). Must be https://api.calmlens.com/mcp, https://api.calmlens.com or https://api.calmlens.com/v1; anything else is refused. |
Approval redirects to your redirect_uri with code, your state and iss (https://api.calmlens.com, RFC 9207). Declining redirects with error=access_denied.
Exchange the code
POST https://api.calmlens.com/oauth/token with a form body:
| Parameter | Value |
|---|---|
grant_type | authorization_code |
code | From the redirect. Valid once, for five minutes. |
code_verifier | The verifier behind your challenge. |
client_id | The client the code was issued to. |
redirect_uri | The same URI as in the authorization request. |
resource | Optional, as above. |
{
"access_token": "clsk_…",
"token_type": "Bearer",
"scope": "read write",
"project_id": "123e4567-e89b-42d3-a456-426614174000"
}The access token is a project API key holding part of the approving person's access. It works as the Bearer key for /mcp and as X-Api-Key on the REST API. It has no expiry and there is no refresh token: it lasts until it's revoked, or until the person no longer holds the access they approved. A code presented a second time fails and revokes the key it issued.
The CLI signs in with the device authorization grant (RFC 8628) at POST https://api.calmlens.com/oauth/device/code, which is reserved for the CalmLens CLI; other clients get unauthorized_client.
Revoke
POST https://api.calmlens.com/oauth/revoke with a form body of token revokes that key (RFC 7009). It always returns 200, and only revokes keys issued through OAuth. People can also disconnect apps from their Portal › Profile.
Errors and limits
OAuth endpoints answer errors as { "error", "error_description" } with status 400 unless noted:
error | Meaning |
|---|---|
invalid_request | A required parameter is missing. |
invalid_redirect_uri | Registration with a redirect URI outside the rules above. |
invalid_grant | The code is unknown, expired, already used, or doesn't match the client, redirect URI, verifier or resource. |
unsupported_grant_type | Use authorization_code. |
unauthorized_client | The device grant, for clients other than the CLI. |
slow_down | 429, with Retry-After: registration and device sign-ins are limited to ten a minute per address. |
temporarily_unavailable | 503: try again later. |
The consent page reports problems in the browser rather than redirecting: an unregistered client or redirect URI, or a resource other than CalmLens.