MCP 실무: Tools, Resources 및 각각의 사용 시점
요약
Model Context Protocol(MCP)의 핵심 구성 요소인 Tools, Resources, Prompts의 개념과 차이점을 설명합니다. AI 애플리케이션이 외부 데이터 및 도구와 표준화된 방식으로 통신할 수 있게 하는 적응 계층으로서의 역할을 다룹니다.
핵심 포인트
- MCP는 AI 모델과 외부 데이터/도구를 연결하는 표준화된 JSON-RPC 기반 프로토콜입니다.
- Tools는 모델이 직접 호출하여 실행할 수 있는 부수 효과를 가진 함수입니다.
- Resources는 URI로 식별되는 읽기 전용 데이터로, 컨텍스트 제공을 목적으로 합니다.
- Prompts는 모델의 동작을 안내하기 위한 재사용 가능한 지침 템플릿입니다.
강의 카탈로그 MCP 서버 구축 과정의 교훈 — POC에서 프로덕션까지.
Model Context Protocol (MCP)란 무엇인가?
**Model Context Protocol (MCP)**는 AI 애플리케이션(Cursor, Claude Desktop 또는 기타 호스트)이 외부 데이터 소스 및 도구에 표준화된 방식으로 연결할 수 있도록 하는 개방형 표준입니다.
MCP를 범용 콘센트라고 생각하세요. 각 에디터가 데이터베이스, API, 스크립트와의 통합 방식을 제각각 발명하는 대신, 모든 것이 전송 계층(stdio 또는 HTTP) 위에서 동일한 프로토콜인 JSON-RPC 2.0을 사용하여 통신합니다.
┌─────────────┐ JSON-RPC ┌─────────────┐ HTTP/SQL ┌─────────────┐
│ Cursor │ ◄──────────────► │ MCP Server │ ◄─────────────► │ Backend │
│ (cliente) │ tools/call │ (adaptador)│ │ (dados) │
...
MCP는 비즈니스 API를 대체하지 않습니다. 이는 언어 모델과 현실 세계 사이의 **적응 계층 (adaptation layer)**입니다.
MCP의 세 가지 프리미티브 (Primitives)
이 프로토콜은 세 가지 유형의 역량을 노출합니다. 이들의 차이점을 이해하는 것이 이 글의 핵심입니다.
| 프리미티브 | 비유 | 제어 주체 | 프로토콜 |
|---|---|---|---|
| Tool | 동사 — 무언가를 수행함 | 모델 (인간의 감독 하에) | tools/call |
| ... |
Tools — 호출 가능한 액션
Tools는 모델이 호출할 수 있는 함수입니다. 각 tool은 이름, 설명, 입력 스키마 (Zod를 통한 JSON Schema)를 가지며 결과를 반환합니다.
{
"name": "criar_curso",
"description": "카탈로그에 새로운 강의를 생성합니다.",
...
모델이 사용하는 시점: 사용자가 액션을 요청할 때 — "12시간 분량의 NestJS 강의를 만들어줘" — 모델은 criar_curso를 호출하기로 결정합니다.
특징:
- **부수 효과 (side effect)**를 가질 수 있음 (생성, 업데이트, 삭제, 이메일 발송)
- 모델이 스스로 수정할 수 있도록
isError: true와 함께 구조화된 에러를 반환 - 인간이 루프 안에 있어야 함 (승인, 로그, 확인)
Resources — 읽기 가능한 수동적 데이터
Resources는 호스트나 모델이 컨텍스트를 얻기 위해 읽을 수 있는 URI로 식별되는 데이터입니다.
cursos://catalogo → 전체 카탈로그 (JSON)
cursos://f47ac10b-58cc-... → 특정 코스의 상세 정보
file:///docs/guia.md → 정적 문서
모델이 사용하는 시점: 사용자가 정보를 조회하고자 할 때 — "어떤 코스들이 있나요?" — 또는 호스트가 리소스를 컨텍스트에 자동으로 첨부할 때 사용합니다.
특징:
- 읽기 전용 (Somente leitura) (설계상 특징)
- URI로 식별 (커스텀 스키마 허용)
- 고정형 (
cursos://catalogo) 또는 템플릿형 (cursos://{uuid}) 가능 - 애플리케이션 주도형 (Application-driven): 호스트가 어떻게 표시할지, 언제 컨텍스트에 포함할지를 결정합니다.
Prompts — 재사용 가능한 템플릿
Prompts는 알려진 흐름에 따라 모델을 안내하는 매개변수화된 지침 모델 (Instruction models) 입니다.
/plan-vacation destination=Barcelona duration=7 budget=3000
사용 시점: 온보딩, 체크리스트, 검토 워크플로우와 같이 일관성을 유지하고자 하는 반복 가능한 흐름에서 사용합니다.
황금률: Tool vs Resource
| 질문 | 답변이 '예'라면 → |
|---|---|
| 작업이 시스템의 무언가를 변경합니까? | Tool |
| ... |
구체적인 예시: 코스 카탈로그
우리 프로젝트의 V2에서는 세 가지 Tool이 있었습니다:
listar_cursos → 읽기 (하지만 Tool로 노출됨)
buscar_curso → 읽기 (하지만 Tool로 노출됨)
criar_curso → 쓰기 ✓
V3에서는 이를 올바르게 분리했습니다:
Resources:
cursos://catalogo → 카탈로그 읽기
cursos://{uuid} → 코스 읽기
...
왜 변경했나요? listar_cursos와 buscar_curso는 부수 효과 (Side effect)가 없었기 때문입니다. 즉, 이들은 작업(Action)으로 위장된 조회(Query)였습니다. 이는 중복을 초래했습니다. 모델이 명확한 기준 없이 동일한 작업을 수행하는 두 가지 방식 사이에서 선택해야 하는 상황이 발생했습니다.
Tool을 사용하는 시점
✅ 다음과 같을 때 Tool을 사용하세요...
| 시나리오 | 예시 |
|---|---|
| 데이터 생성 | criar_curso({ titulo, cargaHoraria }) |
| ... |
❌ 다음과 같을 때 Tool을 사용하지 마세요...
| 시나리오 | 대신 이것을 사용하세요 |
|---|---|
| 부수 효과(side effect) 없는 데이터 목록 조회 | Resource (cursos://catalogo) |
| ... |
Resources를 사용하는 시점
✅ 다음과 같을 때 Resource를 사용하세요...
| 시나리오 | 예시 |
|---|---|
| 카탈로그 또는 참조 목록 | cursos://catalogo |
| ... |
❌ 다음과 같을 때 Resource를 사용하지 마세요...
| 시나리오 | 대신 이것을 사용하세요 |
|---|---|
| 상태를 변경하는 작업 | Tool |
| ... |
고정(Fixed) Resource vs 템플릿(Template) Resource
| 유형 | URI | 사용 시점 |
|---|---|---|
| 고정 (Fixed) | cursos://catalogo | 단일 데이터, 알려진 주소 |
| 템플릿 (Template) | cursos://{uuid} | 매개변수화됨, N개의 인스턴스 |
Prompts를 사용하는 시점
✅ 다음과 같을 때 Prompt를 사용하세요...
- 알려진 매개변수를 가진 **반복 가능한 흐름 (repeatable flow)**이 존재하는 경우
- 모델이 작업을 수행하는 방식에 **일관성 (consistency)**을 부여하고 싶은 경우
- 사용자가 명시적으로 호출하는 경우 (슬래시 명령어, 팔레트)
❌ 다음과 같을 때 Prompt를 사용하지 마세요...
- 작업이 **임시적 (ad hoc)**이고 예측 불가능한 경우 → 모델이 Tool/Resource를 자유롭게 사용하도록 두세요
- 흐름이 많은 동적 변수에 의존하는 경우 → Tool이 더 유연합니다
전송 (Transport): stdio vs HTTP
MCP는 클라이언트와 서버가 어떻게 통신하는지를 정의합니다. 이는 Tool/Resource와는 독립적입니다.
| 전송 방식 | 작동 방식 | 사용 시점 |
|---|---|---|
| stdio | Cursor가 프로세스를 생성(spawn); stdin/stdout을 통한 JSON-RPC 통신 | 로컬 개발, 에디터와의 통합 |
| Streamable HTTP | 원격 HTTP 서버; JSON-RPC를 사용하는 POST/GET | 원격 배포, 다중 클라이언트, HTTP 인증 |
stdio — POC에서의 방식
{
"mcpServers": {
"mcp-cursos": {
...
장점: 단순함, 네트워크 설정 불필요, 생태계 표준.
한계: 로컬 프로세스 — Cursor가 바이너리를 직접 실행(spawn)해야 함.
Streamable HTTP — 향후 진화 방향
서버가 URL(https://api.exemplo.com/mcp)을 노출하며, Authorization: Bearer 헤더를 통해 인증합니다. 팀 간의 공유된 프로덕션 (production) 환경에 권장됩니다.
아키텍처: MCP는 백엔드가 아닙니다
흔히 하는 실수는 모든 로직을 MCP 서버 안에 넣는 것입니다. 우리가 채택한 아키텍처는 책임을 분리합니다:
┌──────────────────────────────────────────────────────────┐
│ apps/mcp MCP 어댑터 (stdio) │
│ - tools 및 resources 등록 │
...
왜 그럴까요?
- 독립적으로 테스트 가능한 백엔드 (
curl, Postman) - 가벼운 (thin) MCP — 프로토콜 어댑테이션만 수행
- 동일한 API가 MCP, 외부 통합 및 모바일 서비스에 제공됨
- 독립적인 배포 (로컬 MCP, 원격 백엔드)
**MCP 명세 (spec)**는 서버가 API와 통신하는 방식을 규정하지 않습니다. REST, gRPC 또는 인프로세스 (in-process) 호출 등 구현 방식은 자유롭게 선택할 수 있습니다.
우리 프로젝트의 진화: V1 → V2 → V3
| 버전 | 읽기 (Read) | 쓰기 (Write) | 영속성 (Persistence) | 인증 (Auth) |
|---|---|---|---|---|
| V1 | Tools (listar, buscar) | Tool (criar) | Mock in-memory | — |
| ... | ||||
| V3에서는 다음 규칙을 적용했습니다: Resource = 읽기, Tool = 동작. |
MCP에 영향을 미치는 도메인 결정 사항
아카이빙 (soft delete)
강의를 삭제하는 대신, 아카이빙 (archiving) 처리를 합니다 — arquivado: true. 강의는 활성 카탈로그에서는 사라지지만, URI를 통해 계속 조회할 수 있습니다.
| 위치 | 동작 |
|---|---|
cursos://catalogo | 활성 (active) 강의만 목록에 표시 |
| ... |
부분 업데이트 (Partial Update)
atualizar_curso는 titulo 및/또는 cargaHoraria를 허용하며, 최소 하나는 필수입니다. 이는 REST의 PATCH 방식과 일치하며 불필요한 데이터를 다시 보내는 것을 방지합니다.
다음 MCP 구현을 위한 빠른 체크리스트
구현하기 전에 다음을 질문해 보세요:
- 무엇이 읽기인가? → 명확한 URI를 가진 Resource
- 무엇이 동작인가? → 유효성 검증된 스키마 (schema)를 가진 Tool
- MCP가 데이터베이스와 직접 통신하는가? → 지양하세요; 중간 API를 사용하세요
- stdio인가 HTTP인가? → 로컬은 stdio, 원격은 HTTP
- 알림 (notifications)이 필요한가? → 일단 없이 시작하세요; 수동 새로고침이 불편할 때 추가하세요
- 읽기용 Tools가 Resources와 공존하는가? → 중복을 피하세요
참고 문헌
참고 문헌
- Model Context Protocol — 공식 문서
- MCP 서버 이해하기 — Tools, Resources, Prompts
- 명세: Tools
- 명세: Resources
- 참조 저장소: 프로젝트 mcp-cursos — V1 (mock)의 POC, V2 (백엔드), V3 (Resources)
결론
MCP는 마법이 아닙니다. 이것은 프로토콜입니다. Tools는 동사(verbs)이고, Resources는 명사(nouns)이며, Prompts는 시나리오(scripts)입니다. 읽기(reading)와 쓰기(writing)를 분리하면 서버가 모델에게 예측 가능해지고, 사용자 본인에게는 유지 관리가 더 쉬워집니다.
간단하게 시작하세요: stdio, 적은 tools, mock 또는 최소한의 API로 시작합니다. 수동적인 읽기가 의미가 있을 때 Resources로 발전시키고, 백엔드를 클라이언트 간에 공유해야 할 때는 원격 HTTP(remote HTTP)로 발전시키세요.
저희가 구축한 코스 카탈로그는 이 모델에 도달하기까지 세 가지 버전을 거쳤습니다. 각 반복 과정은 무언가를 가르쳐 주었고 — 이러한 결정들을 문서화하는 것(ADRs, 용어집, 본 포스트)은 다음 개발자가 바퀴를 재발명하는 것을 방지합니다.
mcp-cursos 프로젝트 구현을 기반으로 작성됨 — 그리들 세션, ADRs 및 실제 코드.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기