MCP 서버 OAuth 구현기 — claude.ai·ChatGPT 커넥터를 여는 유일한 열쇠
API 키로 만든 MCP 서버는 Claude Code·Cursor까지만 갑니다. claude.ai 웹과 ChatGPT를 열려면 OAuth 2.1이 유일한 길이에요. 직접 구현하며 밟은 지뢰 — 401 계약, PRM 경로 변형, DCR 고아 행 — 를 체크리스트로 정리했습니다.
Brandon · 1page.run 만드는 사람
· 5분 읽기
바이브코딩으로든 손코딩으로든 MCP 서버를 하나 만들었다고 해볼게요. API 키를 Bearer 헤더로 받게 하면 Claude Code·Cursor·n8n까지는 잘 붙어요. 저희도 그렇게 시작했어요. 그런데 정작 사용자가 가장 많은 두 곳 — claude.ai 웹과 ChatGPT — 앞에서 막혀요. ChatGPT 커넥터 설정에는 헤더를 입력하는 UI 자체가 없고, claude.ai 웹의 정식 경로도 OAuth뿐이거든요.
그러니까 소비자 채팅 표면이라는 문의 열쇠는 처음부터 하나예요. OAuth 2.1. 이 글은 저희가 1page.run의 MCP 서버에 그 열쇠를 실제로 달면서 배운 것들이에요. 스펙 문서에 있는 얘기는 줄이고, 문서엔 없는데 실측에서만 드러난 지뢰 위주로 정리할게요.
다행인 것부터 — 규격이 하나예요
Anthropic 커넥터 인증 문서와 OpenAI Apps SDK 인증 문서를 나란히 놓고 보면 요구사항이 거의 같아요. OAuth 2.1 + PKCE S256 + 보호 리소스 메타데이터(RFC 9728) + resource 파라미터(RFC 8707). 두 진영의 교집합이 곧 표준이라, 따로 만들 게 없어요. 한 번 구현하면 claude.ai 웹·데스크톱·모바일과 ChatGPT가 동시에 열려요. 저희도 하나 만들어서 양쪽 다 연결 실증까지 마쳤어요.
| 클라이언트 | API 키(헤더) | OAuth |
|---|---|---|
| Claude Code · Cursor · VS Code | ○ | ○ |
| claude.ai 웹·데스크톱·모바일 | 사실상 ✕ | 정식 경로 |
| ChatGPT (개발자 모드·앱 디렉토리) | ✕ (입력 UI 없음) | 유일 경로 |
| n8n · Zapier · Make | ○ | ○ |
실전에서 밟은 지뢰 다섯 개
1. 인증 실패는 반드시 HTTP 401이어야 해요. MCP 툴 핸들러 안에서 200 OK + isError: true로 "로그인이 필요합니다"를 돌려주면, 클라이언트는 그걸 그냥 모델에게 에러 텍스트로 넘겨요. 연결(Connect) 카드가 아예 안 떠요. 로그인 유도는 툴 레이어가 아니라 HTTP 레이어에서, 401 + WWW-Authenticate 헤더에 resource_metadata URL을 실어서 해야 해요. 저희 기존 에러 헬퍼가 정확히 이 함정 모양이어서, OAuth 도입 때 이 계약부터 갈아엎었어요.
2. 디스커버리에는 경로 변형이 있어요. 서버 주소가 https://example.com/api/mcp처럼 경로를 포함하면, 클라이언트는 /.well-known/oauth-protected-resource가 아니라 /.well-known/oauth-protected-resource/api/mcp를 먼저 찾아요(RFC 9728 §3.1). 이 경로 변형 라우트를 빠뜨리면 에러 메시지도 없이 조용히 실패해요. 그리고 메타데이터의 resource 값은 사용자가 입력하는 URL과 문자 그대로 일치해야 해요.
3. PKCE S256은 "지원"이 아니라 "광고"까지예요. 클라이언트는 항상 code_challenge_method=S256을 보내는데, 인가 서버 메타데이터에 code_challenge_methods_supported: ["S256"]이 광고돼 있는지도 봐요. 동작만 되고 광고가 없으면 앞 단계에서 걸러질 수 있어요.
4. DCR은 데이터베이스에 흔적을 남겨요. MCP 클라이언트는 사전 등록이 없어서 동적 클라이언트 등록(RFC 7591)을 열어야 하는데, 이게 연결 시도마다 클라이언트 행을 하나씩 만들어요. 실패한 시도도 동의 기록과 토큰을 남기고요. 저희는 일일 GC 크론을 붙이고, 사용자 설정 화면에서는 클라이언트를 redirect_uri 호스트로 묶어서 "Claude 연결"처럼 보여주고 해제도 호스트 단위로 끊게 했어요. 참고로 최신 스펙 개정(2026-07-28)은 DCR 폐기를 예고하고 CIMD를 권장 경로로 올렸어요 — 신규 구현이라면 둘 다 광고해두는 게 안전해요.
5. 시계가 돌아요 — 응답 10초. 디스커버리·등록·토큰 엔드포인트는 10초 안에 응답하지 못하면 실패 처리돼요. 서버리스 콜드스타트, DB 풀러 첫 연결, 에지 레이트리밋이 전부 이 예산을 갉아먹는 변수예요. 연결 개시 순간엔 요청이 짧게 몰린다는 것도 계산에 넣어야 하고요.
구현 스택 선택 — 있는 인증 위에 얹으세요
인가 서버를 어디에 둘지는 세 갈래였어요. 외부 IdP(SaaS)에 위임하면 스펙 추종을 남이 해주지만 유저 계정이 두 소스로 쪼개져요. 저희는 이미 Better Auth를 쓰고 있어서 OAuth Provider 플러그인으로 기존 유저 테이블 위에 인가 서버를 얹었어요 — DCR·PKCE·PRM·resource 검증이 플러그인 레벨에서 커버돼요. 스택이 다르더라도 원칙은 같아요. 지금 쓰는 인증 시스템을 인가 서버로 승격시키는 경로가, 새 계정 체계를 들이는 경로보다 거의 항상 싸요.
하나 더 — OAuth가 붙은 뒤의 배선이 진짜 작업량이었어요. 레이트리밋·사용량 집계가 API 키 ID에 묶여 있어서, OAuth 연결 1건당 내부적으로 키 행 1개를 발급하는 방식으로 기존 파이프라인(집계·대시보드·폐기 UI)을 한 줄도 안 고치고 살렸어요. 인증 수단을 추가할 때는 "인증 성공 이후"의 코드가 어느 식별자에 묶여 있는지부터 보세요.
검증 순서와 소소한 발견
검증은 MCP Inspector → claude.ai 커스텀 커넥터 → ChatGPT 개발자 모드 순서가 편했어요. Inspector에서 디스커버리·토큰 교환을 먼저 통과시키면 나머지는 UI 문제만 남아요.
붙이고 나서 알게 된 것 두 가지. 툴에 readOnlyHint·destructiveHint annotations를 달면 claude.ai가 읽기/쓰기 툴을 그룹으로 나눠 "항상 허용" 옵션을 줘요 — 매 호출 확인 지옥이 없어요. 그리고 브라우저 기반 MCP 클라이언트는 토큰 교환을 브라우저가 직접 하므로 OAuth 엔드포인트와 .well-known에 CORS를 열어야 해요(세션 경로는 닫아둔 채로요).
이렇게 만든 결과물이 커넥터 URL https://1page.run/api/mcp 하나로 연결되는 1page.run MCP예요. 사용자 입장에서 어떤 경험이 되는지는 이 글에 정리해뒀어요 — 개발자로서 직접 연결해 보면 위 체크리스트가 화면에서 어떻게 보이는지 확인할 수 있어요.
자주 묻는 질문
MCP 서버에 OAuth가 꼭 필요한가요?
대상에 따라 달라요. Claude Code·Cursor·n8n 같은 개발자 도구만 노린다면 API 키 헤더로 충분해요. claude.ai 웹이나 ChatGPT처럼 일반 사용자가 쓰는 채팅 표면에 올리려면 OAuth 2.1이 사실상 유일한 경로예요.
DCR과 CIMD는 뭐가 다른가요?
DCR(RFC 7591)은 클라이언트가 접속 때 자신을 등록하는 방식이라 서버 DB에 클라이언트 행이 쌓이고, CIMD는 클라이언트가 자기 메타데이터 URL을 client_id로 쓰는 방식이라 등록 자체가 없어요. 2026-07-28 스펙 개정이 DCR 폐기를 예고했으니 신규 구현은 CIMD 지원 여부를 확인하고 가능하면 둘 다 광고하세요.
파이썬이나 다른 스택인데도 이 내용이 적용되나요?
네. 이 글의 체크리스트(401 계약, PRM 경로 변형, PKCE 광고, DCR 관리, 10초 예산)는 스펙과 클라이언트 동작에서 오는 요구사항이라 언어·프레임워크와 무관해요. 인가 서버 구현체만 스택에 맞는 걸 고르면 돼요.
만든 MCP 서버를 어떻게 알리나요?
MCP 공식 레지스트리는 DNS TXT 검증으로 네임스페이스를 얻어 등재할 수 있어요. Claude 커넥터 디렉토리는 제출 포털이 Team/Enterprise 조직 전용이라(개인 플랜은 admin 설정이 없어요) 조직 플랜 여부를 먼저 확인하세요.
글쓴이
Brandon
1page.run 만드는 사람
AI로 만든 결과물을 로그인·세팅 없이 링크로 공유하는 가장 빠른 방법을 만들고 있어요. 직접 쓰다 겪은 문제를 그대로 글로 옮겨요.