> ## Documentation Index
> Fetch the complete documentation index at: https://api-docs.voicecheap.ai/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# MCP OAuth 클라이언트 등록

> 정확한 콜백 URL과 제한된 제품 권한 집합으로 공개 클라이언트를 등록합니다.

정확한 콜백 URL과 제한된 제품 권한 집합으로 공개 클라이언트를 등록합니다.

```http theme={null}
POST https://api.voicecheap.ai/oauth/register
```

## 인증

없습니다. 등록은 사용자를 로그인시키거나 계정 연결에 권한을 부여하지 않습니다.

## 요청 본문

<ParamField body="redirect_uris" type="array" required>
  중복을 제거하기 전 콜백 수는 1개부터 10개 사이입니다.

  최소 항목 수: 1. 최대 항목 수: 10.
</ParamField>

<ParamField body="client_name" type="string">
  선택적 클라이언트 라벨입니다. 저장 전에 앞뒤 공백을 제거하며, 제거 후에도 비어 있지 않아야 합니다.

  최소 길이: 1. 최대 길이: 120. 패턴: `^(?=.*\S)[^\u0000-\u001F\u007F]+$`. 기본값: "MCP client".
</ParamField>

<ParamField body="token_endpoint_auth_method" type="field">
  공개 클라이언트만 허용됩니다.

  필수 값: "none". 기본값: "none".
</ParamField>

<ParamField body="grant_types" type="union">
  authorization\_code를 포함해야 하며 refresh\_token은 선택 사항입니다. 생략하거나 null이면 둘 다 선택됩니다. 중복 값은 제거됩니다.

  기본값: \["authorization\_code","refresh\_token"].
</ParamField>

<ParamField body="response_types" type="array">
  값 목록입니다. 항목 구조는 전체 입력 스키마를 참고하세요.

  최소 항목 수: 1. 최대 항목 수: 1. 기본값: \["code"].
</ParamField>

<ParamField body="scope" type="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))* *$`.
</ParamField>

<Accordion title="기술 스키마">
  ```json theme={null}
  {
    "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
  }
  ```
</Accordion>

## 요청 예제

이 경로에 명시된 인코딩 및 인증 방식을 따르세요. 액션 API의 응답 구조와 멱등성 규칙은 액션 호출에만 적용됩니다.

<RequestExample>
  ```bash cURL theme={null}
  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"}'
  ```

  ```javascript JavaScript theme={null}
  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());
  ```

  ```python Python theme={null}
  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)
  ```
</RequestExample>

## 응답

등록된 공개 클라이언트 메타데이터입니다. client\_id를 저장하고 정확히 등록된 redirect\_uri와 함께 사용하세요. client\_secret은 발급되지 않습니다.

```json HTTP 201 theme={null}
{
  "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"
}
```

## 응답 헤더

```json theme={null}
{
  "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 없이 등록된 클라이언트는 갱신 토큰을 받지 않습니다.
* 현재 구현은 알 수 없는 최상위 메타데이터 필드를 무시합니다. 알려진 필드에는 스키마에 표시된 제약 조건이 그대로 적용됩니다.
* 공개 등록 한도는 클라이언트 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` | 범위 문자열에 지원되지 않는 권한이 포함되어 있거나 요청된 범위가 없습니다. |
| 413 | `REQUEST_TOO_LARGE` | 요청이 허용된 본문 크기를 초과합니다. JSON은 1 MiB, URL 인코딩된 양식은 32 KiB로 제한됩니다. |
| 415 | `UNSUPPORTED_CONTENT_TYPE` | 통합 본문 파서는 JSON 또는 URL 인코딩된 폼만 받습니다. 경로에 따라 이 인코딩 중 하나만 요구할 수 있습니다. |
| 429 | `temporarily_unavailable` | 공개 OAuth 요청 속도 제한에 도달했습니다. 현재 제한 시간 구간이 끝날 때까지 기다린 후 다시 시도하세요. |
| 500 | `server_error` | 일시적으로 권한 부여를 사용할 수 없습니다. 나중에 다시 시도하세요. |


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.