---
title: "Register an MCP client (RFC 7591)"
method: POST
path: "/oauth/register"
tags: ["MCP OAuth"]
---

# Register an MCP client (RFC 7591)

`POST /oauth/register`

## Request

- Base URL: `https://api.elaichi.ai`
- URL: `https://api.elaichi.ai/oauth/register`
- Auth: none required

Dynamic client registration. Public and unauthenticated by protocol — any MCP client may register itself, which is why every field is length- and count-bounded and every redirect URI is scheme-checked. The response is the only time `client_secret` is ever returned; it is stored hashed and cannot be read back. Omitting `token_endpoint_auth_method` (or sending `none`) registers a public client that authenticates with PKCE alone, which is how every native MCP client works. Registration grants nothing on its own: a registered client can only act after a user approves it at `GET /oauth/authorize`. Rate limited per client IP.

## Request body

- object
  - `client_name` string: Shown on the consent screen. Defaults to "MCP client". Not verified — see `client_name` on the grant schema.
  - `redirect_uris` string[], required: Where authorization codes may be delivered, matched later by exact string. Accepted: any `https` URI; `http` only on loopback (`localhost`, `127.0.0.1`, `[::1]`), the RFC 8252 native-app pattern; or a private-use reverse-DNS scheme such as `com.example.app:/callback`. Fragments and wildcards are rejected.
  - `grant_types` string[]: Defaults to both. Any other value is rejected.
  - `response_types` string[]: Only `["code"]` is supported.
  - `token_endpoint_auth_method` 'none' | 'client_secret_post' | 'client_secret_basic': Defaults to `none` (public client, PKCE only). Choosing either `client_secret_*` method issues a secret that the token and revocation endpoints will then require.
  - `client_uri` string
  - `logo_uri` string: Rendered on the consent screen beside the app name, `https:` only — an `http:`, `data:` or `blob:` value falls back to a generated monogram, as does one that fails to load. It is fetched with `referrer-policy: no-referrer` and without credentials. Note that it proves nothing: registration is unauthenticated, so this is a mark the registrant chose, exactly like `client_name`, and the consent screen says so to the user. `redirect_uris` remains the only identity signal a lookalike cannot forge.
  - `scope` string: Accepted and ignored; scopes are chosen per authorization.

## Response `201`

Client registered

- object
  - `client_id` string: OAuth client id (`ocli_…`).
  - `client_secret` string: Present only for confidential clients. Returned exactly once — store it now.
  - `client_id_issued_at` integer: Unix seconds.
  - `client_secret_expires_at` integer: `0` — the secret does not expire.
  - `client_name` string
  - `redirect_uris` string[]
  - `grant_types` string[]
  - `response_types` string[]
  - `token_endpoint_auth_method` string
  - `client_uri` string
  - `logo_uri` string
  - `scope` string: Space-delimited list of every scope this server supports.

## Other responses

- `400`: RFC 6749 error body: `invalid_client_metadata` or `invalid_redirect_uri`.
- `401`: Unauthorized
- `403`: Forbidden
- `404`: Not found
- `429`: Rate limited (`temporarily_unavailable`), with `Retry-After`.

---

[API](https://skmtc.dev/elaichi/apis/elaichi-api.md) · [All operations](https://skmtc.dev/elaichi/apis/elaichi-api/llms.txt) · [OpenAPI document](https://skmtc.dev/elaichi/apis/elaichi-api/revisions/b22cfb7b6d15?raw)
