MCP·Skill 내부 구조 — 레퍼런스

MCP의 2계층 통신 구조와 Skill의 3단계 점진 로딩을 내부 동작 수준에서 정리한다  |  공식 문서: modelcontextprotocol.io · Claude Code Skills

대상 버전Claude Docs · Claude Code 2026-06 기준 (MCP 스펙 2025-11-25 / 프로토콜 2025-06-18)
문서 수집일2026-06-27
버전 확인 방법MCP는 initialize 응답의 protocolVersion, Skill은 API 베타 헤더 skills-2025-10-02로 확인
개정 시 확인 사항MCP 스펙 페이지의 Architecture·Lifecycle 절, Skill 문서의 progressive disclosure 절을 재확인

구조 지도 — MCP 2계층 / Skill 3단계

두 기술의 내부 동작을 한눈에 비교하기 위한 지도다. MCP는 통신 프로토콜이라 계층(Data/Transport)으로 나뉘고, Skill은 점진 공개라 로딩 시점(Level 1~3)으로 나뉜다.

MCP (Model Context Protocol)
├── Host                       AI 앱 — Claude Desktop / Claude Code
│   └── MCP Client             서버마다 1:1 연결을 유지
├── Data Layer                 JSON-RPC 2.0 — lifecycle·primitives·notifications
│   ├── Tools                  실행 함수 (tools/list · tools/call)
│   ├── Resources              데이터 스냅샷 = 컨텍스트
│   └── Prompts                재사용 템플릿
└── Transport Layer            stdio(로컬) · Streamable HTTP(원격, SSE)

Agent Skill
├── Level 1 — 메타데이터        name+description, 항상 로드 (~100토큰/스킬)
├── Level 2 — SKILL.md 본문     트리거 시 로드 (보통 <5K토큰)
└── Level 3 — 번들 리소스       필요 시 읽음 (reference/*.md, scripts/*.py)

💡 핵심 한 줄: MCP는 매 요청마다 tool 정의(inputSchema)가 컨텍스트에 상주해 비용이 높고, Skill은 메타데이터만 상시 로드돼 비용이 낮다 — 내부 구조의 차이가 곧 컨텍스트 비용의 차이로 이어진다.


1. MCP 아키텍처 — Host / Client / Server

MCP는 AI 앱을 외부 시스템(파일·DB·API·도구)에 연결하는 오픈 표준으로, 흔히 "AI의 USB-C"로 불린다. 핵심 구성은 세 역할로 나뉘며, 클라이언트와 서버는 항상 1:1로 연결된다. 하나의 호스트가 여러 클라이언트를 두어 여러 서버에 동시에 붙는 구조다.

MCP 3자 구조 Host 안의 Client 1·2·3이 각각 Server A·B·C와 1:1로 연결된다. Server A·B는 로컬 stdio, Server C는 원격 HTTP. Host (Claude Code / Desktop) Client 1 Client 2 Client 3 1:1 1:1 1:1 Server A stdio · 로컬 Server B stdio · 로컬 Server C HTTP · 원격
그림 1. MCP는 Host 안의 여러 Client가 각 Server와 1:1로 연결된다

Host — AI 앱이자 클라이언트의 컨테이너

사용자가 직접 마주하는 AI 애플리케이션이다. Claude Desktop이나 Claude Code가 호스트에 해당하며, 모델을 실행하고 사용자 요청을 받아 어떤 서버의 어떤 기능을 쓸지 조율한다. 호스트는 내부에 여러 MCP Client를 생성해 각 서버와의 연결을 관리한다.

Client — 서버와 1:1로 묶이는 연결 관리자

호스트 안에서 동작하며, MCP Server 하나당 클라이언트 하나가 전담으로 붙는다. 연결 수립(initialize), 메시지 송수신, 세션 종료를 담당한다. 서버가 둘이면 클라이언트도 둘이다 — 이 1:1 격리 덕분에 한 서버의 장애가 다른 연결로 번지지 않는다.

Server — 외부 기능을 노출하는 별도 프로세스

실제 도구·데이터·템플릿을 제공하는 프로그램이다. 로컬에서 stdio로 실행되거나 원격에서 HTTP로 호스팅된다. 모델이 아니라 호스트와 분리된 자체 프로세스로 돌아가므로, 서버 코드는 SDK로 따로 작성하고 별도로 띄워 등록한다.

# stdio 로컬 서버 등록 (Claude Code)
claude mcp add weather -- python /path/to/weather_server.py

# 원격 HTTP 서버 등록
claude mcp add --transport http github https://example.com/mcp

서버가 여럿이어도 호스트 한 곳에서 통합 관리된다. 등록 스코프는 local·project·user로 구분되며, 팀 공유는 프로젝트 루트의 .mcp.json에 정의해 커밋한다.


2. Data Layer / Transport Layer

MCP는 "무엇을 주고받는가"(Data Layer)와 "어떻게 실어 나르는가"(Transport Layer)를 분리한다. 덕분에 같은 메시지 규약을 로컬과 원격 양쪽에서 동일하게 쓸 수 있다.

MCP 2계층 스택 위층은 Data Layer(JSON-RPC 2.0), 아래층은 Transport Layer(stdio / Streamable HTTP·SSE). Data Layer가 메시지 의미를, Transport Layer가 전달 방식을 담당한다. Data Layer — JSON-RPC 2.0 메시지의 의미 (무엇을 주고받는가) initialize · tools/list · tools/call · notifications 추상화 Transport Layer 전달 방식 (어떻게 실어 나르는가) stdio(로컬) / Streamable HTTP·SSE(원격)
그림 2. Data Layer가 메시지 의미를, Transport Layer가 전달 방식을 담당

JSON-RPC 2.0 — Data Layer의 메시지 규약

요청·응답·알림(notification)을 정의하는 표준 RPC 포맷이다. lifecycle(연결 수립·종료), primitives(tools·resources·prompts 호출), notifications(상태 변경 통지)가 모두 이 위에서 오간다. 전송 수단이 무엇이든 메시지 형식은 동일하게 유지된다.

stdio — 로컬 전송

서버가 같은 머신에서 실행될 때 쓰는 전송 방식이다. 호스트가 서버 프로세스를 자식으로 띄우고 표준입출력(stdin/stdout)으로 JSON-RPC 메시지를 주고받는다. 네트워크를 거치지 않아 지연이 낮고, 파일 시스템·로컬 명령처럼 머신에 밀착된 작업에 적합하다.

Streamable HTTP (SSE) — 원격 전송

서버가 원격에 호스팅될 때 쓰는 전송 방식이다. HTTP 요청/응답을 기본으로 하되, 서버가 여러 메시지를 흘려보내야 할 때는 SSE(Server-Sent Events)로 스트리밍한다. 인증은 OAuth, Bearer 토큰, API key, 커스텀 헤더 등을 지원해 SaaS·사내 API 연동에 적합하다.

구분stdioStreamable HTTP
실행 위치 로컬(같은 머신, 자식 프로세스) 원격(별도 호스트)
통신 채널 표준입출력 stdin/stdout HTTP + SSE 스트리밍
인증 로컬 신뢰(별도 인증 불필요) OAuth / Bearer / API key / 헤더
대표 용도 파일·로컬 도구·DB 직결 SaaS·사내 API·다중 사용자

3. primitives & lifecycle

primitives는 서버가 노출하는 기능 단위이고, lifecycle은 연결을 여닫는 절차다. 서버 측 primitives는 Tools·Resources·Prompts 세 가지이며, 클라이언트 측 primitives로 Sampling·Elicitation·Logging이 있다.

tools/list · tools/call — 실행 함수의 발견과 호출

Tools는 모델이 실제로 실행할 수 있는 함수다. 클라이언트는 tools/list로 서버가 제공하는 도구 목록과 각 도구의 inputSchema를 받아오고, 모델이 도구를 쓰기로 하면 tools/call로 인자를 넘겨 실행한다. 이 도구 정의는 매 요청마다 모델 컨텍스트에 실려 토큰을 소모하므로, 불필요한 도구는 비활성화하는 편이 좋다.

Resources — 컨텍스트로 주입되는 데이터 스냅샷

실행이 아니라 읽기 대상인 데이터다. 파일 내용, DB 레코드, 문서 같은 정보를 스냅샷 형태로 모델에 컨텍스트로 제공한다. 부작용 없이 "현재 상태"를 모델에 보여줄 때 쓴다.

Prompts — 재사용 가능한 템플릿

자주 쓰는 지시·워크플로를 매개변수화한 템플릿이다. 사용자가 슬래시 명령처럼 선택해 호출하면 서버가 정형화된 프롬프트를 돌려준다. 같은 작업을 일관된 형태로 반복할 때 유용하다.

initialize — capabilities 협상으로 시작하는 lifecycle

연결은 클라이언트의 initialize 요청과 서버 응답으로 시작된다. 이 단계에서 양측이 protocolVersion과 지원 capabilities를 주고받아 무엇을 쓸 수 있는지 협상한다. 협상이 끝나면 사용 단계로 넘어가고, 마지막에 연결을 종료한다.

요청·응답에는 id가 붙어 짝을 맞추지만, notification에는 id가 없다. 응답을 기대하지 않는 단방향 통지이기 때문이다(예: 도구 목록 변경 알림).

// initialize 요청 (클라이언트 → 서버)
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "initialize",
  "params": {
    "protocolVersion": "2025-06-18",
    "capabilities": { "sampling": {}, "elicitation": {} }
  }
  // 클라이언트 capabilities에는 클라이언트 primitive(sampling/elicitation/roots)만 선언한다.
  // tools/resources/prompts는 서버가 initialize 응답에서 선언하는 서버 capability다.
}

// tools/call 요청 (id 있음 → 응답 필요)
{
  "jsonrpc": "2.0",
  "id": 2,
  "method": "tools/call",
  "params": { "name": "get_weather", "arguments": { "city": "Seoul" } }
}

// notification (id 없음 → 응답 없음)
{
  "jsonrpc": "2.0",
  "method": "notifications/tools/list_changed"
}

Tools Resources Prompts notification = id 없음


4. Skill — progressive disclosure 3단계

Skill은 SKILL.md와 선택적 스크립트·리소스를 담은 폴더다. 핵심 설계는 progressive disclosure(점진 공개) — 필요한 만큼만 단계적으로 컨텍스트에 올려 토큰을 아낀다. 메타데이터만 상시 로드되고, 본문과 리소스는 실제로 필요해진 시점에야 읽힌다.

Skill progressive disclosure 3단계 L1 메타데이터는 항상 로드(~100토큰/스킬), L2 SKILL.md 본문은 트리거 시 로드(5K토큰 미만), L3 번들 리소스는 필요 시 로드. 아래로 갈수록 로드 시점이 늦어진다. 로드 시점이 늦어짐 ↓ L1 메타데이터 (name + description) 언제 로드: 항상 로드 (상시 컨텍스트) 토큰 비용: ~100토큰 / 스킬 L2 SKILL.md 본문 언제 로드: 트리거(매칭)될 때 로드 토큰 비용: 5K토큰 미만 L3 번들 리소스 (reference · scripts) 언제 로드: 필요할 때만 (요청 시) 토큰 비용: 스크립트는 출력만 컨텍스트로
그림 3. Skill은 필요한 만큼만 단계적으로 컨텍스트에 올린다

Level 1 메타데이터 — 항상 로드되는 ~100토큰

YAML frontmatter의 namedescription만 담긴 가장 얇은 층이다. 스킬 하나당 약 100토큰으로, 설치된 모든 스킬의 이 정보가 항상 컨텍스트에 올라가 있다. 모델은 이 description을 보고 "지금 이 스킬을 써야 하나"를 판단한다.

Level 2 SKILL.md — 트리거될 때 로드되는 본문

description이 사용자 요청과 매칭돼 스킬이 트리거되면, 그때 비로소 SKILL.md 본문(마크다운)이 컨텍스트로 로드된다. 절차·규칙·예시가 담기며 보통 5K토큰 미만으로 유지한다. 본문이 길어지면 상세 내용은 별도 파일로 분리해 Level 3으로 미룬다(500줄 이내 권장).

Level 3 리소스 — 필요할 때만 읽는 번들

가장 무거운 층으로, reference/*.md 같은 참고 문서나 scripts/*.py 같은 실행 스크립트가 여기 속한다. 모델이 본문을 따라가다 실제로 필요해진 파일만 읽는다. 특히 스크립트는 코드 자체가 컨텍스트에 로드되는 게 아니라 실행 결과(출력)만 들어온다 — 큰 절차도 토큰을 거의 쓰지 않고 처리할 수 있는 이유다.

단계로드 시점대략 토큰 비용
Level 1 메타데이터 항상(상시 로드) ~100토큰 / 스킬
Level 2 SKILL.md 본문 description 매칭으로 트리거될 때 보통 <5K토큰
Level 3 번들 리소스 본문 진행 중 필요해진 파일만 파일별 가변(스크립트는 출력만)

5. 로딩 메커니즘

점진 공개가 실제로 어떻게 작동하는지를 단계별로 따라가 보자. 핵심은 모델이 파일 시스템과 bash를 도구처럼 써서, 필요한 순간에 필요한 파일만 읽어 들인다는 점이다.

description 매칭 → SKILL.md 로드 → 조건부 리소스

트리거 판단의 출발점은 frontmatter의 description이다. 이 한 줄이 "무엇을 + 언제 사용하는지"를 구체적으로 담고 있어야 모델이 올바른 시점에 스킬을 깨운다. 트리거가 안 되면 십중팔구 description이 모호한 것이므로 거기부터 점검한다.

  1. description 매칭: 상시 로드된 Level 1 메타데이터의 description이 사용자 요청과 부합하는지 모델이 판단한다.
  2. bash로 SKILL.md 읽기: 매칭되면 모델이 파일 읽기로 SKILL.md 본문(Level 2)을 컨텍스트에 올린다.
  3. 조건부 파일 읽기: 본문 지시를 따라가다 참고 문서가 필요해지면 그 reference/*.md만 추가로 읽는다(Level 3).
  4. 스크립트는 출력만 로드: scripts/*.py는 코드를 컨텍스트에 넣지 않고 실행해, 그 출력만 결과로 받는다.

본문에서 !`<command>` 구문을 쓰면 전송 직전에 해당 명령이 실행돼 그 출력이 인라인으로 삽입된다 — 동적 컨텍스트 주입이다. Claude Code에서는 frontmatter에 allowed-tools·disable-model-invocation·context: fork 같은 선택 키로 실행 권한과 동작을 제어할 수 있다.

스킬은 임의 코드 실행과 파일 접근이 가능하므로 신뢰된 출처에서만 설치한다. 트리거가 안 될 때는 claude --debug로 YAML frontmatter 파싱을 확인하고, description에 트리거 단어가 3인칭·구체적으로 들어갔는지 점검한다.

💡 핵심 한 줄: MCP의 tool 정의는 매 요청 상주해 컨텍스트 비용이 높고, Skill은 메타데이터만 상시 로드돼 나머지는 필요할 때만 읽으므로 비용이 낮다. 내부 구조의 차이가 그대로 비용 구조의 차이로 이어진다.


참고 링크


MCP 스펙 2025-11-25 / 프로토콜 2025-06-18 · Claude Code 2026-06 기준 | 2026년 6월