Docs menu

Connect any MCP client to UnderSelf

The UnderSelf MCP server is a Streamable HTTP endpoint at https://api.underself.io/mcp. It speaks OAuth 2.1 with discovery, dynamic client registration and PKCE, so any compliant MCP client connects the same way Claude does: paste the URL, sign in, approve.

Updated September 24, 2026

MCP server URL
https://api.underself.io/mcp

Looking for the friendly version? See the guide for Claude, Grok, ChatGPT, Gemini or Meta Muse, or see every supported app.

What your client needs

RequirementUnderSelf
TransportStreamable HTTP, stateless, JSON responses. POST /mcp only; GET /mcp answers 405. No SSE stream and no Mcp-Session-Id.
AuthorizationOAuth 2.1, authorization code grant with PKCE (S256 only).
DiscoveryProtected resource metadata (RFC 9728) and authorization server metadata (RFC 8414).
Client registrationDynamic client registration (RFC 7591). Token endpoint auth: none, client_secret_post or client_secret_basic.
Scopesdreams:read and dreams:write. Ask for none and you get both. Consent is all or nothing today.
BearerAuthorization: Bearer <access token> header only.

No dynamic registration?.A client that cannot register itself cannot connect on its own today. Write to info@underself.io with your client's name and redirect URI and we will set it up with you.

The OAuth flow, step by step

  • Call without a token. POST https://api.underself.io/mcp answers 401 with WWW-Authenticate: Bearer resource_metadata="https://api.underself.io/.well-known/oauth-protected-resource/mcp".
  • Read the resource metadata. GET /.well-known/oauth-protected-resource/mcp names the resource (https://api.underself.io/mcp), the authorization server (https://api.underself.io) and the scopes.
  • Read the authorization server metadata. GET /.well-known/oauth-authorization-server lists the authorize, token, registration and revocation endpoints, S256, and the supported grants.
  • Register. POST your client metadata, including your redirect URIs, to the registration_endpoint. You get a client_id, and a client_secret if you asked for one.
  • Authorize. Open the authorization_endpoint in the user's browser with response_type=code, your client_id, redirect_uri, state, code_challenge, code_challenge_method=S256 and resource=https://api.underself.io/mcp.
  • Consent. UnderSelf sends the user to app.underself.io/connect/. They sign in with Google or Apple, see your app's name, the host you send them back to and what you can do, and choose Approve or Cancel. On Approve, the browser returns to your redirect_uri with code, state and iss.
  • Get tokens. POST the code and your code_verifier to the token_endpoint. You get an access token (a JWT with audience https://api.underself.io/mcp, valid 15 minutes) and a refresh token.
  • Call tools. POST /mcp with the bearer token: initialize, tools/list, tools/call. Refresh before the access token expires.
Discovery, from any terminal
curl -s https://api.underself.io/.well-known/oauth-protected-resource/mcp
curl -s https://api.underself.io/.well-known/oauth-authorization-server

openid-configuration answers 404.UnderSelf is an OAuth authorization server, not an OpenID provider, so /.well-known/openid-configuration answers 404. That is expected. Use /.well-known/oauth-authorization-server.

Tokens and lifetimes

ItemLifetime and behaviour
Access token15 minutes.
Refresh tokenOpaque and rotated on every use: always store the new one. A connection lapses after 30 days without use, and after 90 days at most. Then the user reconnects.
RevocationPOST to the revocation_endpoint when the user removes your connector. The user can also disconnect from app.underself.io/account, Connected apps.
After a disconnectRefresh stops at once; the last access token stops working within 15 minutes.

The tools in short

ToolScopeWhat it does
record_dreamdreams:writeSaves a dream (20 to 20,000 characters) and, by default, returns its reading.
analyze_dreamdreams:writeA new reading of a saved dream.
get_dreamdreams:readOne dream with its text, summary and latest reading.
list_dreamsdreams:readDreams newest first, up to 50 per page, optionally for one night.
search_dreamsdreams:readDreams whose text or summary mention the query (first 100 characters).
get_patternsdreams:readThe insights chapter, recording activity and the Inner Map.

tools/list shows only the tools the token's scopes allow. A connection reaches nothing outside these six: no delete, no billing, no account changes. Full inputs and outputs are in the tools reference.

tools/call request
{
  "jsonrpc": "2.0",
  "id": 3,
  "method": "tools/call",
  "params": {
    "name": "list_dreams",
    "arguments": { "limit": 2 }
  }
}
Tool result (structuredContent, illustrative)
{
  "dreams": [
    {
      "dream_id": "3f2a9c1e-0000-4000-8000-000000000001",
      "date": "2026-09-23",
      "summary": "Walking through a familiar house that keeps opening onto new rooms.",
      "has_reading": true
    },
    {
      "dream_id": "3f2a9c1e-0000-4000-8000-000000000002",
      "date": "2026-09-21",
      "summary": "A slow train along a coast at dusk.",
      "has_reading": false
    }
  ],
  "page": 1,
  "total": 14,
  "total_pages": 7
}

Every result carries the JSON both as structuredContent and as text, for clients that read only text. A failure is a normal tool result with isError: true and one plain sentence your assistant can read aloud.

What each error means

Sentence the tool returnsWhat happened
This account has reached its dream limit; upgrade at https://underself.io/plansThe plan's dream limit is reached. Nothing was saved.
This account has reached its dream reading limit; upgrade at https://underself.io/plansThe plan's reading limit is reached. From record_dream, the dream is still saved, without a reading, and the note says so.
This account has reached its dream message limit; upgrade at https://underself.io/plansThe plan's message limit is reached while saving the dream text.
This account cannot use UnderSelf right nowThe account is blocked.
This connection is not allowed to do that; reconnect UnderSelf and approve the access it needsThe token lacks the scope for this call.
No dream with that id was found in this UnderSelf accountWrong dream_id, or a dream from another account.
The connection to UnderSelf has expired; reconnect it and try againThe token is expired or revoked. Refresh, or reconnect.
UnderSelf is getting too many requests from this account; wait a minute and retryThe per account rate limit was hit.
UnderSelf took too long to answer; try again in a momentAn upstream call timed out.
The reading is taking longer than expected and may still finish; call get_dream in a minute before asking againFrom analyze_dream: the reading is still running. Check with get_dream before asking again.
UnderSelf could not complete that right now; try again laterSomething failed on our side.
Give a word or phrase to search forsearch_dreams got an empty query.
UnderSelf could not accept that: (reason)The request was invalid; the reason follows.

Retries and slow readings

  • Retries are safe. record_dream with the same text from the same account within 10 minutes returns the earlier dream with duplicate: true, so an agent retry never spends a second dream.
  • Readings take time. A reading can take up to about two minutes. If it runs long, record_dream still returns the saved dream_id with a note; call get_dream a minute later, and analyze_dream only if it still has no reading.
  • Partial saves. If the dream was started but its text did not save, the result has status: "incomplete" and the dream_id. Call record_dream again with the same text within 10 minutes to finish it on the same dream.

Rate limits

  • About 60 requests a minute per account on /mcp.
  • A higher ceiling per network address, set so that assistant platforms calling from shared addresses are not throttled together.
  • Client registration is limited per network address per hour.
  • Plan limits on new dreams and readings are the same as in the UnderSelf app.

Questions, or a client that will not connect? Write to info@underself.io.