curl --request GET 'https://api.voicecheap.ai/oauth/authorize?client_id=mcp_client_AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA&redirect_uri=https%3A%2F%2Fassistant.example.com%2Foauth%2Fcallback&response_type=code&response_mode=query&code_challenge=E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM&code_challenge_method=S256&resource=https%3A%2F%2Fapi.voicecheap.ai%2Fmcp&scope=account%3Aread+projects%3Aread+offline_access&state=example-client-csrf-state'
const input = {
client_id: 'mcp_client_AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA',
redirect_uri: 'https://assistant.example.com/oauth/callback',
response_type: 'code',
response_mode: 'query',
code_challenge: 'E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM',
code_challenge_method: 'S256',
resource: 'https://api.voicecheap.ai/mcp',
scope: 'account:read projects:read offline_access',
state: 'example-client-csrf-state',
};
const response = await fetch(
'https://api.voicecheap.ai/oauth/authorize?client_id=mcp_client_AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA&redirect_uri=https%3A%2F%2Fassistant.example.com%2Foauth%2Fcallback&response_type=code&response_mode=query&code_challenge=E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM&code_challenge_method=S256&resource=https%3A%2F%2Fapi.voicecheap.ai%2Fmcp&scope=account%3Aread+projects%3Aread+offline_access&state=example-client-csrf-state',
{
method: 'GET',
headers: {},
redirect: 'manual',
},
);
console.log(response.status, await response.text());
import os
import requests
response = requests.request(
"GET",
"https://api.voicecheap.ai/oauth/authorize?client_id=mcp_client_AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA&redirect_uri=https%3A%2F%2Fassistant.example.com%2Foauth%2Fcallback&response_type=code&response_mode=query&code_challenge=E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM&code_challenge_method=S256&resource=https%3A%2F%2Fapi.voicecheap.ai%2Fmcp&scope=account%3Aread+projects%3Aread+offline_access&state=example-client-csrf-state",
headers={},
timeout=120,
allow_redirects=False,
)
print(response.status_code, response.text)
OAuth と MCP プロトコル
MCP アカウント認可の開始
有効期間の短い認可リクエストを作成し、ユーザーを VoiceCheap のログインおよび権限同意ページにリダイレクトします。
GET
/
oauth
/
authorize
curl --request GET 'https://api.voicecheap.ai/oauth/authorize?client_id=mcp_client_AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA&redirect_uri=https%3A%2F%2Fassistant.example.com%2Foauth%2Fcallback&response_type=code&response_mode=query&code_challenge=E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM&code_challenge_method=S256&resource=https%3A%2F%2Fapi.voicecheap.ai%2Fmcp&scope=account%3Aread+projects%3Aread+offline_access&state=example-client-csrf-state'
const input = {
client_id: 'mcp_client_AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA',
redirect_uri: 'https://assistant.example.com/oauth/callback',
response_type: 'code',
response_mode: 'query',
code_challenge: 'E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM',
code_challenge_method: 'S256',
resource: 'https://api.voicecheap.ai/mcp',
scope: 'account:read projects:read offline_access',
state: 'example-client-csrf-state',
};
const response = await fetch(
'https://api.voicecheap.ai/oauth/authorize?client_id=mcp_client_AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA&redirect_uri=https%3A%2F%2Fassistant.example.com%2Foauth%2Fcallback&response_type=code&response_mode=query&code_challenge=E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM&code_challenge_method=S256&resource=https%3A%2F%2Fapi.voicecheap.ai%2Fmcp&scope=account%3Aread+projects%3Aread+offline_access&state=example-client-csrf-state',
{
method: 'GET',
headers: {},
redirect: 'manual',
},
);
console.log(response.status, await response.text());
import os
import requests
response = requests.request(
"GET",
"https://api.voicecheap.ai/oauth/authorize?client_id=mcp_client_AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA&redirect_uri=https%3A%2F%2Fassistant.example.com%2Foauth%2Fcallback&response_type=code&response_mode=query&code_challenge=E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM&code_challenge_method=S256&resource=https%3A%2F%2Fapi.voicecheap.ai%2Fmcp&scope=account%3Aread+projects%3Aread+offline_access&state=example-client-csrf-state",
headers={},
timeout=120,
allow_redirects=False,
)
print(response.status_code, response.text)
有効期間の短い認可リクエストを作成し、ユーザーを VoiceCheap のログインおよび権限同意ページにリダイレクトします。
GET https://api.voicecheap.ai/oauth/authorize
認証
このルートでは不要です。その後ブラウザーで行う同意フローにはユーザーのログインが必要で、承認には対象の有料アカウントへのアクセス権が必要です。クエリパラメーター
string
必須
動的登録で返された client_id、またはクライアントメタデータドキュメントの正確な公開 HTTPS URL。最小文字数: 1. 最大文字数: 2048. パターン:
^[^\u0000-\u001F\u007F]+$.string
必須
このクライアントに登録された redirect_uris のいずれかと完全に一致する必要があります。最小文字数: 1. 最大文字数: 2048. パターン:
^[^\u0000-\u001F\u007F]+$.field
必須
完全な入力スキーマで定義された値。必須の値: “code”.
field
完全な入力スキーマで定義された値。必須の値: “query”. デフォルト値: “query”.
string
必須
元の code_verifier の Base64url SHA-256 ダイジェスト。パディングなし。最小文字数: 43. 最大文字数: 43. パターン:
^[A-Za-z0-9_-]{43}$.field
必須
完全な入力スキーマで定義された値。必須の値: “S256”.
string
必須
リソースオーディエンスは完全一致が必要です。この環境の保護対象リソースのメタデータから resource の値を使用してください。本番環境では https://api.voicecheap.ai/mcp です。異なるオリジン、パス、末尾のスラッシュは拒否されます。形式: “uri”.
string
scopes_supported の権限をスペース区切りで指定し、任意で offline_access を含められます。未対応の名前や空白のみの値は拒否され、重複は除去されます。scope を省略するか、openid のみや offline_access のみなど製品権限を指定しない場合、デフォルトの読み取りスコープとこのクライアントの登録済み製品スコープの共通部分が選択されます。email には openid が必要で、email のみは拒否されます。要求する製品権限はこのクライアントに登録済みである必要があります。ID 権限は明示的に要求し、ユーザーの承認を得る必要があります。scope を省略した場合、ID 権限は要求されません。最小文字数: 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))* *$.string
最終コールバックでそのまま返される任意の不透明な値。毎回新しい予測不能な値を使い、クライアント側で検証してください。最小文字数: 1. 最大文字数: 2048. パターン:
^[^\u0000-\u001F\u007F]+$.string
openid が付与された場合に ID トークンに返される、任意の不透明な nonce 値です。クライアントで検証してください。最小文字数: 1. 最大文字数: 2048. パターン:
^[^\u0000-\u001F\u007F]+$.string
テキスト値。必須の値: “consent”.
技術スキーマ
技術スキーマ
{
"type": "object",
"properties": {
"client_id": {
"type": "string",
"minLength": 1,
"maxLength": 2048,
"pattern": "^[^\\u0000-\\u001F\\u007F]+$",
"description": "The client_id returned by dynamic registration, or the exact public HTTPS URL of the client metadata document."
},
"redirect_uri": {
"type": "string",
"minLength": 1,
"maxLength": 2048,
"pattern": "^[^\\u0000-\\u001F\\u007F]+$",
"description": "Must exactly match one of this client's registered redirect_uris."
},
"response_type": {
"const": "code"
},
"response_mode": {
"const": "query",
"default": "query"
},
"code_challenge": {
"type": "string",
"minLength": 43,
"maxLength": 43,
"pattern": "^[A-Za-z0-9_-]{43}$",
"description": "Base64url SHA-256 digest of the original code_verifier, without padding."
},
"code_challenge_method": {
"const": "S256"
},
"resource": {
"type": "string",
"format": "uri",
"description": "Required exact resource audience: use the resource value from this environment's protected-resource metadata. In production this is https://api.voicecheap.ai/mcp. A different origin, path, or trailing slash is rejected.",
"examples": [
"https://api.voicecheap.ai/mcp"
]
},
"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, selects the default read scopes intersected with this client's registered product scopes. email requires openid; email alone is rejected. Requested product permissions must be registered for this client. Identity permissions must be explicitly requested and approved by the user; omitting scope requests no identity permissions.",
"examples": [
"account:read projects:read offline_access"
]
},
"state": {
"type": "string",
"minLength": 1,
"maxLength": 2048,
"pattern": "^[^\\u0000-\\u001F\\u007F]+$",
"description": "Optional opaque value echoed on the final callback. Use a fresh unpredictable value and verify it in the client."
},
"nonce": {
"type": "string",
"minLength": 1,
"maxLength": 2048,
"pattern": "^[^\\u0000-\\u001F\\u007F]+$",
"description": "Optional opaque nonce echoed in the ID token when openid is granted. Verify it in the client."
},
"prompt": {
"type": "string",
"const": "consent"
}
},
"required": [
"client_id",
"redirect_uri",
"response_type",
"code_challenge",
"code_challenge_method",
"resource"
],
"additionalProperties": true
}
リクエスト例
このルートに記載されたエンコード方式と認証方法に従ってください。アクション API のエンベロープと冪等性のルールは、アクションの呼び出しにのみ適用されます。curl --request GET 'https://api.voicecheap.ai/oauth/authorize?client_id=mcp_client_AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA&redirect_uri=https%3A%2F%2Fassistant.example.com%2Foauth%2Fcallback&response_type=code&response_mode=query&code_challenge=E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM&code_challenge_method=S256&resource=https%3A%2F%2Fapi.voicecheap.ai%2Fmcp&scope=account%3Aread+projects%3Aread+offline_access&state=example-client-csrf-state'
const input = {
client_id: 'mcp_client_AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA',
redirect_uri: 'https://assistant.example.com/oauth/callback',
response_type: 'code',
response_mode: 'query',
code_challenge: 'E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM',
code_challenge_method: 'S256',
resource: 'https://api.voicecheap.ai/mcp',
scope: 'account:read projects:read offline_access',
state: 'example-client-csrf-state',
};
const response = await fetch(
'https://api.voicecheap.ai/oauth/authorize?client_id=mcp_client_AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA&redirect_uri=https%3A%2F%2Fassistant.example.com%2Foauth%2Fcallback&response_type=code&response_mode=query&code_challenge=E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM&code_challenge_method=S256&resource=https%3A%2F%2Fapi.voicecheap.ai%2Fmcp&scope=account%3Aread+projects%3Aread+offline_access&state=example-client-csrf-state',
{
method: 'GET',
headers: {},
redirect: 'manual',
},
);
console.log(response.status, await response.text());
import os
import requests
response = requests.request(
"GET",
"https://api.voicecheap.ai/oauth/authorize?client_id=mcp_client_AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA&redirect_uri=https%3A%2F%2Fassistant.example.com%2Foauth%2Fcallback&response_type=code&response_mode=query&code_challenge=E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM&code_challenge_method=S256&resource=https%3A%2F%2Fapi.voicecheap.ai%2Fmcp&scope=account%3Aread+projects%3Aread+offline_access&state=example-client-csrf-state",
headers={},
timeout=120,
allow_redirects=False,
)
print(response.status_code, response.text)
レスポンス
VoiceCheap の同意ページへの HTTP リダイレクト。JSON の結果本文はありません。Location には不透明な request 値が含まれ、その後ユーザーがログインして権限を選択します。HTTP/1.1 302 Found
Cache-Control: no-store
Pragma: no-cache
Referrer-Policy: no-referrer
Location: https://app.voicecheap.ai/mcp/authorize?request=mcp_req_BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB
レスポンスヘッダー
{
"Cache-Control": "no-store",
"Pragma": "no-cache",
"Referrer-Policy": "no-referrer",
"Location": "https://app.voicecheap.ai/mcp/authorize?request=mcp_req_BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
}
操作の注意事項
- 認識されるスカラーパラメーターは、ASCII 制御文字や DEL を含まない空でない文字列として、1回だけ指定する必要があります。重複するクエリ/フォームフィールドは配列にデコードされ、スカラーとして読み取る際に拒否されます。
- すべてのクエリ値を URL エンコードしてください。redirect_uri の表記は、このクライアントの登録内容と完全に一致する必要があります。
- S256 のみを使用してください。plain PKCE と implicit/token レスポンスには対応していません。トークン交換用に、元の 43–128 文字の code_verifier を保持してください。
- resource パラメーターは必須で、選択した環境のMCPリソースURLと完全に一致する必要があります。
- デフォルトの製品スコープセットは account:read usage:read projects:read transcription:read subtitles:read tts:read voices:read images:read thumbnails:read です。デフォルト値を使用するリクエストは、クライアントが登録したスコープに絞り込まれます。製品スコープが1つも残らない場合、認可は失敗します。
- scope が offline_access のみの場合は、デフォルトの製品スコープ選択が使用されます。製品権限の表示や付与の前に offline_access は除去されます。
- 同意リクエストは10分後に期限切れとなり、回答できるのは1回のみです。ユーザーは、要求された製品権限のうち空でないサブセットのみを承認できます。
- 承認後、クライアントのコールバックは code、iss、および指定されていれば元の state を受け取ります。認可コードの有効期限は 2 分で、交換できるのは1回だけです。
- 拒否後、クライアントのコールバックには error=access_denied、error_description、iss と、指定されていた場合は元の state が返されます。code は発行されません。
- 無効なクライアントメタデータ、リダイレクトの不一致、PKCE、スコープ、resource に関するエラーは、OAuth JSON エラーとして直接返されます。未検証のコールバックにはリダイレクトされません。
- 公開認可エンドポイントの上限は、クライアント IP ごとに毎分 30 リクエストで、ワーカー間で共有されます。
- openid と email は製品権限とは別の ID 権限であり、公開されている場合のみ受け付けられます。email には openid が必要です。初回の認可で ID スコープのみを指定すると、同意画面に表示される既定の製品権限が使われます。
- 動的登録で返された client_id、またはクライアントメタデータドキュメントの正確な公開 HTTPS URL。
- OpenID Connect では prompt=consent をサポートします。他の prompt 値と max_age は拒否されます。サイレント認可と新規ログインの強制はサポートされません。
エラー
| HTTP ステータス | コード | 意味または対処方法 |
|---|---|---|
| 400 | invalid_request | 本文または認識されたパラメーターが無効、不足、重複、またはエンコード不正です。 |
| 400 | invalid_client | client_id が不明であるか、クライアントメタデータドキュメントを取得または検証できません。 |
| 400 | unsupported_response_type | response_type が code ではありません。 |
| 400 | invalid_target | resource がないか、その環境の MCP URL と完全に一致していません。 |
| 400 | invalid_scope | 要求された製品スコープが未対応、未登録、または利用可能な権限が残りません。 |
| 429 | temporarily_unavailable | 公開 OAuth リクエストのレート上限に達しました。現在の制限期間が終わってから再試行してください。 |
| 500 | server_error | 認可を一時的に利用できません。後で再試行してください。 |
| 503 | temporarily_unavailable | クライアントメタデータは一時的に利用できません。後で再試行してください。 |
このページは役に立ちましたか?

