MCP 서버·Skill 개발 — 레퍼런스
MCP 서버와 Agent Skill을 직접 만들 때 필요한 SDK·구조·작성 규칙 정리 | 공식 문서: modelcontextprotocol.io · code.claude.com/docs/en/skills
claude --debug로 SKILL.md YAML 파싱 확인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 인터페이스를 노출하므로, 클라이언트 입장에서는 구현 언어가 보이지 않는다.
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 요청/응답으로 시작한다. 이 단계에서 호스트와 서버가 protocolVersion과 capabilities를 협상한 뒤 정상 통신으로 넘어간다. 알림(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의 필수 키는 name과 description 두 개다. 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` |
전송 전 명령 실행 → 출력 인라인 삽입 | 본문에 작성하는 동적 주입 표기 |
allowed-tools·disable-model-invocation 같은 키로 실행 범위를 좁혀 두는 편이 안전하다.6. 점진 공개를 위한 번들 구조
Skill의 핵심 설계 원칙은 점진 공개(progressive disclosure)다. 메타데이터만 상시 로드하고, 본문은 발동될 때 읽고, 상세 리소스는 정말 필요할 때만 읽는 3단계 구조다. 이 구조를 살리려면 SKILL.md를 짧게 유지하고 긴 내용은 별도 파일로 분리해야 한다.
- Level1 메타데이터:
name+description, 항상 로드(스킬당 약 100토큰). 발동 판단에만 쓰인다. - Level2 SKILL.md 본문: 트리거 시 로드, 보통 5K 토큰 미만. 핵심 절차를 담는다.
- 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 파싱을 확인한다.
참고 링크
- 📖 Model Context Protocol 공식 (modelcontextprotocol.io)
- 🧩 Claude Code — Skills 문서
- ⚙️ Agent Skills Overview (platform.claude.com)
Claude Docs · Claude Code 2026-06 기준 (MCP 스펙 2025-11-25 / 프로토콜 2025-06-18) | 문서 수집일 2026년 6월 27일