MCP 클라이언트와 서버는 JSON 메시지를 주고받으며 통신하지만, 이러한 메시지는 실제로 어떻게 전송될까요? 이때 사용되는 통신 채널을 전송 방식(transport)이라고 하며, 이를 구현하는 방법에는 여러 가지가 있습니다 - HTTP 요청부터 WebSocket, 심지어 엽서에 JSON을 적는 방법까지 있습니다(다만 마지막 방법은 프로덕션 환경에서 사용하는 것을 권장하지 않습니다).
Stdio 전송 방식
MCP 서버나 클라이언트를 처음 개발할 때 가장 흔히 사용되는 전송 방식은 stdio 전송 방식입니다. 이 방식은 간단합니다: 클라이언트가 MCP 서버를 서브프로세스로 실행하고, 표준 입력 및 출력 스트림을 통해 통신합니다.

작동 방식은 다음과 같습니다:
- 클라이언트는 서버의
stdin을 사용하여 서버로 메시지를 보냅니다 - 서버는
stdout에 씀으로써 응답합니다 - 서버나 클라이언트 모두 언제든지 메시지를 보낼 수 있습니다
- 클라이언트와 서버가 같은 머신에서 실행될 때만 작동합니다
실제로 Stdio 동작 확인하기
별도의 클라이언트를 작성하지 않고도 터미널에서 직접 MCP 서버를 테스트할 수 있습니다. uv run server.py로 서버를 실행하면 서버는 stdin을 수신 대기하고 stdout에 응답을 씁니다. 즉, JSON 메시지를 터미널에 직접 붙여넣어 서버의 응답을 즉시 확인할 수 있습니다.
터미널 출력에는 초기화 및 도구 호출에 대한 예시 메시지를 포함하여 전체 메시지 교환 과정이 표시됩니다.
MCP 연결 시퀀스
모든 MCP 연결은 특정한 세 단계의 메시지 핸드셰이크로 시작해야 합니다:

- Initialize Request - 클라이언트가 먼저 이를 보냅니다
- Initialize Result - 서버가 기능(capabilities)으로 응답합니다
- Initialized Notification - 클라이언트가 확인합니다(응답은 예상되지 않음)
이 핸드셰이크가 끝난 후에만 도구 호출이나 프롬프트 목록 조회 같은 다른 요청을 보낼 수 있습니다.
메시지 유형과 흐름
MCP는 양방향으로 흐르는 다양한 메시지 유형을 지원합니다:

핵심은 일부 메시지는 응답이 필요하지만(요청 → 결과) 다른 메시지는 그렇지 않다는 점입니다(알림). 클라이언트와 서버 모두 언제든지 통신을 시작할 수 있습니다.
네 가지 통신 시나리오
어떤 전송 방식을 사용하든, 네 가지 서로 다른 통신 패턴을 처리해야 합니다:

- 클라이언트 → 서버 요청: 클라이언트가 stdin에 씁니다
- 서버 → 클라이언트 응답: 서버가 stdout에 씁니다
- 서버 → 클라이언트 요청: 서버가 stdout에 씁니다
- 클라이언트 → 서버 응답: 클라이언트가 stdin에 씁니다
stdio 전송 방식의 묘미는 그 단순함에 있습니다 - 이 두 채널을 사용하면 어느 쪽이든 언제든지 통신을 시작할 수 있습니다.
왜 중요한가
stdio 전송 방식을 이해하는 것이 중요한 이유는, 이것이 양방향 통신이 매끄럽게 이루어지는 "이상적인" 경우를 나타내기 때문입니다. HTTP와 같은 다른 전송 방식으로 넘어가면, 서버가 항상 클라이언트로 요청을 시작할 수는 없는 제약을 마주하게 됩니다. stdio 전송 방식은 다른 전송 방식의 제약을 다루기 전에, 완전한 MCP 통신이 어떤 모습인지 이해하기 위한 기준선 역할을 합니다.
개발 및 테스트 용도로는 stdio 전송 방식이 완벽합니다. 클라이언트와 서버가 서로 다른 머신에서 실행되어야 하는 프로덕션 배포 환경에서는, 각기 다른 장단점을 가진 다른 전송 방식 옵션을 고려해야 합니다.