NxtCloud NxtCloud Workshop / Claude Code & MCP 핸즈온
로그인

Lab 06. MCP 실습

이 랩에서 다루는 것
  • Database MCP: 읽기 전용으로 PostgreSQL 을 붙여 자연어로 조회
  • 내 MCP 서버 만들기: FastMCP 로 직접 만들어 Claude Code 에서 호출하며 원리를 체득
  • 이중 방어: MCP 레이어 + 계정 권한
  • 관리 & 보안 체크리스트: 끝나고 무엇을 정리하나
claude mcp add 문법 복습
claude mcp add [옵션] <이름> -- <실행명령> [인자...]
  • 환경변수는 --env KEY=value (공식 문법)
  • 전송은 기본 stdio(로컬 프로세스), 원격 HTTP 는 --transport http
  • 비밀값이 들어가는 서버는 local(기본) 또는 user scope 로 (커밋되는 .mcp.json 금지)

확인·삭제.

claude mcp list            # 연결 상태
claude mcp remove <>   # 삭제
핸즈온 A: Database MCP (PostgreSQL, 읽기 전용)

자연어를 SQL 로 바꿔 DB 를 조회합니다. 읽기 전용으로 붙이는 게 안전의 핵심입니다.

강사가 배포한 접속 문자열을 사용합니다. 비밀값이 들어가므로 local scope(기본)로 붙입니다.

claude mcp add postgres \
  --env DATABASE_URI="postgresql://student:READONLYPASS@<RDS엔드포인트>:5432/shopdb?sslmode=require" \
  -- docker run -i --rm -e DATABASE_URI crystaldba/postgres-mcp --access-mode=restricted
  • --access-mode=restricted: 읽기 전용 트랜잭션 + 실행시간 제한
  • Docker 대신 Python 이면 마지막 부분만 -- uvx postgres-mcp --access-mode=restricted 로 교체

🐧 WSL2 · Linux 사용자 (1회 설정): 위 명령은 sudo 없이 docker 를 호출합니다. permission denied ... /var/run/docker.sock 오류가 나면 현재 사용자를 docker 그룹에 한 번 추가하세요. (Docker Desktop · macOS 사용자는 불필요)

sudo usermod -aG docker $USER   # docker 그룹에 현재 사용자 추가
newgrp docker                   # 새 그룹 권한을 현재 셸에 즉시 적용 (재로그인 대체)

연결을 확인하고 세션을 엽니다.

claude mcp list      # postgres: connected
claude

세션에서 이렇게 질문해 봅니다.

“주문이 가장 많은 도시 top 3 을 알려줘.” “월별 매출(주문금액 합계) 추이를 표로 보여줘.” “가장 많이 팔린 상품 top 3 은?”

에이전트가 SELECT 를 작성·실행하고 결과를 요약합니다.

🔒 이중 방어: 읽기 전용은 MCP 레이어(restricted) + DB 계정 권한(SELECT only) 두 겹으로 막습니다. 접속 문자열은 project scope(.mcp.json)에 넣지 마세요(커밋되어 유출).

🎯 체크포인트
  • claude mcp listpostgres: connected 표시
  • 세 가지 질의 중 하나 이상을 실행해 결과를 받았다
핸즈온 B: 내 MCP 서버 직접 만들기

지금까지는 남이 만든 MCP 를 붙였습니다. 이번엔 직접 만들어 원리를 안에서 봅니다. 외부 인증·계정이 필요 없습니다.

사전 요구: Python 3.10+ 와 uv. 서버는 FastMCP 로 작성합니다.

1. 서버 파일 작성

my_mcp.py 한 파일이면 됩니다. 도구(tool) 두 개를 노출합니다.

# my_mcp.py — 내 첫 MCP 서버
from fastmcp import FastMCP

mcp = FastMCP("my-first-mcp")

@mcp.tool()
def course_secret() -> str:
    """이 수업의 비밀 코드를 반환한다. 모델은 이 값을 추측할 수 없다."""
    return "NXT-MCP-2026"

@mcp.tool()
def add(a: int, b: int) -> int:
    """두 정수를 더한다."""
    return a + b

if __name__ == "__main__":
    mcp.run()   # 기본 stdio 전송 (Lab 05 의 그 stdio)

2. 등록

claude 세션 , my_mcp.py 가 있는 디렉터리에서 붙입니다. 로컬 프로세스이므로 local scope(기본)면 충분합니다.

claude mcp add my-first-mcp -- uv run --with fastmcp python "$(pwd)/my_mcp.py"
  • uv run --with fastmcp: fastmcp 를 즉석 설치해 실행(별도 설치 불필요)
  • "$(pwd)/my_mcp.py": 등록 시점에 절대경로로 고정

3. 확인 & 호출

claude mcp list      # my-first-mcp: connected
claude

세션에서 이렇게 요청합니다.

“이 수업의 비밀 코드를 알려줘.” “17 더하기 25는?”

에이전트가 내 서버의 course_secret · add 도구를 직접 호출해 답합니다.

💡 호출을 눈으로 확인: course_secret 값(NXT-MCP-2026)은 모델이 추측할 수 없습니다. 이 값이 돌아왔다면 Claude 가 내 서버를 실제로 호출했다는 증거입니다. 방금 만든 게 Lab 05 의 Server, Claude Code 가 Host + Client, mcp.run()stdio 전송입니다.

🎯 체크포인트
  • claude mcp listmy-first-mcp: connected 표시
  • 세션에서 비밀 코드(NXT-MCP-2026)를 받아 봤다 (= 내 서버가 호출됨)
  • Host · Client · Server 가 각각 무엇이었는지 말할 수 있다
마무리: 관리 & 보안 체크리스트
claude mcp list                 # playwright · postgres · my-first-mcp 확인
claude mcp remove <>        # 정리

보안 체크리스트

  • 비밀값은 local/user scope 에만 (.mcp.json 커밋 금지)
  • DB 는 읽기 전용 (MCP restricted + 계정 SELECT only)
  • 브라우저는 신뢰된 사이트만 (간접 인젝션 주의)
  • 외부 서버는 신뢰할 수 있는 것만 (archived/취약 서버 금지)
  • 수업 후 정리: 실습 MCP 제거(claude mcp remove), DB 접속 정보 폐기·회전

한 문장: MCP 는 강력하지만 기본 인증이 없다. 그래서 ‘무엇을 연결하느냐’만큼 ‘어떤 권한으로 연결하느냐’가 중요하다.

마무리

여기까지 오면 Claude Code 의 한 바퀴를 다 돈 것입니다.

  • 생태계 에서 Claude Code 의 자리를 잡고
  • 설치 & Bedrock 인증 으로 내 머신에 올리고
  • 기본 동작법 으로 세션·권한·CLAUDE.md 를 다루고
  • MCP 를 체험 → 이론 → 실습으로 붙여 외부 세계와 연결했습니다

다음으로: 본인 업무에서 자주 쓰는 외부 도구·데이터 하나를 골라 MCP 로 붙여 보세요. ‘무엇을, 어떤 권한으로’ 를 먼저 정하는 습관이 그대로 이어집니다.