mcp-server
MCP 서버 두 개, 제품 하나, 프로토콜 개정 두 번
동일한 쇼핑 카트를 두 번 구현했습니다. 한 번은 기존의 상태 저장형 MCP 스펙(2025-11-25)으로, 한 번은 새로운 무상태형 스펙(2026-07-28)으로. 두 서버를 나란히 실행하고 그중 하나가 무너지는 모습을 지켜보세요.
목표는 동작하는 코드가 아니라 스펙이 왜 바뀌었는지 이해하는 것입니다. 여기의 모든 실험은 실패가 분명하게 드러나고 그 이유가 통신선 위에서 보이도록 설계되었습니다.
MCP란 무엇인가? (세 문장)
MCP — Model Context Protocol — 은 AI 애플리케이션이 다른 누군가가 작성한 도구를 호출하기 위한 표준 방식입니다. 이는 N개의 애플리케이션 × M개의 통합을 N + M으로 대체하는데, Language Server Protocol이 모든 편집기가 자체 TypeScript 지원을 작성하던 것을 대체한 것과 같은 방식입니다. 구체적으로는 합의된 메서드 이름을 가진 JSON-RPC 2.0 메시지이며, stdio 또는 HTTP를 통해 전송됩니다.
너무 빠르게 지나갔다면 더 긴 버전을 읽어보세요: docs/01 — MCP가 존재하는 이유.
Related MCP server: Online Boutique AI Assistant MCP Server
이 저장소가 보여주는 것
다섯 가지 도구 — catalog_list, cart_create, cart_add_item, cart_view, cart_checkout — 는 두 서버에서 동일한 이름을 가지며, MCP를 전혀 모르는 공유 cart-core 패키지에 동일한 비즈니스 로직이 들어 있습니다. 두 서버의 유일한 차이는 프로토콜 계층이며, 이것이 바로 연구 대상입니다.
지켜볼 수 있는 네 가지:
server-old는 단순 라운드로빈 로드 밸런서 뒤에서 자체 핸드셰이크조차 완료하지 못합니다.server-new는 로드 밸런서가 존재하는지조차 인식하지 못합니다.대화 중에
server-old를 재시작하면 카트는 영구히 사라지며, 클라이언트가 복구를 위해 보낼 수 있는 요청은 없습니다."이 총액을 확인하시겠습니까?"라는 질문은
server-old에게 인간이 생각하는 전체 시간 동안 소켓을 붙잡아 두는 비용을 발생시킵니다(측정 결과 1522ms).server-new는 두 개의 독립적인 요청으로 처리하며 4ms + 15ms가 걸리고, 시작한 머신과 다른 머신에서 완료할 수 있습니다.안정적인 목록 정렬과 캐시 힌트 — 그리고 누락된
.sort()하나가 연간 약 $4,200의 가치가 있는 이유를 보여주는 계산.
서버는 의도적으로 프로토콜 코드를 공유하도록 리팩터링되지 않았습니다. 두 서버 사이에 의도적인 중복이 있어서 각각을 처음부터 끝까지 읽고 diff할 수 있습니다.
통신선을 반드시 확인하세요
두 서버 모두 모든 요청을 HTTP 수준에서 출력합니다: 메서드, 경로, 모든 MCP 헤더, JSON-RPC 메서드와 파라미터, 처리한 인스턴스, 그리고 resultType을 포함한 응답까지. SDK 추상화 뒤에 숨겨진 것은 없습니다. 실험이 실행되는 동안 한 가지만 읽는다면 색상이 입혀진 로그 줄을 읽으세요.
사전 요구 사항
Node.js 20 이상 (25.5에서 개발됨).
node -v로 확인하세요.ANSI 색상을 렌더링하는 터미널 — 로그가 이에 크게 의존합니다.
포트 3000–3002, 3011, 3012가 비어 있어야 합니다.
데이터베이스 없음, Docker 없음, 클라우드 계정 없음. 공유 상태는 JSON 파일입니다.
이 저장소에는 Python이 전혀 없습니다.
설치
git clone <this repo>
cd mcp-server
npm install
npm run typecheck # should print nothing and exit 0npm install은 두 세대의 MCP SDK를 동시에 포함하는 npm 워크스페이스를 설정합니다. 두 SDK는 패키지 이름이 다르므로 별칭 트릭 없이 공존합니다:
패키지 | 버전 | 사용처 |
| 1.30.0 |
|
| 2.0.0 |
|
네 가지 실험, 순서대로
각각은 하나의 명령입니다. 각각 자체 서버를 시작하고 종료합니다 — 두 번째 터미널이 필요 없습니다. 실행 후에 연결된 문서를 읽으세요. 각 문서는 방금 본 것과 그 이유를 설명합니다.
순서 | 명령 | 가르치는 내용 |
1 |
| 로드 밸런서 뒤의 두 인스턴스 — 기존 서버는 인사말조차 끝내지 못합니다. 새 서버는 아무렇지 않습니다. 여기서 시작하세요. |
2 |
| 대화 중 재시작 — 카트가 실제로 어디에 살았는지, 그리고 "그냥 Redis를 추가하면" 절반만 해결되는 이유. |
3 |
| 체크아웃 전 확인 — 1522ms의 붙잡힌 소켓 vs 두 개의 4ms 요청, 그리고 기존 방식이 서버리스에서 절대 실행될 수 없는 이유. |
4 |
| 캐시 힌트와 안정적인 정렬 — 스톱워치가 아닌 카운터로 캐시 적중을 증명하고, |
그런 다음 네 가지를 하나로 묶는 아키텍처 문서를 읽으세요:
01 — MCP가 존재하는 이유 — N×M 문제, 그리고 MCP가 무엇이고 무엇이 아닌지. MCP가 처음이라면 먼저 읽으세요.
02 — 기존 아키텍처 — 핸드셰이크, 세션 id, 그리고 모든 운영상의 문제점을 원인까지 추적.
03 — 새 아키텍처 — 핸들, MRTR, 캐시 힌트, 그리고 포기하는 것.
04 — 나란히 비교 — 릴리스의 모든 변경 사항, 이 저장소에서 찾을 수 있는 위치, 그리고 이 저장소가 다루지 않는 것에 대한 솔직한 목록.
직접 조작해보기
적어도 한 번은 해볼 가치가 있습니다. 속도를 직접 정하고 각 로그 줄이 나타날 때 읽을 수 있기 때문입니다.
# Old server (port 3001)
npm run old:server
npm run client -- --target old --scenario basic
npm run client -- --target old --scenario checkout
npm run client -- --target old --scenario checkout --decline
# New server (port 3002)
npm run new:server
npm run client -- --target new --scenario basic
npm run client -- --target new --scenario checkout
npm run client -- --target new --scenario discover # server/discover — new spec only두 인스턴스와 로드 밸런서를 수동으로:
PORT=3002 INSTANCE_ID=A npm run new:server
PORT=3012 INSTANCE_ID=B npm run new:server
PORT=3000 TARGETS=http://localhost:3002,http://localhost:3012 npm run lb
npm run client -- --target new --url http://localhost:3000/mcp --scenario basic두 서버 터미널을 지켜보세요: 동일한 카트 id가 각각이 처리한 요청에 나타나며, 어느 쪽도 신경 쓰지 않습니다.
curl로 찔러보기
새 스펙 요청이 얼마나 자기 서술적인지 가장 직접적으로 느낄 수 있는 방법입니다. npm run new:server를 시작한 다음:
# A complete, valid request — note how much has to be in it
curl -s -X POST http://localhost:3002/mcp \
-H 'content-type: application/json' \
-H 'accept: application/json, text/event-stream' \
-H 'mcp-protocol-version: 2026-07-28' \
-H 'mcp-method: tools/call' \
-H 'mcp-name: catalog_list' \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{
"name":"catalog_list","arguments":{},
"_meta":{"io.modelcontextprotocol/protocolVersion":"2026-07-28",
"io.modelcontextprotocol/clientInfo":{"name":"curl","version":"1"},
"io.modelcontextprotocol/clientCapabilities":{}}}}'이제 한 조각씩 깨뜨리면서 오류가 어떻게 변하는지 지켜보세요:
변경 | 기대 결과 |
|
|
|
|
|
|
|
|
헤더와 |
|
| 405 — GET 엔드포인트가 사라졌습니다 |
기존 서버에 동일한 작업을 하면 initialize를 먼저 하라는 메시지를 받게 됩니다.
저장소 구조
packages/
cart-core/ the actual product. zero MCP knowledge. shared by both servers.
server-old/ MCP 2025-11-25. sessions, handshake, held-open streams.
server-new/ MCP 2026-07-28. stateless, handles, MRTR, cache hints.
client-demo/ both clients — one per SDK generation.
round-robin/ ~50-line load balancer. no stickiness, on purpose.
experiments/ four runnable scripts + a write-up each.
docs/ the four architecture notes.
.cart-store/ server-new's shared state. a JSON file. delete it freely.코드 읽는 순서: cart-core/src/cart.ts (제품이 하는 일) → server-old/src/index.ts → server-new/src/index.ts. 두 서버의 프로토콜 수준 코드는 줄 단위로 주석이 달려 있습니다. 배관 코드는 그렇지 않습니다.
npm run clean은 빌드 출력물과 .cart-store를 제거합니다.
용어집
전체에서 사용되는 용어, 물릴 순서대로.
로드 밸런서 — 서버의 동일한 복사본 여러 개 앞에 있는 상자로, 들어오는 요청을 복사본들에 분산합니다. 기본 정책은 라운드로빈입니다: 각 요청을 목록의 다음 복사본으로 보냅니다. 어떤 복사본이든 어떤 요청이든 응답할 수 있다고 가정하는데, 이것이 바로 기존 MCP 스펙이 깨뜨린 가정입니다. 여기서는 packages/round-robin, 약 50줄입니다.
세션 — 여러 요청에 걸친 클라이언트의 서버 측 메모리. 2025-11-25에서는 서버가 핸드셰이크 중에 Mcp-Session-Id를 발급하고, 클라이언트가 모든 요청에 이를 반영했으며, 서버는 이를 인메모리 맵의 키로 사용했습니다. 세션 id는 한 프로세스의 힙을 가리키는 포인터이며, 모든 문제의 근원입니다.
무상태(Stateless) — 서버는 요청 사이에 아무것도 유지하지 않습니다. 모든 요청은 서비스를 제공하는 데 필요한 모든 것을 담고 있습니다. 이것이 의미하지 않는 것을 주목하세요: 여전히 카트가 있고, 여전히 저장됩니다. 사라진 것은 특정 프로세스에 암시적으로, 연결을 키로 하여 보관되던 상태입니다. 공유 데이터베이스의 애플리케이션 상태는 무상태 프로토콜과 완벽하게 호환됩니다.
고정 세션(Sticky session) (세션 선호도) — 한 클라이언트의 모든 요청이 동일한 서버 복사본으로 돌아가도록 로드 밸런서를 구성하는 것, 일반적으로 쿠키나 헤더를 해싱하여 수행합니다. 상태 저장 프로토콜을 위한 표준 해결책입니다. 작동은 하지만, 균등한 부하 분산, 고통 없는 배포, 유용한 오토스케일링, 그리고 애플리케이션 프로토콜을 이해할 필요가 없는 로드 밸런서를 희생합니다. 비용 목록은 docs/02에 있습니다.
유발(Elicitation) — 서버가 작업 중에 최종 사용자에게 질문을 던지는 것("총액은 $180.36입니다, 확인하시겠습니까?"). 기존 스펙에서는 서버가 붙잡아 둔 스트림을 통해 클라이언트에게 자체 요청을 보내고, 인간이 그것에 대해 생각하는 동안 도구 핸들러 내부에서 차단했습니다. 이 단일 기능은 살아 있는 프로세스, 열린 소켓, 그리고 동일한 상자로의 라우팅 보장을 요구했습니다.
MRTR (Multi Round-Trip Requests) — 2026-07-28이 유발을 수행하는 방식입니다. 서버는 resultType: "input_required"와 inputRequests의 질문들, 그리고 불투명한 서명된 requestState를 담은 정상적인 200을 반환합니다. 그 요청은 끝났습니다 — 아무것도 붙잡혀 있지 않습니다. 클라이언트는 답변을 모아 inputResponses와 동일한 requestState를 담은 새 요청(새 JSON-RPC id)을 보냅니다. 진행 중이던 상태는 프로세스에 앉아 있는 대신 클라이언트를 통해 이동했으며, 그래서 2라운드는 완전히 다른 머신이 서비스할 수 있습니다.
핸들(Handle) — 서버가 발급한 식별자로, 일반적인 도구 출력으로 반환된 후 일반적인 인자로 다시 전달됩니다. cart_create는 cartId를 반환하고, cart_add_item은 그것을 받습니다. 이것이 2026-07-28이 세션 상태를 대체하는 방식이며, 기존 설계와의 차이는 누가 키를 보유하는가입니다: 보이지 않게 전송 계층이, 대비 모델이 읽고 전달할 수 있는 값으로 클라이언트가. 주의: 핸들 단독으로는 베어러 토큰입니다 — 인증된 사용자로 범위를 한정해야 하며, docs/04에서 솔직하게 다룹니다.
프롬프트 캐싱 — LLM 제공자는 프롬프트의 접두사를 캐시합니다: 동일한 시작 바이트를 다시 보내면 제공자는 해당 토큰을 재처리하는 대신 계산된 상태를 재사용하며, 입력 가격의 대략 10분의 1입니다. 두 가지 속성이 이를 취약하게 만듭니다: 일치는 정확한 바이트 단위이고, 앞에서부터 위치적입니다. 따라서 도구 목록이나 카탈로그가 접두사에 있고 두 항목이 자리를 바꾸면, 교체 이후의 모든 토큰에 대한 할인을 잃습니다. 이것이 2026-07-28이 서버가 목록을 결정적 순서로 반환해야 한다고 말하는 이유이며, listProducts()가 이름이나 가격이 아닌 고유한 id로 정렬하는 이유입니다 — 고유 키는 정렬 구현이 다르게 해석할 동률이 없는 전체 순서를 제공합니다. 가격이 포함된 실제 예시: 실험 04.
세 가지만 기억한다면
"무상태"는 "상태 없음"이 아니라 "프로세스에 고정된 상태 없음"을 의미합니다. 카트는 여전히 존재합니다. 어떤 인스턴스든 도달할 수 있는 곳으로 이동했을 뿐입니다.
고정 세션은 실질적인 비용이 따르는 실질적인 해결책이었고, 그 비용 중 하나는 인프라가 애플리케이션 프로토콜을 파싱하게 만든 것입니다.
서버리스를 해제한 것은 무상태성이 아니라 MRTR입니다. 무상태성은 MCP를 로드 밸런서 뒤에 놓았습니다. 유발은 여전히 인간이 대화 상자를 읽는 동안 프로세스가 살아 있어야 했습니다 — 그리고 그것이 바로 서버리스가 제거한 것입니다.
This server cannot be installed
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Related MCP Connectors
Hosted MCP for e-commerce: live product catalog, stock, and pricing for AI agents.
Remote MCP for Living Stack offer discovery and buyer-authorized checkout preparation.
Remote MCP for Universal Cart merchant readiness MCP, structured receipts, audit logs, and reviewer-
Agent-native commerce with trusted catalog, durable carts, and Stripe Checkout via MCP and UCP.
Related MCP Servers
- FlicenseNot gradedqualityDmaintenanceA Model Context Protocol server that enables AI assistants to interact with a complete e-commerce application, providing authentication, product browsing, and shopping cart management through standardized MCP tools.
- AlicenseNot gradedqualityDmaintenanceMCP server for Online Boutique AI Assistant that exposes 18 e-commerce microservice functions via the Model Context Protocol, enabling any MCP client to manage products, carts, checkout, payments, and shipping.MIT
- FlicenseNot gradedqualityDmaintenanceEnables AI assistants to manage products, shopping carts, and orders in an online store through a well-defined MCP API.
- AlicenseAqualityCmaintenanceA UCP-compliant MCP storefront server that exposes product catalog operations (search, cart, checkout) as MCP tools, following UCP schema version 2026-04-08.5MIT
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/ritik913553/mcp-server'
If you have feedback or need assistance with the MCP directory API, please join our Discord server