2026년 4월 23일 | 개발 공부
MCP는 Model Context Protocol의 약자고, AI 애플리케이션이 외부 도구와 데이터 소스에 표준 방식으로 붙기 위한 프로토콜이다. 문서를 다시 읽으면서 내가 제일 크게 정리한 건 로컬 MCP 서버를 subprocess로 붙이는 일과 리모트 MCP 서버를 HTTP 서비스로 붙이는 일이 생각보다 훨씬 다른 문제라는 점이었다. 겉으로는 둘 다 JSON-RPC 메시지를 주고받는 구조지만, 실제로 갈리는 건 단순한 전송 방식이 아니라 연결의 주체, 세션의 수명, 보안 경계였다.
처음에는 나도 이걸 transport만 갈아끼우는 선택지 정도로 봤다. 그런데 MCP architecture overview, transport spec, local/remote 연결 가이드를 같이 놓고 보니, 기본 단위가 단순히 server 하나가 아니라 host-client-server 삼단 분리라는 점이 먼저 보였다. 이 틀이 머리에 들어오고 나니 왜 로컬 쪽에서는 stdio가 기본값처럼 취급되고, 리모트 쪽에서는 session, reconnect, Origin 검증, third-party trust 이야기가 더 앞에 나오는지도 한 번에 정리됐다.
Figure 1. 같은 MCP라도 왼쪽은 subprocess와 표준 입출력 계약에 가깝고, 오른쪽은 공유 서비스와 세션·인증 계약에 더 가깝다.
1. MCP를 읽을 때 먼저 나눠야 하는 건 host, client, server였다
MCP 문서가 반복해서 강조하는 건 MCP Host, MCP Client, MCP Server 세 층이다. 여기서 host는 Claude Code나 Claude Desktop 같은 AI 애플리케이션이고, client는 특정 서버와의 연결을 유지하는 컴포넌트다. 그리고 server는 tools, resources, prompts를 노출하는 프로그램이다. 즉 내가 MCP 서버 하나를 붙인다고 말할 때도, 실제로는 host가 그 서버마다 별도 client를 하나씩 만든다는 구조를 먼저 이해해야 한다.
이 구분이 중요한 이유는 서버를 어디에 두느냐보다 연결을 누가 어떻게 유지하느냐가 달라지기 때문이다. 문서에도 local MCP 서버는 보통 stdio transport로 단일 client를 상대하고, remote MCP 서버는 Streamable HTTP로 여러 client를 상대한다고 적혀 있다. 나는 이 문장을 보고 나서야, 로컬 파일시스템 서버와 인터넷 위의 SaaS형 MCP 서버를 같은 mental model로 잡으면 계속 헷갈릴 수밖에 없겠다는 생각이 들었다.
- Host: 여러 MCP client를 조정하는 상위 앱
- Client: 특정 MCP server와의 dedicated connection을 유지하는 연결 단위
- Server: 도구, 리소스, 프롬프트를 실제로 제공하는 프로그램 또는 서비스
이 삼단 구조를 먼저 잡아 두면, transport를 고를 때도 "내 서버는 어떤 기능을 제공하나"보다 이 연결이 단일 사용자 로컬 실행인지, 여러 사용자가 붙는 네트워크 서비스인지를 먼저 묻게 된다. 내게는 이 순서 변화가 생각보다 컸다.
2. stdio는 단순해서 좋은 게 아니라, subprocess 계약이 분명해서 좋았다
transport spec의 stdio 섹션은 의외로 아주 구체적이다. client가 server를 subprocess로 띄우고, server는 stdin에서 JSON-RPC 메시지를 읽고 stdout으로 응답을 쓴다. 메시지는 줄바꿈 단위로 구분되고, stdout에는 MCP 메시지가 아닌 걸 쓰면 안 된다. 반대로 stderr는 로그 용도로 써도 되며, client는 그 출력을 오류라고 단정하면 안 된다고까지 적혀 있다.
나는 이 부분이 꽤 마음에 들었다. 로컬 서버를 붙일 때 실제로 자주 만나는 문제는 네트워크보다 프로세스 실행, 표준 입출력 오염, 환경 변수, 실행 파일 경로 같은 데서 먼저 난다. stdio transport는 그 경계를 아주 솔직하게 드러낸다. 서버를 내 머신에서 띄우고, host가 그 프로세스와 직접 이야기하는 구조라서, 복잡한 인증이나 멀티클라이언트 상태보다 실행 가능성과 I/O 규율이 더 중요하다.
spec에서 clients should support stdio whenever possible라고 적어 둔 것도 같은 맥락으로 읽혔다. 로컬 도구를 붙이는 가장 낮은 마찰 경로가 여전히 stdio이기 때문이다. 파일시스템, 로컬 DB, 사내 개발용 스크립트처럼 같은 머신, 같은 사용자 문맥에서 쓰는 서버라면, 굳이 네트워크 계층을 하나 더 끼우지 않는 편이 오히려 더 자연스럽다.
local MCP
host -> client -> subprocess
stdin / stdout
stderr for logs
3. Streamable HTTP는 transport가 아니라 shared service 운영으로 넘어가는 지점에 가깝다
반대로 Streamable HTTP는 같은 JSON-RPC라도 느낌이 완전히 다르다. spec을 보면 서버는 이제 독립 프로세스로 떠 있고, 여러 client 연결을 처리할 수 있는 서비스가 된다. 통신은 HTTP POST와 GET를 쓰고, 필요하면 SSE로 여러 서버 메시지를 스트리밍할 수도 있다. 예전 HTTP+SSE transport를 이 경로가 대체했다는 점도 같이 적혀 있는데, 이걸 보면 리모트 쪽은 이미 단순 요청-응답보다 세션과 스트리밍을 포함한 서비스 표면으로 발전하고 있다는 걸 알 수 있다.
여기서 내가 가장 크게 본 차이는 세션이었다. Streamable HTTP 서버는 초기화 응답에서 MCP-Session-Id를 줄 수 있고, 그 세션이 끝나면 이후 요청에 404를 돌릴 수 있다. 클라이언트는 그 신호를 받으면 새 InitializeRequest로 세션을 다시 시작해야 한다. 즉 원격 MCP는 단순히 "서버가 인터넷 어딘가에 있다"는 정도가 아니라, 세션 수명과 재초기화 전략을 같이 가진다는 뜻이다.
이 지점부터는 local stdio와 완전히 다른 고민이 생긴다. 로컬에서는 프로세스를 다시 띄우면 끝나는 경우가 많지만, 리모트에서는 어떤 세션이 살아 있는지, 끊긴 스트림을 어디까지 재개할지, 여러 client가 동시에 붙을 때 서버 상태를 어떻게 볼지가 먼저 문제가 된다. 나는 그래서 Streamable HTTP를 볼 때 더 이상 "stdio의 네트워크 버전"으로 읽지 않게 됐다.
| 질문 | stdio | Streamable HTTP |
|---|---|---|
| 서버의 위치 | host가 직접 띄우는 로컬 subprocess | 독립적으로 떠 있는 네트워크 서비스 |
| 주요 관심사 | 실행 경로, stdin/stdout 규율, 환경 변수 | 세션, 인증, reconnect, 멀티클라이언트 처리 |
| 메시지 흐름 | newline-delimited JSON-RPC | HTTP POST/GET + optional SSE |
| 실패 후 재개 | 프로세스 재기동 관점이 큼 | 세션 만료, 404, 재초기화 루프가 중요 |
4. local MCP와 remote MCP는 보안 질문도 다르게 던진다
local connection 가이드는 MCP 서버를 secure, controlled access to local resources and tools라고 설명한다. Claude Desktop 예시에서도 핵심은 로컬 파일을 읽고, 폴더를 만들고, 사용자가 명시적으로 승인하는 흐름이다. 여기서는 trust boundary가 비교적 선명하다. 내 머신 안에서 내가 설치한 서버를 띄우고, host가 그 서버에 붙는다. 물론 위험 명령이나 권한 범위는 따져야 하지만, 문제의 출발점은 보통 내가 띄운 프로세스를 어디까지 믿을 것인가다.
remote 가이드와 Anthropic 문서는 질문이 다르다. remote MCP는 internet-hosted tools, services, and data sources에 연결하는 방식이고, 접근성의 장점 때문에 여러 클라이언트에서 쉽게 재사용할 수 있다. 대신 Anthropic 문서도 third-party remote MCP servers are not owned, operated, or endorsed by Anthropic라고 분명히 적는다. 즉 이 레이어에 들어가면 이미 기능보다 먼저 누가 운영하는 서버인지, 어떤 인증을 요구하는지, 어떤 보안 관행을 갖췄는지를 봐야 한다.
transport spec의 보안 경고도 같은 방향이다. Streamable HTTP를 구현할 때는 Origin header 검증이 필요하고, 로컬에서 띄우더라도 가능하면 localhost에만 bind하고, 모든 연결에 적절한 authentication을 붙이라고 적혀 있다. 이 문장을 보고 나면 remote MCP를 단순한 편의 기능으로만 보기가 어렵다. remote로 간 순간부터는 protocol이 아니라 웹 서비스 운영의 문법이 같이 들어온다.
5. 그래서 내가 먼저 묻게 된 건 "어느 transport가 더 좋나"가 아니었다
예전에는 MCP 서버를 볼 때 어떤 SDK를 썼는지, 어떤 툴을 노출하는지가 먼저 눈에 들어왔다. 지금은 질문 순서가 바뀌었다. 이 서버는 단일 사용자 로컬 워크플로를 위한가, 여러 클라이언트가 붙는 공유 서비스를 위한가. 이걸 먼저 나누고 나면 बाकी 판단이 훨씬 빨라진다.
- 로컬 파일, 로컬 스크립트, 내 머신 안 도구가 핵심이면 stdio 쪽이 더 자연스럽다.
- 웹 앱, 팀 공유 서비스, 인터넷 호스팅 도구가 핵심이면 Streamable HTTP가 더 맞다.
- 리모트가 항상 상위 버전은 아니다. 오히려 로컬 문제를 remote로 밀어 올리면 보안과 운영 복잡도만 커질 수도 있다.
이 관점은 스펙을 읽는 순서도 바꿔 준다. transport 문서를 볼 때 이제는 "HTTP냐 아니냐"보다 single-client subprocess contract와 multi-client service contract의 차이를 먼저 본다. 같은 MCP라도 어느 쪽 경계를 택하느냐에 따라, 디버깅 포인트와 장애 모드와 사용자 신뢰 모델이 통째로 바뀌기 때문이다.
나한테 남은 한 줄 요약은 이거다. MCP에서 stdio와 Streamable HTTP는 같은 기능을 다른 케이블로 연결하는 선택지가 아니라, 서로 다른 운영 경계를 고르는 일에 가깝다. 이걸 먼저 받아들이고 나면 local server를 붙이는 날과 remote connector를 설계하는 날의 체크리스트를 굳이 같은 종이에 적지 않게 된다.
원문은 MCP architecture overview, MCP transports spec, local server guide, remote server guide, Anthropic remote MCP docs를 같이 보면 맥락이 잘 이어진다.
'[개발 공부]' 카테고리의 다른 글
| MLA KV Cache, 긴 문맥에서 먼저 줄어드는 병목 (0) | 2026.04.28 |
|---|---|
| GraphSAGE feature importance, 단독 랭킹보다 조합으로 읽기 (1) | 2026.04.23 |
| Query Coverage, 질문 완성도를 따로 보는 레이어 (0) | 2026.04.21 |
| Quality Gate, PASS/FAIL 사이에 WARN을 두는 방식 (0) | 2026.04.20 |
| Reranker를 검색 마지막 미세조정보다 순서 복구 단계로 이해하게 된 이유 (0) | 2026.04.06 |