ChatGPT Apps SDK로 앱 만들기: MCP·OAuth 구현과 심사 후기
안녕하세요, 3Min API 개발을 이끌고 있는 yeonghyeon입니다.
지난 몇 주간 3Min API의 ChatGPT Apps 등록을 진행했습니다. 한 차례 리젝을 거쳐 정정 후 승인을 받았고, 그 사이에 같은 코드베이스 위에서 Claude Connectors도 함께 제출했습니다. 이 글은 같은 길을 가시려는 다른 개발자분들께 드리는 정리본입니다. 무엇을 구현해야 하는지, 어디서 리젝되기 쉬운지, 심사 단계에서 무엇이 진행되는지를 한 번에 보실 수 있도록 정리했습니다.
특정 클라우드 인프라나 배포 환경에 의존하는 이야기는 의도적으로 빼두었습니다. ChatGPT Apps 등록은 어떤 스택 위에서도 같은 흐름으로 진행되니까요.
개요 — ChatGPT Apps와 그 바탕인 MCP
ChatGPT Apps는 OpenAI가 ChatGPT 안에서 외부 서비스를 호출할 수 있도록 만든 확장 모델입니다. 사용자는 ChatGPT 화면에서 우리 앱을 추가하고, 대화 흐름 안에서 우리 백엔드의 도구(tool)를 직접 호출할 수 있게 됩니다.
그 기반은 Model Context Protocol(MCP)입니다. MCP는 LLM과 외부 도구를 연결하기 위한 오픈 스펙으로, 도구 목록을 노출하고 그 도구를 안전하게 호출하는 약속을 정의합니다. ChatGPT Apps도 MCP 위에서 도는 클라이언트 중 하나이고, Anthropic의 Claude Connectors 역시 같은 스펙 위에 있습니다. 즉 MCP 서버를 한 번 잘 구현해 두면 양쪽 생태계를 함께 대응할 수 있습니다. 우리도 같은 코드 한 벌로 두 군데에 모두 제출했고, 도구 정의·인증 흐름은 그대로 공유하고 있습니다.
왜 자체 AI 챗봇 대신 도구가 되기로 했나
처음에는 자체 챗봇을 만드는 것도 검토했습니다. 다만 사용자분들의 사용 패턴을 보면, 이미 본인 비즈니스의 맥락은 ChatGPT나 Claude에 충분히 학습시킨 상태였습니다. 우리가 또 하나의 챗봇을 만들면 사용자는 같은 설명을 다시 해야 합니다.
그래서 방향을 바꿨습니다. 이미 신뢰받고 있는 LLM에 우리는 도구로 합류하는 것입니다. 사용자는 평소 쓰던 ChatGPT/Claude에서 본인 비즈니스 이야기를 그대로 이어가시고, 그 안에서 3Min API 도구를 호출해 실제 API를 만들고 운영하실 수 있습니다. 자체 챗봇은 여전히 검토 중이지만 우선순위는 도구화입니다.
구현
SDK 선택 — MCP 표준 SDK 단일 사용
OpenAI가 별도의 ChatGPT Apps 전용 SDK를 제공하긴 하지만, 우리는 그쪽을 사용하지 않았습니다. 서버 구현에 쓴 의존성은 MCP 표준 SDK 한 벌(@modelcontextprotocol/sdk)뿐입니다. 이유는 단순합니다. 같은 서버가 ChatGPT Apps와 Claude Connectors 양쪽에서 모두 인식되어야 하기 때문입니다. 표준 SDK만으로 작성해 두면 한쪽 생태계의 변경에 따라 코드가 갈라지지 않고, 두 디렉터리에 같은 빌드를 그대로 제출할 수 있습니다.
OpenAI 측 사양은 Build your MCP server와 Connect from ChatGPT에서 확인하실 수 있습니다. 두 문서는 ChatGPT 클라이언트가 우리 서버를 어떻게 인식하고 호출하는지를 클라이언트 관점에서 설명합니다 — 표준 MCP 서버를 충실히 구현해 두면 OpenAI 쪽 요구사항이 자연스럽게 충족됩니다.
인증 — OAuth 2.1을 기본으로, 같은 서버 위에 API Key 보조
ChatGPT Apps가 받는 인증은 사실상 OAuth 2.1 한 갈래입니다. OpenAI 공식 가이드는 "ChatGPT does not support … custom API keys or customer-provided mTLS certificates"라고 명확히 적어 두었습니다. 즉 ChatGPT Apps 디렉터리에 등록하려는 앱이라면 OAuth 2.1만 잘 구현하면 됩니다(사용자 데이터를 만지지 않는 도구라면 noauth 익명 모드도 허용되지만, 우리처럼 본인 계정의 엔드포인트와 로그를 다루는 도구는 OAuth가 필수입니다).
다만 같은 MCP 서버를 Cursor·Claude Code·Gemini CLI 같은 로컬 클라이언트에서도 그대로 쓰려면 사정이 다릅니다. 로컬 환경은 사용자가 키를 직접 보관할 수 있는 자리이고, OAuth 흐름은 오히려 무겁습니다. 그래서 우리는 OAuth 2.1을 ChatGPT Apps·Claude Connectors의 기본 인증으로 구현하고, 그 위에 API Key 인증을 한 갈래 더 얹는 형태를 택했습니다. 같은 도구 정의가 두 인증 모두에서 그대로 동작합니다.
OAuth 2.1 — ChatGPT Apps와 Claude Connectors의 기본 인증
호스팅 환경에서 동작하는 클라이언트는 사용자가 직접 키를 관리하기 어렵고, OpenAI 정책상 다른 선택지도 없습니다. 다음 표준을 준수해야 클라이언트가 우리 서버를 제대로 인식합니다.
- RFC 7591 — OAuth Dynamic Client Registration. 클라이언트가 등록 단계를 직접 수행합니다.
- RFC 8414 — Authorization Server Metadata.
/.well-known/oauth-authorization-server에서 인증 엔드포인트를 노출합니다. - RFC 9728 — Protected Resource Metadata. MCP 리소스 자체의 메타데이터를 노출합니다.
- RFC 7636 PKCE — Authorization Code 가로채기 공격을 막는 S256 challenge.
흐름은 표준 그대로입니다. 클라이언트가 동적 등록 → 사용자 인증 후 authorization code 발급(코드 1회용·10분 만료) → PKCE 검증과 함께 token 교환 → access token(1시간) + refresh token(30일 rotation). 만료된 access token은 refresh token으로 갱신하고, 갱신 시마다 refresh token 자체도 회전시켜 탈취 위험을 줄입니다.
구현에서 가장 신경 쓴 부분은 code의 원자적 1회용 보장입니다. authorization code는 가로챌 가치가 큰 자원이라, 토큰 교환 단계에서 race가 발생하지 않도록 update 단계에서 한 번에 claim하도록 처리했습니다.
API Key — 같은 코드를 CLI/IDE에서도 쓰기 위한 보조
ChatGPT Apps는 OAuth로 닿지만, 같은 도구 정의를 Cursor·Claude Code·Gemini CLI·Smithery 같은 로컬 MCP 클라이언트에서도 그대로 쓸 수 있도록 API Key 한 갈래를 더 얹었습니다. 이쪽은 사용자가 본인 키를 직접 보관할 수 있는 환경이라 단순히 x-api-key 헤더로 발급받은 키를 보내는 방식이 가장 깔끔합니다. 우리는 tm_mcp_ 접두어가 붙은 사용자별 키를 발급하고, 키 단위로 활성/비활성과 마지막 사용 시각을 추적합니다.
노출한 도구 8종과 권한 표시
3Min API가 ChatGPT/Claude에 노출하는 도구는 8개입니다. 각 도구에는 MCP 표준의 세 가지 annotation을 명시해야 합니다. 의미부터 정리하면 다음과 같습니다.
| annotation | 의미 | true 예시 |
|---|---|---|
readOnlyHint | 읽기 전용인가 (데이터 변경이 없는가) | 로그·통계 조회 |
destructiveHint | 되돌릴 수 없는 변경을 일으킬 수 있는가 | 엔드포인트 수정·삭제 |
openWorldHint | 외부 세계와 통신하거나 부수효과를 만드는가 | 외부 webhook 발송 |
이 세 값을 우리 도구 8개에 적용하면 다음과 같이 정리됩니다.
| 도구 | 요약 | readOnly | destructive | openWorld |
|---|---|---|---|---|
help | 서비스 가이드 조회 | true | false | false |
endpoints | 엔드포인트 CRUD·배포 | false | true | false |
api_call | 엔드포인트 호출 | false | true | true |
logs | 호출 로그·검색 | true | false | false |
stats | 사용량 통계 | true | false | false |
collaborators | 협업 키·초대 | false | true | true |
subscription | 구독 정보 | true | false | false |
archives | 아카이브 다운로드 | true | false | true |
의도적으로 노출하지 않은 영역
계정 자체의 삭제, 결제수단 변경, MCP 인증 키의 발급·폐기처럼 사용자 동의 한 번이 어긋나면 복구가 불가능하거나 인증 체계 자체와 직결되는 액션은 도구로 만들지 않았습니다. 이런 작업까지 LLM의 판단에 맡기면 한 번의 잘못된 호출이 곧장 보안 사고로 이어지고, 무엇보다 ChatGPT Apps 심사 가이드라인이 권장하는 방향과도 맞지 않습니다.
테스트 — 두 인증 경로를 모두 돌려봐야 합니다
정식 제출 전에 우리는 두 갈래를 모두 돌려보았습니다.
- OAuth 경로 — ChatGPT 웹 버전에서 설정 → Apps & Connectors → Advanced settings에서 개발자 모드를 켠 뒤, 'Create app'으로 우리 MCP 서버 URL을 등록했습니다. ChatGPT가 등록한 서버의
/.well-known/...을 자동으로 읽어 OAuth 메타데이터를 인식하므로, 인증 화면을 직접 거쳐 도구 호출까지 진행해볼 수 있습니다. - API Key 경로 — Claude Code, Cursor, Gemini CLI에 MCP 서버를 추가하고 도구 호출을 검증했습니다. 동시에 Smithery에도 등록해 디렉터리 노출을 확인했습니다.
두 경로의 코드 자체는 같지만, OAuth 쪽은 well-known 메타데이터·동의 화면·토큰 갱신까지 함께 통과해야 하기 때문에 한 단계 더 까다롭습니다. 등록 전에 실제 ChatGPT가 우리 서버에 도달해 도구 목록을 받아오는 단계까지 한 번 직접 돌려보시는 것을 권합니다.
심사 제출 — 한 번 리젝되어 본 뒤에 안 것들
제출 자체는 OpenAI 공식 가이드(app-submission-guidelines)를 보면서 ChatGPT에 질문해가며 진행해도 무리가 없습니다. 다만 폼 작성 단계에서 ChatGPT가 실제로 우리 서버에 접속해 도구 목록을 가져오기 때문에, 제출 시점에 상용환경에 미리 배포되어 있어야 합니다. 테스트용 계정 정보(이메일·비밀번호 또는 별도 검수 계정)도 함께 제공해야 OpenAI 심사 인력이 도구를 직접 호출해볼 수 있습니다.
주의 1 — 도구 권한 표시는 보수적으로
가장 흔한 리젝 사유가 여기입니다. 우리도 처음 제출 때 외부와 통신하는 세 도구(api_call, collaborators, archives)의 openWorldHint를 false로 두었다가 리젝을 받았습니다. 정정 후 통과했고요.
이 세 도구 중 archives는 외부 다운로드 URL을 발급하니까 openWorldHint=true가 명백합니다. 반면 api_call(사용자 정의 webhook으로 외부 통신을 트리거)과 collaborators(협업자에게 이메일 초대 발송)는 외부와 닿는 경로가 간접적이라 잠시 망설이게 됩니다. 결과적으로 우리는 두 도구도 보수적으로 true로 표시하는 게 정답이었습니다.
판단 기준은 단순합니다. 한 번 리젝되면 다음 검수까지 다시 약 2주를 기다려야 합니다. 도구가 외부와 조금이라도 통신하는 흐름이 있다면 망설이지 말고 true로 표시하시는 게 시간 비용이 가장 작습니다. 도구가 만들어내는 부작용을 한 줄씩 적어보고, 부작용이 우리 시스템 바깥까지 도달하는지 확인해 보시면 표시가 자연스럽게 맞춰집니다.
주의 2 — 스크린샷은 "캡처"가 아니라 "템플릿 작업"입니다
제출 폼에는 ChatGPT 안에서 우리 앱이 어떻게 보일지 보여주는 스크린샷 항목이 있습니다. 임의로 캡처해서 올리면 무조건 반려됩니다. 이 단계에는 OpenAI가 정한 명확한 가이드가 있고, 제출 화면 안에서 가이드 링크가 직접 안내됩니다. 흐름은 이렇습니다.
- 제출 폼의 스크린샷 단계에서 가이드 링크가 OpenAI 공식 Figma 파일로 연결됩니다.
- 그 Figma 파일에 들어 있는 스크린샷 템플릿을 본인 Figma 워크스페이스로 복사합니다.
- 우리 앱이 ChatGPT 안에서 실제로 동작한 화면을 그 템플릿 안의 정해진 자리에 붙여넣습니다. 비율·여백·구도가 템플릿에 이미 잡혀 있어서, 우리는 콘텐츠만 채워 넣으면 됩니다.
- 완성된 보드를 이미지로 export해 제출 폼에 업로드합니다.
즉 제출되는 결과물은 단순한 화면 캡처가 아니라 OpenAI가 정한 액자 위에 우리 앱 화면을 배치한 산출물입니다. 가이드는 제출 직전 단계에서 다시 한 번 확인하시고 그대로 따라가시면 됩니다. 이 단계를 건너뛰고 자체 캡처를 올리면 그대로 반려됩니다.
심사 후기 — 승인 후에도 한 단계 남아있습니다
제출 후 결과까지는 약 2주 이상 걸렸습니다. 리젝되어 정정 후 재제출하면 다시 비슷한 기간이 소요됩니다. OpenAI 개발자 대시보드에서 상태를 확인할 수 있고, 리젝/승인 시점에는 메일이 옵니다.
승인 메일을 받았다고 해서 바로 노출되지는 않습니다. 대시보드에서 Publish 버튼을 한 번 더 눌러야 일반 사용자에게 보이기 시작합니다.
노출 직후 한 가지 더 챙기실 부분이 있습니다. 사용자가 우리 앱을 추가했다고 해서 ChatGPT가 모든 대화에서 우리 도구를 자동으로 호출하지는 않습니다. 같은 주제라도 도구가 끼지 않는 응답이 자주 보입니다. 이때는 @ 멘션으로 우리 앱을 직접 호출하시면 됩니다. 사용자분들께 이 안내를 함께 전달해 두시면 도구 사용률이 크게 올라갑니다.
Claude Connectors 진행 현황
Claude Connectors는 같은 MCP 위에서 동작합니다. 코드 추가 작업은 거의 없었습니다. 다만 심사 절차는 OpenAI 쪽보다 덜 정리되어 있습니다. 제출 상태를 확인할 수 있는 대시보드가 따로 없고, 우리는 신청한 지 한 달이 다 되어 가는데 통과/리젝 회신을 아직 받지 못했습니다. 같은 작업을 양쪽에 동시에 제출하시는 것은 권하지만, 일정은 OpenAI 쪽을 기준으로 잡으시는 것이 안전합니다.
직접 시도해보기
3Min API의 MCP 도구를 ChatGPT 안에서 직접 써보고 싶으시면, ChatGPT Apps 추가 화면에서 "3Min API"로 검색해 추가하실 수 있습니다. 한 번 인증을 거치고 나면 평소 ChatGPT 대화에서 @3Min API 멘션으로 도구를 호출하실 수 있습니다.
로컬 MCP 클라이언트(Claude Code, Cursor 등)에서 시도해보고 싶으시면 로그인 후 설정 → AI 연동 화면에서 API 키를 발급받아 바로 시도하실 수 있습니다. 도구별 상세와 호출 예시는 AI 연동 가이드에 정리해 두었습니다.
같은 길을 걷는 다른 분들께 작은 지름길이 되기를 바랍니다.
Related Posts
이제 ChatGPT에서 API를 만드세요 — 3Min API가 ChatGPT 앱에 합류했습니다
ChatGPT Apps에 3Min API가 합류했어요. 코드를 작성하거나 서버를 빌리지 않아도 ChatGPT와 대화하는 흐름 안에서 직접 API를 만들고 테스트하고 거래처에 협업자로 초대할 수 있어요. 시작 단계의 와인 판매 비즈니스 시나리오로 어떤 차이가 나는지 풀어드릴게요.
AI로 API를 관리하세요: MCP 지원 시작
3Min API가 MCP를 지원합니다. AI 어시스턴트를 연결해서 엔드포인트 관리, 로그 확인 등을 대화로 처리해 보세요.
JSON이란? 비개발자 사장님을 위한 기초 가이드
와인샵 사장님 이야기를 마무리하는 JSON 입문 가이드. 엑셀 시트를 출발점으로 키·값·객체·배열의 재귀 구조를 따라가고, 오늘 바로 내 비즈니스 주문을 JSON으로 적어보는 실습까지 사전지식 없이 정리했습니다.