서버도 클라이언트에게 먼저 말을 걸 수 있다? MCP는 양방향 프로토콜이며, 단방향 이해는 모두 잘못되었다
요약
본 자료는 Anthropic Academy의 교육 내용으로, Model Context Protocol(MCP)가 클라이언트 요청에 대한 단방향 응답 방식이 아님을 강조합니다. MCP는 서버 역시 능동적으로 통신을 시작할 수 있는 양방향 프로토콜입니다. 따라서 메시지 유형과 발신자에 따라 처리 방식을 명확히 구분하는 것이 중요합니다.
핵심 포인트
- MCP는 클라이언트 요청/응답에 국한되지 않는 양방향 프로토콜이다.
- 메시지는 JSON 기반이며, 응답이 필요한 '요청-결과'와 단방향 '알림'으로 구분된다.
- 서버도 능동적으로 요청이나 알림을 보내므로 클라이언트는 이를 수신해야 한다.
- 응답이 필요 없는 경우(알림, 브로드캐스트)를 명확히 인지하고 구현해야 한다.
서버도 클라이언트에게 먼저 말을 걸 수 있다? MCP는 양방향 프로토콜이며, 단방향 이해는 모두 잘못되었다
이 글은 Anthropic Academy의 《Model Context Protocol: Advanced Topics》 제 9과 실습 교육 자료입니다. 원제 직역은 《JSON message types(JSON 메시지 유형)》입니다. 결론부터 말씀드리겠습니다. 이 내용은 상식에 반합니다.
MCP는 클라이언트가 질문하고 서버가 답변하는 단방향 프로토콜이 아닙니다. 서버 역시 능동적으로 통신을 시작할 수 있습니다. 이러한 양방향 특성은 전송 방식을 선택할 때 매우 중요하며, 일부 전송 방식은 메시지의 흐름(direction)을 제한합니다.
먼저 용어를 명확히 하겠습니다. **JSON 메시지 (JSON message)**는 MCP 통신의 모든 매개체입니다. 클라이언트와 서버가 주고받는 모든 말은 JSON 메시지 한 줄로 이루어집니다. **요청-결과 (request-result)**는 쌍으로 존재하는 메시지입니다. 요청 하나에는 반드시 그에 대응하는 결과가 있습니다. **알림 (notification)**은 단방향 메시지로, 보내면 끝이며 응답이 필요 없습니다. **전송 (transport)**은 메시지가 전달되는 통로를 의미하며, 예를 들어 STDIO나 StreamableHTTP 등이 있습니다.
실제 예시를 들자면, Claude가 서버의 도구를 호출할 때, 클라이언트는 'Call Tool Request'라는 메시지를 보내고, 서버는 처리 후 'Call Tool Result'라는 메시지를 돌려줍니다. 질문과 답변처럼 보여 단방향인 것처럼 보이지만, 서버 역시 역으로 요청이나 알림을 보낼 수 있으며, 클라이언트도 이를 받아들이고 응답해야 합니다.
그들은 사람들이 자주 착각하는 '익숙한 구식 경로'를 지적했습니다. 바로 MCP를 일반 API로 오해하여, 마치 클라이언트만 요청을 보내고 서버는 응답만 할 수 있다고 생각하는 것입니다. 공식적인 반박은 매우 명확합니다. 메시지 유형은 발신자에 따라 나뉘며, 클라이언트 메시지와 서버 메시지는 두 세트입니다. 서버 메시지 안에는 클라이언트를 향한 요청도 있고, 광범위하게 전파되는 알림(broadcast notification)도 포함되어 있습니다.
모든 메시지에 응답이 필요한 것은 아닙니다. 다음 중 어느 하나만 충족해도 응답할 필요가 없습니다:
- 그것이 알림(notification)인 경우: 단방향으로 발송되며, 규약상 응답을 요구하지 않습니다.
- 그것이 브로드캐스트(broadcast)인 경우: 모든 사람에게 보내는 것이므로 일대일 질의응답이 아닙니다.
- 그것이 상태 변화인 경우: 예를 들어 도구 목록이 변경되었거나 리소스가 업데이트된 경우, 알려주는 것만으로 충분합니다.
1. 메시지를 단방향으로 오해하는 세 가지 실수
- 서버 요청을 받지 못함: 서버가 능동적으로 요청을 보낼 수 있다는 것을 모르고, 클라이언트 측에 서버 요청을 받는 코드를 작성하지 않은 경우입니다.
- 알림 응답 대기 시간 초과 (Timeout): 알림은 애초에 응답할 필요가 없는데도 불구하고, 강제로 응답을 기다리다가 프로세스가 멈추는 경우입니다.
- 잘못된 전송 방식 선택: 메시지 흐름의 방향을 제한하는 전송 방식을 선택하여, 서버가 시작한 요청이 전달되지 못하고 기능 구현에 문제가 생기는 경우입니다.
2. 대안: 두 가지 종류, 네 가지 방향으로 구분하기 (코딩 전에 분리)
두 가지 주요 카테고리는 다음과 같이 나뉩니다: 요청-결과 쌍(Call Tool, List Prompts, Read Resource, Initialize 등)은 각각 대응하는 결과가 있고; 알림(Progress, Logging Message, Tool List Changed, Resource Updated 등)은 단방향이며 응답이 필요 없습니다. 발신자 기준으로도 두 세트로 나뉩니다: 클라이언트 메시지는 서버로 가는 요청과 알림이고; 서버 메시지는 클라이언트로 가는 요청과 브로드캐스트 알림입니다. 코딩을 시작하기 전에 이 메시지가 어떤 종류에 속하며, 어느 방향인지 먼저 구분한 다음, 응답이 필요한지, 어떻게 응답할지를 결정해야 합니다.
3. 목표 형태: 머릿속에 메시지 유형표를 새기기
이상적인 상태는 개발자가 메시지를 보는 순간 그것의 종류와 방향을 즉시 말할 수 있는 것입니다: 요청인지 알림인지, 클라이언트가 보낸 것인지 서버가 보낸 것인지, 응답이 필요한지 여부까지요. 전체 메시지 유형 목록은 MCP 공식 규약 저장소에 있으며, 각 언어 SDK 저장소와 분리되어 권위적인 출처입니다. TypeScript로 작성된 것은 단지 데이터 구조를 명확하게 설명하기 위한 것이며, 실제로 TypeScript 코드를 실행하는 것은 아닙니다.
4. 전환을 위한 세 단계: 단방향 이해에서 양방향 이해로
첫 번째 단계: 규약 저장소의 메시지 목록 확인
SDK 예제만 보지 말고, 규약 저장소의 목록이 가장 완벽합니다. 사용하고 있는 모든 메시지가 어떤 카테고리에 속하는지 먼저 점검해야 합니다.
두 번째 단계: 서버 메시지 처리 로직 추가
클라이언트는 요청을 보내는 것 외에도, 서버가 보낸 요청과 알림을 수신할 수 있어야 합니다. 이 부분이 빠지면 양방향이라는 개념 자체가 거짓이 됩니다.
세 번째 단계: 전송 방식 선택 전에 흐름 확인
전송 방식을 고를 때 항상 한 가지 질문을 던져야 합니다: 서버가 시작한 메시지가 이 전송 방식으로 통과할 수 있는가? 만약 안 된다면, 기능 구현에 제약이 생깁니다.
5. 팀원들이 동일한 메시지 이해 구조를 구축하는 방법
메시지 이해의 가장 큰 함정은 기술 자체가 아니라 양측 간의 이해 불일치입니다: 서버는 알림에는 응답해야 한다고 생각하고, 클라이언트는 요청에는 응답할 필요가 없다고 생각하는 식입니다. 팀은 다음 세 가지를 먼저 합의해야 합니다: 메시지 분류는 규약 저장소를 기준으로 삼아 감으로 처리하지 않기; 알림은 무조건 응답하지 않도록 코드를 작성하기; 새로운 메시지 유형이 도입되기 전에 반드시 카테고리와 방향을 명시하고, 표시할 수 없으면 도입하지 않기.
6. 공식 도구 키트 외에 기억해야 할 것들
이번 강의에서는 완벽한 도구 상자를 제공하지 않았지만, 경계는 매우 명확하게 설명했습니다: 메시지 유형을 이해하는 것은 다양한 전송 방식을 사용하는 데 특히 중요합니다. 뒤따르는 StreamableHTTP의 제약 사항들은 모두 이 메시지 흐름(message flow)에서 근본적으로 발생합니다. 메시지 유형 구분이 안 되면, 전송 방식 선택은 그저 추측에 불과합니다.
결론: 원칙이 구조보다 중요하다
프로토콜은 파이프라인이 아니라 대화입니다. 원칙이 구조보다 중요합니다. 누가 말할 수 있는지, 누가 응답해야 하는지를 먼저 구분한 후에 전송 방식을 논의해야 합니다. 순서가 바뀌면 전부 단방향 파이프라인만 구축하게 됩니다.
반복되는 동작 규칙은 다음과 같습니다: 다음에 MCP 메시지를 처리할 때는 세 가지 질문을 던지십시오—요청인가 알림인가, 누가 보냈는가, 응답이 필요한가—이 세 가지에 답할 수 있을 때 비로소 코딩을 시작해야 합니다.
원본 링크: https://t.co/mLgqaNj0oC
원문: Anthropic Academy (S1哥 번역)
S1哥의 관점
이번 강의에서 가장 핵심적인 부분은, 많은 사람들이 가지고 있던 '클라이언트가 질문하고 서버가 답변한다'는 환상을 완전히 깨뜨린 것입니다. 과거에는 모두 MCP를 일반 API처럼 사용했지만, 이제 공식적으로는 서버 역시 능동적으로 사용자에게 말을 걸 수 있다고 합니다. 진정으로 값비싼 것은 서버 요청을 처리하는 몇 줄의 코드를 추가하는 것이 아니라, 단방향 이해에 기반하여 전체 시스템을 구축했다가 실제로 서비스에 적용했을 때 서버 발신 기능들이 모두 작동하지 않는다는 사실을 깨닫는 것입니다.
저는 이 입장에 서 있습니다: 프로토콜은 반드시 양방향(bidirectional)으로 이해해야 합니다. 클라이언트 요청만 처리하고 서버 요청은 받지 못하는 클라이언트는 '모르겠다'고 말하는 것보다 더 위험합니다. 전자는 사용자에게 '이미 연결되었다'는 착각을 심어주며 서비스를 시작하게 만들지만, 후자는 적어도 멈춰서 생각할 시간을 벌어줍니다.
그래서 질문 하나를 드리겠습니다: 현재 사용하고 계신 클라이언트 코드 중 서버 요청 처리를 위한 코드가 아예 작성되지 않은 부분이 몇 군데나 있습니까? 댓글로 숫자를 알려주세요. 솔직하게 말해봅시다.
AI 자동 생성 콘텐츠
본 콘텐츠는 X Claude/Anthropic의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기