에이전트 컨텍스트의 표준화: 왜 MCP가 새로운 REST인가
요약
Model Context Protocol(MCP)은 AI 에이전트와 데이터 소스 간의 통신을 표준화하는 새로운 아키텍처입니다. 기존의 파편화된 도구 호출 방식에서 벗어나, LLM 클라이언트와 데이터 서버를 분리하여 확장성과 보안성을 제공합니다.
핵심 포인트
- MCP는 AI 컨텍스트를 위한 REST와 같은 표준 프로토콜 역할을 수행함
- 데이터 소비자(LLM)와 생산자(MCP 서버)를 분리하여 상호운용성 확보
- 벤더 종속적인 도구 호출 스키마 문제를 해결하고 아키텍처 파편화 방지
- 토큰 효율성과 컨텍스트 윈도우 관리를 위한 구조적 접근 제공
웹 개발의 서부 개척 시대(Wild West)를 기억하시나요? REST와 HTTP가 시스템 간의 통신 방식을 표준화하기 전에는, 현대적인 웹 애플리케이션을 구축한다는 것이 독점적인 원격 프로시저 호출 (RPC), CORBA 구현체, 그리고 커스텀 XML-over-TCP 소켓의 미로를 헤매는 것을 의미했습니다. 만약 클라이언트 애플리케이션을 세 가지 서로 다른 백엔드 서비스—예를 들어 사용자 관리 장부, 재고 데이터베이스, 그리고 결제 엔드포인트—에 연결하고 싶다면, 완전히 다른 세 가지 네트워킹 스택을 작성해야 했습니다. 세 가지 서로 다른 직렬화 (serialization) 형식을 배워야 했고, 벤더별로 특화된 연결 라이프사이클과 씨름해야 했으며, 깨지기 쉬운 에러 처리 래퍼 (wrapper)를 유지 관리해야 했습니다.
그것은 그야말로 혼돈 그 자체였습니다.
오늘날 에이전트형 AI 엔지니어링 (agentic AI engineering)은 정확히 그 REST 이전의 시대에 갇혀 있습니다. 오늘날 AI 에이전트를 구축하고 있다면, 여러분은 이미 그 고통을 알고 있을 것입니다. 대규모 언어 모델 (Large Language Model, LLM)이 기업 데이터베이스와 통신하거나 실시간 모니터링 로그를 가져오게 하려고 커스텀 통합 스캐폴딩 (scaffolding)을 작성합니다. 임시 시스템 프롬프트를 만들고, 불규칙한 모델 출력을 파싱하며, 단일 벤더의 API에 맞춘 도구 호출 (tool-calling) 스키마를 수동으로 매핑하고, 모델이 응답을 작성할 때 추가적인 마크다운 태그를 붙이는 순간 깨져버리는 취약한 JSON 파싱 루프를 작성합니다.
만약 OpenAI 기반 에이전트를 위해 Jira 인스턴스를 쿼리하는 훌륭한 도구를 연결했다면, 그 도구는 Anthropic 기반 에이전트나 Ollama를 통해 실행되는 로컬 오픈 웨이트 (open-weights) 모델과는 완전히 호환되지 않습니다.
**Model Context Protocol (MCP)**가 등장했습니다. 이것은 단순한 점진적인 프레임워크 업데이트가 아닙니다. 이것은 에이전트 엔지니어링이 갈망해 온 근본적인 아키텍처 표준화입니다. MCP는 "AI 컨텍스트를 위한 REST"입니다. MCP는 데이터 소비자 (LLM 클라이언트)와 데이터 생산자 (MCP 서버)를 분리하여, 생성형 AI 에이전트의 세계에 합리성, 보안성, 그리고 확장성을 가져다줍니다.
이론적 토대: 토큰, 컨텍스트 윈도우, 그리고 아키텍처 파편화
MCP가 왜 AI 엔지니어링 세계를 뒤흔들고 있는지 이해하려면, LLM이 외부 세계와 상호작용하는 방식의 내부 구조(under the hood)를 살펴봐야 합니다.
LLM은 본질적으로 귀하의 기업 데이터베이스, 회사의 내부 Slack 채널, 또는 로컬 디스크에 있는 파일 구조에 대해 알지 못합니다. LLM은 오직 자신의 활성 **컨텍스트 윈도우 (Context Window)**에 직접 주입된 내용만을 알고 있습니다. 에이전트에게 외부 데이터에 대한 접근 권한을 부여하려면, 데이터베이스 스키마(schema), API 문서 파일, 또는 터미널 명령의 반환 값(return value) 등 모든 정보가 텍스트로 직렬화(serialized)되고, 토큰화(tokenized)되어 모델의 메모리에 직접 밀어 넣어져야 합니다.
이는 **토큰 (Token)**의 가혹한 경제적 및 기술적 현실로 우리를 인도합니다. 토큰은 LLM을 위한 텍스트 처리의 기본 단위로, 영어 텍스트 기준으로 대략 4글자와 맞먹습니다. 모델은 텍스트를 토큰 단위로 처리하며, 컨텍스트 윈도우는 비용 문제와 이차적 어텐션 스케일링 페널티(quadratic attention-scaling penalties, 악명 높은 "중간 소실 (lost in the middle)" 현상)에 의해 엄격하게 제한되기 때문에, 컨텍스트의 단 1바이트조차 매우 중요합니다.
MCP 이전 시대에 개발자들은 비대하고 구조화되지 않은 JSON 페이로드(payload), 지저분한 마크다운(markdown) 표, 그리고 방대한 시스템 지침(system instructions)을 모델에 직접 던져 넣으며, LLM이 그 노이즈를 파싱(parse)할 수 있기를 기도하곤 했습니다. 이러한 임시방편적(ad-hoc) 접근 방식은 두 가지 끔찍한 문제를 야기했습니다:
- 장황하고 일관성 없는 시스템 지침에 값비싼 토큰을 낭비하게 만들었습니다.
- 신뢰할 수 없는 도구 출력물 내에 숨겨진 프롬프트 인젝션(prompt injection) 공격과 같은 심각한 보안 취약점을 유발했습니다.
MCP는 LLM 호스트(clients)와 기능 제공자(servers) 사이에 보편적인 계약(contract)을 수립함으로써 이러한 파편화 문제를 해결합니다. 웹 브라우저가 Apache 서버든 Nginx 서버든, 혹은 C, Go, Rust로 작성되었든 상관없이 두 서버 모두 HTTP를 사용하기만 하면 웹사이트를 구동할 수 있는 것과 마찬가지로, MCP를 실행하는 LLM 애플리케이션 클라이언트는 MCP 서버의 내부 구현 세부 사항을 알 필요가 없습니다. 서버가 로컬 SQLite 데이터베이스를 쿼리하든, 격리된 Docker 컨테이너 내부에서 명령을 실행하든, 혹은 라이브 웹페이지를 스크래핑하든, 통신은 JSON-RPC 2.0을 기반으로 구축된 표준화되고 디커플링된(decoupled) 와이어 프로토콜(wire protocol)을 통해 이루어집니다.
MCP의 아키텍처 해부: 클라이언트, 서버, 그리고 전송 계층
신뢰할 수 있는 에이전트 시스템(agentic systems)을 구축하려면 Model Context Protocol을 구성하는 세 가지 별개의 기둥인 호스트/클라이언트(Hosts/Clients), 서버(Servers), 그리고 **전송 계층(Transports)**을 이해해야 합니다.
1. MCP 호스트 및 클라이언트
**호스트(Host)**는 전반적인 AI 애플리케이션을 의미합니다. 이는 Cursor나 VS Code와 같은 AI 기반 IDE 확장 프로그램, 데스크톱 에이전트 애플리케이션, 또는 고급 제어 흐름 그래프(control flow graphs)로 구축된 커스텀 TypeScript 오케스트레이터(orchestrator)가 될 수 있습니다. 호스트는 사용자의 의도를 관리하고, 보안 경계를 강제하며, 사용자 인터페이스를 렌더링하는 책임을 집니다.
**클라이언트(Client)**는 호스트 내부에 존재합니다. 클라이언트의 유일한 임무는 MCP 서버와 1:1 세션 연결을 유지하는 것입니다. 결정적으로, 클라이언트는 도구(tools)를 직접 실행하지 않습니다. 대신, 서버로부터 어떤 도구, 리소스(resources), 프롬프트(prompts)를 사용할 수 있는지 찾아내고, 이러한 기능들을 현재 LLM이 기대하는 네이티브 스키마 형식(예: OpenAI의 함수 호출(function-calling) 정의 또는 Anthropic의 도구 사용(tool-use) 블록)으로 변환하며, 모델의 도구 호출을 가로채서 실행을 위해 서버로 다시 라우팅합니다.
2. MCP 서버
**MCP 서버(MCP Server)**는 클라이언트에게 세 가지 특정 프리미티브(primitives)를 노출하도록 설계된 가볍고 독립적인 프로세스입니다:
- Resources (리소스): 클라이언트가 읽을 수 있는 수동적 데이터 소스(Passive data sources)입니다. 리소스는 파일이나 데이터베이스 레코드와 같이 작동합니다. 고유한 URI(예:
postgres://users/schema또는file:///logs/error.log)를 가지며, 클라이언트는 이를 읽어 LLM 프롬프트 창에 컨텍스트를 직접 주입할 수 있습니다. - Tools (도구): 모델이 작업을 수행하거나 상태를 변경(mutate state)하기 위해 호출할 수 있는 능동적이고 실행 가능한 함수(executable functions)입니다. 수동적인 리소스와 달리, 도구는 모델의 명시적인 의도와 사용자의 승인을 필요로 합니다. 예시로는
execute_sql_query,send_slack_message, 또는restart_server등이 있습니다. - Prompts (프롬프트): 서버가 클라이언트에게 제공하는 템플릿 또는 사전 패키징된 워크플로(workflows)입니다. 이를 통해 서버 유지 관리자는 도메인 특화된 전문가 프롬프트 전략을 서버 바이너리 내에 직접 묶어서 제공할 수 있습니다. 예를 들어, 데이터베이스 MCP 서버는 LLM이 표준화된 방식으로 데이터베이스 실행 계획을 검사하도록 안내하는
optimize_slow_query라는 이름의 프롬프트 템플릿을 노출할 수 있습니다.
3. 전송 계층 (Transport Layers)
전송 계층(Transport layer)은 MCP 클라이언트와 MCP 서버가 JSON-RPC 2.0 메시지를 교환하는 방식을 제어합니다. MCP는 전송 방식에 완전히 무관(transport-agnostic)하지만, 실제 아키텍처에서는 두 가지 주요 전송 방식이 지배적입니다:
- Stdio (표준 입출력): 클라이언트가 MCP 서버를 로컬 자식 프로세스(예: Node.js 프로세스, Python 스크립트 또는 컴파일된 Go 바이너리)로 생성합니다. 통신은 줄바꿈으로 구분된 JSON-RPC 메시지를 사용하여 표준 입력 및 출력 스트림을 통해 이루어집니다. 이 전송 방식은 보안이 매우 뛰어나고 네트워크 설정이 전혀 필요하지 않으며, 로컬 데스크톱 도구 및 단일 테넌트(single-tenant) 환경에 이상적입니다.
- SSE (Server-Sent Events): 분산형, 원격 또는 멀티 테넌트(multi-tenant) 클라우드 아키텍처를 위해, MCP는 서버에서 클라이언트로의 스트리밍을 위한 Server-Sent Events와 클라이언트에서 서버로의 명령을 위한 표준 HTTP POST 요청을 결합하여 HTTP를 통한 통신을 지원합니다.
심층 분석: 결합도 문제 해결 및 토큰 경제 최적화
전통적인 커스텀 통합 (custom integrations) 방식에서는 에이전트의 오케스트레이션 (orchestration) 로직이 접촉하는 데이터 소스와 불가분하게 결합되어 있습니다. 만약 내부 직원 데이터베이스를 쿼리하여 그 데이터를 LLM 프롬프트에 주입하는 TypeScript 함수를 작성한다면, 해당 함수에는 하드코딩된 데이터베이스 드라이버 (database drivers), 연결 문자열 환경 변수 (connection string environment variables), 네트워크 타임아웃에 대한 커스텀 에러 핸들링 (custom error handling), 그리고 토큰 예산 (token budget)에 맞게 출력을 압축하도록 설계된 특정 데이터 포맷팅 로직이 포함됩니다.
만약 나중에 주요 LLM 벤더를 변경하기로 결정한다면—예를 들어, 완전히 다른 네이티브 함수 호출 스키마 (native function-calling schema)를 가진 구형 모델에서 신형 모델로 마이그레이션하는 경우—프롬프트 생성 코드, 도구 정의 (tool definitions), 그리고 파싱 래퍼 (parsing wrappers)를 모두 리팩터링 (refactor)해야 합니다. 설상가상으로, 데이터베이스 쿼리 도구가 입력을 정화 (sanitize)하는 방식에서 보안 취약점이 발견된다면, 해당 커스텀 쿼리 로직을 구현한 기업 전체의 모든 에이전트 애플리케이션을 일일이 패치해야 합니다.
MCP는 엄격한 인터페이스 분리 (interface segregation)를 통해 이 관계를 역전시킵니다:
- 보안 및 샌드박싱 (Security and Sandboxing): MCP 서버는 자체 프로세스 공간에서 실행됩니다.
stdio를 통해 연결된 경우, 호스트 애플리케이션이 환경 변수, 파일 시스템 액세스 및 네트워크 권한을 제어합니다. LLM은 데이터베이스에 직접 접근하지 않습니다. LLM은 단지 클라이언트에게 도구 호출 (tool call)을 제안할 뿐이며, 클라이언트는 요청을 서버로 전달하기 전에 정책 엔진 (policy engines)을 통해 해당 요청을 검증합니다. - 재사용성 및 조합성 (Reusability and Composability): MCP 서버는 범용 프로토콜을 사용하기 때문에, 커뮤니티에서 작성한 단 하나의 GitHub용 MCP 서버를 VS Code 확장 프로그램, 터미널 기반 코딩 에이전트, Discord 봇, 그리고 기업용 워크플로우 오케스트레이터가 동시에 사용할 수 있습니다.
컨텍스트 윈도우의 디맨드 페이징 (Demand-Paging)
에이전트 설계의 핵심적인 이론적 과제는 컨텍스트의 풍부함 (context richness)과 토큰 소비 (token expenditure) 사이의 트레이드오프 (trade-off)를 관리하는 것입니다. 단순한 구현 방식에서는 개발자들이 시작 시점에 데이터베이스 스키마 전체나 방대한 로그 파일을 시스템 프롬프트 (system prompt)에 그대로 쏟아붓습니다. 이는 에이전트가 전혀 건드릴 일이 없는 무관한 테이블이나 과거 로그에 수천 개의 토큰을 낭비하게 만듭니다.
MCP를 사용하면 컨텍스트 주입 (context injection)은 지연(lazy), 동적(dynamic), 그리고 풀 기반(pull-based)으로 이루어집니다:
- 탐색 단계 (Discovery Phase): MCP 클라이언트가 연결을 초기화할 때, 서버의 기능(capabilities)을 쿼리합니다. 서버는 사용 가능한 리소스와 도구(tools) 목록이 담긴 가벼운 매니페스트 (manifest)를 반환하며, 이때 소비되는 토큰은 무시할 수 있는 수준입니다.
- 목록화 및 URI 해석 (Listing and URI Resolution): LLM은 도구와 리소스의 설명을 검토합니다. 만약 특정 리소스(예:
orders테이블의 스키마)로부터 정보가 필요하다고 판단하면, 해당 정확한 URI(postgres://schema/orders)에 대한 읽기 요청을 보냅니다. - 타겟 주입 (Targeted Injection): MCP 서버는 오직 그 특정 리소스만을 가져와서 형식을 맞춘 뒤, 컨텍스트 윈도우 (context window)에 정확히 필요한 시점에 주입할 수 있도록 클라이언트에 반환합니다.
이는 운영체제 (operating systems)의 가상 메모리 페이징 (virtual memory pagination)과 유사합니다. 수 기가바이트에 달하는 프로그램 전체를 한꺼번에 물리적 RAM에 로드하는 대신, 운영체제는 엄격하게 수요(on demand)에 따라 메모리 블록을 페이징합니다. MCP는 이러한 디맨드 페이징 (demand-paged) 메모리 관리를 LLM 컨텍스트 윈도우로 직접 가져옵니다.
TypeScript로 프로덕션급 SaaS MCP 서버 구축하기
MCP가 데이터 액세스 및 도구 통합을 어떻게 표준화하는지 진정으로 이해하기 위해, SaaS 고객 지원 환경을 위해 구축된 구체적이고 독립적인 TypeScript 기반의 MCP 서버 구현 사례를 살펴보겠습니다.
이 서버는 사용자 구독 상세 정보와 계정 지표를 가져오는 데이터베이스 도구를 노출합니다. 이를 통해 MCP를 준수하는 어떤 클라이언트라도 새로운 모델이 출시될 때마다 매번 커스텀 API 클라이언트 래퍼 (wrapper)를 작성할 필요 없이 고객 데이터를 안전하게 쿼리할 수 있습니다.
/**
* @file mcp-saas-server.ts
* @description 기초적이고 독립적인 Model Context Protocol (MCP) 서버
...
구현의 라인별 상세 분석 (Line-by-Line Breakdown)
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기