MCP 개념

Model Context Protocol(MCP)은 AI 어시스턴트가 외부 데이터 소스 및 도구와 통신할 수 있도록 하는 개방형 프로토콜입니다. MCP를 통해 Claude와 같은 LLM은 파일 시스템, 데이터베이스, API 등 다양한 컨텍스트 소스에 표준화된 방식으로 접근할 수 있습니다.

업데이트 안내: 모델/요금/버전/정책 등 시점에 민감한 정보는 변동될 수 있습니다. 최신 내용은 공식 문서를 확인하세요.
빠른 시작
  • MCP는 Anthropic이 개발한 오픈 프로토콜
  • JSON-RPC 2.0 기반의 클라이언트-서버 아키텍처
  • stdio, HTTP SSE 등 다양한 전송 계층 지원
  • Tools, Resources, Prompts 세 가지 핵심 개념

MCP란 무엇인가?

정의

Model Context Protocol(MCP)은 AI 모델이 외부 데이터 소스 및 도구와 상호작용할 수 있도록 설계된 개방형 표준 프로토콜입니다. 2024년 Anthropic이 공개한 MCP는 AI 어시스턴트의 컨텍스트 제공 방식을 표준화하여, 다양한 데이터 소스를 통합하고 확장 가능한 생태계를 구축하는 것을 목표로 합니다.

개념
┌─────────────────────────────────────────────────────────┐
│  MCP의 핵심 가치                                          │
├─────────────────────────────────────────────────────────┤
│  1. 표준화: 모든 데이터 소스에 일관된 인터페이스 제공      │
│  2. 확장성: 새로운 도구와 리소스를 쉽게 추가              │
│  3. 재사용성: 한 번 작성한 서버를 여러 클라이언트에서 사용 │
│  4. 분리: 데이터 제공자와 AI 모델의 독립적 개발           │
└─────────────────────────────────────────────────────────┘

개발 배경

전통적으로 AI 어시스턴트는 다음과 같은 문제에 직면했습니다:

  • 컨텍스트 제한: 모델의 지식 컷오프 이후 정보나 실시간 데이터에 접근 불가
  • 통합 복잡도: 각 데이터 소스마다 커스텀 통합 코드 필요
  • 유지보수 부담: API 변경 시 모든 통합 코드 수정 필요
  • 재사용 불가: 한 어시스턴트용 통합을 다른 어시스턴트에서 재사용 어려움

MCP는 이러한 문제를 해결하기 위해 설계되었습니다.

기존 방식 vs MCP
측면 기존 방식 MCP
통합 방식 각 데이터 소스마다 커스텀 코드 표준 프로토콜
재사용성 낮음 높음
유지보수 복잡 단순
확장성 제한적 우수

주요 사용 사례

예제
// 1. 파일 시스템 접근
"프로젝트 src/ 디렉토리의 모든 TypeScript 파일을 분석해줘"
→ MCP 파일시스템 서버가 파일 목록과 내용 제공

// 2. 데이터베이스 쿼리
"지난 30일간 매출 상위 10개 제품을 보여줘"
→ MCP PostgreSQL 서버가 데이터베이스 쿼리 실행

// 3. API 통합
"GitHub에서 open 상태인 이슈 중 'bug' 레이블이 있는 것들을 찾아줘"
→ MCP GitHub 서버가 GitHub API 호출

// 4. 도구 실행
"이 Python 코드를 실행하고 결과를 보여줘"
→ MCP 실행 환경 서버가 코드 실행

MCP 아키텍처

전체 구조

AI 클라이언트 Claude Desktop Claude CLI Custom Client MCP 프로토콜 JSON-RPC 2.0 Transport Layer (stdio / HTTP SSE) MCP 서버 Filesystem Database API Integrations 데이터 소스 파일 시스템 로컬 파일 디렉토리 데이터베이스 PostgreSQL SQLite 웹 API GitHub Slack 기타 검색 엔진 실행 환경 메시지 흐름 1. 클라이언트: "list tools" 요청 2. 서버: 사용 가능한 도구 목록 응답 3. 클라이언트: "call tool" 요청 (매개변수 포함) 4. 서버: 도구 실행 및 결과 반환 5. 클라이언트: 결과를 AI 모델에 전달 6. AI: 결과를 기반으로 응답 생성

핵심 컴포넌트

1. MCP 클라이언트

AI 어시스턴트 애플리케이션이 MCP 서버와 통신하기 위해 구현하는 컴포넌트입니다.

TypeScript
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";

// 클라이언트 생성
const client = new Client({
  name: "my-ai-client",
  version: "1.0.0"
}, {
  capabilities: {
    tools: {}
  }
});

// 서버에 연결 (stdio 전송)
const transport = new StdioClientTransport({
  command: "python",
  args: ["server.py"]
});

await client.connect(transport);

// 사용 가능한 도구 목록 조회
const tools = await client.listTools();
console.log(tools);

2. MCP 서버

실제 데이터 소스나 도구에 접근하여 AI 클라이언트에게 기능을 제공하는 서비스입니다.

Python
from mcp.server import Server
from mcp.server.stdio import stdio_server

# 서버 생성
server = Server("my-mcp-server")

# 도구 등록
@server.tool()
async def read_file(path: str) -> str:
    """파일 내용을 읽습니다."""
    with open(path, 'r') as f:
        return f.read()

# 서버 실행
async def main():
    async with stdio_server() as streams:
        await server.run(
            streams[0],
            streams[1],
            server.create_initialization_options()
        )

3. 전송 계층 (Transport Layer)

클라이언트와 서버 간 메시지 전송을 담당합니다.

전송 방식 특징 사용 사례
stdio 표준 입출력 사용, 프로세스 간 통신 로컬 서버, CLI 도구
HTTP SSE Server-Sent Events, 단방향 스트리밍 원격 서버, 웹 애플리케이션
WebSocket (계획 중) 양방향 실시간 통신 대화형 애플리케이션

프로토콜 계층

애플리케이션 계층 Tools, Resources, Prompts 프로토콜 계층 JSON-RPC 2.0 메시지 전송 계층 stdio / HTTP SSE / WebSocket 네트워크 계층 TCP/IP, Local IPC

MCP vs 기존 방식

Function Calling과의 비교

측면 Function Calling MCP
정의 위치 API 요청 시 함수 정의 전달 서버에서 도구 정의 관리
실행 주체 클라이언트 애플리케이션 MCP 서버
재사용성 낮음 (매번 정의 필요) 높음 (서버 재사용)
상태 관리 클라이언트 서버 (가능)
복잡도 간단한 도구에 적합 복잡한 통합에 적합
비교 예제
// Function Calling 방식
const response = await anthropic.messages.create({
  model: "claude-4-5",
  messages: [{ role: "user", content: "파일 읽어줘" }],
  tools: [{
    name: "read_file",
    description: "파일을 읽습니다",
    input_schema: {
      type: "object",
      properties: {
        path: { type: "string" }
      }
    }
  }]
});

// 도구 실행은 클라이언트가 직접 처리
if (response.stop_reason === "tool_use") {
  const result = await readFile(toolUse.input.path);
  // 결과를 다시 Claude에게 전달...
}

// ─────────────────────────────────────

// MCP 방식
const client = new MCPClient();
await client.connect(fileSystemServer);

// 도구 목록은 서버에서 자동 제공
const tools = await client.listTools();

// 도구 실행도 서버가 처리
const result = await client.callTool("read_file", {
  path: "/path/to/file"
});

Tool Use와의 관계

MCP는 Tool Use를 보완하고 확장하는 프로토콜입니다:

  • Tool Use: AI 모델이 도구를 호출하도록 요청하는 기능 (모델 레벨)
  • MCP: 도구를 정의하고 실행하는 표준 프로토콜 (인프라 레벨)
통합 예제

Claude의 Tool Use 기능과 MCP를 함께 사용하면:

  1. MCP 서버가 사용 가능한 도구 목록 제공
  2. 클라이언트가 이를 Tool Use 형식으로 변환하여 Claude에게 전달
  3. Claude가 도구 사용 결정
  4. 클라이언트가 MCP 서버를 통해 도구 실행
  5. 결과를 Claude에게 반환

직접 API 통합과의 비교

비교
// 직접 API 통합 (각 클라이언트에서 반복)
class GitHubIntegration {
  constructor(token) {
    this.token = token;
  }

  async listIssues(repo) {
    const response = await fetch(`https://api.github.com/repos/${repo}/issues`, {
      headers: { Authorization: `token ${this.token}` }
    });
    return response.json();
  }

  // 수십 개의 메서드...
}

// ─────────────────────────────────────

// MCP 방식 (한 번만 작성, 모든 클라이언트에서 재사용)
const githubServer = await client.connect("github-mcp-server");

// 모든 GitHub 기능을 표준 인터페이스로 사용
const issues = await client.callTool("github_list_issues", {
  repo: "anthropics/anthropic-sdk-python"
});

프로토콜 개요

JSON-RPC 2.0

MCP는 JSON-RPC 2.0 표준을 기반으로 합니다.

JSON
// 요청 메시지
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "read_file",
    "arguments": {
      "path": "/home/user/example.txt"
    }
  }
}

// 응답 메시지
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "content": [
      {
        "type": "text",
        "text": "Hello, MCP!"
      }
    ]
  }
}

// 에러 응답
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32602,
    "message": "Invalid params",
    "data": {
      "details": "File not found"
    }
  }
}

메시지 유형

유형 설명 예제
Request 클라이언트 → 서버 요청 tools/list, tools/call
Response 서버 → 클라이언트 응답 성공 시 result, 실패 시 error
Notification 응답 불필요한 알림 notifications/progress

핵심 메서드

1. 초기화

JSON
// initialize 요청
{
  "jsonrpc": "2.0",
  "id": 0,
  "method": "initialize",
  "params": {
    "protocolVersion": "2024-11-05",
    "clientInfo": {
      "name": "Claude Desktop",
      "version": "1.0.0"
    },
    "capabilities": {
      "tools": {}
    }
  }
}

// initialize 응답
{
  "jsonrpc": "2.0",
  "id": 0,
  "result": {
    "protocolVersion": "2024-11-05",
    "serverInfo": {
      "name": "filesystem-server",
      "version": "1.0.0"
    },
    "capabilities": {
      "tools": {},
      "resources": {}
    }
  }
}

2. 도구 관련

JSON
// tools/list - 도구 목록 조회
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/list"
}

// 응답
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "tools": [
      {
        "name": "read_file",
        "description": "파일 내용을 읽습니다",
        "inputSchema": {
          "type": "object",
          "properties": {
            "path": {
              "type": "string",
              "description": "파일 경로"
            }
          },
          "required": ["path"]
        }
      }
    ]
  }
}

// tools/call - 도구 실행
{
  "jsonrpc": "2.0",
  "id": 2,
  "method": "tools/call",
  "params": {
    "name": "read_file",
    "arguments": {
      "path": "/etc/hosts"
    }
  }
}

3. 리소스 관련

JSON
// resources/list - 리소스 목록
{
  "jsonrpc": "2.0",
  "id": 3,
  "method": "resources/list"
}

// resources/read - 리소스 읽기
{
  "jsonrpc": "2.0",
  "id": 4,
  "method": "resources/read",
  "params": {
    "uri": "file:///path/to/document.txt"
  }
}

4. 프롬프트 관련

JSON
// prompts/list - 프롬프트 목록
{
  "jsonrpc": "2.0",
  "id": 5,
  "method": "prompts/list"
}

// prompts/get - 프롬프트 가져오기
{
  "jsonrpc": "2.0",
  "id": 6,
  "method": "prompts/get",
  "params": {
    "name": "code-review",
    "arguments": {
      "language": "python"
    }
  }
}

전송 계층

stdio 전송

가장 일반적인 전송 방식으로, 서버를 자식 프로세스로 실행하고 표준 입출력으로 통신합니다.

TypeScript
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";

// Python 서버 실행
const transport = new StdioClientTransport({
  command: "python",
  args: ["-m", "mcp_server_filesystem"],
  env: {
    ...process.env,
    "PYTHONPATH": "/path/to/server"
  }
});

await client.connect(transport);

장점:

  • 간단한 설정
  • 로컬 프로세스로 보안 우수
  • 플랫폼 독립적

단점:

  • 원격 서버 지원 불가
  • 프로세스 생명주기 관리 필요

HTTP SSE 전송

Server-Sent Events를 사용한 HTTP 기반 전송입니다.

TypeScript
import { SSEClientTransport } from "@modelcontextprotocol/sdk/client/sse.js";

// 원격 서버 연결
const transport = new SSEClientTransport(
  new URL("https://mcp-server.example.com/sse")
);

await client.connect(transport);

장점:

  • 원격 서버 지원
  • HTTP 표준 기술 활용
  • 방화벽 친화적

단점:

  • 단방향 스트리밍 (서버 → 클라이언트)
  • 네트워크 오버헤드

전송 방식 비교

특성 stdio HTTP SSE
위치 로컬 로컬/원격
설정 복잡도 낮음 중간
지연시간 매우 낮음 낮음~중간
보안 프로세스 격리 TLS, 인증 필요
확장성 제한적 우수

핵심 개념

Tools (도구)

클라이언트가 호출할 수 있는 함수나 작업입니다.

Python
@server.tool()
async def calculate(expression: str) -> str:
    """수학 표현식을 계산합니다.

    Args:
        expression: 계산할 수학 표현식 (예: "2 + 2")

    Returns:
        계산 결과
    """
    try:
        result = eval(expression)
        return str(result)
    except Exception as e:
        return f"에러: {e}"

특징:

  • 실행 가능한 작업 (부작용 가능)
  • 입력 스키마 정의 필요
  • 동기/비동기 실행 지원

Resources (리소스)

서버가 제공하는 데이터나 컨텐츠입니다.

Python
@server.resource("file://{path}")
async def read_file_resource(path: str) -> str:
    """파일을 리소스로 제공합니다."""
    with open(path, 'r') as f:
        return f.read()

특징:

  • 읽기 전용 데이터
  • URI로 식별
  • 컨텍스트 제공 목적

Prompts (프롬프트)

재사용 가능한 프롬프트 템플릿입니다.

Python
@server.prompt()
async def code_review_prompt(language: str, code: str) -> str:
    """코드 리뷰 프롬프트를 생성합니다."""
    return f"""다음 {language} 코드를 리뷰해주세요:

```{language}
{code}
```

다음 관점에서 검토해주세요:
1. 코드 품질
2. 성능
3. 보안
4. 베스트 프랙티스
"""

특징:

  • 템플릿 기반
  • 매개변수화 가능
  • 재사용성 높음

개념 비교

개념 목적 읽기/쓰기 예제
Tools 작업 수행 읽기/쓰기 파일 쓰기, 이메일 전송
Resources 데이터 제공 읽기 전용 파일 내용, DB 레코드
Prompts 프롬프트 템플릿 읽기 전용 코드 리뷰, 번역

연결 생명주기

1. 연결 (Connection) • 전송 계층 초기화 (stdio/HTTP SSE) • 서버 프로세스 시작 또는 네트워크 연결 2. 초기화 (Initialization) • 클라이언트: initialize 요청 (프로토콜 버전, capabilities) • 서버: initialize 응답 (서버 정보, capabilities) • 클라이언트: initialized 알림 전송 3. 작업 (Operation) • tools/list, resources/list, prompts/list • tools/call, resources/read, prompts/get • 반복 호출 가능 4. 종료 (Shutdown) • 클라이언트: 연결 해제 • 서버: 리소스 정리, 프로세스 종료
TypeScript
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";

async function main() {
  // 1. 클라이언트 생성
  const client = new Client({
    name: "example-client",
    version: "1.0.0"
  }, {
    capabilities: {
      tools: {}
    }
  });

  // 2. 전송 계층 설정
  const transport = new StdioClientTransport({
    command: "python",
    args: ["server.py"]
  });

  try {
    // 3. 연결 및 초기화
    await client.connect(transport);
    console.log("연결 성공!");

    // 4. 작업 수행
    const tools = await client.listTools();
    console.log("사용 가능한 도구:", tools);

    const result = await client.callTool("read_file", {
      path: "/etc/hosts"
    });
    console.log("결과:", result);

  } finally {
    // 5. 종료
    await client.close();
    console.log("연결 종료");
  }
}

main().catch(console.error);

Capabilities (기능 선언)

클라이언트와 서버는 초기화 시 지원하는 기능을 선언합니다.

클라이언트 Capabilities

JSON
{
  "capabilities": {
    "tools": {},                    // 도구 호출 지원
    "resources": {
      "subscribe": true          // 리소스 변경 구독
    },
    "prompts": {},                  // 프롬프트 사용 지원
    "sampling": {}                  // 샘플링 요청 수신 (서버→클라이언트)
  }
}

서버 Capabilities

JSON
{
  "capabilities": {
    "tools": {
      "listChanged": true        // 도구 목록 변경 알림
    },
    "resources": {
      "subscribe": true,         // 리소스 구독 지원
      "listChanged": true        // 리소스 목록 변경 알림
    },
    "prompts": {
      "listChanged": true        // 프롬프트 목록 변경 알림
    },
    "logging": {}                   // 로그 메시지 전송
  }
}
협상 메커니즘

클라이언트와 서버는 초기화 시 capabilities를 교환하여 공통으로 지원하는 기능만 사용합니다. 예를 들어, 서버가 resources.subscribe를 지원하지 않으면 클라이언트는 리소스 구독 기능을 사용할 수 없습니다.

에러 처리

표준 에러 코드

코드 의미 설명
-32700 Parse error JSON 파싱 실패
-32600 Invalid request 잘못된 요청 형식
-32601 Method not found 지원하지 않는 메서드
-32602 Invalid params 잘못된 매개변수
-32603 Internal error 내부 서버 오류

커스텀 에러

Python
from mcp.server import McpError

@server.tool()
async def read_file(path: str) -> str:
    if not os.path.exists(path):
        raise McpError(
            code=-32001,
            message="File not found",
            data={"path": path}
        )

    with open(path, 'r') as f:
        return f.read()

클라이언트 에러 처리

TypeScript
try {
  const result = await client.callTool("read_file", {
    path: "/nonexistent.txt"
  });
} catch (error) {
  if (error.code === -32001) {
    console.error("파일을 찾을 수 없습니다:", error.data.path);
  } else {
    console.error("예상치 못한 오류:", error.message);
  }
}

다음 단계

학습 경로
  1. MCP 서버 개발: MCP 서버 개발 가이드에서 Python과 TypeScript로 서버를 만드는 방법 학습
  2. MCP 클라이언트: MCP 클라이언트 가이드에서 Claude Desktop, Claude CLI 설정 방법 학습
  3. 실전 예제: MCP 실전 예제에서 파일 시스템, 데이터베이스, API 통합 예제 확인
  4. 생태계 탐색: MCP 생태계에서 공개 서버 및 리소스 확인
  5. 고급 주제: MCP 고급 주제에서 보안, 성능, 배포 전략 학습

핵심 정리

  • MCP 개념의 핵심 개념과 흐름을 정리합니다.
  • MCP란 무엇인가?를 단계별로 이해합니다.
  • 실전 적용 시 기준과 주의점을 확인합니다.

실무 팁

  • 입력/출력 예시를 고정해 재현성을 확보하세요.
  • MCP 개념 범위를 작게 잡고 단계적으로 확장하세요.
  • MCP란 무엇인가? 조건을 문서화해 대응 시간을 줄이세요.