Register an MCP client (RFC 7591)
/oauth/register
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
Shown on the consent screen. Defaults to "MCP client". Not verified — see client_name on the grant schema.
Defaults to both. Any other value is rejected.
authorization_coderefresh_token
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.
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.
Only ["code"] is supported.
code
Accepted and ignored; scopes are chosen per authorization.
Defaults to none (public client, PKCE only). Choosing either client_secret_* method issues a secret that the token and revocation endpoints will then require.
noneclient_secret_postclient_secret_basic
Response Body
OAuth client id (ocli_…).
Unix seconds.
Present only for confidential clients. Returned exactly once — store it now.
0 — the secret does not expire.
Space-delimited list of every scope this server supports.
curl -X POST 'https://api.elaichi.ai/oauth/register' \
-H 'Content-Type: application/json' \
-d '{"client_name":"your_client_name","redirect_uris":[],"grant_types":[],"response_types":[],"token_endpoint_auth_method":"none","client_uri":"your_client_uri","logo_uri":"your_logo_uri","scope":"your_scope"}'const body = {
"client_name": "your_client_name",
"redirect_uris": [],
"grant_types": [],
"response_types": [],
"token_endpoint_auth_method": "none",
"client_uri": "your_client_uri",
"logo_uri": "your_logo_uri",
"scope": "your_scope"
};
const response = await fetch('https://api.elaichi.ai/oauth/register', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(body),
});
const data = await response.json();
console.log(data);import os
import requests
url = "https://api.elaichi.ai/oauth/register"
headers = {
"Content-Type": "application/json",
}
payload = {
"client_name": "your_client_name",
"redirect_uris": [],
"grant_types": [],
"response_types": [],
"token_endpoint_auth_method": "none",
"client_uri": "your_client_uri",
"logo_uri": "your_logo_uri",
"scope": "your_scope"
}
response = requests.post(url, headers=headers, json=payload)
print(response.json())