Model Context Protocol (모델 컨텍스트 프로토콜)

한 줄 정의

Model Context Protocol(MCP)은 AI 클라이언트(Claude Code, Cursor, Copilot 등)와 데이터 소스 또는 백엔드 도구 간에 컨텍스트, 리소스, 실행 권한을 주고받기 위한 합의된 오픈소스 통신 표준 프로토콜이다.

핵심 요지

  • 통합 어댑터 아키텍처: 각 AI 클라이언트(Anthropic 도구 스키마, Cursor API, OpenAI 함수 호출 스키마 등)가 개별적으로 요구하던 서로 다른 wrapper 규격을 하나의 표준 프로토콜로 통합하여 AI 도구 연동 비용을 획기적으로 낮춘다.
  • 클라이언트 주도의 비대칭 구조: 클라이언트가 서버 구동, 도구 호출, 렌더링 방식의 통제권을 갖고, 서버는 가용 기능(도구, 리소스, 프롬프트)을 나열 및 실행하는 단순 반응형 구조를 취하여 가벼운 프로토콜 스펙을 유지한다.
  • 3대 핵심 기능 명세:
    • 도구(Tools): 에이전트가 호출 가능한 실행형 함수 (가장 널리 쓰임).
    • 리소스(Resources): 에이전트가 컨텍스트 구축 시 참고하는 읽기 전용 데이터 (데이터베이스 스키마, 파일 등).
    • 프롬프트(Prompts): 사용자가 자연어로 실행할 수 있는 슬래시 명령어 형식의 템플릿.
  • 로컬 친화적 전송 계층: 로컬 환경에서는 네트워크 스택 없이 표준 입출력(stdio) 기반의 자식 프로세스 통신을 기본으로 삼아 보안 경계 내에서 유연하게 작동한다.
  • 엔터프라이즈 클라우드 호스팅: 로컬 stdio 형태를 넘어 분산형 소형 특화 서버, 중앙 레지스트리, Envoy 프록시 및 JWT/SPIFFE 보안 인증을 결합한 클라우드 호스팅 생태계로 확장할 수 있다.

상세

1. 전송 모드와 보안 아키텍처

① stdio 모드 (로컬 개발)

  • 호스트 애플리케이션이 MCP 서버 스크립트를 자식 프로세스로 직접 실행하여 stdin/stdout을 통해 JSON-RPC 메시지를 교환한다.
  • 포트 개방, TLS, 네트워크 인증이 불필요하므로 로컬 파일 제어나 git 정보 검색 등에 가장 안전하고 성능상 유리하다.

② Streamable HTTP 모드 (네트워크 연동)

  • 여러 요청 간 서버 상태 유지 및 이기종 클라우드 환경 연동 시 사용한다.
  • 2025년 3월 스펙 업데이트로 SSE(Server-Sent Events) 방식은 deprecated 되었으며, Streamable HTTP 전송 방식을 사용해야 한다.
  • 외부망 노출에 대비하여 Dynamic Client Registration 기반의 OAuth 2.1 인증 레이어가 필수적이다.

③ 엔터프라이즈 클라우드 분산 아키텍처 (Pinterest 사례)

  • 도메인별 특화 소형 서버 분산: 거대한 모놀리식 서버 대신 Presto, Spark, Airflow, Knowledge 등 도메인별 소형 특화 서버로 분산시켜 모델의 컨텍스트 오버헤드를 줄이고 개별 보안 권한을 세밀하게 제어한다.
  • 중앙 레지스트리(Registry): 웹 UI 및 API를 통해 정식 승인된 서버들의 목록과 지원 부서, 보안 수준, 노출 도구를 규정하는 단일 소스(Source of Truth) 역할을 한다.
  • 2단계 보안 장벽 (AuthN/AuthZ):
    1. 최종 사용자 흐름: 클라이언트가 백그라운드에서 사용자 세션과 OAuth 흐름을 연동(piggyback)해 JWT를 획득하고, Envoy 프록시가 JWT 서명을 검증해 X-Forwarded-User, X-Forwarded-Groups 헤더를 삽입한다.
    2. 서버 코드 미세 인가: 서버 코드 내에서 @authorize_tool(policy="...") 형태의 데코레이터를 이용해 광고 수익 조회 등의 민감 도구를 특정 그룹(예: Ads-eng)에만 노출되도록 제약한다.
    3. 서비스 전용 흐름: 사용자가 직접 관여하지 않는 읽기 전용 인프라 작업에서는 SPIFFE(서비스 메시 ID)를 기반으로 인가를 마친다.
  • 인간 개입 (Human-in-the-loop): 고비용 연산이나 데이터 덮어쓰기(Overwrite) 같은 민감한 인프라 제어 도구는 모델이 실행 계획을 제시한 뒤 엔지니어의 수동 승인을 받도록 설계하며, 표준 스펙의 응답 유도(elicitation) 경고창을 활용한다.

2. 로컬 개발 및 디버깅 루프

  • 인스펙터(Inspector) 활용: AI 클라이언트는 챗 UI가 끊기지 않도록 스키마 불일치나 오류를 무시하거나 사과 수준에서 챗을 이어가므로, 직접 연동 테스트를 하면 버그를 발견하기 어렵다. 공식 @modelcontextprotocol/inspector 브라우저 UI 대시보드(npx @modelcontextprotocol/inspector python <filename>.py)를 띄워 JSON-RPC 교환 명세 및 도구를 격리 환경에서 실측해야 한다.
  • 표준 출력 오염(Standard-output pollution) 주의: stdio 통신 경로 상에서 print() 함수를 사용하면 JSON-RPC 스트림이 파괴되어 통신 오류를 유발한다. 디버깅 로그는 반드시 파이썬 logging 모듈을 활용하여 표준 에러(stderr) 채널로 출력해야 한다.
  • 3대 에러 모드:
    1. 스키마 불일치(Schema Mismatch): 타입 힌트와 어긋난 데이터 반환 시 발생.
    2. 기능 누락(Missing Capability): 서버가 명세하지 않은 도구를 외부에서 호출할 때 발생.
    3. 표준 출력 오염: print() 등 stdout 채널의 불필요한 출력으로 인한 RPC 파괴.
  • 핫 리로딩(Hot-Reloading): FastMCP는 코드 변경을 감지하면 자동으로 서버 프로세스를 재시작하는 개발 모드를 지원한다.

3. MCP 도입의 제한점 및 경계선

  • 지연 시간(Latency) 오버헤드: JSON-RPC 직렬화 및 IPC/네트워크 프로세스 왕복 통신으로 인해 오버헤드가 수반되므로, 1밀리초 미만(sub-millisecond)의 반응 속도를 요구하는 루프 크리티컬 경로에는 적합하지 않다.
  • 스트리밍 미지원: 도구의 반환 결과를 실시간 청크로 쪼개어 스트리밍하는 구조는 현재 스펙에서 지원하지 않는다 (향후 로드맵 대기).
  • 인터랙티브 UI 격리: 데이터와 렌더링 프레젠테이션 레이어를 격리하므로, 커스텀 UI 화면이나 동형 인터랙션 뷰 자체를 서버에서 클라이언트로 내려 보낼 수 없다.
  • 다이렉트 연동의 효율성: 클라이언트와 백엔드를 모두 단일 팀이 통제하고 향후 플랫폼 확장 계획이 없다면, 굳이 MCP 레이어를 끼워 넣는 것보다 다이렉트 연동이 유지보수 측면에서 나을 수 있다.

4. 성공 메트릭 및 관측 가능성 (Observability)

  • 엔터프라이즈 환경에서는 공용 텔레메트리 라이브러리를 통해 호출량, 입출력 로그, 장애를 모니터링한다.
  • 북극성 지표 (절약된 시간 - Time Saved): 핀터레스트의 경우 월간 66,000회의 도구 호출을 수행하는 844명의 실사용 개발자군을 기반으로, 도구 사용당 절약 시간을 실측하여 월 7,000시간에 상응하는 리소스 절약 메트릭을 달성했다.

예시

Python FastMCP 도구 및 리소스 구현

FastMCP 패키지를 사용해 git 로그 및 스탠드업 요약을 지원하는 단일 파일 구현 예시다.

# standup_server.py
import subprocess
from typing import TypedDict
from fastmcp import FastMCP
 
mcp = FastMCP("standup-helper")
 
class StandupSummary(TypedDict):
    branch: str
    since: str
    commit_count: int
    commits: list[str]
 
# 1. 실행 가능한 도구 (Tool) 정의
@mcp.tool()
def summarize_standup(branch: str = "main", since: str = "yesterday") -> StandupSummary:
    """Summarize recent git activity for a standup.
    
    Reads the local git log on the given branch since the given time window.
    Returns commit count and one-line subjects.
    """
    try:
        result = subprocess.run(
            ["git", "log", f"--since={since}", "--pretty=format:%h %s", branch],
            capture_output=True,
            text=True,
            timeout=5, # 무한 대기 방지용 타임아웃
            check=True
        )
    except (subprocess.CalledProcessError, subprocess.TimeoutExpired) as exc:
        return {
            "branch": branch,
            "since": since,
            "commit_count": 0,
            "commits": [f"git error: {exc}"]
        }
 
    lines = [line for line in result.stdout.splitlines() if line]
    return {
        "branch": branch,
        "since": since,
        "commit_count": len(lines),
        "commits": lines
    }
 
# 2. 읽기 전용 리소스 (Resource) 정의
@mcp.resource("recent_commits://main")
def recent_commits_main() -> str:
    """Last 10 commits on the main branch, plain text."""
    result = subprocess.run(
        ["git", "log", "-n", "10", "--pretty=format:%h %ad %s", "--date=short", "main"],
        capture_output=True,
        text=True,
        timeout=5
    )
    return result.stdout or "(no commits found)"
 
# 3. 사용자 프롬프트 템플릿 (Prompt) 정의
@mcp.prompt("standup_template")
def standup_template(focus: str = "shipping work") -> str:
    """Reusable standup question prompt template."""
    return (
        f"Summarize what I worked on yesterday, focusing on {focus}. "
        f"Use the summarize_standup tool to get the git log, then write a one-paragraph standup note."
    )
 
if __name__ == "__main__":
    mcp.run() # 기본값 stdio 구동

AI 클라이언트 설정 (mcpServers 연동)

동일한 stdio 설정 스펙을 이용해 Claude Code, Cursor, Claude Desktop 등에 단 한 줄의 어댑터 없이 연결할 수 있다.

{
  "mcpServers": {
    "standup-helper": {
      "command": "python3",
      "args": ["/Users/railscraft/code/standup_server.py"]
    }
  }
}

충돌

현재 확인된 충돌 없음.

관련 노트