모델 컨텍스트 프로토콜(MCP), 소프트웨어 개발의 새로운 표준
최근 소프트웨어 공학계에서는 Model Context Protocol (MCP)라는 용어가 자주 등장하고 있습니다. 하지만 MCP가 무엇인지, 그리고 개발자가 MCP에 주목해야 하는 이유는 무엇인지를 아는 사람이 많지 않습니다. 이 글은 세 가지 핵심 질문에 대해 답을 제공합니다: MCP가 무엇인지, MCP가 필요한 이유는 무엇인지, 그리고 개발자가 어떻게 MCP를 사용하거나 구축할 수 있는지입니다. Model Context Protocol (MCP)이란? MCP는 프롬프트 엔지니어링의 복잡성이 증가함에 따라 등장한 프로토콜입니다. 대규모 언어 모델(LLMs)의 성능을 향상시키기 위해 개발자들은 구조적이고 관련성이 높은 맥락을 제공하는 것이 중요하다는 것을 인식했습니다. MCP의 기반: 수동 프롬프트에서 MCP까지 MCP의 중요성을 이해하기 위해서는 과거에 LLM에 맥락을 제공하는 방법과 RAG와 함수 호출이 이를 어떻게 발전시켰는지를 살펴봐야 합니다. MCP 이전: 수동 프롬프트와 문제점 RAG나 함수 호출 등의 솔루션이 등장하기 전, 모델에 맥락을 제공하는 것은 수동적이면서 오류가 많이 발생하는 과정이었습니다. 예를 들어, 내부 회사 질문을 답변하는 챗봇을 만든다고 하면, 다음과 같은 단계를 거쳤을 것입니다: 사용자 질문 분석 관련 문서나 지식베이스 검색 관련 정보 추출 프롬프트에 정보 삽입 이 접근법은 간단한 경우에 효과적이지만, 데이터 양이 증가하고 작업이 더욱 복잡해질수록 수동으로 맥락을 삽입하는 것은 문제가 되었습니다. 개발자들은 각 요청마다 맞춤형 프롬프트를 작성해야 했고, 시스템은 정보의 출처나 신뢰성에 대한 구조적인 방식을 알 수 없었습니다. Retrieval-Augmented Generation (RAG): 데이터로부터 맥락 얻기 RAG는 이러한 문제를 해결하기 위해 도입되었습니다. RAG는 모델이 사용자의 질문에 관련된 외부 정보를 동적으로 가져올 수 있게 해줍니다. 예를 들어: 사용자: "내 휴가일은 얼마나 되나요?" 시스템은 HR 정책 문서를 검색 관련 단락을 찾음 프롬프트에 추가: "회사 정책에 따르면: 직원은 유급 휴가 15일을 받을 수 있습니다..." LLM은 이 맥락을 바탕으로 답변 생성 RAG는 모델을 다시 학습하지 않고도 정확성과 응답 품질을 향상시키는 방법을 제공했지만, 여전히 읽기 전용이었습니다. 모델은 데이터를 볼 수 있지만, 시스템과 상호작용하거나 액션을 수행할 수는 없었습니다. 함수 호출: 수동 지식에서 능동적 행동으로 다음 중요한 단계는 LLM이 텍스트 처리뿐만 아니라 도구를 사용할 수 있게 되면서 시작되었습니다. 이는 OpenAI와 Google 같은 제공업체들이 소개한 함수 호출 기능 덕분이었습니다. 함수 호출을 통해 모델은 다음과 같은 작업을 수행할 수 있습니다: 사용자의 의도 해석 도구 또는 함수 호출 결과 반환 예를 들어, 모델은 다음과 같이 사용자 프로필을 가져올 수 있습니다: json { "function_call": { "name": "get_user_profile", "arguments": { "user_id": "123456" } } } 이 기능은 큰 발전이었지만, 각 플랫폼이 고유한 접근법을 가지고 있어 함수 정의가 표준화되지 않았습니다. 개발자들은 다시 한 번 모델, 도구, 데이터 원본을 연결하기 위한 접착 코드를 작성해야 했습니다. MCP: 앱과 LLM 사이의 공통 언어 2024년 11월, Anthropic는 애플리케이션과 LLM 사이의 구조적 맥락 교환을 위한 표준인 Model Context Protocol (MCP)를 제안했습니다. MCP는 앱이 AI 모델에 구조적 맥락을 전달하는 일관되고 표준화된 방법을 정의합니다. USB-C가 하드웨어 장치 연결 방식을 통일한 것처럼, MCP는 맥락을 "플러그인"하는 방법을 통일하여 시스템 간의 통합을 쉽게하고 더 깨끗하며 일관되게 만듭니다. MCP는 앱과 LLM 사이의 중간 프로토콜 계층 역할을 합니다. 개발자는 필요한 맥락이 무엇인지, 그 출처가 어디인지, 어떻게 사용되어야 하는지를 정의할 수 있으며, 모든 모델마다 구체적인 구현을 하드코딩할 필요가 없습니다. MCP의 구성 요소 호스트(Host): 사용자가 LLM과 상호작용하는 메인 환경을 제공하는 애플리케이션. 입력/출력 처리와 내장된 MCP 클라이언트를 포함합니다. 클라이언트(Client): 호스트 내에서 실행되며 하나 이상의 MCP 서버와 통신을 관리합니다. 서비스 발견, 기능 교환, 메시지 라우팅을 담당합니다. 서버(Server): 모델에 도구(함수), 리소스(데이터), 프롬프트(템플릿) 등의 기능을 노출합니다. 서버는 hanshake 과정에서 기능 교환(Capability Exchange)을 통해 자신의 기능을 등록합니다. 기능 교환(Capability Exchange): MCP 클라이언트가 사용 가능한 MCP 서버의 도구, 프롬프트, 리소스 등을 알아내는 발견 메커니즘입니다. 이 과정을 통해 LLM은 어떤 기능이 사용 가능한지 이해할 수 있습니다. 데이터 형식 및 프로토콜: MCP는 JSON-RPC 2.0 메시징 프로토콜을 사용하여 클라이언트와 서버 간의 요청과 응답을 구조화합니다. 메시지는 메서드 이름, 매개변수, 결과 또는 오류 페이로드를 포함하는 엄격한 형식을 따릅니다. MCP의 작동 방식 MCP 클라이언트, 서버, LLM이 어떻게 상호 작용하는지 고려해보겠습니다. 다음은 MCP 기반 요청의 전형적인 흐름을 보여주는 다이어그램입니다: 맥락 수집 도구 실행 (선택 사항) 결과 반환 이 과정은 앱과 모델 간의 구조화된 통신을 단순화하여, MCP가 어떻게 작동하는지를 보여줍니다. 실용적인 사용 사례 실제 사용 사례를 통해 MCP의 작동 방식을 구체적으로 살펴볼까요? 사용자: "데스크톱에 어떤 문서가 있나요?" 호스트(Claude Desktop / Cursor)는 사용자의 쿼리를 받음 호스트는 프롬프트를 분석하고 로컬 파일 시스템 접근이 필요하다고 판단 내장된 MCP 클라이언트는 파일 시스템 MCP 서버와 연결을 초기화 클라이언트와 서버는 기능 교환을 수행 서버는 사용 가능한 도구 목록(e.g., list_txt_files)을 제공 클라이언트는 적절한 도구를 호출 서버는 결과(데스크톱 문서 목록)를 반환 호스트는 이를 사용하여 최종 답변 생성 호스트는 사용자에게 최종 답변을 표시 개발자가 MCP를 어떻게 사용하거나 구축할 수 있는지 현재 가장 쉬운 방법은 Cursor를 사용하는 것입니다. Cursor는 개발자 중심의 AI IDE로, 이미 MCP를 네이티브로 지원합니다. Cursor에서 MCP 사용하기 Cursor는 내장된 MCP 클라이언트를 통해 다음과 같은 작업을 수행합니다: "이 함수에 대한 단위 테스트 작성" "이 코드는 어떤 것에 의존하나요?" "프로젝트에서 TODO 스캔" 실제로-backend: Cursor는 쿼리를 LLM(Claude나 GPT)에게 전송 사용 가능한 도구와 맥락을 자동으로 발견 필요한 경우 적절한 도구(e.g., 파일 분석기, 종속성 트리 빌더)를 호출 도구 결과를 모델에게 반환 LLM은 구조화된 출력을 바탕으로 정확한 답변을 생성 Cursor에서 사용자 정의 도구 구축하기 자신의 백엔드 로직을 Cursor의 AI 워크플로우에 통합하려면, Python을 사용하여 사용자 정의 MCP 서버를 생성할 수 있습니다. 여기에 단계별 설정 방법을 설명합니다: 필수 사항 설치: Python 프로젝트에 mcp 라이브러리를 설치해야 합니다. uv를 사용하는 것이 좋지만, pip를 사용해도 됩니다. uv가 이미 설치되어 있지 않다면: sh brew install astral-sh/astral/uv curl -Ls https://astral.sh/uv/install.sh | sh 새 MCP 프로젝트 초기화: sh mcp uv init mcp-demo 기본 프로젝트 구조가 mcp-demo 폴더에 설정됩니다. 프로젝트 디렉토리 이동: sh cd mcp-demo 가상 환경 생성: sh uv venv 가상 환경 활성화: sh source .venv/bin/activate 가상 환경을 활성화하면 시스템 Python과 독립적으로 패키지가 설치되고 실행됩니다. 핵심 MCP 패키지 추가: sh uv add mcp CLI 유틸리티 추가: sh uv add 'mcp[cli]' 코드 작성 코드를 명확하게 작성하기 위해 두 개의 파일로 나누겠습니다. server.py ```python from mcp.server.fastmcp import FastMCP import os mcp = FastMCP() @mcp.tool() def list_text_files() -> list[dict]: """프로젝트 폴더 내의 모든 .txt 파일 목록을 반환합니다.""" files = [] for root, _, filenames in os.walk("."): for fname in filenames: if fname.endswith(".txt"): full_path = os.path.join(root, fname) files.append({ "path": full_path, "name": fname }) return files @mcp.tool() def count_words_in_file(path: str) -> dict: """주어진 파일의 단어 수를 센 후 반환합니다.""" if not os.path.isfile(path): return {"error": f"파일을 찾을 수 없습니다: {path}"} with open(path, "r", encoding="utf-8") as f: content = f.read() word_count = len(content.split()) return { "file": path, "word_count": word_count } ``` main.py ```python from server import mcp if name == "main": mcp.run(transport="stdio") ``` 서버 로컬 실행 (디버그 목적) 로컬에서 도구를 실행하고 Cursor 외부에서 테스트하려면: sh uv run mcp dev main.py - 이 명령은 Dev 모드에서 MCP 서버를 시작하고, 도구 호출 및 결과를 터미널에 출력합니다. MCP 인스펙터 사용 (디버그 목적) Dev 서버를 실행하면, 터미널에 다음과 같은 로컬 URL이 표시됩니다: - "Inspector available at: http://localhost:6274/?MCP_PROXY_AUTH_TOKEN=..." 이 URL을 브라우저에서 열면, MCP 인스펙터를 통해 서버가 노출하는 도구를 수동으로 테스트하고 탐색할 수 있습니다. Cursor에서 활성화하기 MCP 서버가 준비되면, 다음과 같이 전역 Cursor 설정에 추가하여 모든 Cursor 프로젝트에서 사용할 수 있습니다: json { "mcpServers": { "word-counter": { "command": "uv", "args": ["run", "python", "{FILE_PATH}/mcp-demo/main.py"] } } } 이렇게 하면 Cursor가 MCP 서버를 자동으로 시작하고, 에디터 내 AI 어시스턴트에게 도구를 노출합니다. Cursor를 다시 시작하면, 다음과 같은 명령을 입력할 수 있습니다: - "이 폴더 내의 모든 텍스트 파일 목록" - "이 파일의 단어 수 세기" Cursor는 요청을 MCP 서버를 통해 라우팅하여 Python 도구를 실행하고, 결과를 인라인으로 반환합니다. MCP와 Cursor를 결합하면, 개발자는 보일러플레이트 코드를 거의 작성하지 않고도 똑똑하고 AI가 증강된 개발 도구를 만들 수 있습니다. 마무리 생각 소프트웨어 엔지니어로서, 우리는 API 연결, 엣지 케이스 처리, 접착 코드 유지 관리에 많은 시간을 보내곤 합니다. MCP는 이를 변화시킵니다. MCP는 이미 수행하고 있는 일상적인 작업을 더 깨끗하고 일관된 방식으로 처리할 수 있도록 제공하며, 이제 언어 모델이 involved된 상태에서 작업할 수 있습니다. AI가 현대 개발 스택의 일부로 자리 잡으면서, MCP와 같은 프로토콜을 배우는 것은 선택이 아닌 필수가 되고 있습니다. 이를 통해 우리는 더 똑똑하고 빠르며 유지 관리가 쉬운 시스템을 구축할 수 있게 됩니다.