콘텐츠로 이동

Lab 03: MCP 서버 구현

초급 마감: 2026-03-24
  • FastMCP로 MCP 서버 구현 (Tool + Resource + Prompt 3대 프리미티브)
  • MCP Inspector로 서버 검증
  • 입력 검증과 경로 순회 방어 적용
  • 파일시스템 + Git MCP 서버 구성

MCP는 AI 코딩 에이전트가 외부 도구·데이터 소스와 표준화된 방식으로 통신하는 프로토콜이다. 3대 프리미티브 — Tool(모델이 호출), Resource(앱이 제어), Prompt(사용자가 선택) — 를 통해 기능을 노출한다.

AI 코딩 에이전트
MCP 클라이언트
MCP 서버 (Tool + Resource + Prompt)
파일시스템 / Git / 커스텀 API

AI 코딩 CLI의 설정 파일에 내장 서버를 추가한다.

설정 파일: ~/.claude/settings.json (또는 프로젝트별 .claude/settings.json)

{
"mcpServers": {
"filesystem": {
"command": "npx",
"args": [
"-y",
"@modelcontextprotocol/server-filesystem",
"/home/[학번]/lab-03"
]
},
"git": {
"command": "npx",
"args": [
"-y",
"@modelcontextprotocol/server-git",
"--repository",
"/home/[학번]/lab-03"
]
}
}
}
  1. 프로젝트 구조 및 환경 설정

    Terminal window
    mkdir -p lab-03-mcp && cd lab-03-mcp
    python -m venv .venv
    source .venv/bin/activate
    # uv 사용 시 (권장)
    uv add "mcp[cli]" fastmcp
    # pip 사용 시
    pip install fastmcp
    lab-03-mcp/
    ├── custom_server.py # FastMCP 서버 (Tool + Resource + Prompt)
    ├── settings.json # MCP 설정
    └── test_sample.py # custom 서버의 run_pytest로 실행할 테스트
  2. 내장 서버 목록 확인

    Terminal window
    claude mcp list
    # filesystem npx -y @modelcontextprotocol/server-filesystem ...
    # git npx -y @modelcontextprotocol/server-git ...
  3. 파일시스템 서버 테스트

    Terminal window
    claude "현재 디렉터리의 파일 목록을 보여줘."
    # filesystem 서버의 list_directory 도구가 호출되어야 함
  4. Git 서버 테스트

    Terminal window
    git init /home/[학번]/lab-03
    claude "최근 커밋 3개의 메시지를 요약해줘."

3. 커스텀 MCP 서버 — custom_server.py

섹션 제목: “3. 커스텀 MCP 서버 — custom_server.py”

FastMCP로 3대 프리미티브(Tool + Resource + Prompt)를 모두 포함하는 서버를 구현한다.

custom_server.py
import os
import subprocess
import sys
from fastmcp import FastMCP
mcp = FastMCP("lab03-custom", description="Lab 03 실습 서버")
# === 허용 경로 설정 (경로 순회 방어) ===
ALLOWED_BASE = os.path.realpath(os.path.dirname(__file__))
def _validate_path(path: str) -> str:
"""경로 순회 공격을 방어한다. 허용된 디렉터리 외부 접근을 차단."""
real_path = os.path.realpath(path)
if not real_path.startswith(ALLOWED_BASE):
raise ValueError(
f"접근 거부: {path} — 허용 범위({ALLOWED_BASE}) 외부"
)
return real_path
# === Tool 1: pytest 실행 ===
@mcp.tool()
def run_pytest(path: str) -> str:
"""지정한 디렉터리에서 pytest를 실행하고 결과를 반환한다.
Args:
path: pytest를 실행할 디렉터리 경로
"""
safe_path = _validate_path(path)
try:
result = subprocess.run(
["python", "-m", "pytest", safe_path, "-v", "--tb=short"],
capture_output=True,
text=True,
timeout=60,
)
return result.stdout + result.stderr
except subprocess.TimeoutExpired:
return "ERROR: pytest 실행 시간 초과 (60초)"
except Exception as e:
print(f"[ERROR] {e}", file=sys.stderr)
return f"ERROR: {e}"
# === Tool 2: 라인 수 카운트 ===
@mcp.tool()
def count_lines(file_path: str) -> str:
"""파일의 라인 수를 반환한다.
Args:
file_path: 라인을 셀 파일의 경로
"""
safe_path = _validate_path(file_path)
try:
with open(safe_path, "r") as f:
lines = f.readlines()
return f"{len(lines)} lines"
except FileNotFoundError:
return f"ERROR: 파일을 찾을 수 없습니다 — {file_path}"
except Exception as e:
print(f"[ERROR] {e}", file=sys.stderr)
return f"ERROR: {e}"
# === Resource: 프로젝트 통계 ===
@mcp.resource("project://stats")
def project_stats() -> str:
"""프로젝트 디렉터리의 파일 통계를 반환한다."""
py_files = []
for root, _dirs, files in os.walk(ALLOWED_BASE):
for f in files:
if f.endswith(".py"):
py_files.append(os.path.join(root, f))
total_lines = 0
for fp in py_files:
try:
with open(fp) as f:
total_lines += len(f.readlines())
except Exception:
pass
return (
f"Python 파일 수: {len(py_files)}\n"
f"총 라인 수: {total_lines}\n"
f"프로젝트 경로: {ALLOWED_BASE}"
)
# === Prompt: 코드 리뷰 ===
@mcp.prompt()
def code_review(file_path: str) -> str:
"""코드 리뷰를 요청하는 구조화된 프롬프트 템플릿.
Args:
file_path: 리뷰할 파일의 경로
"""
return (
f"다음 파일을 코드 리뷰해 주세요: {file_path}\n\n"
f"리뷰 항목:\n"
f"1. 보안 취약점 (경로 순회, 명령 주입, 입력 검증)\n"
f"2. 에러 핸들링 (예외 처리, 안전한 실패)\n"
f"3. 코드 품질 (가독성, 네이밍, 구조)\n"
f"4. 테스트 가능성 (테스트하기 쉬운 설계인가)"
)
if __name__ == "__main__":
mcp.run()

settings.json에 커스텀 서버를 추가한다.

{
"mcpServers": {
"filesystem": { "...": "..." },
"git": { "...": "..." },
"custom": {
"command": "python",
"args": ["/home/[학번]/lab-03-mcp/custom_server.py"]
}
}
}
Terminal window
# Inspector 실행
npx @modelcontextprotocol/inspector python custom_server.py
# 브라우저에서 http://localhost:6274 접속 후:
# 1. tools/list → run_pytest, count_lines 확인
# 2. resources/list → project://stats 확인
# 3. prompts/list → code_review 확인
# 4. tools/call → run_pytest 실행, 결과 확인
# 5. 스크린샷 캡처 (과제 제출용)
Terminal window
claude "lab-03-mcp 디렉터리에서 pytest를 실행해줘."
# run_pytest 도구가 호출되어야 함

assignments/lab-03/[학번]/에 PR:

  • settings.json — filesystem, git, custom 세 서버가 등록된 설정 파일
  • custom_server.py — FastMCP 기반, 3대 프리미티브(Tool + Resource + Prompt) 구현
  • test_sample.py — custom 서버의 run_pytest로 실행할 테스트 파일 (최소 3개 테스트)
  • Inspector 스크린샷 — tools/list, resources/list, prompts/list 확인
  • README.md — 각 MCP 서버의 동작 확인 결과, 보안 검증(경로 순회 차단 테스트), 트러블슈팅 기록