MCP 서버·Skill 개발 실습 예시
SKILL.md 한 장에서 시작해 MCP tool 등록, 스크립트 번들 Skill의 eval까지 직접 만들어 보는 실습 가이드 | ← 레퍼런스 문서로 돌아가기
SKILL.md 한 장으로 끝내는 것이 목표다.
-
Skill 폴더 만들기
프로젝트 루트에서 실행한다. 프로젝트 범위 Skill은
.claude/skills/<name>/아래에 둔다. 폴더 이름은 frontmatter의name과 맞춰commit-helper로 한다.mkdir -p .claude/skills/commit-helper
-
SKILL.md 작성
frontmatter의
name(소문자·숫자·하이픈, 64자 이내)과description(무엇을·언제 쓰는지, 1024자 이내)이 필수다.description에는 Claude가 자동으로 골라 쓰도록 트리거가 될 단어("커밋", "commit")를 담는다. 본문의!`git diff HEAD`는 전송 직전에 실행되어 그 출력이 인라인으로 끼워진다.cat > .claude/skills/commit-helper/SKILL.md <<'EOF' --- name: commit-helper description: 스테이징·작업 트리의 변경을 보고 Conventional Commits 형식의 커밋 메시지 초안을 작성한다. 사용자가 "커밋 메시지 써줘", "commit message" 등을 요청할 때 사용한다. --- # Commit Helper 아래는 현재 작업 트리의 변경 사항이다. !`git diff HEAD` 위 diff를 바탕으로 커밋 메시지를 작성한다. - 첫 줄은 `type(scope): summary` 형식(50자 이내). - type은 feat / fix / docs / refactor / test / chore 중에서 고른다. - 본문에는 무엇을, 왜 바꿨는지 1~3개 항목으로 설명한다. - 추측한 변경 사항은 적지 않는다. diff에 보이는 것만 근거로 삼는다. EOF
-
트리거 확인
새 세션에서 Claude Code를 실행하고 자연어로 요청한다.
description이 요청과 맞아떨어지면 Claude가commit-helperSkill을 스스로 불러온다. 의도대로 떴는지, 다른 작업에서 불필요하게 끼어들지는 않는지 본다.> 지금 변경한 내용으로 커밋 메시지 초안 잡아줘
name과 description(약 100토큰)만 항상 올라가 있다가, 요청이 description과 맞으면 그때 SKILL.md 본문이 컨텍스트로 들어온다. 본문이 로드되는 순간 !`git diff HEAD`가 실행되어 그 출력이 본문 안에 채워지므로, Claude는 "지금 이 저장소의 실제 변경"을 근거로 메시지를 쓴다.
description부터 손본다. "무엇을 하는지 + 언제 쓰는지"를 3인칭으로 구체적으로 쓰고 사용자가 실제로 입력할 법한 단어를 넣는다. YAML이 깨지면 아예 로드되지 않으니 claude --debug로 파싱 결과를 확인한다. 개인 전역으로 쓰려면 같은 폴더 구조를 ~/.claude/skills/에 두면 된다.
get_weather라는 tool을 가진 최소 MCP 서버를 Python으로 만들어, stdio로 실행하고 claude mcp add로 등록한다. 전제: Python 3.10+와 가상환경이 준비돼 있고, 현재 프로젝트 폴더에서 작업한다.
-
SDK 설치
공식 Python SDK 패키지를 설치한다. 가상환경 안에서 설치해야 이후
claude mcp add에 넘길 실행 명령이 그 환경의 인터프리터를 가리킬 수 있다.pip install mcp
-
서버에 tool 등록
FastMCP를 쓰면
@mcp.tool()데코레이터 하나로 함수가 tool이 된다. 함수 시그니처의 타입 힌트가inputSchema로, docstring이 tool 설명으로 변환된다. Claude는 이 설명과 스키마를 보고 호출 여부와 인자를 정한다.# weather_server.py from mcp.server.fastmcp import FastMCP mcp = FastMCP("weather") @mcp.tool() def get_weather(city: str) -> str: """주어진 도시의 현재 날씨를 한 줄로 반환한다. Args: city: 날씨를 조회할 도시 이름 (예: "Seoul") """ # 실제로는 외부 날씨 API를 호출한다. 여기서는 예시 값. return f"{city}: 맑음, 24도, 습도 40%" if __name__ == "__main__": mcp.run(transport="stdio") -
Inspector로 단독 점검
Claude에 붙이기 전에 서버가 tool을 제대로 노출하는지 확인한다. MCP Inspector를 띄우면 브라우저에서
tools/list로 목록을,tools/call로 실제 호출 결과를 직접 테스트할 수 있다.npx @modelcontextprotocol/inspector python weather_server.py
-
Claude Code에 연결
--뒤에 서버 실행 명령을 그대로 적는다. stdio 전송이므로 Claude가 이 명령으로 서버 프로세스를 띄우고 표준입출력으로 JSON-RPC를 주고받는다. 등록 후list·get으로 상태를 확인한다.claude mcp add weather -- python weather_server.py claude mcp list claude mcp get weather
initialize로 protocolVersion과 capabilities를 협상한다. 이후 tools/list로 get_weather의 이름·설명·inputSchema를 받아 두고, 사용자의 요청이 날씨와 닿으면 tools/call로 { "city": "Seoul" } 같은 인자를 보내 결과를 받아온다. tool은 별도 프로세스에서 실행되므로, Skill처럼 모델의 코드 실행 환경 안에서 도는 것이 아니다.
inputSchema는 매 요청마다 컨텍스트를 차지한다. 쓰지 않는 tool이 많은 서버를 늘 켜 두면 토큰만 새므로 필요할 때만 붙이거나 불필요한 tool은 비활성화한다. 또한 로컬 stdio 서버는 파일 삭제·명령 실행까지 할 수 있으니 신뢰할 수 있는 코드만 등록하고, tool 설명에 악성 지시를 심는 prompt injection에 주의한다. 프로젝트 단위로 팀과 공유하려면 .mcp.json에 정의를 커밋한다.
skill-creator 플러그인 설치.
-
번들 구조 잡기
SKILL.md 옆에
scripts/와reference/를 둔다. SKILL.md는 짧게(500줄 이내) 유지하고, 길어지는 규칙·예외 목록은reference/*.md로 빼서 필요할 때만 읽도록 한다. 이렇게 분리하는 것이 점진적 공개의 핵심이다..claude/skills/release-validator/ ├── SKILL.md ├── scripts/ │ └── validate.py └── reference/ └── rules.md -
검증 스크립트 작성
판정 로직은 모델이 아니라 코드가 책임진다. 스크립트는 통과/실패와 사유만 출력하면 된다. 점진적 공개에서 스크립트는 코드 전체가 컨텍스트에 올라오지 않고, 실행 결과(출력)만 모델에게 전달된다.
# scripts/validate.py import sys, re, pathlib def main(path: str) -> int: text = pathlib.Path(path).read_text(encoding="utf-8") errors = [] if not re.search(r"^## \[\d+\.\d+\.\d+\]", text, re.M): errors.append("시맨틱 버전 헤딩(## [x.y.z])이 없습니다.") if "TODO" in text: errors.append("미완성 표시 'TODO'가 남아 있습니다.") if errors: print("FAIL") for e in errors: print(f" - {e}") return 1 print("PASS") return 0 if __name__ == "__main__": sys.exit(main(sys.argv[1])) -
SKILL.md에서 권한 좁히고 리소스 연결
allowed-tools로 이 Skill이 쓸 수 있는 도구를 검증에 필요한 최소로 제한한다. 본문에는 스크립트 실행 방법만 간단히 적고, 세부 규칙은reference/rules.md를 "필요할 때 읽으라"고 가리킨다.--- name: release-validator description: CHANGELOG/릴리스 노트가 팀 규칙(시맨틱 버전 헤딩, 미완성 표시 없음)을 지키는지 검증한다. 사용자가 릴리스 노트나 CHANGELOG 검증을 요청할 때 사용한다. allowed-tools: Bash, Read --- # Release Validator 릴리스 노트를 검증하려면 아래 스크립트를 실행한다. ``` python scripts/validate.py <파일경로> ``` - 출력이 `PASS`면 통과를 알린다. - `FAIL`이면 출력된 사유를 그대로 사용자에게 전달한다. - 규칙의 배경과 예외 처리는 `reference/rules.md`를 읽고 판단한다.
-
skill-creator로 eval 실행
skill-creator플러그인은 Skill 작성과 평가를 돕는다. eval로 다양한 요청에서 의도대로 트리거되는지, 스크립트 호출과 응답이 일관적인지 점검하고 결과에 따라description이나 본문을 다듬는다.# (마켓플레이스 미등록 시 먼저) /plugin marketplace add anthropics/claude-plugins-official /plugin install skill-creator@claude-plugins-official # 이후 skill-creator의 안내에 따라 release-validator에 대한 eval을 실행
· Level 1 —
name + description(약 100토큰)만 항상 상주.
· Level 2 — 요청이 트리거되면 SKILL.md 본문(보통 5K토큰 미만)이 로드.
· Level 3 — 본문이 가리킬 때에만
reference/rules.md를 읽고, scripts/validate.py는 코드가 아니라 실행 출력만 컨텍스트로 들어온다.
덕분에 규칙이 길고 로직이 복잡해도 평소 컨텍스트 비용은 메타데이터 수준으로 유지된다.
allowed-tools로 권한을 좁히면 의도치 않은 파일 수정·네트워크 접근을 막을 수 있다. 팀 배포는 .claude/skills를 git에 커밋하거나 플러그인 마켓플레이스(/plugin marketplace add <repo>)로 배포한다. 단, Skill은 Anthropic 제품 전용이라 다른 클라이언트에서는 재사용되지 않는다.
Claude Docs · Claude Code 2026-06 기준 (MCP 스펙 2025-11-25 / 프로토콜 2025-06-18) | 2026년 6월 27일 작성
← 개발 레퍼런스