본문으로 건너뛰기
yhc509

MCP를 직접 연결하지 마세요

·9 min read

최근 rider-debug라는 작은 도구를 만들었다. 실행 중인 Unity Editor에 붙은 Rider 디버거를, 에이전트가 세션을 새로 시작하지 않고도 직접 조작하게 해 주는 Claude Code 스킬 겸 CLI다. 브레이크포인트를 걸고, 변수를 까 보고, 실행을 한 줄씩 넘긴다.

그런데 이 글에서 하고 싶은 얘기는 도구의 기능이 아니다. 왜 이걸 MCP로 등록하지 않고 굳이 코드로 감쌌는가다.

나는 MCP를 잘 안 켠다

거의 병적으로 피한다. 이유는 단순하다. MCP를 여럿 켜 두면 tool 정의가 그만큼 context를 먹고, 한 번 꺼 둔 걸 세션 도중에 다시 켜려면 세션 자체를 새로 시작해야 한다. Claude Code와 Codex를 오래 굴리는 동안 이 두 가지가 계속 거슬렸다.

그래서 나는 웬만하면 MCP 대신 CLI를 만든다. 전에 Unity를 에이전트로 조작하려고 unity-cli-bridge를 만든 것도 같은 이유였다. CLI는 필요할 때만 부르면 되고, 출력도 내가 원하는 만큼만 흘려보낼 수 있다. 항상 떠 있으면서 context를 점유하는 MCP와는 부담의 성격이 다르다.

CLI를 만들고 싶었지만, Rider엔 CLI가 없었다

Rider의 디버거 기능(브레이크포인트, 변수 조회, 실행 제어)을 에이전트가 쓰게 하고 싶었는데, 이걸 CLI로 직접 두드릴 방법이 없었다. 있는 건 Debugger MCP Server라는 플러그인 하나뿐이었다. 디버거 엔진은 그게 전부 갖고 있었다.

그러니 선택은 둘 중 하나였다. MCP를 그냥 등록해서 쓰거나, 그 MCP를 내가 원하는 형태로 감싸거나. 앞에서 적은 이유로 등록은 내키지 않았다. 그래서 감쌌다.

등록하는 대신, 필요할 때 연결한다

감싼다는 게 구체적으로 뭐냐면, 디버거에 SSE + JSON-RPC로 필요할 때만 연결하는 작은 CLI를 두는 것이다. MCP로 등록하지 않으니 tool 정의가 세션 시작부터 context에 올라오지 않고, 디버깅이 필요해진 바로 그 순간 이미 돌아가던 세션 안에서 그대로 붙는다.

이게 디버깅이라는 작업에선 특히 컸다. 디버깅은 보통 "지금 이 맥락에서" 갑자기 필요해진다. 코드를 한참 들여다보다가 "여기 실제로 어떤 값이 들어오는지 확인해야겠다" 싶은 순간 말이다. 그때마다 세션을 새로 시작해야 한다면 그 맥락이 통째로 날아간다. on-demand 연결은 그 맥락을 지키려는 선택이었다.

감싸니까 통제권이 생긴다

얇은 래퍼지만, 감싸는 행위 자체가 핵심이었다. 감싸면 세 가지를 내 입맛대로 가져갈 수 있다.

  • 세션을 유지하면서 원할 때만 연결한다.
  • context에 tool 정의를 미리 올리지 않는다.
  • output을 원하는 수준으로 필터링한다.

세 번째는 만들고 기능 테스트를 돌리다가 실감했다. 실제 버그를 잡는 상황이 아니라 그냥 동작 확인이었는데도, 인스턴스 값 하나를 조회하면 필요 없는 값까지 우르르 쏟아졌다. 그대로 모델에 흘려보내면 디버깅 루프 한 바퀴에 context가 훅 빠진다. 그래서 CLI 레이어에서 변수 덤프를 걸러 내고(get_vars --simple --names=...), tool 카탈로그도 24시간 캐시해서 매번 다시 읽지 않게 했다. 필터링을 모델 앞단에서 끝내는 것이다.

나중에 보니, 이미 권장되던 패턴이었다

이 구조를 먼저 만들었다. 며칠 뒤 정리해서 저장소에 올리다가 문득 "이 구조가 의미가 있나?" 싶었다. Claude에게 물어봤더니 Anthropic이 이미 권장하는 방식이라며 글을 하나 찾아왔다. Code execution with MCP다.

글의 요지는 두 가지였다. MCP tool 정의를 전부 미리 올리면 요청을 처리하기도 전에 context가 수십만 토큰씩 차고, tool이 주고받는 중간 결과마저 매번 모델을 거치며 토큰을 중복으로 먹는다. 그러니 MCP를 코드 API로 노출해 에이전트가 코드를 짜서 호출하게 하고, 정의는 필요할 때만 불러오고(progressive disclosure), 데이터는 실행 환경에서 걸러 낸 뒤에야 모델에 넘기라는 것이다. 글에 실린 사례에선 같은 작업의 토큰이 15만에서 2천으로 줄었다. 98.7% 절감이다.

읽으면서 묘했던 건, 내가 한 on-demand 연결과 CLI 레이어 필터링이 각각 거기서 말하는 "정의를 미리 안 올리기"와 "중간 결과를 모델 앞에서 걸러 내기"에 그대로 맞아떨어졌다는 점이다. 나는 그 글의 존재를 모르는 채로 만들었다. 불편을 피하려고 내 방식대로 밀었을 뿐인데, 도착한 결론이 남이 best practice라고 부르던 지점과 같았다. 솔직히 만드는 내내 제일 기분 좋았던 부분이다.

마무리

그래서 rider-debug는 Debugger MCP Server 위에 올린 얇은 래퍼지만, 나한텐 그 "감싼다"가 전부였다. 컨텍스트를 어떻게 쓸지, 세션을 어떻게 유지할지, 출력을 어디까지 보여줄지를 내가 정할 수 있게 되니까.

덧붙이면, 이건 아직 개인 R&D다. 프로덕션에서 본격적으로 써 본 적은 없다. 정작 디버거를 붙이고 싶은 버그는 재현이 어렵고, 재현이 쉬운 버그는 코드나 로그만 봐도 정리되기 때문이다.

사이드 프로젝트에서 기회가 오면 제대로 써 볼 생각이다. 쓴다면 모든 디버깅을 AI에 맡기는 그림은 아닐 것 같다. 내가 이슈를 추적하다 브레이크포인트를 걸어 둔 지점에서, Claude나 Codex와 함께 상황을 파악하는 쪽에 가깝지 않을까. 애초에 만든 동기 자체가, 에이전트가 코드를 읽고 추측만 하는 게 아니라 살아 있는 프로세스를 직접 짚어 가며 가설을 증명하게 만들 수 있을까였다. 그 질문은 아직 열려 있다. 더 나가면, 토큰 비용이 더 낮아지고 AI가 테스트하기 좋은 프로젝트(게임이라면 실시간보다 턴제 쪽 장르)를 다루게 됐을 때, unity-cli-bridge와 묶어 디버깅 자동화 시스템까지 짜 보는 망상도 가끔 한다.