MCP · TRANSPORT

전송 레이어

🚦 3가지 전송 방식 📐 아키텍처 비교 💻 설정 코드

MCP는 전송 레이어를 교체 가능하게 설계했습니다. stdio · HTTP+SSE · WebSocket 세 가지 방식의 구조, 적합한 시나리오, 구현 코드를 다룹니다.

전송 방식 비교

방식연결 위치방향스트리밍인증적합한 경우
stdio 로컬 (동일 머신) 양방향 (stdin/stdout) 지원 OS 권한 Claude Desktop, Cursor, 로컬 툴
HTTP+SSE 원격 (네트워크) 요청: HTTP POST
응답: SSE 스트림
지원 OAuth 2.1, API Key 공유 서버, 멀티 클라이언트
WebSocket 원격 (네트워크) 완전 양방향 지원 OAuth 2.1, API Key 저지연 실시간, 서버 푸시 필요

① stdio 전송

가장 단순한 방식. Host가 서버 프로세스를 자식 프로세스로 실행하고, stdin에 JSON-RPC 메시지를 쓰고 stdout에서 응답을 읽습니다. 네트워크 없이 로컬에서만 동작합니다.

MCP Host (Claude Desktop 등) stdin ← JSON-RPC 요청 stdout → JSON-RPC 응답 MCP Server (자식 프로세스) 동일 머신 · 네트워크 불필요 · stderr은 로그용 그림 1. stdio 전송 — stdin/stdout으로 JSON-RPC 메시지 교환
pythonstdio 서버 실행
# server.py
from mcp.server.fastmcp import FastMCP

mcp = FastMCP("my-server")

if __name__ == "__main__":
    # stdio가 기본값 — 아무 인수 없이 실행
    mcp.run(transport="stdio")

# Claude Desktop config (claude_desktop_config.json)
# {
#   "mcpServers": {
#     "my-server": {
#       "command": "python",
#       "args": ["/path/to/server.py"]
#     }
#   }
# }

② HTTP + SSE 전송

원격 서버에 연결할 때 사용합니다. 클라이언트 → 서버 메시지는 HTTP POST로, 서버 → 클라이언트 메시지(응답, 알림)는 Server-Sent Events(SSE) 스트림으로 전달합니다.

MCP Client HTTP 클라이언트 GET /sse (초기 연결) POST /messages (요청) data: ... (SSE 스트림 응답) MCP Server GET /sse POST /messages 🌐 네트워크 (인터넷/인트라넷) 그림 2. HTTP+SSE 전송 — 요청은 POST, 응답/알림은 SSE 스트림
pythonSSE 서버 실행 (FastMCP)
from mcp.server.fastmcp import FastMCP

mcp = FastMCP("remote-server")

# @mcp.tool(), @mcp.resource() 등록 ...

if __name__ == "__main__":
    mcp.run(
        transport="sse",
        host="0.0.0.0",
        port=8080,
    )

# Docker 배포 예시
# FROM python:3.11-slim
# RUN pip install "mcp[cli]"
# CMD ["python", "server.py"]
# EXPOSE 8080

③ WebSocket 전송 (2025 Draft)

완전한 양방향 실시간 통신이 필요한 경우에 적합합니다. 서버가 클라이언트에게 먼저 메시지를 보내는 시나리오(서버 주도 알림)에 유리합니다. 2025년 스펙에서 표준화 진행 중입니다.

pythonWebSocket 서버 (저수준 API)
from mcp.server import Server
from mcp.server.websocket import websocket_server
import uvicorn

server = Server("ws-server")

# Tool/Resource 핸들러 등록 (저수준 API)
@server.call_tool()
async def handle_tool(name, arguments):
    ...

# ASGI 앱으로 노출
app = websocket_server(server)

if __name__ == "__main__":
    uvicorn.run(app, host="0.0.0.0", port=8765)

전송 방식 선택 가이드

🗺️ 결정 트리
  • 로컬 머신에서만 실행?stdio
  • 원격 서버, 여러 클라이언트 공유?HTTP+SSE
  • 서버가 먼저 알림을 보내야 하거나 초저지연 필요?WebSocket
  • Claude Desktop / Cursor에 연결?stdio (현재 지원 표준)