curl --request POST 'https://api.voicecheap.ai/oauth/register' \
--header 'Content-Type: application/json' \
--data '{"client_name":"Acme assistant","redirect_uris":["https://assistant.example.com/oauth/callback"],"token_endpoint_auth_method":"none","grant_types":["authorization_code","refresh_token"],"response_types":["code"],"scope":"account:read projects:read offline_access"}'
const input = {
client_name: 'Acme assistant',
redirect_uris: ['https://assistant.example.com/oauth/callback'],
token_endpoint_auth_method: 'none',
grant_types: ['authorization_code', 'refresh_token'],
response_types: ['code'],
scope: 'account:read projects:read offline_access',
};
const response = await fetch('https://api.voicecheap.ai/oauth/register', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(input),
redirect: 'manual',
});
console.log(response.status, await response.text());
import os
import requests
response = requests.request(
"POST",
"https://api.voicecheap.ai/oauth/register",
headers={"Content-Type":"application/json"},
json={"client_name": "Acme assistant", "redirect_uris": ["https://assistant.example.com/oauth/callback"], "token_endpoint_auth_method": "none", "grant_types": ["authorization_code", "refresh_token"], "response_types": ["code"], "scope": "account:read projects:read offline_access"},
timeout=120,
allow_redirects=False,
)
print(response.status_code, response.text)
OAuth et protocole MCP
Enregistrer un client OAuth MCP
Enregistrez un client public avec des URL de rappel exactes et un ensemble limité d’autorisations produit.
POST
/
oauth
/
register
curl --request POST 'https://api.voicecheap.ai/oauth/register' \
--header 'Content-Type: application/json' \
--data '{"client_name":"Acme assistant","redirect_uris":["https://assistant.example.com/oauth/callback"],"token_endpoint_auth_method":"none","grant_types":["authorization_code","refresh_token"],"response_types":["code"],"scope":"account:read projects:read offline_access"}'
const input = {
client_name: 'Acme assistant',
redirect_uris: ['https://assistant.example.com/oauth/callback'],
token_endpoint_auth_method: 'none',
grant_types: ['authorization_code', 'refresh_token'],
response_types: ['code'],
scope: 'account:read projects:read offline_access',
};
const response = await fetch('https://api.voicecheap.ai/oauth/register', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(input),
redirect: 'manual',
});
console.log(response.status, await response.text());
import os
import requests
response = requests.request(
"POST",
"https://api.voicecheap.ai/oauth/register",
headers={"Content-Type":"application/json"},
json={"client_name": "Acme assistant", "redirect_uris": ["https://assistant.example.com/oauth/callback"], "token_endpoint_auth_method": "none", "grant_types": ["authorization_code", "refresh_token"], "response_types": ["code"], "scope": "account:read projects:read offline_access"},
timeout=120,
allow_redirects=False,
)
print(response.status_code, response.text)
Enregistrez un client public avec des URL de rappel exactes et un ensemble limité d’autorisations produit.
POST https://api.voicecheap.ai/oauth/register
Authentification
Aucune. L’enregistrement ne connecte pas d’utilisateur et n’autorise pas de connexion à un compte.Corps de la requête
array
requis
Entre 1 et 10 URL de rappel avant suppression des doublons.Nombre minimal d’éléments: 1. Nombre maximal d’éléments: 10.
string
Libellé facultatif du client ; les espaces en début et en fin sont supprimés avant l’enregistrement et il doit rester non vide.Longueur minimale: 1. Longueur maximale: 120. Motif:
^(?=.*\S)[^\u0000-\u001F\u007F]+$. Valeur par défaut: “MCP client”.field
Clients publics uniquement.Valeur requise: “none”. Valeur par défaut: “none”.
union
Doit inclure authorization_code ; refresh_token est facultatif. Une valeur omise ou null sélectionne les deux. Les doublons sont supprimés.Valeur par défaut: [“authorization_code”,“refresh_token”].
array
Liste de valeurs. Consultez le schéma d’entrée complet pour connaître la structure de ses éléments.Nombre minimal d’éléments: 1. Nombre maximal d’éléments: 1. Valeur par défaut: [“code”].
string
Permissions de scopes_supported séparées par des espaces, pouvant inclure offline_access. Les noms non pris en charge et les valeurs composées uniquement d’espaces sont rejetés ; les doublons sont supprimés. Omettre scope ou ne fournir aucune permission produit, par exemple openid seul ou offline_access seul, enregistre toutes les permissions produit prises en charge. email nécessite openid ; email seul est rejeté. Quand OpenID Connect est disponible, l’enregistrement inclut openid et email comme capacités du client, même si scope est omis. L’enregistrement n’accorde aucun accès ; les permissions d’identité nécessitent une demande d’autorisation explicite et le consentement de l’utilisateur.Longueur minimale: 1. Longueur maximale: 2048. Motif:
^ *(?:account:read|usage:read|settings:read|settings:write|team:read|team:write|projects:read|projects:write|projects:delete|translations:write|transcription:read|transcription:write|subtitles:read|subtitles:write|tts:read|tts:write|voices:read|voices:write|images:read|images:write|thumbnails:read|thumbnails:write|folders:read|folders:write|glossary:read|glossary:write|webhooks:read|webhooks:write|publishing:read|publishing:write|offline_access|openid|email)(?: +(?:account:read|usage:read|settings:read|settings:write|team:read|team:write|projects:read|projects:write|projects:delete|translations:write|transcription:read|transcription:write|subtitles:read|subtitles:write|tts:read|tts:write|voices:read|voices:write|images:read|images:write|thumbnails:read|thumbnails:write|folders:read|folders:write|glossary:read|glossary:write|webhooks:read|webhooks:write|publishing:read|publishing:write|offline_access|openid|email))* *$.Schéma technique
Schéma technique
{
"type": "object",
"properties": {
"redirect_uris": {
"type": "array",
"minItems": 1,
"maxItems": 10,
"items": {
"type": "string",
"minLength": 1,
"maxLength": 2048,
"pattern": "^[^\\s\\u0000-\\u001F\\u007F-\\u009F#\\\\]+$",
"format": "uri",
"anyOf": [
{
"pattern": "^[Hh][Tt][Tt][Pp][Ss]://"
},
{
"pattern": "^http://(?:localhost|127\\.0\\.0\\.1|\\[::1\\])(?::[0-9]{1,5})?(?:/|\\?|$)"
},
{
"const": "cursor://anysphere.cursor-mcp/oauth/callback"
}
],
"description": "An absolute HTTPS callback, an HTTP callback using the exact lowercase loopback spelling localhost, 127.0.0.1, or [::1], or exactly cursor://anysphere.cursor-mcp/oauth/callback. Must parse as a URL with no username/password, whitespace, control characters, backslashes, or fragment. A loopback port must be a valid URL port. The exact spelling is retained."
},
"description": "Between 1 and 10 callbacks before duplicates are removed."
},
"client_name": {
"type": "string",
"minLength": 1,
"maxLength": 120,
"pattern": "^(?=.*\\S)[^\\u0000-\\u001F\\u007F]+$",
"default": "MCP client",
"description": "Optional client label; trimmed before saving and must remain nonempty."
},
"token_endpoint_auth_method": {
"const": "none",
"default": "none",
"description": "Public clients only."
},
"grant_types": {
"anyOf": [
{
"type": "array",
"minItems": 1,
"items": {
"enum": [
"authorization_code",
"refresh_token"
]
},
"contains": {
"const": "authorization_code"
}
},
{
"type": "null"
}
],
"default": [
"authorization_code",
"refresh_token"
],
"description": "Must include authorization_code; refresh_token is optional. Omitted or null selects both. Duplicate values are removed."
},
"response_types": {
"type": "array",
"minItems": 1,
"maxItems": 1,
"items": {
"const": "code"
},
"default": [
"code"
]
},
"scope": {
"type": "string",
"minLength": 1,
"maxLength": 2048,
"pattern": "^ *(?:account:read|usage:read|settings:read|settings:write|team:read|team:write|projects:read|projects:write|projects:delete|translations:write|transcription:read|transcription:write|subtitles:read|subtitles:write|tts:read|tts:write|voices:read|voices:write|images:read|images:write|thumbnails:read|thumbnails:write|folders:read|folders:write|glossary:read|glossary:write|webhooks:read|webhooks:write|publishing:read|publishing:write|offline_access|openid|email)(?: +(?:account:read|usage:read|settings:read|settings:write|team:read|team:write|projects:read|projects:write|projects:delete|translations:write|transcription:read|transcription:write|subtitles:read|subtitles:write|tts:read|tts:write|voices:read|voices:write|images:read|images:write|thumbnails:read|thumbnails:write|folders:read|folders:write|glossary:read|glossary:write|webhooks:read|webhooks:write|publishing:read|publishing:write|offline_access|openid|email))* *$",
"description": "Space-delimited permissions from scopes_supported, optionally including offline_access. Unsupported names and whitespace-only values are rejected; duplicates are removed. Omitting scope or supplying no product scopes, such as openid alone or offline_access alone, registers all supported product scopes. email requires openid; email alone is rejected. When OpenID Connect is available, registration includes openid and email as client capabilities even when scope is omitted. Registration grants no access; identity permissions require an explicit authorization request and user consent.",
"examples": [
"account:read projects:read offline_access"
]
}
},
"required": [
"redirect_uris"
],
"additionalProperties": true
}
Exemples de requêtes
Suivez l’encodage et l’authentification documentés pour cette route. L’enveloppe et les règles d’idempotence de l’API d’actions s’appliquent uniquement aux invocations d’actions.curl --request POST 'https://api.voicecheap.ai/oauth/register' \
--header 'Content-Type: application/json' \
--data '{"client_name":"Acme assistant","redirect_uris":["https://assistant.example.com/oauth/callback"],"token_endpoint_auth_method":"none","grant_types":["authorization_code","refresh_token"],"response_types":["code"],"scope":"account:read projects:read offline_access"}'
const input = {
client_name: 'Acme assistant',
redirect_uris: ['https://assistant.example.com/oauth/callback'],
token_endpoint_auth_method: 'none',
grant_types: ['authorization_code', 'refresh_token'],
response_types: ['code'],
scope: 'account:read projects:read offline_access',
};
const response = await fetch('https://api.voicecheap.ai/oauth/register', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(input),
redirect: 'manual',
});
console.log(response.status, await response.text());
import os
import requests
response = requests.request(
"POST",
"https://api.voicecheap.ai/oauth/register",
headers={"Content-Type":"application/json"},
json={"client_name": "Acme assistant", "redirect_uris": ["https://assistant.example.com/oauth/callback"], "token_endpoint_auth_method": "none", "grant_types": ["authorization_code", "refresh_token"], "response_types": ["code"], "scope": "account:read projects:read offline_access"},
timeout=120,
allow_redirects=False,
)
print(response.status_code, response.text)
Réponse
Métadonnées du client public enregistré. Conservez client_id et utilisez-le avec le redirect_uri exact enregistré. Aucun client_secret n’est émis.HTTP 201
{
"client_id": "mcp_client_AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
"client_id_issued_at": 1792152000,
"client_name": "Acme assistant",
"redirect_uris": ["https://assistant.example.com/oauth/callback"],
"token_endpoint_auth_method": "none",
"grant_types": ["authorization_code", "refresh_token"],
"response_types": ["code"],
"scope": "account:read projects:read offline_access"
}
En-têtes de réponse
{
"Cache-Control": "no-store",
"Pragma": "no-cache"
}
Notes de l’opération
- Les paramètres scalaires reconnus doivent être fournis une seule fois, sous forme de chaînes non vides sans caractères de contrôle ASCII ni DEL. Les champs de requête ou de formulaire répétés sont décodés en tableaux et rejetés lorsqu’ils sont lus comme des scalaires.
- Utilisez Content-Type application/json. Un corps d’enregistrement encodé en URL est rejeté avec la réponse OAuth invalid_request ; les types de média non pris en charge peuvent être rejetés en amont par l’analyseur de l’intégration.
- Les URI de redirection sont conservées et comparées à l’identique. Toute modification de la casse, de l’écriture du port, du chemin, de la requête ou de la barre oblique finale nécessite l’enregistrement de cette URI exacte.
- Les URL de rappel HTTP acceptent uniquement les noms de bouclage bruts localhost, 127.0.0.1 et [::1]. Les hôtes de réseau privé, les autres représentations d’adresses IP, *.localhost et LOCALHOST en majuscules ne sont pas admis.
- La seule URL de rappel acceptée avec un schéma d’URI personnalisé est exactement cursor://anysphere.cursor-mcp/oauth/callback.
- Chaque URL de rappel doit réussir la validation ; une seule URL de rappel invalide entraîne le rejet de l’enregistrement entier. Les identifiants de connexion dans l’URL, les espaces, les caractères de contrôle, les fragments et les barres obliques inverses sont refusés.
- Le client peut enregistrer n’importe quelle portée produit prise en charge, mais l’utilisateur doit approuver séparément le sous-ensemble demandé lors de l’autorisation. L’enregistrement seul n’accorde aucun accès.
- Seuls les types d’autorisation authorization_code et, facultativement, refresh_token sont pris en charge. Un client enregistré sans refresh_token ne reçoit aucun jeton d’actualisation.
- Les champs de métadonnées inconnus au premier niveau sont ignorés par l’implémentation actuelle. Les champs connus conservent les contraintes indiquées dans le schéma.
- La limite publique d’enregistrement est de 10 requêtes par 10 minutes et par adresse IP client, partagée entre les workers. Les corps JSON sont limités à 1 MiB.
- Un enregistrement réussi renvoie HTTP 201.
- openid et email sont des permissions d’identité, distinctes des permissions produit, acceptées uniquement lorsqu’elles sont annoncées. email nécessite openid. Lors de l’autorisation initiale, une demande contenant seulement des permissions d’identité utilise les permissions produit par défaut affichées sur l’écran de consentement.
Erreurs
| Statut HTTP | Code | Signification ou solution |
|---|---|---|
| 400 | invalid_request | Le corps ou un paramètre reconnu est invalide, manquant, en double ou mal encodé. |
| 400 | invalid_client_metadata | Nombre d’URL de rappel invalide, libellé client vide, ou authentification client, types d’octroi ou types de réponse non pris en charge. |
| 400 | invalid_redirect_uri | Une URL de rappel utilise une forme non prise en charge ou présentant un risque. |
| 400 | invalid_scope | La chaîne scope contient une autorisation non prise en charge ou ne contient aucune autorisation demandée. |
| 413 | REQUEST_TOO_LARGE | La requête dépasse la taille de corps autorisée. Le JSON est limité à 1 MiB et les formulaires encodés en URL à 32 KiB. |
| 415 | UNSUPPORTED_CONTENT_TYPE | L’analyseur de corps de l’intégration accepte uniquement le JSON ou les formulaires encodés en URL. La route peut exiger spécifiquement l’un de ces encodages. |
| 429 | temporarily_unavailable | La limite de fréquence des requêtes OAuth publiques a été atteinte. Attendez la fin de la fenêtre en cours avant de réessayer. |
| 500 | server_error | L’autorisation est temporairement indisponible. Réessayez plus tard. |
Cette page vous a-t-elle été utile ?

