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 전송 방식이 완벽합니다. 클라이언트와 서버가 서로 다른 머신에서 실행되어야 하는 프로덕션 배포의 경우, 각각의 장단점을 가진 다른 전송 방식 옵션을 고려해야 합니다.