AI Agent · MCP

원격 MCP 서버를 여러 클라이언트에 붙이기: URL 중심 OAuth 디스커버리와 등록 패턴

AI아키텍트 2026. 8. 1. 14:10

내부 기능을 원격 MCP 서버 (Remote MCP Server, Streamable HTTP) 로 노출하면, 하나의 서버를 여러 AI 클라이언트가 자연어로 함께 쓸 수 있습니다. Codex, Claude Code, claude.ai, Claude Desktop, ChatGPT — 클라이언트는 제각각이지만 서버는 하나입니다(같은 제품이라도 웹·데스크톱 표면이 커넥터를 공유하기도 합니다). 그런데 막상 붙이려고 하면 "클라이언트마다 등록 방법이 다른데?"라는 벽에 부딪힙니다.

좋은 소식은, 클라이언트와 서버가 호환되는 MCP Authorization 사양과 클라이언트 등록 방식을 지원하면, 등록은 대체로 URL 중심으로 수렴한다는 점입니다. 클라이언트가 서버에 처음 접속하면 메타데이터 디스커버리 (Discovery) 가 작동해, 어느 인가 서버로 로그인해야 하는지를 클라이언트가 알아냅니다. 이 글은 그 공통 인증 흐름을 한 번 이해한 뒤, 여러 클라이언트의 등록을 세 가지 형태로 묶어 정리하고, 원격 특유의 파일 업로드와 운영 공통까지 다룹니다.

본문의 서버·인가 서버 URL·클라이언트 ID·도구 이름은 모두 합성 예시(example.com)이며 특정 제품·회사·계정을 가리키지 않습니다. 클라이언트 제품명(Claude Code, ChatGPT 등)과 연관 글·공식 문서 링크는 실제이며, 서버·인가 서버 식별자만 합성 예시입니다. MCP·OAuth의 세부 동작과 클라이언트 UI는 버전·요금제·관리자 정책에 따라 달라지므로, 개념을 먼저 이해하고 각 클라이언트의 최신 공식 문서로 확인하시기 바랍니다.

1. 개요: 서버는 하나, 클라이언트는 여럿

원격 MCP 서버는 다음이 확정되면 여러 클라이언트에서 대체로 같은 방식으로 붙습니다.

항목 예시(합성) 의미
MCP 엔드포인트 https://mcp.example.com/mcp 원격 HTTP(Streamable) 서버 주소
인가 사양 MCP Authorization(OAuth 2.1 기반) + 메타데이터 디스커버리 + PKCE 클라이언트가 디스커버리로 인가 서버를 찾음
클라이언트 등록 사전 등록 ID / CIMD / DCR 중 하나 클라이언트·서버가 지원하는 방식에 따라 다름

핵심은 엔드포인트 URL을 넣으면 인가 서버 발견은 클라이언트가 디스커버리로 처리한다는 것입니다(호환이 전제). 다만 인가 서버 주소를 손으로 적지 않아도 되는 것과 달리, 클라이언트 식별(등록) 은 사전 등록 ID·클라이언트 ID 메타데이터 문서(CIMD)·동적 클라이언트 등록(DCR) 중 무엇을 지원하느냐에 따라 절차가 갈립니다. 이 표준 흐름 자체는 이미 MCP OAuth 2.1 인증 구조에서 깊게 다뤘으니, 이 글은 그것을 실제 클라이언트에 적용하는 쪽에 집중합니다.

2. 왜 URL 중심으로 되는가: 메타데이터 디스커버리와 인가 흐름

클라이언트가 URL을 출발점으로 인증까지 도달하는 것은 디스커버리(2단)와 인가 코드 흐름을 분리해서 보면 명확합니다. PKCE는 디스커버리 단계가 아니라 인가 흐름의 일부입니다.

[디스커버리]
1) 보호 리소스 메타데이터 (RFC 9728, Protected Resource Metadata)
   → MCP 서버가 "나를 보호하는 인가 서버(authorization_servers)는 여기"라고 알려줌
2) 인가 서버 메타데이터 (RFC 8414 또는 OIDC Discovery)
   → authorize·token 엔드포인트 등을 알려줌 (jwks_uri는 구성에 따라 없을 수도 있음)
[인가 흐름]
3) 인가 코드 흐름 + PKCE(S256)
   → 공개 클라이언트가 client secret 없이 안전하게 코드 교환
4) 토큰 대상 지정 (RFC 8707 resource)
   → 발급 토큰이 이 MCP 서버(리소스)를 대상으로 함을 명시

즉 클라이언트는 (1)로 인가 서버를 찾고, (2)로 그 엔드포인트를 알아낸 뒤, (3)~(4)로 이 리소스를 대상으로 한 토큰을 안전하게 받습니다. 사람이 넣는 값은 엔드포인트 URL(과 등록 방식에 따라 client_id)뿐입니다. 다만 스코프는 "자동 협상"되는 것이 아니라, 보호 리소스 메타데이터의 scopes_supportedWWW-Authenticatescope가 각각 다른 역할로 기준을 제시하고, openid·offline_access 같은 값은 OIDC·인가 서버 정책에 종속됩니다.

3. 공통 인증 흐름 — 한 번 이해하면 나머지가 같다

여러 클라이언트의 첫 연결은 대체로 같은 흐름을 탑니다.

[클라이언트]  ── 최초 도구 호출 ──▶  [MCP 서버]
     ▲                                   │
     │        401 + WWW-Authenticate      │  (resource_metadata·필요 scope 제시)
     ◀───────────────────────────────────┘   (인가 서버는 RFC 9728 문서의
     │                                          authorization_servers 에서 확인)
     ├─ 브라우저로 인가 서버 로그인 페이지 이동
     ├─ 로그인 / 동의
     ├─ 콜백으로 authorization code 수신 → PKCE 코드 교환 → 토큰 발급
     ▼
[이후]  Authorization: Bearer <token> 로 도구 자동 호출 (토큰 저장·재사용)

정리하면, 등록 시에는 URL(과 등록 방식에 따라 client_id) 을 넣고, 최초 도구 호출에서 브라우저 로그인을 거치면, 그다음부터는 저장된 토큰으로 동작합니다. 다만 "로그인 한 번"이 영구를 보장하지는 않습니다 — refresh token이 없거나 만료·회수되면 재인증이 필요하고, 장기 연결에는 offline_access·refresh token 발급 여부를 확인해야 합니다. 이 흐름을 이해하면 아래 클라이언트들은 "URL을 어디에 넣느냐"의 차이로 수렴합니다.

4. 등록의 세 가지 형태

여러 클라이언트는 등록 UI만 다르고, 실제로는 세 가지 형태로 묶입니다.

형태 대상 클라이언트 넣는 곳
A · 설정 파일 CLI 계열(Codex·Claude Code) 설정 파일에 URL·client_id
B · 커스텀 커넥터 UI 웹·데스크톱(claude.ai·ChatGPT·Claude Desktop) 커넥터 추가 화면에 URL
C · stdio 브리지 커넥터 미지원·구버전 데스크톱 로컬 stdio ↔ 원격 HTTP 중계

5. 형태 A — 설정 파일형 CLI 등록

Codex(CLI) 는 설정 파일(TOML)에 서버 블록을 추가합니다. OAuth는 client_id만 지정하면 됩니다.

[mcp_servers.example]
url = "https://mcp.example.com/mcp"

[mcp_servers.example.oauth]
client_id = "example-mcp"

Claude Code(CLI) 는 한 줄 명령이 가장 간단합니다.

claude mcp add --transport http example-remote https://mcp.example.com/mcp

설정 파일을 직접 편집한다면 다음과 같습니다.

{
  "mcpServers": {
    "example-remote": {
      "type": "http",
      "url": "https://mcp.example.com/mcp",
      "oauth": { "clientId": "example-mcp", "callbackPort": 8765 }
    }
  }
}

저장 후 클라이언트를 재시작하고, 첫 도구 호출에서 브라우저 인증을 한 번 거치면 토큰이 저장됩니다. Claude Code는 /mcp 명령으로 연결·인증 상태를 확인할 수 있습니다. 원격 HTTP + OAuth 방식이므로 로컬 실행 파일(command)이나 bearer 토큰 환경 변수는 필요 없습니다.

6. 형태 B — 웹·데스크톱 커스텀 커넥터

claude.ai(웹)ChatGPT(웹) 는 커스텀 커넥터 UI에 URL을 넣습니다(요금제/버전에 따라 Developer mode·고급 설정이 필요할 수 있음).

1) Settings → Connectors → Add custom connector
2) MCP Server URL 에 https://mcp.example.com/mcp 입력 → 저장
3) Connect → 401 → 디스커버리 → 인가 서버 로그인 페이지 이동
4) 의도한 계정으로 로그인/동의 → 콜백 복귀 → 도구 목록 노출 확인

데스크톱 앱(claude.ai·ChatGPT 계열)은 대개 같은 계정의 커넥터를 공유합니다. 웹에서 한 번 인증해 두면 데스크톱 앱에 자동으로 나타나, 재인증 없이 쓰이는 경우가 많습니다. 앱에서 직접 추가할 때도 절차는 웹과 동일합니다. 메뉴 명칭·위치(예: Connectors, Apps & Connectors)는 버전마다 다를 수 있으나, 원격 MCP는 URL만 넣으면 표준 OAuth 디스커버리로 인증된다는 점은 같습니다.

7. 형태 C — stdio 브리지(원격 HTTP를 로컬 stdio로 중계)

원격 커넥터를 직접 지원하지 않거나 구버전인 데스크톱 앱에서는 stdio 브리지를 씁니다. 로컬에서 도는 작은 중계기가 원격 HTTP + OAuth를 로컬 표준입출력(stdio)으로 이어 주고, 최초 실행 시 브라우저 인증까지 대신 처리합니다.

{
  "mcpServers": {
    "example-remote": {
      "command": "npx",
      "args": ["-y", "mcp-remote", "https://mcp.example.com/mcp"]
    }
  }
}

Windows에서는 앱이 npx를 직접 실행하지 못하는 경우가 많아 "command": "cmd" + "/c" 래핑이 안정적이며, Node.js가 먼저 설치돼 있어야 합니다. 이는 정식 등록 방식이라기보다 레거시 fallback입니다 — mcp-remote 같은 브리지는 실험적(proof-of-concept)일 수 있으니 비공식임을 인지하고, -y로 매번 최신을 받기보다 검증한 버전을 고정하는 편이 안전합니다.

8. 원격에서 로컬 파일 업로드 — 사전 서명 티켓

원격 HTTP 서버는 내 노트북의 로컬 경로를 직접 역참조할 수 없고, MCP에는 모든 클라이언트가 공통으로 지원하는 로컬 파일 업로드 절차가 없습니다(도구 입력은 JSON Schema라 base64로 바이트를 넣을 수는 있지만, 크기·성능·클라이언트 지원 때문에 일반적으로 부적합합니다). 그래서 이 서버는 사전 서명 티켓 기반의 3단계 업로드를 씁니다(표준·필수가 아니라 이 서버가 채택한 방식).

1) prepare_upload(file_name)
   → upload_id 와 '구조화된 업로드 지시'(upload_url·method·허용 headers·
     expires_at·max_bytes) 반환. ※ '실행할 명령 문자열'을 반환하지 않는다
2) 그 지시로 바이트를 out-of-band HTTP 업로드
   (가능하면 bearer token 대신 짧은 만료의 사전 서명 URL 사용)
3) create_resource(upload_id=…) → 서버가 업로드본으로 리소스 생성

⚠️ 보안: 서버가 반환한 값을 셸에서 명령으로 그대로 실행하면 안 됩니다. 실행 문자열을 되돌려 그대로 실행하는 방식은 명령 주입·셸 이스케이프·토큰의 프로세스 목록/셸 기록 노출 위험이 있습니다. 서버는 실행 문자열이 아니라 구조화된 데이터를 주고, 클라이언트가 검증된 HTTP 클라이언트로 업로드해야 합니다. 이 패턴(사전 서명/티켓 → 바이트 채널 → 확정)의 원리와 함정은 원격 MCP 파일 업로드 설계: Upload Ticket 패턴에서 자세히 다뤘습니다. 참고로 로컬에서 stdio로 서버를 직접 실행하는 경우엔, 도구가 경로 인자를 지원하고 그 프로세스에 파일 읽기 권한이 있을 때 로컬 경로로 바로 업로드할 수 있습니다(서버 프로세스 = 내 머신).

9. 운영 공통 — 도구 카탈로그·ID 체인·재시도 코드

등록을 마쳤다면, 실제 사용에서 반복되는 공통 규칙 몇 가지를 알아 두면 편합니다.

  • 도구 분류 — 도구는 대개 읽기·쓰기·삭제로 나뉩니다. 삭제처럼 되돌리기 어려운 작업은 에이전트가 호출 전 사용자에게 의도를 재확인해야 합니다(AI Agent 승인 정책).
  • ID 체인 — 리소스는 넓은 것에서 좁은 것으로 좁혀 가며 조회합니다(예: 워크스페이스 → 그룹 → 항목 → 파일). 상위 ID를 먼저 얻어 하위 호출에 넘깁니다.
  • 상태 코드(retcode) 규약 — 성공/토큰 만료/로그인 필요/권한 없음을 코드로 구분합니다. ⚠️ 단, 이런 retcode·ID 체인·완료 플래그는 MCP 공통 표준이 아니라 특정 애플리케이션 API 설계입니다(MCP 표준의 protocol error·tool result isError·tool annotations와는 별개). 여기서는 "이런 앱 계약이 흔히 있다"는 예시로 봐 주세요.
구분 신호(예시) 대응
성공 정상 코드 계속 진행
토큰 만료·로그인 필요 인증 관련 코드 /mcp 재인증 → 안 풀리면 클라이언트 재시작
권한 없음 권한 관련 코드 소유자/관리자 전용 도구인지 역할 확인
비동기 상태 "요청됨 ≠ 완료됨" 완료 플래그·본문 존재 여부로 별도 판단

특히 "요청됨"과 "본문이 실제로 있음"은 다릅니다. 요약·처리 같은 비동기 작업은 "요청 접수"만으로 결과가 준비됐다고 단정하면 안 되며, 별도의 완료/가용 신호로 판단해야 합니다(비동기 AI 요약 완료 상태 설계). 이런 실행 경로를 자연어 지시부터 도구 호출까지 검증하는 방법은 자연어에서 MCP Tool Call까지: 통합 테스트에서 다뤘습니다.

10. 함정과 트러블슈팅

  • 커넥터가 안 보인다 — 요금제/버전에 따라 커스텀 커넥터·Developer mode가 필요할 수 있습니다. 계정 커넥터 공유가 되는 앱은 웹에서 먼저 인증해 두십시오.
  • 연결은 됐는데 401이 반복 — 토큰 만료·로그인 필요 신호이면 각 클라이언트의 재인증 경로(예: Claude Code /mcp, Codex codex mcp login <name>)로 재인증하고, 안 풀리면 클라이언트를 재시작합니다. refresh token이 없으면 재로그인이 필요합니다.
  • Windows에서 브리지가 안 뜬다npx를 직접 못 실행하는 경우가 많아 cmd /c 래핑이 안정적입니다. Node.js 설치가 선행돼야 합니다.
  • 스코프는 최소로 — 등록 시 넓은 권한을 요구하지 말고, 서버가 제시하는 읽기 전용 최소 스코프부터 시작하는 것이 안전합니다(자격 증명 관리).
  • 업로드 후 처리 실패 — 특정 포맷을 처리기가 지원하지 않아 업로드는 됐는데 후속 처리가 실패할 수 있습니다. 지원 포맷으로 변환 후 올리는 편이 안전합니다.

11. 체크리스트와 마무리

[ ] 서버가 RFC 9728(보호 리소스 메타데이터)로 인가 서버를 알려주는가
[ ] 클라이언트에 URL(과 필요 시 client_id)만 넣고 디스커버리에 맡겼는가
[ ] 최초 1회 브라우저 인증 후 토큰이 저장·재사용되는가
[ ] 커넥터 미지원 환경은 stdio 브리지로 이었는가
[ ] 로컬 파일은 사전 서명 티켓으로 올렸는가(명령 문자열을 그대로 실행하지 않았는가)
[ ] 스코프를 최소 권한으로 시작했는가
[ ] "요청됨 ≠ 완료됨"을 완료 신호로 구분하는가

정리하면, 원격 MCP 서버를 여러 클라이언트에 붙이는 일은 클라이언트·서버가 MCP Authorization 사양과 등록 방식을 지원한다면 대체로 URL 중심으로 수렴합니다. 클라이언트가 몇 개든 손으로 적는 값이 크게 늘지 않는다는 것이 표준의 힘입니다. 다만 클라이언트 등록 방식(사전 등록·CIMD·DCR)·refresh token·요금제·관리자 정책에 따라 추가 절차가 있을 수 있으니, 각 제품의 공식 문서를 함께 확인하세요. 등록 이후의 신뢰(스코프 최소화·재인증·비동기 상태 판단)까지 챙기면, 하나의 서버가 팀의 여러 AI 도구에서 안전하게 재사용됩니다.

함께 읽으면 좋은 글: MCP OAuth 2.1 인증 구조, 원격 MCP 파일 업로드 설계, 자연어에서 MCP Tool Call까지, 비동기 AI 요약 완료 상태 설계.