사내 단일 MCP Gateway 도입기 (feat: IBM mcp-context-forge)
IBM mcp-context-forge 로 사내 단일 MCP Gateway 를 구축한 도입기입니다. MCP Federation 패턴, 팀 단위 RBAC 과 OAuth 토큰 위임, 업스트림 기여 과정까지 담았습니다.
들어가며
올해 초부터 사내 AI 인프라를 구성하는 일을 맡고 있다. LLM 게이트웨이, 에이전트 런타임, 트레이싱 같은 것들을 하나씩 쌓아왔는데, 돌이켜보면 도입 효과도 가장 컸던 것은 따로 있었다. MCP Gateway, 정확히는 MCP Federation 패턴이다.
구체적인 요구사항들은 아래와 같았다.
- BI 도구(Redash)를 에이전트에서 실행하고 싶어요
- 쿼리 도구(Trino)를 에이전트에서 실행하고 싶어요
- 개발 직군이 아닌데도 쓸 수 있나요?
- 이런이런 MCP 사용해도 되나요?
하나하나 떼어놓고 보면 MCP 서버 하나 붙이거나 호스팅 하면 되는 일이다. 문제는 이게 조직 규모로 곱해질 때 생긴다. 사용자 수십 명 x MCP 서버 십수 개가 되는 순간, 각자의 로컬 머신에 mcp.json 을 배포하고 서버마다 인증 정보(API 키, OAuth 앱)를 나눠주고 버전을 관리하는 일은 현실적으로 불가능해진다. 시크릿이 개인 머신 곳곳에 흩어지는 것도 보안 관점에서도 좋은 구성은 아니다.
그래서 개별 MCP 서버를 하나씩 붙여주는 대신, 게이트웨이를 한 번 제대로 구축하기로 했다. 이 글에서는 왜 게이트웨이 패턴이었는지, 후보 중에 왜 IBM 의 mcp-context-forge 를 골랐는지, 그리고 도입하면서 만난 버그들을 업스트림 컨트리뷰션으로 메꿔온 과정을 정리해보려고 한다.
MCP Gateway 는 무엇을 해결하나
MCP Gateway 는 여러 업스트림 MCP 서버를 게이트웨이 뒤에 모아두고 사용자에게는 단일 MCP 엔드포인트 하나만 노출하는 패턴이다. 게이트웨이가 업스트림 서버들의 tool 을 연합해서 하나의 카탈로그처럼 제공하기 때문에 Federation 이라고도 부른다.

이 패턴이 해결해주는 것은 네 가지다.
- 단일 엔드포인트: 사용자는 게이트웨이 주소 하나만 등록하면 된다. 새 MCP 서버가 추가되어도 클라이언트 설정은 바뀌지 않는다.
- 인증의 중앙화: 각 업스트림 MCP 의 OAuth 인증을 게이트웨이가 대신 수행하고, 발급받은 토큰도 게이트웨이가 위임 관리한다. 사용자의 로컬 머신에는 업스트림 시크릿이 내려가지 않는다.
- 큐레이션과 권한: 어떤 팀에게 어떤 tool 을 노출할지 관리자가 중앙에서 결정할 수 있다. tool 이 수백 개가 되면 "다 주는 것"이 오히려 독이 되기 때문에, 용도별로 묶어서 제공하는 기능이 꼭 필요하다.
- 감사(audit): 누가 어떤 tool 을 호출했는지 한 곳에 남는다.
사실 "왜 엔터프라이즈 환경에서는 CLI 가 아니라 MCP 인가"는 할 말이 더 있는데, 그건 별도의 글로 다루려고 한다. 요약하면 개인 로컬 환경에서는 CLI 가 더 나은 경우가 많지만 "수백 명이 사내 시스템에 에이전트로 접근한다"는 전제가 붙는 순간 중앙에서 관리되는 MCP 쪽으로 저울이 기운다.
(물론 그럼에도 CLI 가 주는 장점, 예를들자면 파이프라이닝을 통한 출력 제어로 토큰 사용량 절감이라든지.. 하는것은 CLI 쪽이 여전히 우세하긴 하다)
왜 mcp-context-forge 였나
2026년 초 시점에 검토했던 후보는 크게 두 갈래였다.
| agentgateway (solo.io) | mcp-context-forge (IBM) | |
|---|---|---|
| 성격 | MCP/A2A 데이터플레인(프록시) | 관리 UI 를 갖춘 게이트웨이 플랫폼 |
| 강점 | 트래픽 처리 계층으로서의 완성도 | 팀/RBAC/토큰 위임 같은 조직 기능 |
| 당시 아쉬웠던 점 | 관리자 UI, 셀프서비스 온보딩 부재 | 자잘한 버그, 미완성 기능 |
| 라이선스 | 오픈소스 | 오픈소스 (별도 유료 플랜 없음) |
agentgateway 는 solo.io 가 주도하는 오픈소스 프로젝트로, MCP/A2A 트래픽을 다루는 데이터플레인으로서는 인상적이었다. 다만 당시 시점에는 사내 서비스로 만들 때 필요한 관리자 UI, 팀 단위 관리, 셀프서비스 온보딩 같은 것들이 부족해서 도입하기엔 아직 이르다고 판단했다. 프록시로서는 훌륭하지만 우리에게 필요한 건 프록시가 아니라 플랫폼이었다.
mcp-context-forge 는 IBM 이 공개한 오픈소스 MCP 게이트웨이다. 처음 봤을 때 눈에 띈 건 두 가지였다.
첫째, 별도의 엔터프라이즈 플랜이 없다. 오픈소스 중에는 SSO, RBAC, 감사 로그 같은 핵심 기능을 유료 에디션에 묶어두는 open-core 모델이 많은데, mcp-context-forge 는 그런 구분 없이 전부 열려 있었다.
둘째, 조직 규모 확장을 처음부터 감안한 설계였다. 팀(Team) 개념, 역할 기반 접근 제어(RBAC), 리소스 소유자(resource owner) 모델, 그리고 OAuth 토큰 위임(token delegation)까지, 게이트웨이에 기대했던 "플랫폼" 기능이 어느 정도 이미 구현되어 있었다. 여러 업스트림 MCP 를 용도별로 묶어 노출하는 Virtual Server 개념도 있어서 큐레이션 요구사항과 정확히 맞아떨어졌다. (Virtual Server 는 일종의 단일 MCP 엔드포인트를 제공해주면서, MCP servers / tools / A2A Agents 등을 논리적으로 묶어서 제공해주는 개념이다)
그래서 mcp-context-forge 로 결정하고 사내 IdP(SSO) 연동과 함께 쿠버네티스 위에 올렸다.

토큰 위임은 어떻게 동작하나
이 글에서 계속 나올 개념이라, 토큰 위임(token delegation)이 대략 어떻게 돌아가는지만 짚고 넘어가려고 한다.
먼저 관리자가 업스트림 MCP 를 게이트웨이에 등록할 때, 해당 서비스의 OAuth 클라이언트 정보(client id/secret)도 함께 등록한다. 이 시크릿은 게이트웨이에만 존재하고 사용자에게는 내려가지 않는다. 앞에서 "로컬 머신에 시크릿이 흩어지지 않는다"고 했던 것이 이 구조 덕분이다.
사용자 쪽 흐름은 평범한 OAuth authorization code 플로우다.

핵심은 토큰을 저장해두고, 이후 호출마다 게이트웨이가 대신 인증하는 부분이다. 발급받은 access token 과 refresh token 은 게이트웨이 DB 에 암호화되어 저장되고, 이후 그 사용자가 해당 업스트림의 tool 을 호출할 때마다 게이트웨이가 저장해둔 토큰을 꺼내 업스트림 MCP 에 전달한다. 토큰이 만료되면 함께 저장해둔 refresh token 으로 다시 발급받는다.
인증의 단위가 "사용자 x 업스트림"이라는 점도 운영에서 꽤 중요했다. Virtual Server 는 업스트림 MCP 들을 용도별로 묶은 뷰이기 때문에, 같은 업스트림을 포함하는 Virtual Server 가 여러 개 있어도 사용자는 그 업스트림에 한 번만 인증하면 된다. 사용자는 큐레이션된 업스트림마다 최초 1회 동의 화면만 거치면 되고 그 뒤로는 어떤 Virtual Server 에서 쓰든 인증이 따라온다.
여기까지는 게이트웨이가 업스트림 MCP 에 대신 인증하는 쪽 이야기다. 반대로 사용자가 게이트웨이 자체에 인증하는 "진입점" 쪽은 조금 뒤에서 따로 다룬다.
처음부터 완전하게 잘 동작하는건 아니었다
여기까지만 보면 순탄한 도입기 같지만 실제로는 그렇지 않았다. 쓰기 시작하자마자 버그와 부족한 기능이 줄줄이 나왔다. 몇 가지만 꼽아보면 이렇다.
- OAuth 토큰 갱신(refresh)이 특정 프로바이더에서 실패했다. 토큰 엔드포인트가 JSON 이 아니라 form-encoded 로 응답하는 프로바이더(GitHub 등)가 있는데, 게이트웨이가 JSON 만 가정하고 파싱했기 때문이다.
- 관리 UI 의 토큰 폐기(revoke) 버튼이 빈 토큰 ID 로 DELETE 요청을 보내는 버그가 있었다.
- 팀 스코프의 viewer 역할에는 tool 실행 권한이 아예 없어서 "조회만 가능한 사용자에게도 tool 실행은 허용"하는 우리 정책을 표현할 수 없었다.
선택지는 둘이었다. 이슈를 올려두고 기다리거나, 직접 고치거나. 나는 후자를 택했다. 어차피 사내에서는 fork 를 빌드해서 쓰고 있었으니, 급한 버그는 fork 에 먼저 패치해서 배포하고 같은 패치를 업스트림 PR 로 올리는 사이클을 돌렸다.
고칠 거면 업스트림에도 돌려주기
이렇게 진행한 업스트림 기여가 쌓이면서 위에서 언급한 버그 상당수는 지금 본가(upstream)에 머지되어 있다. 대표적인 것만 나열하면 이렇다.
feat(auth): Virtual Server 진입점의 OAuth access token 검증(JWKS). 바로 아래에서 자세히 다룬다 (#3715)fix(oauth): refresh_token 의 form-encoded 응답 파싱 지원 (#4259)fix(ui): 토큰 revoke 버튼이 빈 ID 로 DELETE 를 보내던 버그 (#4047)feat(rbac): 팀 스코프 viewer 역할에 tools.execute 권한 추가 (#3882)feat(ui): 소유자가 아닌 사용자도 접근 가능한 OAuth 게이트웨이에 인증할 수 있도록 개선 (#3935)feat(admin): A2A 에이전트 등록 폼에 protocol version 선택 추가 (#4761)fix(ui): OAuth 게이트웨이 편집 시 issuer 필드가 채워지도록 수정 (#3756)fix: 관리 UI 편집 시 서버 team_id 가 유실되던 문제 수정 (#3780)fix: 게이트웨이 상태 정리 시 진행 중인 auth_code 를 지우지 않도록 guard 추가 (#3792)
부산물도 있었다. 게이트웨이에 물릴 업스트림 MCP 서버들을 하나씩 연동하다 보면 그쪽 버그를 밟게 되는데, 그렇게 n8n 의 MCP OAuth audience 불일치 수정이 n8n 업스트림에 머지되기도 했다 (n8n#30055). MCP 생태계 전체가 아직 젊다 보니, 게이트웨이 도입기가 곧 생태계 디버깅기(?)가 된다.
이 방식의 좋은 점은 fork 부채가 계속 줄어든다는 것이다. fork 로 앞서가되 업스트림에 돌려주면, 다음 버전 업그레이드 때 들고 갈 커스텀 패치가 줄어 있다. 반대로 fork 에만 쌓아두면 업그레이드 때마다 이자를 내야 한다.
진입점도 OAuth 로: Virtual Server 인증
게이트웨이의 인증에는 두 개의 면이 있다. 게이트웨이가 각 업스트림 MCP 에 인증하는 면(앞에서 설명한 토큰 위임)과, 사용자(정확히는 MCP 클라이언트)가 게이트웨이에 인증하는 면, 즉 진입점이다.
도입 당시 진입점 인증의 기본 선택지는 게이트웨이가 자체 발급하는 정적 API 토큰이었다. 사용자가 몇 명일 때는 문제가 없지만 조직 규모로 가면 이야기가 달라진다. 토큰을 발급해서 나눠주고 주기적으로 회전시키고 조직을 떠난 사람의 토큰을 정리하는 일이 전부 운영 부담으로 남는다. 무엇보다 MCP 스펙 자체가 인증을 OAuth 로 정리해가는 흐름인데, 게이트웨이 문 앞에서만 정적 토큰을 쓰는 것이 어색했다.
그래서 Virtual Server 진입점에 OAuth 인증을 붙이는 기능을 만들어 업스트림에 올렸고 머지되었다. 동작은 단순하다. 클라이언트가 사내 IdP 에서 발급받은 access token(JWT) 을 들고 오면, 게이트웨이가 IdP 의 공개키(JWKS)로 서명을 직접 검증한다. 검증된 사용자는 SSO 팀 매핑으로 팀과 역할이 자동으로 부여되고 그대로 게이트웨이의 RBAC 으로 이어진다.

이걸로 통행 구조가 끝까지 OAuth 로 정리되었다. 건물에 비유하면 이렇다.
- 진입점 OAuth: 정문에서 사원증을 확인한다 (이 요청이 누구인지 IdP 기준으로 검증)
- RBAC: 층별 출입 권한을 확인한다 (어떤 Virtual Server 와 tool 을 쓸 수 있는지 결정)
- 토큰 위임: 각 사무실 문은 그 사람 명의의 열쇠로 연다 (업스트림 MCP 에는 그 사용자가 위임해둔 토큰으로 인증)
물론 진입점에 OAuth 를 붙였다고 인증 이야기가 끝난 것은 아니다. 붙는 클라이언트와 IdP 가 다양해질수록 예상치 못한 문제가 계속 튀어나왔는데, 대표적인 것이 audience 검증이었다. "이 토큰이 원래 누구에게 쓰라고 발급된 것인가"를 게이트웨이가 어떻게 확인할 것인가 하는 문제다. 그런데 이 값을 토큰 어디에 담는지, 검증을 얼마나 엄격하게 기대하는지가 IdP 마다 제각각이었다. OAuth 의 audience 가 이렇게까지 깊고 어려운 주제일 줄은 이때 처음 알았다. (개인적으로 이 작업을 하면서 OAuth 에 대해서 좀 더 알게되기도 하고, Idp 마다 표준에 대한 구현 방식이 조금씩 다르거나 지원되는 범위가 미묘하게 달라서 생기는 문제가 많다는것도 알게되었다. 알고싶진 않았지만..)
온보딩은 5분이면 끝난다
도입 후 몇 달이 지난 지금, 새 구성원이 사내 MCP 를 쓰기 시작하는 데 걸리는 시간은 5분이 채 되지 않는다. 개발 직군이 아니어도 똑같다. 실제 온보딩 플로우는 이렇다.
- 코딩 에이전트에서 온보딩 스킬을 실행한다 (안내와 링크가 출력된다)
- 게이트웨이에 SSO 로 로그인한다 (사내 계정이면 가입 절차랄 것도 없다)
- 안내된 링크에서 각 업스트림 MCP(Jira, Slack, Notion 등)의 OAuth 인증을 최초 1회씩 완료한다
- 끝. 이후 토큰 갱신은 게이트웨이가 알아서 한다
앞서 말한 진입점 OAuth 덕에, 이 플로우 어디에도 "정적 토큰을 발급받아 붙여넣는" 단계가 없다는 점이 핵심이다.


큐레이션된 카탈로그에는 공식(official) MCP 서버와 사내 시스템용으로 직접 만든 커스텀 MCP 서버가 섞여 있는데, 사용자 입장에서는 구분할 필요가 없다. 전부 게이트웨이 하나에서 나오는 tool 이기 때문이다.

기대하지 않았던 효과도 있었다. 코딩 에이전트를 갈아탈 때의 마이그레이션 비용이 사실상 사라졌다. Claude Code 를 쓰다가 Codex 로 옮겨도 게이트웨이 엔드포인트와 본인 인증은 그대로라 인증 정보를 옮기거나 재발급받을 일이 없다. 에이전트 도구가 빠르게 바뀌는 요즘, 도구 선택과 인프라를 분리해둔 것이 생각보다 큰 자유를 줬다.
MCP 로 시작했는데, 에이전트 인프라가 됐다
mcp-context-forge 에는 A2A(Agent-to-Agent) 게이트웨이 기능도 포함되어 있다. A2A 프로토콜을 지원하는 에이전트를 게이트웨이에 등록하면, 클라이언트 입장에서는 MCP tool 처럼 호출할 수 있다.
우리는 이 기능으로 사내 관측/조회용 에이전트들(메트릭, 로그, 쿠버네티스 리소스 조회 등)을 게이트웨이에 등록해서 쓰고 있다. 재밌게도 순서가 거꾸로였다. MCP Gateway 로 시작했는데, 게이트웨이에 이미 큐레이션된 MCP tool set 이 쌓여 있으니 그 위에 DevOps/SRE 에이전트를 만드는 일이 훨씬 쉬워졌다. 에이전트를 만들 때 가장 손이 많이 가는 "도구 연결과 인증"이 이미 플랫폼에 있기 때문이다. 게이트웨이가 에이전트 인프라의 토대가 된 셈이다.

실제로 얼마나 쓰이나
게이트웨이를 세워두면 좋은 점 하나는, 사내에서 MCP 가 실제로 어떻게 쓰이는지가 한곳에 모인다는 것이다. 지금까지 게이트웨이를 거쳐 나간 tool 호출은 수십만 건 단위가 됐다.
눈에 띄는 건 상위권이 대부분 데이터 조회라는 점이다. Trino 쿼리 실행, 메타데이터 검색, 메트릭/로그 조회 같은 데이터 조회 계열 tool 이 각각 수만 회 이상 불리며 최상위를 차지한다. 사람이든 에이전트든, 결국 "지금 상태가 어떤지 물어보는" 일에 MCP 를 제일 많이 쓰고 있다는 뜻이다.

그런데, MCP 라고 다 같은 MCP 가 아니었다
게이트웨이에 붙일 MCP 를 하나씩 정리하다 보니, MCP 라고 다 같은 게 아니라는 걸 알게 됐다. 게이트웨이 뒤에 자연스럽게 들어오는 MCP 가 있고, 아무리 봐도 잘 맞지 않는 MCP 가 있었다. 기준은 하나였다. 그 MCP 가 다루는 대상이 "네트워크 너머의 공유 자원"인가, 아니면 "사용자의 로컬 환경"인가.
- SaaS/내부 API 를 감싼 MCP: Grafana, GitHub, Sentry, Datadog, Slack 같은 것들이다. 결국 뒷단이 네트워크로 접근하는 공유 API 라, 게이트웨이가 사용자를 대신해 호출해주는 구조와 완벽하게 맞는다. 지금 우리가 큐레이션한 MCP 는 대부분 여기에 속한다.
- 로컬 환경을 제어하는 MCP: Playwright, Chrome, 파일시스템 같은 것들이다. 이들은 "그 사용자의 브라우저", "그 사람의 파일"처럼 로컬 자원을 다룬다. 중앙 게이트웨이 뒤에 두면 "누구의 브라우저를 열 것인가"부터 답이 없다. 그래서 아직 게이트웨이로는 연동하지 못한 영역이다. 원격 브라우저 풀을 띄우는 식의 우회가 없진 않겠지만, 애초에 게이트웨이가 풀려던 문제와는 결이 다르다.
정리하면 게이트웨이는 "공유 자원형 MCP"에는 강력하지만, "로컬 자원형 MCP"는 처음부터 다른 접근이 필요하다. 이 경계선을 알고 나니 "그건 게이트웨이에 등록하면 됩니다"와 "그건 게이트웨이로는 안 되는 종류입니다"를 구분해서 답할 수 있게 됐다. MCP 를 하나의 균일한 무언가로 보지 않게 된 것, 이것도 도입기의 소득이라면 소득이다.
마치며
정리하면 세 가지다.
- 조직 규모에서 AI 에이전트에 도구를 연결하는 문제는 개별 연동이 아니라 게이트웨이(Federation) 패턴으로 푸는 것이 맞았다. 도입 이후 "에이전트에서 xx 쓰고 싶어요"류의 요구사항은 대부분 "게이트웨이에 등록"으로 수렴했다.
- mcp-context-forge 는 처음부터 완성도가 높진 않았다. 하지만 팀/RBAC/토큰 위임 같은 뼈대가 전부 열려 있는 오픈소스였기 때문에, 부족한 부분은 fork 로 앞서가고 업스트림으로 돌려주는 사이클로 메꿀 수 있었다. 지금 다시 선택하라고 해도 같은 선택을 할 것 같다. (지금은 완성도가 많이 올라왔다고 생각한다)
- 표준(MCP, A2A) 위에 인프라를 세워두니, 그 위의 도구는 얼마든지 갈아탈 수 있게 되었다. 변화가 빠른 시기일수록 "무엇이 표준으로 남을 것인가"에 인프라를 거는 쪽이 안전했다.