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 和 MCP 协议
注册 MCP OAuth 客户端
使用完全匹配的回调 URL 和一组受限的产品权限注册公开客户端。
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)
使用完全匹配的回调 URL 和一组受限的产品权限注册公开客户端。
POST https://api.voicecheap.ai/oauth/register
身份验证
无需身份验证。注册不会使用户登录,也不会授权账户连接。请求体
array
必填
去重前须有 1 到 10 个回调地址。最少项目数: 1. 最多项目数: 10.
string
可选的客户端标签;保存前会去除首尾空白,处理后必须非空。最小长度: 1. 最大长度: 120. 匹配模式:
^(?=.*\S)[^\u0000-\u001F\u007F]+$. 默认值: “MCP client”.field
仅限公共客户端。指定值: “none”. 默认值: “none”.
union
必须包含 authorization_code;refresh_token 可选。省略或设为 null 时选择两者。重复值会被移除。默认值: [“authorization_code”,“refresh_token”].
array
值列表。项目结构请参阅完整输入架构。最少项目数: 1. 最多项目数: 1. 默认值: [“code”].
string
以空格分隔、选自 scopes_supported 的权限,可包含 offline_access。不支持的名称和仅含空白字符的值会被拒绝;重复项会被移除。省略 scope 或不提供任何产品权限(例如仅提供 openid 或 offline_access)时,会注册所有受支持的产品范围。email 必须与 openid 一起使用;仅提供 email 会被拒绝。OpenID Connect 可用时,即使省略 scope,注册也会将 openid 和 email 纳入客户端能力。注册本身不授予访问权;身份权限需要显式授权请求及用户同意。最小长度: 1. 最大长度: 2048. 匹配模式:
^ *(?: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))* *$.技术架构
技术架构
{
"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
}
请求示例
请遵循此路由文档规定的编码和身份验证方式。操作 API 的封装格式和幂等性规则仅适用于操作调用。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)
响应
已注册的公开客户端元数据。请保存 client_id,并将其与完全匹配的已注册 redirect_uri 一起使用。不签发 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"
}
响应头
{
"Cache-Control": "no-store",
"Pragma": "no-cache"
}
操作说明
- 受支持的标量参数必须仅提供一次,值须为不含 ASCII 控制字符或 DEL 的非空字符串。重复的查询或表单字段会解码为数组,在按标量读取时会被拒绝。
- 使用 Content-Type application/json。URL 编码的注册请求体会被拒绝,并返回 OAuth invalid_request 响应;不受支持的媒体类型可能先被集成解析器拒绝。
- 重定向 URI 会原样保留并进行精确比较。更改大小写、端口写法、路径、查询或末尾斜杠时,必须注册该确切 URI。
- HTTP 回调仅接受原样的回环名称 localhost、127.0.0.1 和 [::1]。私有网络主机、其他 IP 拼写形式、*.localhost 和大写的 LOCALHOST 均不符合要求。
- 唯一接受的自定义 URI 协议回调必须完全为 cursor://anysphere.cursor-mcp/oauth/callback。
- 每个回调都必须通过验证;只要一个回调无效,整个注册请求就会被拒绝。URL 中的凭据、空白字符、控制字符、片段标识符和反斜杠均不允许使用。
- 客户端可注册任意受支持的产品权限范围,但用户须在授权时另行批准所请求的子集。仅完成注册不会授予任何访问权限。
- 仅支持 authorization_code 和可选的 refresh_token 授权类型。注册时未包含 refresh_token 的客户端不会收到刷新令牌。
- 当前实现会忽略未知的顶层 metadata 字段。已知字段仍须遵守架构中显示的约束。
- 公开注册限制为每个客户端 IP 每 10 分钟 10 次请求,由所有工作进程共用。JSON 请求体上限为 1 MiB。
- 注册成功时返回 HTTP 201。
- openid 和 email 是独立于产品权限的身份权限,仅在公布支持时接受。email 需要 openid。在首次授权时,仅包含身份权限的请求会使用同意页面显示的默认产品权限。
错误
| HTTP 状态 | 代码 | 含义或恢复方式 |
|---|---|---|
| 400 | invalid_request | 请求体或已识别的参数无效、缺失、重复或编码错误。 |
| 400 | invalid_client_metadata | 回调地址数量无效、客户端标签为空,或客户端身份验证、授权类型、响应类型不受支持。 |
| 400 | invalid_redirect_uri | 某个回调 URL 使用了不受支持或不安全的形式。 |
| 400 | invalid_scope | scope 字符串包含不支持的权限,或未请求任何范围。 |
| 413 | REQUEST_TOO_LARGE | 请求超出允许的请求体大小。JSON 上限为 1 MiB,URL 编码表单上限为 32 KiB。 |
| 415 | UNSUPPORTED_CONTENT_TYPE | 集成请求体解析器仅接受 JSON 或 URL 编码表单。具体路由可能要求使用其中一种编码。 |
| 429 | temporarily_unavailable | 已达到公开 OAuth 请求速率上限。请等待当前时间窗口结束后重试。 |
| 500 | server_error | 授权暂时不可用。请稍后重试。 |
此页面对您有帮助吗?

