초보자를 위한 Stateless MCP 가이드
요약
Model Context Protocol(MCP)이 세션 기반에서 Stateless(무상태) 방식으로 개정되었습니다. 이번 변화를 통해 서버 확장성이 향상되고 로드 밸런싱이 용이해졌으며, 클라이언트가 상태를 제어하는 방식이 변경되었습니다.
핵심 포인트
- MCP가 프로토콜 수준의 세션을 제거하고 Stateless 방식으로 전환됨
- 로드 밸런서 뒤에서 서버를 확장하기가 더 쉬워짐
- 스티키 라우팅이나 공유 세션 저장소 없이도 운영 가능
- 클라이언트가 도구 정의를 캐싱하고 리소스 제어권을 더 많이 가짐
MCP가 막 Stateless(무상태)가 되었다는 뉴스를 어디에서나 접하고 있지만, 저는 그것이 무엇을 의미하는지 전혀 알지 못했습니다. 그래서 직접 파헤쳐 보고 이에 대한 블로그 포스트를 작성하기로 했습니다.
초보자를 위한 Stateless MCP
Model Context Protocol (MCP)는 AI 어시스턴트를 도구, 데이터베이스 및 외부 애플리케이션에 연결합니다. 2026-07-28 사양 개정에서, MCP는 프로토콜 수준의 세션(Sessions)을 제거하고 Stateless(무상태)가 되었습니다.
이러한 변화는 원격 MCP 서버를 운영하기 더 쉽게 만듭니다. 서버는 스티키 라우팅(Sticky routing)이나 공유된 MCP 세션 저장소 없이도 일반적인 로드 밸런서(Load balancers) 뒤에서 확장(Scale)할 수 있습니다. 클라이언트는 도구 정의(Tool definitions)를 안전하게 캐싱할 수 있으며, 에이전트는 어떤 애플리케이션 리소스를 공유할지에 대해 더 많은 제어권을 갖게 됩니다.
하지만 Stateless가 MCP 서버가 더 이상 아무것도 기억할 수 없음을 의미하지는 않습니다. 브라우저는 여전히 열려 있는 탭을 가질 수 있고, 데이터베이스 트랜잭션(Database transaction)은 여전히 커밋되지 않은 변경 사항을 가질 수 있으며, 쇼핑 카트는 여전히 아이템을 담고 있을 수 있습니다. 차이점은 클라이언트가 해당 상태(State)를 참조하는 방식에 있습니다.
MCP 세션이 문제가 된 이유
상태(State)는 시스템이 요청(Requests) 사이에 기억하는 정보입니다. 웹 브라우저를 제어하는 MCP 서버를 상상해 보세요. 에이전트가 open_browser를 호출한 다음, navigate, click, take_screenshot을 호출할 수 있습니다. 서버는 이 네 가지 동작이 모두 동일한 브라우저를 참조한다는 것을 알아야 합니다.
이전 MCP 버전에서는 연결(Connection)이 해당 컨텍스트(Context)를 제공할 수 있었습니다. 클라이언트는 initialize 요청으로 시작했습니다. Streamable HTTP 환경에서 서버는 Mcp-Session-Id로 응답할 수 있었고, 클라이언트는 이를 이후의 요청에 첨부했습니다. 그러면 서버는 해당 ID를 사용하여 세션과 관련된 정보를 복구할 수 있었습니다.
이 방식은 하나의 클라이언트가 하나의 서버 프로세스와 통신할 때는 자연스럽게 작동했습니다. 하지만 MCP 서비스가 로드 밸런서 (load balancer) 뒤에 있는 여러 대의 머신에서 실행될 때는 상황이 더 복잡해졌습니다. 만약 서버 A가 세션을 생성했는데 다음 요청이 서버 B에 도달한다면, 서버 B는 해당 세션을 복구할 수 있는 어떤 방법이 필요했습니다. 인프라 팀은 일반적으로 클라이언트를 서버 A에 고정시키는 스티키 라우팅 (sticky routing)을 사용하거나, 모든 서버가 세션 조회를 위해 사용할 수 있는 공유 데이터베이스 (shared database)를 사용하여 이 문제를 해결했습니다.
또한 세션은 클라이언트마다 서로 다른 의미를 가졌습니다. 세션은 하나의 도구 호출 (tool call), 하나의 대화 (conversation), 하나의 페이지 로드 (page load), 또는 애플리케이션의 수명 동안 지속될 수 있었습니다. 서버 작성자는 세션 내부에 브라우저나 장바구니를 저장할 수 있었지만, 그 상태 (state)가 얼마나 오래 유지될지 또는 어떤 대화들이 이를 공유할지를 안정적으로 예측할 수는 없었습니다.
Stateless MCP에서 변경된 점
2026-07-28 개정판에 따라, initialize 및 notifications/initialized 핸드셰이크 (handshake)가 사라졌습니다. 서버는 더 이상 MCP 세션 ID를 발행하지 않으며, 클라이언트도 이를 저장하거나 다시 전송하지 않습니다.
대신, 모든 요청은 프로토콜 버전과 클라이언트 기능 (capabilities)을 포함하여, 요청을 이해하는 데 필요한 프로토콜 정보를 직접 담고 있습니다. 또한 클라이언트는 세션을 열지 않고도 server/discover를 호출하여 서버가 지원하는 버전과 기능을 알아낼 수 있습니다.
이는 호환 가능한 어떤 서버 인스턴스라도 이전 핸드셰이크로부터 정보를 복구할 필요 없이 들어오는 MCP 요청을 이해할 수 있음을 의미합니다. 만약 도구가 실행 중인 브라우저와 같이 애플리케이션 상태 (application state)를 관리한다면, 서비스는 여전히 해당 리소스를 찾을 수 있는 방법이 필요합니다. Stateless MCP는 프로토콜 세션 상태 (protocol session state)를 제거하는 것이지, 애플리케이션 상태 (application state)를 제거하는 것이 아닙니다.
애플리케이션 상태를 명시적으로 만들기
도구가 호출 간에 상태가 필요할 때, 서버는 흔히 핸들 (handle)이라고 불리는 명시적인 식별자를 반환할 수 있습니다. 핸들은 특별한 MCP 데이터 타입이 아닙니다. 이는 하나의 도구에 의해 반환되어 다른 도구로 전달되는 일반적인 값입니다.
예를 들어, 브라우저 서버는 다음과 같이 작동할 수 있습니다:
// 브라우저를 열고 핸들(handle)을 받습니다
open_browser()
// 반환값: { "browser_id": "browser_abc123" }
...
브라우저는 여전히 서버에 존재하며 탭, 쿠키, 히스토리를 유지합니다. 호출 간의 관계는 이제 연결(connection) 내부에 숨겨지는 대신 도구 인자(tool arguments)에 명시적으로 나타납니다.
명시적인 핸들(handle)은 오케스트레이터(orchestrator)에게 공유 상태(shared state)에 대한 더 많은 제어권을 부여합니다. 만약 세 명의 에이전트(agent)가 함께 쇼핑을 하고 있다면, 각자 별도의 browser_id를 받으면서도 하나의 cart_id를 공유할 수 있습니다. 단일 MCP 세션으로는 이러한 두 가지 상태 경계(state boundaries)를 깔끔하게 표현할 수 없었을 것입니다.
다단계 작업 처리 (Handling Multi-Step Work)
일부 도구는 작업을 완료하기 전에 더 많은 정보가 필요할 수 있습니다. 예를 들어, 배포 도구는 프로덕션(production)에 게시하기 전에 확인을 요청할 수 있습니다. 다회 왕복 요청 (Multi Round-Trip Requests (MRTR)) 패턴 하에서, 도구는 무엇이 필요한지 설명하는 input_required 결과를 반환합니다. 이 결과에는 불투명한(opaque) requestState 값이 포함될 수 있으며, 클라이언트는 원래의 호출을 재시도할 때 요청된 답변과 함께 이 값을 다시 반환합니다. 계속 진행하는 데 필요한 정보는 열려 있는 연결에 묶여 있는 대신, 재시도되는 요청과 함께 전달됩니다.
더 오래 실행되거나 지속적인 작업의 경우, 서버는 Tasks 확장 기능을 통해 작업 핸들(task handles)을 사용할 수 있습니다. 클라이언트는 네트워크 연결을 계속 열어두지 않고도 나중에 작업을 확인하거나 업데이트할 수 있습니다.
도구 목록 캐싱 용이성 확보 (Making Tool Lists Easier to Cache)
이전 MCP 버전에서는 tools/list에 의해 반환되는 도구들이 세션마다 다를 수 있었습니다. 서버가 먼저 connect_database 도구를 노출한 다음, 연결이 수립된 후에 query_database를 추가할 수도 있었습니다. 목록이 세션 기록에 따라 달라질 수 있었기 때문에, 클라이언트는 이를 다른 곳에서 안전하게 재사용할 수 없었습니다.
서버가 업데이트되거나 사용자의 권한이 변경될 때 도구 목록(Tool lists)은 여전히 변할 수 있지만, 더 이상 MCP 연결(connection)에 따라 달라지지는 않습니다. 서버는 ttlMs 및 cacheScope 값을 반환하여 클라이언트에게 결과가 얼마나 오랫동안 최신 상태로 유지되어야 하는지, 그리고 공유가 가능한지 여부를 알려줍니다. 이를 통해 오케스트레이터(orchestrator)는 서브 에이전트(subagents) 간에 도구 정의를 재사용할 수 있으며, 중복 요청을 줄이고 프롬프트 캐시(prompt-cache) 재사용을 개선할 수 있습니다.
MCP 서버 개발자가 변경해야 할 사항
MCP 서버를 구축하거나 유지 관리하는 경우:
- SDK 및 버전 구성 업데이트:
2026-07-28을 지원하는 SDK를 사용하고, 프레임워크에서 명시적인 옵트인(opt-in)을 요구하는 경우 새로운 리비전(revision)을 활성화하세요. - 세션 상태를 명시적 핸들(explicit handles)로 교체:
browser_id또는connection_id와 같은 식별자를 반환하고, 이후의 호출에서 이를 요구하도록 하세요. - 정리(cleanup) 계획 수립: 연결이 종료되는 것에 의존하는 대신, 임시 리소스에 만료 정책을 부여하거나 명시적인 정리 도구를 제공하세요.
- 모든 핸들(handle)에 대해 권한 부여: ID를 호출자가 해당 리소스에 접근할 수 있다는 증거가 아닌, 리소스의 이름으로 취급하세요.
- 부수 효과(side effects)를 재시도 가능하도록 안전하게 만들기: 카드 결제나 배포 트리거와 같은 작업에는 적절한 경우 멱등성 키(idempotency keys)를 사용하세요.
핵심 요약 (The Takeaway)
MCP는 단일 머신에서 실행되는 로컬 프로세스에 자연스럽게 작동하는 연결 지향적(connection-oriented) 모델로 시작되었습니다. 생태계가 클라우드 서비스, 호스팅된 게이트웨이, 멀티 에이전트 시스템(multi-agent systems)으로 확장됨에 따라 연결 수준의 상태(connection-level state)는 확장성의 병목 현상이 되었습니다.
2026-07-28 사양은 이러한 관심사들을 분리합니다. 프로토콜 세부 사항은 요청과 함께 전달되고, 애플리케이션 상태는 명시적 핸들을 사용하며, 대화형 워크플로(interactive workflows)는 연속 상태(continuation state)를 전달하고, 도구 정의는 깔끔하게 캐싱됩니다. MCP 서버는 여전히 기억해야 할 모든 것을 기억합니다. 단지 그 상태를 요청 내부에 직접 명시할 뿐입니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기