MCP 서버·Skill 개발 — 레퍼런스

MCP 서버와 Agent Skill을 직접 만들 때 필요한 SDK·구조·작성 규칙 정리  |  공식 문서: modelcontextprotocol.io · code.claude.com/docs/en/skills

대상 버전Claude Docs · Claude Code 2026-06 기준 (MCP 스펙 2025-11-25 / 프로토콜 2025-06-18)
문서 수집일2026-06-27
버전 확인 방법MCP: 공식 사이트 Specification 페이지의 날짜 / Skill: claude --debug로 SKILL.md YAML 파싱 확인
개정 시 확인 사항SDK 패키지 버전(mcp, @modelcontextprotocol/sdk), frontmatter 선택 키 변동, API beta 헤더 값

개발 산출물 구조

MCP 서버는 별도 프로세스로 실행되는 코드이고, Skill은 Claude가 읽어 들이는 폴더다. 산출물의 형태부터 다르므로 개발 진입 방식도 갈린다.

개발 산출물
├── MCP 서버 (별도 프로세스 · SDK 코드)        언어 SDK로 작성, 호스트가 실행
│   ├── tools/list 핸들러                       노출할 tool 목록 + inputSchema 반환
│   ├── tools/call 핸들러                       tool 실행 본문 — 실제 동작 수행
│   └── resources / prompts (선택)              컨텍스트 스냅샷 · 재사용 템플릿
└── Skill (폴더 · 마크다운+스크립트)            ~/.claude/skills 또는 .claude/skills 에 배치
    ├── SKILL.md                                YAML frontmatter + 마크다운 본문(필수)
    │   ├── name / description                  Level1 메타데이터 — 항상 로드
    │   └── 본문 (절차·규칙)                     Level2 — 트리거 시 로드, 500줄 권장
    ├── reference/*.md                          Level3 — 필요 시에만 읽는 상세 문서
    └── scripts/*.py                            Level3 — 실행 출력만 컨텍스트로 로드

1. MCP 서버 개발 — SDK

MCP 서버는 호스트(Claude Desktop·Claude Code)와 분리된 별도 프로세스다. 호스트는 stdio나 HTTP로 서버에 연결하고, 서버는 JSON-RPC 2.0 메시지에 응답한다. 따라서 서버 개발은 "언어 SDK를 설치하고 핸들러를 등록한 실행 파일을 만드는 작업"이다.

pip install mcp — Python SDK로 서버 작성

Python으로 MCP 서버를 만들 때 쓰는 공식 패키지다. 설치 후 서버 인스턴스를 만들고 tool/resource/prompt 핸들러를 등록한 뒤, stdio 같은 트랜스포트로 실행한다. 호스트는 이 실행 명령을 등록해 두고 필요할 때 프로세스를 띄운다.

pip install mcp
# 서버 진입점 예: python server.py  (호스트가 이 명령으로 프로세스 실행)

@modelcontextprotocol/sdk — TypeScript SDK로 서버 작성

Node/TypeScript 환경의 공식 SDK다. Python SDK와 동일한 프리미티브(tools·resources·prompts)를 제공하며, 호스트는 node dist/server.js 같은 실행 명령으로 서버를 띄운다.

npm install @modelcontextprotocol/sdk

Tier1 언어 — 공식 SDK가 제공되는 언어

Anthropic은 Python·TypeScript·C#·Go에 대해 공식(Tier1) SDK를 제공한다. 같은 MCP 스펙을 구현하므로 팀의 기존 스택에 맞춰 언어를 고르면 된다. 서버는 어떤 언어로 짜든 동일한 JSON-RPC 인터페이스를 노출하므로, 클라이언트 입장에서는 구현 언어가 보이지 않는다.

MCP 서버는 호스트와 별개로 실행되는 코드다. 서드파티 서버를 등록하면 그 프로세스가 로컬에서 파일 삭제·명령 실행까지 할 수 있으므로, 신뢰할 수 있는 출처의 서버만 실행하고 실행 전 동의 단계를 거쳐야 한다.

2. MCP — tool/resource/prompt 등록

서버가 노출하는 프리미티브는 세 가지다. Tools(실행 함수), Resources(데이터 스냅샷=컨텍스트), Prompts(재사용 템플릿). 이 중 모델이 직접 호출하는 핵심은 Tools이며, 두 개의 메서드로 동작한다.

tools/list — 노출할 tool 목록 알리기

호스트가 서버에 "어떤 tool을 제공하느냐"고 물을 때 응답하는 메서드다. 각 tool의 이름·설명과 함께 inputSchema를 반환한다. 모델은 이 목록을 보고 어떤 tool을 언제 호출할지 판단하므로, 설명과 스키마가 곧 사용성을 결정한다.

tools/call — tool 실제 실행

모델이 특정 tool을 호출하면 호스트가 이 메서드로 서버에 인자를 전달하고, 서버가 본문을 실행해 결과를 돌려준다. tools/list가 "메뉴판"이라면 tools/call은 "주문 실행"에 해당한다.

inputSchema — JSON Schema로 인자 정의

각 tool의 입력 인자를 JSON Schema로 기술한다. 모델은 이 스키마를 보고 올바른 형태의 인자를 만들어 호출한다. 다만 이 정의는 매 요청마다 컨텍스트를 차지하므로, 불필요한 tool은 비활성화해 토큰 비용을 줄이는 편이 좋다.

아래는 Python SDK로 작성한 최소 서버의 구조 개요다. tool 목록을 알리는 핸들러와 실제 실행 핸들러를 등록한 뒤 stdio로 구동한다.

# server.py — 최소 MCP 서버 구조 개요 (Python SDK)
# 1) 서버 인스턴스 생성
# 2) tools/list 핸들러 등록 → name, description, inputSchema(JSON Schema) 반환
# 3) tools/call 핸들러 등록 → 전달된 인자로 실제 동작 수행 후 결과 반환
# 4) stdio 트랜스포트로 서버 실행 (호스트가 이 프로세스를 띄움)

lifecycle은 initialize 요청/응답으로 시작한다. 이 단계에서 호스트와 서버가 protocolVersioncapabilities를 협상한 뒤 정상 통신으로 넘어간다. 알림(notification) 메시지는 응답을 기대하지 않으므로 id 필드가 없다.

프리미티브역할대표 메서드
Tools 모델이 호출하는 실행 함수 tools/list, tools/call
Resources 읽기용 데이터 스냅샷(컨텍스트) 리소스 목록·읽기
Prompts 재사용 가능한 프롬프트 템플릿 프롬프트 목록·조회

개발 중 동작을 확인할 때는 MCP Inspector를 쓴다. 서버 실행 명령을 인자로 넘기면 tools·resources·prompts를 직접 호출해 응답을 점검할 수 있다.

npx @modelcontextprotocol/inspector <server cmd>
# Claude Desktop 연동 시 로그: ~/Library/Logs/Claude/mcp*.log

3. Skill 개발 — SKILL.md 작성

Skill은 SKILL.md 한 파일에서 시작한다. 상단의 YAML frontmatter가 메타데이터를, 그 아래 마크다운 본문이 실제 절차·규칙을 담는다. 별도의 빌드나 서버 호스팅이 없으므로 폴더를 만들고 마크다운을 쓰는 것만으로 동작한다.

SKILL.md — frontmatter + 본문

frontmatter의 필수 키는 namedescription 두 개다. name은 64자 이하 소문자·숫자·하이픈만 쓰며 anthropic·claude는 예약어라 쓸 수 없다. description은 1024자 이하로 "무엇을 하는 스킬인지 + 언제 쓰는지"를 적는다. 본문에는 Claude가 따라야 할 절차를 마크다운으로 서술한다.

---
name: pdf-form-filler
description: PDF 양식의 입력 필드를 채운다. 사용자가 PDF 폼 작성·서식 채우기를 요청할 때 사용한다.
---

# PDF 양식 채우기

1. 입력 PDF에서 폼 필드 목록을 추출한다.
2. 사용자가 제공한 값을 각 필드에 매핑한다.
3. 채워진 PDF를 저장한다.

Skill 폴더는 다음 위치 중 하나에 둔다. 개인용은 ~/.claude/skills/<name>/SKILL.md, 프로젝트 공유용은 .claude/skills/<name>/SKILL.md이며, 플러그인은 skills/ 디렉터리에 담아 배포한다. 팀 공유는 .claude/skills를 git에 커밋하거나 플러그인으로 묶는다.


4. 효과적인 description

Skill이 자동으로 발동될지는 거의 전적으로 description에 달려 있다. Claude는 항상 로드되는 Level1 메타데이터(name+description, 스킬당 약 100토큰)만 보고 발동 여부를 판단하기 때문이다. 트리거가 안 되면 가장 먼저 점검할 곳도 description이다.

description — 트리거 단어·3인칭·구체적

좋은 description은 사용자가 실제로 쓸 만한 트리거 단어를 포함하고, 3인칭으로 "무엇을 + 언제" 쓰는지 구체적으로 적는다. 반대로 모호하거나 트리거 단어가 없으면 적절한 상황에서도 발동하지 않는다.

구분예시이유
좋음 "Excel 스프레드시트를 생성·편집한다. 사용자가 .xlsx 파일, 표 데이터, 셀 수식 작업을 요청할 때 사용한다." 무엇을·언제가 명확하고 .xlsx·표·수식 등 트리거 단어 포함
나쁨 "파일을 다룬다." / "데이터 작업을 돕는다." 너무 일반적이라 어떤 상황에 써야 하는지 모델이 판단 불가

💡 핵심 한 줄: description은 "무엇을 + 언제"를 3인칭·구체적으로 — 사용자의 표현을 트리거 단어로 그대로 담아라.


5. Claude Code frontmatter 키

Claude Code에서는 필수 키(name·description) 외에 동작을 제어하는 선택 키를 frontmatter에 추가할 수 있다. 도구 사용 범위 제한, 실행 격리, 동적 컨텍스트 주입 등이 가능하다.

allowed-tools / disable-model-invocation — 실행 제어

allowed-tools는 이 스킬이 쓸 수 있는 도구를 한정하고(반대로 disallowed-tools로 금지), disable-model-invocation은 모델이 자동으로 스킬을 발동하지 못하게 막는다. 사용자가 명시적으로 부르는 스킬에 적합하다.

context: fork / paths — 격리·범위

context: fork는 스킬을 격리된 컨텍스트(서브 에이전트)에서 실행해 메인 대화를 어지럽히지 않게 한다. paths로는 스킬이 관여할 경로 범위를 지정한다. 이 밖에 agent·model·shell·user-invocable 등의 키로 실행 환경을 세밀하게 조정한다.

!`cmd` — 동적 컨텍스트 주입

본문에 !`<command>` 형태를 쓰면 해당 명령이 전송 전에 실행되고, 그 출력이 인라인으로 삽입된다. 현재 브랜치명·날짜·파일 목록처럼 매번 달라지는 값을 스킬 본문에 동적으로 끼워 넣을 때 쓴다.

키 / 표기역할비고
allowed-tools 사용 가능한 도구 화이트리스트 disallowed-tools로 금지 목록 지정
disable-model-invocation 모델 자동 발동 차단 사용자 직접 호출 전용 스킬에 사용
user-invocable 사용자 호출 허용 여부 발동 주체 제어
context: fork 격리된 컨텍스트에서 실행 메인 대화 오염 방지
paths 관여 경로 범위 지정 적용 대상 한정
agent / model / shell 실행 에이전트·모델·셸 지정 실행 환경 조정
!`cmd` 전송 전 명령 실행 → 출력 인라인 삽입 본문에 작성하는 동적 주입 표기
Skill은 임의 코드 실행이 가능하고 네트워크를 통한 데이터 유출 위험이 있다. 신뢰된 출처의 스킬만 설치하고, allowed-tools·disable-model-invocation 같은 키로 실행 범위를 좁혀 두는 편이 안전하다.

6. 점진 공개를 위한 번들 구조

Skill의 핵심 설계 원칙은 점진 공개(progressive disclosure)다. 메타데이터만 상시 로드하고, 본문은 발동될 때 읽고, 상세 리소스는 정말 필요할 때만 읽는 3단계 구조다. 이 구조를 살리려면 SKILL.md를 짧게 유지하고 긴 내용은 별도 파일로 분리해야 한다.

  1. Level1 메타데이터: name+description, 항상 로드(스킬당 약 100토큰). 발동 판단에만 쓰인다.
  2. Level2 SKILL.md 본문: 트리거 시 로드, 보통 5K 토큰 미만. 핵심 절차를 담는다.
  3. Level3 번들 리소스: 필요할 때만 읽음. 스크립트는 코드 자체가 아니라 실행 출력만 컨텍스트로 들어온다.

reference/ — 상세 문서 분리

API 명세, 긴 표, 예외 처리 같은 상세 내용은 reference/*.md로 빼낸다. Claude는 본문에서 필요성을 인지했을 때만 이 파일을 읽으므로, 평소에는 토큰을 소비하지 않는다.

scripts/ — 결정적 동작의 스크립트화

매번 똑같이 수행해야 하는 결정적 작업은 scripts/*.py 등으로 분리한다. 자유도가 낮은(반드시 정확해야 하는) 동작일수록 텍스트 지시보다 스크립트가 안정적이다. 스크립트는 실행되어 그 출력만 컨텍스트에 올라오므로 토큰 측면에서도 효율적이다.

my-skill/
├── SKILL.md          # 500줄 이내 권장 — 핵심 절차만
├── reference/
│   └── api.md        # 상세 명세 (필요 시 로드)
└── scripts/
    └── convert.py    # 결정적 동작 (실행 출력만 로드)

SKILL.md는 500줄 이내를 권장한다. 본문이 길어지면 점진 공개의 이점이 사라지고 발동 시마다 큰 컨텍스트를 소비하게 되므로, "Claude가 이미 아는 것"은 적지 말고 간결하게 쓰는 편이 좋다. 발동이 안 될 때는 claude --debug로 SKILL.md의 YAML 파싱을 확인한다.


참고 링크


Claude Docs · Claude Code 2026-06 기준 (MCP 스펙 2025-11-25 / 프로토콜 2025-06-18) | 문서 수집일 2026년 6월 27일