LabHub
배우기 러닝패스 코스

에이전트가 내 DB 를 지웠다 · 표준 라이브러리로 짜는 stdio 서버 · 실습

표준 라이브러리만으로 MCP stdio 서버를 만든다

LabHub 에서 이어서 보기

목표

json·sys·sqlite3 만으로 MCP 서버를 한 편 씁니다. 클라이언트가 보내는
initializenotifications/initializedtools/listtools/call 을 순서대로
받아 내고, 마지막에는 그 서버를 자식 프로세스로 띄우는 클라이언트까지 씁니다.

왜 중요한가

SDK 를 쓰면 다섯 줄로 끝나는 일입니다. 그런데 사고는 SDK 가 감춰 준 자리에서
납니다 — stdout 에 찍은 디버그 한 줄이 클라이언트의 JSON 파서를 죽이고, 알림에
답을 보내서 짝이 안 맞는 응답이 생기고, 도구가 실패했는데 프로토콜 오류로
돌려보내서 모델이 "서버가 고장났다" 고 판단합니다. 프레이밍(줄 하나 = 메시지
하나), id 로 짝 맞추기, 알림 무시, 오류 두 종류의 구분 — 이 네 가지가 MCP 의
실체이고, 손으로 한 번 짜 보면 SDK 의 로그가 무슨 말을 하는지 읽힙니다.

단계

1. /root/mcp/seed.sql 을 저장하고 /root/mcp/shop.db 로 적재하세요. customers 5행, orders 8행이어야 합니다.
2. /root/mcp/server.py 를 만드세요. stdin 에서 한 줄씩 JSON-RPC 요청을 읽고, initializeprotocolVersion2025-06-18 이고 capabilities.toolsserverInfo.name 이 있는 result 로 답해야 합니다.
3. 같은 파일에서 알림(id 없는 메시지)에는 답하지 않고, 모르는 메서드는 -32601, 깨진 JSON 은 -32700 오류(id 는 null)로 답하게 하세요. 서버가 죽으면 안 됩니다.
4. tools/list 에 도구 두 개를 돌려주세요 — list_customerscount_orders(inputSchema.properties.status, 문자열, required). 각 도구에 descriptiontype: objectinputSchema 가 있어야 합니다.
5. tools/call 을 구현하세요. count_orders{"status":"paid"} 를 주면 DB 의 paid 건수가 content[0].text 에 들어 있어야 하고, list_customers 는 다섯 고객 이름을 모두 담아야 합니다. DB 경로는 환경변수 MCP_DB 가 있으면 그것을, 없으면 /root/mcp/shop.db 를 씁니다.
6. 오류를 두 종류로 나누세요. 없는 도구 이름은 JSON-RPC 오류 -32602, count_orders 에 모르는 상태값(예: banana)을 주면 result 에 isError: true 와 설명 텍스트를 돌려주어야 합니다.
7. /root/mcp/client.py 를 만드세요. 서버를 subprocess 로 띄워 initialize → notifications/initialized → tools/list → tools/call(count_orders, paid) 을 보내고, 결과를 /root/mcp/session.jsontools(이름 목록)와 paid_orders(응답 텍스트)로 적습니다. 환경변수 MCP_SESSION_OUT 이 있으면 그 경로에 씁니다.
8. 서버의 모든 로그를 stderr 로 보내세요. 요청마다 메서드 이름이 stderr 에 한 줄씩 찍히고, stdout 에는 JSON 응답 외에 아무것도 나오면 안 됩니다.

참고

단계 8개

  1. 가게 DB 를 만든다
  2. initialize 에 답한다
  3. 알림에는 답하지 않는다
  4. 도구 목록을 낸다
  5. 도구를 실제로 실행한다
  6. 오류는 두 종류다
  7. 클라이언트로 한 세션을 돈다
  8. stdout 은 프로토콜 전용이다