MCP 개념과 원리 이해
MCP란 무엇인가?
MCP(Model Context Protocol)는 AI 모델(LLM)과 외부 시스템(도구, 데이터 소스 등)을 연결하기 위한 개방형 표준 통신 프로토콜입니다. 쉽게 말해, AI 어시스턴트(예: ChatGPT, Claude 등)가 외부 세계의 데이터를 읽거나 툴을 실행할 수 있도록 인터페이스를 표준화한 것입니다. 이를 통해 파일 읽기, 함수 실행, 외부 지식베이스 활용 등 다양한 작업을 특정 모델에 종속되지 않는 방식으로 지원합니다.
등장 배경
Anthropic사가 2024년 11월 MCP를 발표했습니다. 발표 배경에는 AI 모델과 각종 도구/데이터를 연동하는 복잡성이 있었습니다. 기존에는 새로운 데이터 소스나 API를 연결할 때마다 각 LLM별로 별도 커넥터를 만들어야 했습니다. 이처럼 N×M 방식으로 모델-툴 조합마다 통합 작업이 필요했던 문제를 MCP가 해결하고자 했습니다. 즉, 한 번 MCP 서버를 만들면 여러 AI 플랫폼에서 공통으로 활용할 수 있게 된 것입니다. Anthropic은 MCP를 오픈 소스로 공개했고, 이후 OpenAI와 Google DeepMind 등 주요 AI 업체들도 MCP를 채택하여 표준으로 자리잡았습니다.
역할 및 중요성
MCP는 LLM이 외부 지식과 기능에 접근함으로써 보다 지능적인 에이전트로 동작하도록 돕습니다.
이를테면:
- AI 비서가 사용자 일정(예: 구글 캘린더)이나 메모(노션 등)에 접근하여 개인화된 답변을 제공할 수 있습니다.
- 코딩 어시스턴트가 디자인 툴(Figma)의 결과물을 불러와 웹 앱 코드를 생성할 수 있습니다.
- 엔터프라이즈 챗봇이 사내 여러 데이터베이스에서 정보를 질의하여 답변함으로써 기업 지식을 활용할 수 있습니다.
- AI 모델이 외부 API(날씨 정보 등)를 호출하거나 시스템 명령(예: 3D 프린터 작동)을 실행해 현실 세계에 영향주는 작업도 가능해집니다.
이러한 능력 확장은 개발자에게는 새로운 툴 연동 시 일일이 통합 코드를 작성하는 수고를 덜어주고, AI 애플리케이션에는 풍부한 기능 생태계를 제공하며, 최종 사용자에게는 더 유용한 AI 기능을 제공합니다.
MCP의 등장은 다양한 AI 플랫폼 간 호환성과 확장성을 높여주어, 마치 USB-C 포트가 기기 연결을 표준화했듯이 AI 분야에서 플러그앤플레이 방식으로 도구를 연결하는 길을 열었습니다.
아키텍처
MCP는 클라이언트-서버 아키텍처를 취합니다. AI 애플리케이션(호스트)이 MCP 클라이언트를 통해 하나 이상의 MCP 서버에 접속하는 형태입니다.
주요 구성 요소는:
MCP 호스트(Host): AI 모델을 구동하는 애플리케이션 또는 플랫폼입니다. 예를 들어 VSCode의 Copilot Chat, Claude Desktop 애플리케이션, 또는 ChatGPT 애플리케이션 자체가 MCP 호스트입니다. 호스트는 내부에 여러 MCP 클라이언트를 관리하여 각종 서버와 연결합니다.
MCP 클라이언트(Client): 호스트 내에서 동작하며, 개별 MCP 서버와 1:1로 통신을 담당하는 모듈입니다. 예를 들어 VSCode가 파일시스템 MCP 서버와 GitHub MCP 서버 두 곳에 연결한다면, VSCode 내부적으로 두 개의 MCP 클라이언트 인스턴스가 존재하게 됩니다. 클라이언트는 서버와 지속적인 연결을 유지하면서 요청/응답을 중계합니다.
MCP 서버(Server): AI에게 맥락(Context)을 제공하는 외부 프로그램입니다. 데이터 소스나 기능을 노출하며, 표준화된 인터페이스로 클라이언트의 요청을 처리합니다. MCP 서버는 로컬(예: 파일시스템 MCP 서버는 사용자의 PC에서 실행)일 수도, 원격(예: GitHub MCP 서버는 GitHub 클라우드에서 HTTP로 통신)일 수도 있습니다. 서버의 종류에 따라 LLM이 사용할 툴이나 데이터를 제공하게 됩니다.
이러한 구조에서 MCP는 언어 서버 프로토콜(LSP)과 자주 비유됩니다. LSP가 다양한 개발 도구와 프로그래밍 언어 컴파일러/해석기 사이의 통신을 표준화했듯, MCP는 다양한 AI 모델과 외부 툴/데이터 소스 간 통신을 표준화합니다.
동작 방식
호스트 애플리케이션이 MCP 서버에 연결하면, 초기화 과정에서 서로의 지원 기능(capabilities)을 교환하고 세션을 수립합니다. 이후 AI 모델이 툴을 호출하거나 데이터를 요청하고자 할 때, 호스트는 해당 MCP 클라이언트를 통해 JSON-RPC 메시지를 서버로 보냅니다. 서버는 요청을 처리한 후 결과를 JSON-RPC 응답으로 돌려주고, 호스트는 이 결과를 모델의 답변 생성에 활용합니다. 모든 메시지는 JSON 포맷의 표준 규격으로 주고받기 때문에, 서로 다른 언어와 플랫폼 간에도 MCP 호환만 되면 통신이 가능합니다.
Agent Framework에서의 MCP 활용
MCP의 등장 이전에는 LangChain 같은 에이전트 프레임워크들이 자체적으로 툴을 정의하고 LLM이 이를 사용할 수 있게 했습니다. 예를 들어 LangChain에서는 파이썬 함수로 툴을 정의하고, 에이전트가 문자열 파싱을 통해 그 함수를 호출하는 식이었습니다. 이러한 방식은 프레임워크마다 다르고, 특정 LLM(예: OpenAI API의 함수 호출 등) 종속적인 면이 있었습니다.
MCP는 이러한 에이전트 프레임워크에 새로운 접근을 제공합니다:
툴/데이터 표준화: MCP를 통해 정의된 툴은 JSON 스키마로 입력/출력을 명시하기 때문에, LangChain 같은 프레임워크에서도 일관된 방식으로 툴 목록과 사용법을 얻을 수 있습니다. 실제로 LangChain은 MCP에 대응하기 위해 MCP 어댑터를 내놓아, 기존 MCP 서버들을 LangChain 툴로 바로 사용할 수 있게 했습니다 (LangChain MCP Adapters 프로젝트). 이를 통해 LangChain 에이전트도 MCP 생태계의 툴들을 쉽게 활용할 수 있습니다.
에이전트 개발 단순화: FastAPI에 Swagger가 있듯, MCP 서버는 자체 기술된 툴 목록을 제공하므로 에이전트 쪽에서는 툴 추가/제거가 단순해집니다. 에이전트 프레임워크 개발자는 일일이 툴 사용 로직을 코딩하기보다, MCP 서버와 프로토콜 레벨에서 통신하는 클라이언트만 구현하면 됩니다. 예를 들어 OpenAI의 에이전트 SDK도 MCP를 지원하여, 하나의 통일된 방식으로 다양한 툴 사용 에이전트를 만들 수 있음을 발표했습니다.
모델 독립성: MCP 툴은 Anthropic Claude, OpenAI GPT, Google Gemini 등 어느 LLM과도 연결 가능합니다. 에이전트 프레임워크 입장에서는 특정 LLM 벤더의 플러그인 체계(예: OpenAI 플러그인)만 지원하는 게 아니라, MCP 한 가지만 지원해도 여러 모델에서 쓸 수 있는 장점이 있습니다. 이식성과 재사용성이 높아지므로, 기업들은 자체 에이전트를 만들 때 MCP 지원을 우선 고려하는 추세입니다.
결과적으로 MCP는 LangChain 등의 기존 에이전트 설계 철학과 보완적으로 작용합니다. LangChain이 고수준의 체인을 구성하는 로직을 제공한다면, MCP는 저수준 툴/데이터 접근 계층을 표준화하여 그 체인의 말단 액션들을 강력하게 만들어줍니다. 앞으로 에이전트 개발에서는 MCP 서버를 얼마나 잘 활용하느냐가 성능과 확장성의 열쇠가 될 것으로 예상됩니다.
MCP 기본 문법 및 메시지 구조
MCP 통신은 JSON-RPC 2.0 표준을 따릅니다.
모든 메시지는 다음과 같은 형태의 JSON 객체로 교환됩니다.
{
"jsonrpc": "2.0",
"id": ...,
"method": "...",
"params": {
...
}
}
요청(request)에는 method와 필요한 params가 담기며, 응답(response)은 해당 요청의 id를 매칭하여 result 또는 error 필드를 담습니다. MCP는 이 JSON-RPC 포맷 위에 여러 메소드(namespace)를 정의하여, 툴/리소스/프롬프트의 검색과 사용을 표준화합니다.
주요 프로토콜 요소와 메시지의 구조는 다음과 같습니다:
기본 통신 흐름:
초기화 (Initialization): mcp/open 등의 handshake 과정으로 서버와 클라이언트가 연결되며, 서로의 capabilities를 교환합니다.
예를 들어 서버는
{ "capabilities": { "tools": { "listChanged": true }, "resources": { }, "prompts": { } } }와 같이 자신이 지원하는 기능들(툴/리소스/프롬프트 제공 여부 등)을 알립니다. 클라이언트(호스트 측)도
roots(워크스페이스 폴더 정보 제공)나 인증 방법 등을 협상합니다. 이 초기화 후 서버는 사용 가능한 툴, 리소스, 프롬프트의 목록을 제공할 준비가 됩니다.툴/리소스 발견 (Discovery): 호스트는 우선 서버에 어떤 기능들이 있는지 알아야 합니다. 이를 위해 tools/list, resources/list, prompts/list와 같은 메소드를 호출해 목록을 조회합니다. 예를 들어 툴 목록 요청은 아래와 같습니다:
// 클라이언트 → 서버: 사용 가능한 툴 목록 요청 { "jsonrpc": "2.0", "id": 1, "method": "tools/list", "params": { } }응답으로 서버는 툴들의 메타데이터 목록을 반환합니다:
{ "jsonrpc": "2.0", "id": 1, "result": { "tools": [ { "name": "get_weather", "title": "Weather Information Provider", "description": "Get current weather information for a location", "inputSchema": { "type": "object", "properties": { "location": { "type": "string", "description": "City name or zip code" } }, "required": ["location"] } // outputSchema 생략 가능 (없으면 자유 형식 텍스트 응답) }, ... (다른 툴들 목록) ] } }여기서 각 툴은 name (고유 식별자), description (설명), inputSchema (입력 JSON 구조), outputSchema (출력 구조, 있을 경우) 등을 포함합니다. 마찬가지로 resources/list 요청을 보내면 서버가 제공하는 리소스(파일, 데이터 등)의 목록 (각각 uri, title, mimeType 등을 포함)을 받게 됩니다. prompts/list를 호출하면 서버가 노출하는 프롬프트 템플릿 목록 (name, description, 필요 인자 등)을 얻습니다.
툴 실행 (Tool Call): 모델(호스트)은 툴 목록을 확인한 후, 필요 시 특정 툴을 호출할 수 있습니다. 예를 들어 사용자의 질문에 따라 날씨 정보를 얻어야 한다면,
get_weather툴을 호출하게 됩니다. 이때 클라이언트는tools/call메소드를 사용합니다.호출 메시지 예시:
// 클라이언트 → 서버: 툴 실행 요청 { "jsonrpc": "2.0", "id": 2, "method": "tools/call", "params": { "name": "get_weather", "arguments": { "location": "Seoul" } } }서버는
name에 해당하는 툴을 실행하고 결과를 반환합니다.날씨 툴의 응답 예시:
{ "jsonrpc": "2.0", "id": 2, "result": { "content": [ { "type": "text", "text": "서울의 현재 날씨: 온도 22°C, 맑음" } ], "isError": false } }응답의
content필드는 툴 결과를 담고 있으며, 여기서는 간단한 텍스트지만 구조화된 데이터나 이미지, 오디오 등도 MCP 포맷에 맞게 포함될 수 있습니다.isError가true일 경우 툴 실행 중 오류가 발생한 것을 의미하며, 오류 메시지가content에 포함되거나 별도error필드로 전송될 수 있습니다.리소스 읽기 (Resource Read): 서버가 제공하는 리소스는 주로 읽기 전용 컨텍스트 데이터입니다. 예를 들어 파일 내용 읽기, DB 쿼리 결과, 문서 내용 등이 해당됩니다. 리소스 목록에서
uri를 확인한 후, 클라이언트는resources/read를 호출하여 실제 내용을 얻을 수 있습니다.예를 들어 uri:
"file:///project/src/main.py"인 리소스를 읽는 요청:{ "jsonrpc": "2.0", "id": 3, "method": "resources/read", "params": { "uri": "file:///project/src/main.py" } }응답으로:
{ "jsonrpc": "2.0", "id": 3, "result": { "contents": [ { "uri": "file:///project/src/main.py", "mimeType": "text/x-python", "text": "def hello():\n print('world')\n" } ] } }이처럼 파일의 MIME 타입과 텍스트 내용이 포함되어 전송됩니다. 리소스는 이 외에도 바이너리(
base64인코딩) 전송, chunk 스트리밍 등도 가능하나 기본 원리는 목록→읽기의 2단계를 거친다는 것입니다.프롬프트 사용 (Prompt Get): MCP의 프롬프트는 미리 정의된 대화 메시지 템플릿입니다. 사용자가 필요에 따라 서버의 프롬프트를 호출하면, 해당 프롬프트에 맞는 메시지가 생성되어 모델의 컨텍스트에 주입됩니다. 프롬프트의 사용은 일반적으로 사용자 트리거로 이루어집니다 (예: UI에서 슬래시 명령으로 노출).
예를 들어 서버에
"name": "code_review"라는 프롬프트가 있고 인자로code를 받는다고 합시다. 사용자가 코드 리뷰를 원할 때 이 프롬프트를 호출하면, 클라이언트는prompts/get메소드를 사용합니다.{ "jsonrpc": "2.0", "id": 4, "method": "prompts/get", "params": { "name": "code_review", "arguments": { "code": "# 예시 코드\nprint('Hello, world')" } } }서버는 해당 프롬프트 템플릿에 arguments를 채워 메시지들을 반환합니다. 예를 들어 이 프롬프트가 “주어진 코드를 리뷰해줘”라는 사용자 메시지를 생성하는 템플릿이라면:
{ "jsonrpc": "2.0", "id": 4, "result": { "description": "코드 리뷰 프롬프트", "messages": [ { "role": "user", "content": { "type": "text", "text": "다음 코드를 분석하고 개선점을 제안해줘:\n\n# 예시 코드\nprint('Hello, world')" } } ] } }이 메시지가 즉시 AI 모델에게 전달되어 (마치 사용자가 그렇게 말한 것처럼) 코드 리뷰 작업이 시작됩니다. 프롬프트 템플릿은 이렇게 여러 메시지를 한 번에 전달할 수도 있고, 역할(role)이 user/assistant로 지정되어 대화 맥락을 형성할 수도 있습니다.
Note: 프롬프트는 사용자가 UI에서 선택하는 방식으로 노출되는 것이 일반적이며, 모델이 임의로 **prompts/get**을 자동 호출하지는 않습니다. 프롬프트는 일종의 **워크플로 자동화** 또는 **템플릿 제공** 기능으로, 사용자 경험을 돕는 역할입니다.- JSON 스키마와 타입: MCP의 뛰어난 점은 모든 상호작용이 스키마로 명세된다는 것입니다. 툴의 입력/출력은 JSON Schema로 정의되어, 에이전트 UI에서 자동으로 폼을 만들거나, LLM이 함수 인자를 정확히 구성하는 데 활용할 수 있습니다. 또한 content 부분도
type필드를 통해 텍스트, 이미지, 오디오, 임베디드 리소스 링크 등으로 명시됩니다.
예컨대, 이미지 결과라면
{ "type": "image", "uri": "data:image/png;base64,...", "alt": "차트" }같은 식으로 응답할 수 있고, 클라이언트는 이를 사용자가 볼 수 있도록 처리합니다.
- 통신 채널: MCP는 전송 계층(Transport)으로 두 가지를 지원합니다: STDIO(표준 입출력 파이프 연결)와 HTTP (SSE 포함)입니다. STDIO는 로컬에서 클라이언트-서버를 프로세스 간 통신으로 연결할 때 쓰이며, VSCode 같은 앱이 로컬 MCP 서버를 실행할 때 사용합니다. HTTP는 원격 서버에 연결할 때 사용하며, 필요에 따라 SSE(Server-Sent Events)를 통해 스트리밍 응답도 처리합니다. 양쪽 다 JSON-RPC 메시지를 운반하는 통로일 뿐, 데이터 내용은 동일한 JSON-RPC 규격을 따릅니다. (SDK나 구현체가 이를 숨겨주므로 개발자는 큰 차이를 느끼지 못하고, 설정만 STDIO/HTTP로 구분합니다.)
- JSON 스키마와 타입: MCP의 뛰어난 점은 모든 상호작용이 스키마로 명세된다는 것입니다. 툴의 입력/출력은 JSON Schema로 정의되어, 에이전트 UI에서 자동으로 폼을 만들거나, LLM이 함수 인자를 정확히 구성하는 데 활용할 수 있습니다. 또한 content 부분도
정리하면, MCP의 문법은 JSON-RPC 기반 요청/응답 형태로 일관되며, 메소드 이름을 통해 어떤 기능을 수행할지 명시합니다. 초기화-목록조회-호출/읽기 등의 패턴을 익히면 새로운 MCP 서버도 금방 연동할 수 있습니다. 다음에는 실제 MCP가 어떻게 활용되는지 사례를 통해 살펴보겠습니다.