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 y protocolo MCP
Registrar un cliente OAuth de MCP
Registra un cliente público con URL de callback exactas y un conjunto limitado de permisos de producto.
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)
Registra un cliente público con URL de callback exactas y un conjunto limitado de permisos de producto.
POST https://api.voicecheap.ai/oauth/register
Autenticación
Ninguna. El registro no inicia la sesión de un usuario ni autoriza una conexión de cuenta.Cuerpo de la solicitud
array
requerido
Entre 1 y 10 URL de retorno antes de eliminar los duplicados.Número mínimo de elementos: 1. Número máximo de elementos: 10.
string
Etiqueta opcional del cliente; se eliminan los espacios de los extremos antes de guardarla y debe seguir sin estar vacía.Longitud mínima: 1. Longitud máxima: 120. Patrón:
^(?=.*\S)[^\u0000-\u001F\u007F]+$. Valor predeterminado: “MCP client”.field
Solo clientes públicos.Valor obligatorio: “none”. Valor predeterminado: “none”.
union
Debe incluir authorization_code; refresh_token es opcional. Si se omite o es null, se seleccionan ambos. Se eliminan los valores duplicados.Valor predeterminado: [“authorization_code”,“refresh_token”].
array
Lista de valores. Consulta la estructura de sus elementos en el esquema completo de entrada.Número mínimo de elementos: 1. Número máximo de elementos: 1. Valor predeterminado: [“code”].
string
Permisos de scopes_supported separados por espacios, que pueden incluir offline_access. Se rechazan los nombres no admitidos y los valores que solo contienen espacios en blanco; se eliminan los duplicados. Si se omite scope o no se indican permisos de producto, por ejemplo solo openid o solo offline_access, se registran todos los ámbitos de producto admitidos. email requiere openid; email solo se rechaza. Cuando OpenID Connect está disponible, el registro incluye openid y email como capacidades del cliente aunque se omita scope. El registro no concede acceso; los permisos de identidad requieren una solicitud de autorización explícita y el consentimiento del usuario.Longitud mínima: 1. Longitud máxima: 2048. Patrón:
^ *(?: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))* *$.Esquema técnico
Esquema técnico
{
"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
}
Ejemplos de solicitud
Sigue la codificación y la autenticación documentadas para esta ruta. El formato envolvente y las reglas de idempotencia de la API de acciones solo se aplican a las invocaciones de acciones.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)
Respuesta
Metadatos del cliente público registrado. Guarda client_id y úsalo con el redirect_uri exacto registrado. No se emite ningún client_secret.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"
}
Cabeceras de respuesta
{
"Cache-Control": "no-store",
"Pragma": "no-cache"
}
Notas de la operación
- Los parámetros escalares reconocidos deben proporcionarse una sola vez como cadenas no vacías, sin caracteres de control ASCII ni DEL. Los campos repetidos de consulta o formulario se decodifican como arrays y se rechazan al leerse como escalares.
- Usa Content-Type application/json. Un cuerpo de registro codificado como URL se rechaza con la respuesta OAuth invalid_request; el analizador de la integración puede rechazar antes los tipos de contenido no compatibles.
- Las URI de redirección se conservan y se comparan de forma exacta. Los cambios en mayúsculas y minúsculas, representación del puerto, ruta, consulta o barra final requieren registrar esa URI exacta.
- Los callbacks HTTP solo aceptan los nombres de bucle local literales localhost, 127.0.0.1 y [::1]. No se admiten hosts de redes privadas, representaciones alternativas de IP, *.localhost ni LOCALHOST en mayúsculas.
- El único callback aceptado con un esquema de URI personalizado es exactamente cursor://anysphere.cursor-mcp/oauth/callback.
- Cada URL de retorno debe superar la validación; una sola URL de retorno no válida rechaza todo el registro. Se rechazan las credenciales en la URL, los espacios en blanco, los caracteres de control, los fragmentos y las barras invertidas.
- El cliente puede registrar cualquier ámbito de producto compatible, pero el usuario debe aprobar por separado el subconjunto solicitado durante la autorización. El registro por sí solo no concede acceso.
- Solo se admiten los tipos de concesión authorization_code y, opcionalmente, refresh_token. Un cliente registrado sin refresh_token no recibe ningún token de actualización.
- La implementación actual ignora los campos de metadatos de nivel superior desconocidos. Los campos conocidos mantienen las restricciones mostradas en el esquema.
- El límite público de registro es de 10 solicitudes cada 10 minutos por IP de cliente, compartido entre los workers. Los cuerpos JSON están limitados a 1 MiB.
- Un registro correcto devuelve HTTP 201.
- openid y email son permisos de identidad, distintos de los permisos del producto, y solo se aceptan cuando se anuncian. email requiere openid. Durante la autorización inicial, una solicitud que solo contiene permisos de identidad usa los permisos predeterminados del producto mostrados en la pantalla de consentimiento.
Errores
| Estado HTTP | Código | Significado o solución |
|---|---|---|
| 400 | invalid_request | El cuerpo o un parámetro reconocido no es válido, falta, está duplicado o tiene una codificación incorrecta. |
| 400 | invalid_client_metadata | Número de URL de retorno no válido, etiqueta de cliente vacía o autenticación de cliente, tipos de concesión o tipos de respuesta no compatibles. |
| 400 | invalid_redirect_uri | Una URL de callback usa un formato no compatible o inseguro. |
| 400 | invalid_scope | La cadena scope contiene un permiso no admitido o no contiene ningún ámbito solicitado. |
| 413 | REQUEST_TOO_LARGE | La solicitud supera el tamaño de cuerpo permitido. JSON está limitado a 1 MiB y los formularios codificados como URL a 32 KiB. |
| 415 | UNSUPPORTED_CONTENT_TYPE | El analizador del cuerpo de la integración solo acepta JSON o formularios codificados como URL. La ruta puede exigir específicamente una de esas codificaciones. |
| 429 | temporarily_unavailable | Se ha alcanzado el límite público de frecuencia de solicitudes OAuth. Espera a que termine la ventana actual antes de reintentarlo. |
| 500 | server_error | La autorización no está disponible temporalmente. Vuelve a intentarlo más tarde. |
¿Esta página le ayudó?

