하나의 에이전트에 3개의 MCP 서버를 연결했더니 무서울 정도로 빨라졌다
요약
MCP(Model Context Protocol) 서버를 다수 연결할 때 발생하는 보안 및 거버넌스 부재 문제를 다룹니다. 에이전트의 권한 제어, 승인 절차, 감사 추적을 관리하기 위한 컨트롤 플레인과 UI 구축 과정을 설명합니다.
핵심 포인트
- MCP 서버 다중 연결 시 권한 관리 및 거버넌스 도구의 부재 확인
- API 게이트웨이나 커스텀 미들웨어의 한계점 분석
- 에이전트 제어를 위한 관리자 UI와 승인 프로세스의 중요성
- 보안을 위한 감사(Audit) 및 알림(Alerts) 기능의 필수성
지난주 터미널을 확인하다가 내 AI 에이전트가 README를 검색할 때와 똑같은 권한으로 프로덕션(production) 배포 권한까지 가지고 있다는 사실을 깨달았습니다. 정책도, 승인 절차도, 감사 추적(audit trail)도 없었습니다. 세 개의 MCP 서버가 직접 연결되어 나란히, 동일한 신뢰 수준으로 작동하고 있었습니다.
처음 두 개는 괜찮았습니다. 문서 검색(docs search)과 코드 검색(code search)이었고, 읽기 전용(Read-only)이었습니다. 하지만 세 번째 서버는 빌드를 프로덕션으로 승격(promote)시킬 수 있었습니다. 그때 깨달음이 왔습니다. MCP는 에이전트가 도구(tools)와 통신하는 방식을 표준화했지만, 두 개 이상의 서버를 연결한 이후에 발생하는 상황에 대해서는 아무런 대책도 마련해주지 않았습니다.
공식 MCP 서버 저장소(repo)는 88,900개의 스타(stars)와 11,300개의 포크(forks)를 보유하고 있습니다. Claude, ChatGPT, VS Code, Cursor — 이들은 모두 MCP를 사용합니다. 서버를 탐색하기 위한 레지스트리(registry)도 있습니다. 생태계는 급성장하고 있지만, 이 모든 것을 위한 거버넌스(governance) 도구는 단 하나도 찾을 수 없었습니다.
내가 처음 시도했던 것들
세 가지 경로를 시도했지만, 어느 것도 작동하지 않았습니다.
**API 게이트웨이(API gateways)**는 HTTP를 라우팅합니다. 하지만 MCP 도구 스키마(tool schemas)를 이해하지 못합니다. "이 에이전트는 deployment:promote는 사용할 수 있지만 deployment:rollback은 사용할 수 없다"와 같은 규칙을 작성할 수 없습니다. 게이트웨이는 그것들이 무엇인지 모르기 때문입니다.
**커스텀 미들웨어(Custom middleware)**는 이를 만든 사람이 떠나고 아무도 승인 체인(approval chain)이 어떻게 작동하는지 이해하지 못하게 될 때까지는 작동합니다. UI도 없습니다. 새로운 서버가 추가될 때마다 코드 변경이 필요합니다.
아무것도 하지 않음 — 그냥 서버를 직접 연결하고 에이전트 코드를 신뢰하는 방식입니다. 이것이 제 설정이었습니다. 서버가 두 개일 때는 괜찮게 느껴졌습니다. 하지만 세 개가 되자 "이 에이전트가 실제로 무엇을 할 수 있는가?"라는 질문에 답할 수 없게 되었습니다.
그래서 저는 만들기 시작했습니다.
로드맵은 3일째에 무너졌다
계획이 있었습니다. v0.1.0: API — 서버, 기능(capabilities), 정책(policies). v0.2.0: 관리자 UI(Admin UI) 및 승인. v0.3.0: 감사(audit), 알림(alerts), 신뢰 태세(trust posture).
그러다 UI가 없는 컨트롤 플레인(control plane)은 컨트롤 플레인이 아니라는 사실을 깨달았습니다. 어떤 서버가 등록되어 있는지, 어떤 승인이 대기 중인지 확인하기 위해 아무도 엔드포인트(endpoint)에 curl 명령어를 날리지 않습니다. UI는 원래 v0.2 기능이 될 예정이었습니다. 저는 그것을 v0.1.0으로 앞당겼습니다.
승인(approvals)에서도 같은 일이 일어났습니다. 그다음은 감사(audit), 그다음은 알림(alerts)이었습니다. 각 레이어는 이전 레이어에 의존하고 있었습니다. 이 중 하나라도 생략한다는 것은 "에이전트가 무엇을 하는지 보고, 제어하고, 감사한다"라는 핵심 약속을 저버리는 것을 의미했습니다. 제가 멈췄을 때쯤, v0.1.0은 v0.2와 v0.3의 대부분을 흡수해 버린 상태였습니다. 계획되었던 3개의 레이어가 8개가 되었습니다.
이것이 현명한 판단이었는지 아니면 고집이었는지는 모르겠습니다. 일정은 세 배로 늘어났습니다. 하지만 몇 달 뒤에나 의미가 생길 파편화된 기능들을 출시하는 것이 더 괴롭게 느껴졌습니다.
실제로 시간이 어디에 쓰였는가
백엔드(Backend)를 구축하는 데는 며칠이 걸렸습니다. FastAPI, PostgreSQL, Redis, OPA — 표준 스택(standard stack)을 사용했습니다.
몇 주를 잡아먹은 것들:
PRD(제품 요구 사항 문서)와 고객 사용자 여정(Customer User Journeys, CUJ). 단 하나의 컴포넌트를 작성하기 전에, 저는 모든 흐름을 매핑했습니다: 서버 등록, 승인 검토, 사고 감사. 실패하면 어떻게 될까? 빈 상태(Empty states), 에러 상태(error states), 권한 거부 상태(permission-denied states). 모든 CUJ는 API가 묻지 않았던 질문들에 대한 답을 강요했습니다. 저는 제가 과도하게 계획을 세우고 있는 것은 아닌지 계속 자문했습니다.
UI. 원래는 v0.2 기능이었습니다. 이를 v0.1.0으로 앞당긴다는 것은 내비게이션(navigation), 에러 경계(error boundaries), 신뢰 상태 컬러 코딩(trust posture color coding), 역할 기반 필터링(role-based filtering), 승인 패널(approval panel) 등 모든 화면을 엔드 투 엔드(end-to-end)로 구현해야 함을 의미했습니다. 그다음은 테스트였습니다. 68개의 Playwright E2E 시나리오를 작성했습니다. 백엔드 테스트는 몇 초 만에 끝났지만, UI 테스트는 영원처럼 느껴졌습니다.
CI(지속적 통합). Poetry v2에서 --no-update 옵션이 삭제되었습니다. npm 11은 TypeScript peer deps를 거부했습니다. 둘 다 제 코드와는 아무런 상관이 없는 문제였습니다. "이미 잘 작동하던" 인프라를 수정하는 데 하루를 허비했습니다.
Mypy. 41개 파일에 걸쳐 170개의 타입 에러(type errors)가 발생했습니다. 각각의 에러는 제가 소홀히 했던 설계 결정이 타입 위반이라는 가면을 쓰고 나타난 것이었습니다. 엄격 모드(Strict mode)는 제가 이를 숨기지 못하게 했습니다.
PyPI. 세 번의 시도가 있었습니다. mcp-fabric은 이미 선점되었습니다. mcpfabric도 막혔고, fabric-mcp도 막혔습니다. 네 번째 시도: mcp-fabric-toolmesh.
제가 계속 되새기는 숫자는 이것입니다: 41개 파일에 걸친 170개의 타입 에러. 그것은 버그가 아닙니다. 그것은 도구가 그냥 넘어가기를 거부함으로써 수면 위로 드러난, 명확하지 않은 사고(unclear thinking)의 결과였습니다.
결과물
MCP 서버를 등록하면 Fabric이 해당 도구들을 자동으로 탐색(auto-discover)합니다. 원시 도구 이름(Raw tool names)은 도메인 수준의 기능(capabilities)으로 정규화됩니다. 예를 들어, /promoteDeploy와 /promote는 모두 deployment:promote로 매핑됩니다. 모든 요청은 OPA 정책을 통과합니다. 민감한 기능은 사람의 승인을 요구할 수 있습니다. 모든 것은 로그로 기록됩니다. 서버 성능이 저하되면 경고(Alerts)가 발생합니다. 에이전트 클래스별로 ID 토큰(Identity tokens)이 부여됩니다. 권한 번들링을 위한 기능 팩(Capability packs)도 제공됩니다.
474개의 테스트. 6개의 병렬 CI 작업. PyPI에 배포 완료. 오픈 소스.
저는 제 개인적인 3개의 서버를 대상으로 이를 실행해 왔습니다. Fabric을 사용하기 전에는 모든 토큰이 모든 것에 접근할 수 있었습니다. 이제 개발자 에이전트(developer agent)는 문서와 코드를 검색합니다. 릴리스 엔지니어(release engineer)는 배포를 요청하지만, 승인을 위해 일시 중지됩니다. "코드를 신뢰하는 것"과 "모든 요청을 검증하는 것" 사이의 차이를 만드는 데는 단 30분의 설정 시간밖에 걸리지 않았습니다.
내가 다르게 했을 일들
API를 만든 후가 아니라, 첫날부터 PRD(제품 요구 사항 문서)와 CUJ(핵심 사용자 여정)를 정의했을 것입니다. 저는 백엔드를 먼저 구축한 다음, 그에 맞춰 UI를 설계했습니다. 거꾸로 한 셈입니다. CUJ 매핑을 통해 API가 놓쳤던 간극을 찾아낼 수 있었습니다.
UI는 v0.2 단계의 기능이 아닙니다. 만약 당신의 도구에 사람의 상호작용이 필요하다면, UI는 MVP(최소 기능 제품)입니다. curl을 위한 API와 사람을 위한 UI를 각각 설계했더니, 두 기능이 일치하지 않을 때 리팩터링(refactoring)을 해야 했습니다.
범위 통합(Scope consolidation)은 범위 확장(scope creep)이 아닙니다. 0.2와 0.3 버전을 0.1 버전에 묶어 넣은 것은 계획이 잘못된 것처럼 보였습니다. 지나고 보니 각 레이어는 이전 레이어에 의존하고 있었습니다. 진짜 실수는 원래의 로드맵이 이들이 독립적이라고 가정했던 점입니다.
도구 거버넌스(tool governance)가 MCP 상위의 레이어여야 하는지, 아니면 스펙 자체가 결국 흡수해야 할 요소인지에 대해서는 여전히 고민 중입니다. 저는 제 환경에 필요한 것을 만들었을 뿐이며, 이것이 일반화될 수 있을지는 모르겠습니다.
만약 MCP 서버를 운영 중이라면, 이 문제를 어떻게 처리하고 계신가요? 직접 연결하고 요행을 바라시나요? 미들웨어(middleware)를 작성하시나요? 아니면 생태계가 갖춰지길 기다리고 계시나요? 대부분의 답변은 "아직 처리하고 있지 않다"일 것이라고 생각하며, 이는 충분히 논의할 가치가 있는 주제라고 느낍니다.
GitHub: https://github.com/deghosal-2026/mcp-fabric
PyPI: https://pypi.org/project/mcp-fabric-toolmesh/
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기