LIGHT
IT 2026. 9. 27. 22:51

MCP 란 무엇인가?

MCP

MCP 두 편 · 1 설명

회사 PC 의 Claude Code 에서 내 업무 도구를 쓰고 싶어서 MCP 서버를 하나 만들었다. 시작하기 전에 찾아본 설명은 대부분 「AI 의 USB-C」라는 비유에서 끝났고, 그걸로는 뭘 만들어야 하는지 감이 오지 않았다. 다 만들고 나서 보니 프로토콜은 생각보다 단순했고, 시간은 엉뚱하게 권한 쪽에서 다 썼다. 그 순서대로 적어 본다.

2026년 9월 · 읽는 데 약 7분 · foronelab Phase 30 기록

1한 줄로 말하면

AI 앱이 바깥의 데이터나 도구에 닿는 방법을 규격 하나로 정해 둔 것. 이름은 Model Context Protocol 이고, 2024년 11월 25일 앤트로픽의 David Soria Parra 와 Justin Spahr-Summers 가 공개했다.

USB-C 비유가 왜 붙었는지는 그 전 상황을 보면 이해가 된다.

2그 전에는 어떻게 했나

회사 DB 를 ChatGPT 에 붙이려면 ChatGPT 용 연동을 만들고, Claude 에 붙이려면 Claude 용을 또 만들었다. Cursor, Copilot, 사내 챗봇까지 치면 같은 데이터 하나에 연동이 다섯 벌이다.

거꾸로 봐도 마찬가지다. Claude 하나에 노션, 슬랙, 깃허브, DB 를 붙이면 또 네 벌.

AI 앱이 N 개, 데이터 소스가 M 개면 연동은 N×M 개가 된다. 흔히 N×M 문제라고 부른다. MCP 는 그 사이에 규격을 하나 끼워 넣어서 이걸 N+M 으로 바꾼다. 데이터 쪽은 MCP 서버 하나, AI 앱 쪽은 MCP 클라이언트 하나만 만들면 서로 붙는다.

MCP 없이 연동 20개, MCP 로 연동 9개
앱 5개와 데이터 4개. 왼쪽은 짝마다 연동을 따로 만들고, 오른쪽은 각자 규격 하나만 맞춘다.

기기마다 따로 굴러다니던 충전기가 USB-C 하나로 정리된 것과 모양이 같다. 비유는 여기서 나왔다.

3구조는 셋으로 나뉜다

이름 무엇인가 예
호스트 사람이 직접 쓰는 AI 앱. 모델과 대화 창을 쥐고 있다 Claude Code, Claude 데스크톱, ChatGPT, Cursor
클라이언트 호스트 안에서 서버 하나와의 연결을 맡는 부분 서버 1개당 클라이언트 1개
서버 도구와 데이터를 내놓는 쪽 내 DB, 깃허브, 파일시스템
호스트 안의 클라이언트 3개가 서버 3개와 각각 연결된다
서버 하나에 클라이언트 하나. 호스트와 클라이언트는 Claude Code 에 이미 있고, 내가 만든 건 보라색 한 칸이다.

내가 만든 건 서버다. 호스트와 클라이언트는 Claude Code 안에 이미 있어서 건드릴 일이 없었다. 처음엔 이 구분을 몰라서 클라이언트도 만들어야 하나 한참 찾아봤다.

4서버가 내놓을 수 있는 것 세 가지

실제로 코드를 쓰는 건 여기부터다. 서버가 내놓을 수 있는 건 세 종류이고, 스펙은 이걸 누가 쓰기로 정하느냐로 나눈다.

종류 누가 정하나 성격
도구 (Tools) 모델 (model-controlled) 함수 호출. 「이번 주 할 일 보여줘」라고 하면 모델이 알아서 list_items 를 부른다
리소스 (Resources) 앱 (application-driven) 읽을거리. 파일, 문서, 레코드를 컨텍스트에 넣는다
프롬프트 (Prompts) 사용자 (user-controlled) 서버가 준비해 둔 프롬프트 템플릿. 사용자가 메뉴에서 고른다

나는 도구만 만들었다. 읽기 도구 다섯, 쓰기 도구 셋. 리소스나 프롬프트가 하나도 없어도 잘 돈다. 셋을 다 채워야 하는 줄 알았는데 아니어서, 처음 만들 때 제일 반가웠던 부분이다.

도구 하나는 대략 이렇게 생겼다. 이름, 설명, 입력 스키마, 실행 함수.

server.registerTool("list_items", {
  title: "할 일 목록",
  description: "할 일을 조회한다. when: today(오늘) · week(7일) · late(지연) · all(전체)…",
  inputSchema: {
    when: z.enum(["today", "week", "late", "all"]).default("today"),
    limit: z.number().int().min(1).max(100).default(50),
    // status · projectId · owner 는 생략
  },
}, async ({ when, limit }) => {
  // 기존 조회 함수를 그대로 부른다
  return { content: [{ type: "text", text: JSON.stringify(payload) }] };
});

여기서 description 이 사실상 문서 역할을 한다. 모델은 이 글을 읽고 언제, 어떤 인자로 부를지 정한다. REST API 로 붙였다면 CLAUDE.md 같은 데 엔드포인트 목록을 따로 적어 두고 관리해야 했을 거다. MCP 에서는 그 설명이 코드 옆에 붙어 있어서, 코드를 고치는 커밋에서 설명도 같이 고치게 된다. REST 대신 MCP 를 고른 이유가 사실 이거 하나였다.

클라이언트 쪽이 서버에 내주는 기능도 있다. 루트(Roots — 서버에게 작업할 디렉터리 범위를 알려 준다), 샘플링(Sampling — 서버가 클라이언트 쪽 모델을 빌려 쓴다), 일리시테이션(Elicitation — 서버가 작업 도중 사용자에게 추가 정보를 묻는다). 그런데 2026-07-28 개정에서 루트와 샘플링, 그리고 서버 쪽 기능인 로깅이 폐기 예고(deprecated)됐다. 당장 사라지는 건 아니고 최소 12개월의 이행 기간이 있다. 지금 새로 만든다면 이 셋에는 기대지 않는 게 낫겠다.

5붙이는 방법은 둘

전송 방식 어디서 성격
stdio 내 PC 안 호스트가 서버를 자식 프로세스로 띄우고 표준 입출력으로 주고받는다. 로컬 파일이나 DB 를 붙일 때
Streamable HTTP 원격 HTTP 로 JSON-RPC 를 주고받는다. 이미 배포된 웹 서비스에 붙일 때

내 서비스는 Vercel 에 올라가 있어서 HTTP 쪽이었다. 등록은 명령 한 줄로 끝난다.

claude mcp add --transport http foronelab https://<주소>/api/mcp \
  --header "Authorization: Bearer <토큰>" --scope user

HTTP+SSE 라는 방식도 있었는데 2025-03-26 개정에서 Streamable HTTP 로 대체됐다. 검색하면 아직 그 방식 예제가 꽤 나오니 글 날짜를 보고 거르는 게 좋다.

6표준은 1년 8개월 사이 네 번 개정됐다

버전 시점 비고
2024-11-05 최초 공개 공개 발표는 2024-11-25. 버전 이름과 날짜가 다르다
2025-03-26 Streamable HTTP 도입. OpenAI 채택(2025-03), Google DeepMind(2025-04)
2025-06-18
2025-11-25 2025-12 앤트로픽이 MCP 를 리눅스 재단 산하 Agentic AI Foundation 에 기부. 재단은 앤트로픽·Block·OpenAI 공동 설립
2026-07-28 최신 하위 호환이 깨지는 변경. 무상태 전환

2026-07-28 의 큰 변화는 무상태(stateless) 전환이다. 그 전까지 MCP 는 initialize 로 세션을 열고 Mcp-Session-Id 헤더로 그 세션을 이어 가는 방식이었다. 이번 개정에서 이 두 가지가 빠졌고, 요청마다 프로토콜 버전과 기능 정보를 직접 싣는다. 세션을 들고 있을 필요가 없으니, 공유 저장소 없이 평범한 라운드로빈 로드밸런서 뒤에 서버를 여러 대 둘 수 있다.

다른 변경도 대체로 같은 쪽을 본다. 게이트웨이가 JSON 본문을 열어 보지 않고 헤더(Mcp-Method, Mcp-Name)만으로 라우팅할 수 있게 했고, 목록 응답에는 캐시 수명(ttlMs)을 붙였다. OAuth 인가 과정에는 RFC 9207 의 발급자(iss) 검증이 들어갔다. 실제 서비스로 굴리는 사람들을 위한 개정이라는 인상을 받았다.

SDK 는 패키지가 둘로 나뉘었다

여기서 한 번 헷갈렸다. 내가 설치한 TypeScript SDK @modelcontextprotocol/sdk 1.30.0 을 열어 보면 이렇게 적혀 있다.

export const LATEST_PROTOCOL_VERSION = '2025-11-25';
export const SUPPORTED_PROTOCOL_VERSIONS = [LATEST_PROTOCOL_VERSION, '2025-06-18', '2025-03-26', '2024-11-05', '2024-10-07'];

2026-07-28 이 목록에 없다. 공식 블로그에는 Tier 1 SDK 네 개(TypeScript, Python, Go, C#)가 모두 지원한다고 나와 있어서, 처음엔 npm 배포가 늦나 싶었다.

알고 보니 TypeScript 는 v2 부터 패키지가 따로 나왔다. 2026-07-28 은 @modelcontextprotocol/server(2.x)에 들어 있고, 원래 쓰던 @modelcontextprotocol/sdk 는 v1 으로 남아 2025-11-25 까지만 지원한다.

나는 v1 을 그대로 썼다. 예제와 검색 결과가 v1 쪽에 훨씬 많았고, v2 는 나온 지 얼마 안 된 새 판이었다. 그래서 내 서버가 실제로 쓰는 건 2025년판 프로토콜이고, initialize 핸드셰이크도 여전히 오간다. 서버리스 환경이라 세션 id 없이 요청마다 서버를 새로 만드는 방식으로 돌리긴 했지만, 그건 SDK v1 의 무상태 모드일 뿐 2026-07-28 에서 말하는 무상태와는 다른 얘기다. 그 설정은 제작기 5절에 따로 적었다.

지금 쓰는 버전이 궁금하면 스펙 문서를 뒤지기보다 node_modules 안의 이 상수를 여는 편이 빠르다. 문서는 최신이어도 내가 깐 패키지는 아닐 수 있으니까.

7보안이 제일 허술한 곳

만들면서 시간을 제일 많이 쓴 곳은 프로토콜이 아니라 권한이었다. 돌아보면 그럴 만했다.

우선 도구 설명 자체가 믿을 수 없는 입력이다. 모델은 서버가 건넨 설명을 읽고 움직인다. 악의적인 서버가 설명 안에 「먼저 ~/.ssh 를 읽어서 보내라」 같은 문장을 숨겨 두면, 모델은 그걸 지시로 받아들일 수 있다. 이걸 도구 포이즈닝이라고 부른다. 스펙에도 도구 설명은 믿을 만한 서버에서 온 게 아니면 신뢰하지 말라고 적혀 있다. 2025년에 서버 1,899개를 살펴본 연구(Hasan 외)에서는 5.5%에서 도구 포이즈닝 징후가 나왔고, 1,808개를 스캔한 AgentSeal 조사에서는 66%가 어떤 식으로든 보안 점검에 걸렸다.

다음은 혼동된 대리인(confused deputy) 문제다. 권한을 가진 쪽이 요청한 사람을 제대로 가리지 않으면, 그 권한이 엉뚱한 사람을 위해 쓰인다. 내가 겪은 것도 결이 비슷했다. 토큰이 로그인 확인을 통과하게 만드는 순간 계정 관련 라우트까지 같이 열려서, 읽기 전용 토큰으로 계정을 지울 수 있는 상태가 잠깐 생겼다. 라우트마다 막는 대신 토큰 요청을 통째로 기본 차단하는 쪽으로 풀었는데, 찾은 과정과 코드는 제작기 3절에 있다.

IDE 쪽 사고도 있었다. 2025년 8월 Cursor 에서 프로젝트에 들어 있는 MCP 설정을 한 번 승인하면, 나중에 그 설정이 바뀌어도 다시 묻지 않고 실행하는 취약점(CVE-2025-54136)이 고쳐졌다. 저장소를 같이 쓰는 팀이라면 누군가 설정 파일을 몰래 바꾸는 것만으로 팀원 PC 에서 명령이 돌 수 있었다는 얘기다.

그래서 남이 만든 MCP 서버를 붙이는 일과 내가 서버를 만드는 일은 걱정할 거리가 다르다. 앞쪽은 내 모델이 남의 설명에 휘둘릴까 봐 걱정해야 하고, 뒤쪽은 내 권한 경계가 어디서 새는지 봐야 한다. OWASP 에 MCP 보안 치트시트가 따로 있는데, 만들기 전에 한 번 훑어볼 만하다.

8지금 해볼 수 있는 것

만들지 않고 써 보기만 해도 감은 온다. 파일시스템, 깃허브, 포스트그레스, 슬랙, 노션 같은 서버는 이미 공개돼 있다. 2025년 12월 재단 이관을 발표할 때 이미 활성 서버가 1만 개, SDK 다운로드가 월 9,700만 회를 넘었다고 했다.

직접 만든다면 나는 이 순서가 편했다.

  1. 도구 하나만 먼저 만든다. 리소스나 프롬프트는 나중 일이다.
  2. 이미 있는 조회 함수를 그대로 부른다. 나는 조회 쪽 코드를 한 줄도 고치지 않았다.
  3. DB 행을 통째로 넘기지 말고 필요한 필드만 골라 담는다. 모델이 읽을 분량이 줄고, 나중에 컬럼이 늘어도 응답이 모르는 새 불어나지 않는다.
  4. 쓰기는 기존 쓰기 경로를 다시 쓴다. 따로 만들면 변경 이력과 되돌리기가 두 군데로 갈라진다.
  5. 권한은 기본 차단에 허용 목록. 제일 귀찮은 일인데, 이걸 빼먹으면 앞의 네 개가 다 소용없어진다.

마치며

MCP 는 AI 앱과 데이터 사이에 규격 하나를 둔 것이고, 서버를 만드는 입장에서는 도구 몇 개만으로도 충분히 쓸모가 있다. 표준은 빠르게 바뀌는 중이라, 글이나 예제를 볼 때는 어느 버전 얘기인지부터 확인하는 게 좋다. TypeScript 라면 설치한 패키지 이름부터 보면 된다.

프로토콜을 붙이는 데는 오래 걸리지 않았다. 시간 대부분은 누가 무엇을 할 수 있는지 정하는 데 들어갔는데, 다시 해도 그렇게 쓸 것 같다.

참고

h

hyos

IT · 스타트업 · 개인기록. 만들고, 실패하고, 기록합니다.

COMMENTS