지난 글에서는 @mcp.tool()을 이용해서 AI가 직접 DB를 조회할 수 있는 MCP 서버를 만들어봤다.
Tool을 하나 붙이고 나니까 확실히 느낌이 다르다.
Claude가 그냥 답을 만들어내는 게 아니라, 실제로 서버 함수를 호출해서 데이터를 가져오기 시작한다.
그런데 MCP를 조금 더 만져보면 알게 된다. Tool만으로는 생각보다 할 수 있는 게 제한적이다.
MCP에는 Tool 말고도 두 가지 핵심 기능이 더 있다.
| 기능 | 데코레이터 | 역할 |
| Tool | @mcp.tool() | AI가 실행할 수 있는 함수 |
| Resource | @mcp.resource() | AI에게 읽을 수 있는 데이터를 제공 |
| Prompt | @mcp.prompt() | AI에게 재사용 가능한 지시문을 제공 |
이 세 가지를 모두 갖추면 진짜 쓸 수 있는 MCP 서버가 된다.
Tool / Resource / Prompt 차이를 한 번에 이해하는 방법
비유로 설명하면 이렇다.
Tool = AI의 손 (직접 실행, 결과 반환)
Resource = AI의 참고서 (읽기 전용 데이터, 컨텍스트 제공)
Prompt = AI의 업무 매뉴얼 (미리 정의된 지시 템플릿)
예를 들어 "이번 달 매출 보고서 작성해줘" 라는 요청이 오면:
- Resource — 회사 DB 스키마, 보고서 양식 파일을 읽는다
- Tool — SQL 쿼리를 실행해서 데이터를 가져온다
- Prompt — "보고서 작성 전문가 모드"로 전환해서 결과물을 생성한다
STEP 1. Resource 만들기 — @mcp.resource()
Resource는 AI가 언제든지 참조할 수 있는 데이터다. Tool과 달리 실행이 아니라 읽기 전용이다.
기본 구조
Copy@mcp.resource("resource://스키마명")
def 함수명() -> str:
"""리소스 설명"""
return "데이터 내용"
URI 형식(resource://...)으로 이름을 붙인다는 점이 Tool과 다르다.
- DB 스키마를 Resource로 제공하기
AI가 SQL을 작성하려면 테이블 구조를 알아야 한다.
매번 Tool로 조회하는 대신, Resource로 고정해두면 훨씬 효율적이다.
from mcp.server.fastmcp import FastMCP
import sqlite3
mcp = FastMCP("sales-db-server")
@mcp.resource("resource://schema/sales")
def get_schema() -> str:
"""sales.db의 전체 테이블 스키마 반환"""
with sqlite3.connect("sales.db") as conn:
tables = conn.execute(
"SELECT name FROM sqlite_master WHERE type='table'"
).fetchall()
schema_info = []
for (table,) in tables:
columns = conn.execute(
f"PRAGMA table_info({table})"
).fetchall()
col_desc = ", ".join(
f"{col[1]}({col[2]})" for col in columns
)
schema_info.append(f"[{table}] {col_desc}")
return "\n".join(schema_info)
이제 Claude는 resource://schema/sales를 참조하면 전체 DB 구조를 한눈에 파악할 수 있다.
파일을 Resource로 제공하기
텍스트 파일, 설정 파일도 Resource로 만들 수 있다.
@mcp.resource("resource://config/report-template")
def get_report_template() -> str:
"""보고서 작성 양식 반환"""
with open("report_template.txt", "r", encoding="utf-8") as f:
return f.read()
핵심: Resource는 AI의 사전 지식처럼 동작한다.
Tool은 "실행해야 결과가 나오는 것", Resource는 "항상 참조 가능한 것"으로 구분하면 된다.
STEP 2. Dynamic Resource — 동적으로 변하는 데이터
Resource는 고정 데이터만 제공하는 게 아니다. URI 파라미터를 받아서 동적으로 데이터를 반환할 수도 있다.
@mcp.resource("resource://customer/{customer_id}")
def get_customer_profile(customer_id: str) -> str:
"""특정 고객 프로필 반환"""
with sqlite3.connect("sales.db") as conn:
row = conn.execute(
"SELECT * FROM customers WHERE id = ?",
(customer_id,)
).fetchone()
if not row:
return f"고객 ID {customer_id}를 찾을 수 없습니다."
return f"이름: {row[1]}, 이메일: {row[2]}, 지역: {row[3]}"
이제 Claude는resource://customer/42처럼 특정 고객 정보를 바로 참조할 수 있다.
( Tool로 조회하는 것과 비슷해 보이지만 개념적으로는 읽기 전용 데이터 제공에 가깝다.)
STEP 3. Prompt 만들기 — @mcp.prompt()
Prompt는 자주 쓰는 지시 패턴을 템플릿으로 저장하는 기능이다. 쉽게 말해 "Claude에게 미리 세팅해두는 업무 지시서"다.
기본 구조
@mcp.prompt()
def 프롬프트명(파라미터: str) -> str:
"""프롬프트 설명"""
return f"지시문 내용 {파라미터}"
매출 분석 전문가 프롬프트
from mcp.server.fastmcp import FastMCP
from mcp.types import TextContent
@mcp.prompt()
def sales_analyst_prompt(period: str = "이번 달") -> list:
"""매출 분석 전문가 모드 활성화"""
return [
TextContent(
type="text",
text=f"""당신은 데이터 기반 매출 분석 전문가입니다.
{period}의 매출 데이터를 분석할 때 다음 순서로 진행하세요:
1. 전체 주문 수와 총 매출액 확인
2. 상품별, 고객별 매출 비교
3. 이상 패턴(급증/급감) 탐지
4. 핵심 인사이트 3가지 도출
5. 다음 달 개선 제안 작성
모든 수치는 한국 원화(₩)로 표기하고,
숫자는 천 단위 구분자(,)를 사용하세요.
"""
)
]
이렇게 해두면 AI가 보고서를 작성할 때 분석 방식 자체가 일정하게 유지된다.
에러 리포트 프롬프트
@mcp.prompt()
def error_report_prompt(error_type: str) -> list:
"""DB 에러 발생 시 리포트 작성 지시"""
return [
TextContent(
type="text",
text=f"""DB에서 '{error_type}' 에러가 발생했습니다.
다음 항목을 순서대로 점검하고 보고서를 작성하세요:
- 에러 발생 시각과 빈도
- 영향받은 테이블과 데이터 범위
- 임시 조치 방안
- 근본 원인 추정
- 재발 방지 대책"""
)
]
STEP 4. Tool + Resource + Prompt 합치기
이제 Tool + Resource + Prompt를 모두 담은 완성형 MCP 서버를 만들어보자.
# mcp_server_full.py
from mcp.server.fastmcp import FastMCP
from mcp.types import TextContent
import sqlite3
mcp = FastMCP("sales-db-full")
# ── Resource ──────────────────────────────
@mcp.resource("resource://schema/sales")
def get_schema() -> str:
"""DB 전체 스키마"""
with sqlite3.connect("sales.db") as conn:
tables = conn.execute(
"SELECT name FROM sqlite_master WHERE type='table'"
).fetchall()
result = []
for (t,) in tables:
cols = conn.execute(f"PRAGMA table_info({t})").fetchall()
col_str = ", ".join(f"{c[1]}({c[2]})" for c in cols)
result.append(f"[{t}] {col_str}")
return "\n".join(result)
@mcp.resource("resource://customer/{customer_id}")
def get_customer(customer_id: str) -> str:
"""특정 고객 정보"""
with sqlite3.connect("sales.db") as conn:
row = conn.execute(
"SELECT * FROM customers WHERE id=?", (customer_id,)
).fetchone()
return str(row) if row else "고객 없음"
# ── Tool ──────────────────────────────────
@mcp.tool()
def query_sales(sql: str) -> list:
"""SELECT SQL 실행"""
if not sql.strip().upper().startswith("SELECT"):
return [{"error": "SELECT만 허용됩니다"}]
with sqlite3.connect("sales.db") as conn:
return conn.execute(sql).fetchall()
@mcp.tool()
def get_sales_summary() -> dict:
"""매출 요약 통계"""
with sqlite3.connect("sales.db") as conn:
total = conn.execute("SELECT COUNT(*) FROM orders").fetchone()[0]
revenue = conn.execute(
"SELECT SUM(o.quantity * p.price) "
"FROM orders o JOIN products p ON o.product_id = p.id"
).fetchone()[0]
return {"총_주문수": total, "총_매출": revenue}
# ── Prompt ────────────────────────────────
@mcp.prompt()
def sales_analyst_prompt(period: str = "이번 달") -> list:
"""매출 분석 전문가 프롬프트"""
return [TextContent(type="text", text=f"""
당신은 {period} 매출 분석 전문가입니다.
1. 총 매출 요약 확인
2. 상품/고객별 분석
3. 인사이트 3가지 도출
4. 개선 제안 제시
수치는 ₩ 단위, 천 단위 구분자 사용.
""")]
if __name__ == "__main__":
mcp.run(transport="stdio")
Copy
핵심은 세 가지 역할이 명확히 나뉜다는 것이다.
Resource → DB 구조, 고객 정보 같은 참고 데이터
Tool → 실제 SQL 실행
Prompt → 분석 방식 정의
STEP 5. 각 기능의 동작 흐름 비교
| 질문 | 기능 | 흐름 |
| "테이블 구조 알려줘" | Resource | resource://schema/sales 참조 |
| "5월 매출 합계 계산해줘" | Tool | query_sales() 실행 |
| "매출 보고서 작성해줘" | Prompt + Tool | sales_analyst_prompt() → get_sales_summary() |
| "고객 42번 정보 확인해줘" | Resource | resource://customer/42 참조 |
전체 프로젝트 구조
mcp_demo/
├── setup_db.py # DB 초기화
├── mcp_server.py # Tool만 (지난 글)
├── mcp_server_full.py # Tool + Resource + Prompt (이번 글)
├── report_template.txt # 보고서 양식
└── sales.db
마치며
지난 글: @mcp.tool() — AI의 손을 만들었다
이번 글: @mcp.resource() + @mcp.prompt()
MCP의 세 구조를 정리하면 이렇다.
- Tool = AI가 직접 실행하는 것
- Resource = AI가 참조하는 것
- Prompt = AI의 행동 방식을 정의하는 것
이 세 가지를 조합하면 단순한 "DB 조회 봇"이 아니라, 업무 컨텍스트를 이해하는 진짜 AI 어시스턴트가 된다.
시간될 때 이 MCP 서버를 로컬을 넘어 클라우드에 배포하는 방법을 다룰 예정이다. stdio 방식에서 SSE/HTTP 방식으로 전환하고, 팀원 누구나 접속 가능한 MCP 서버를 만들어볼 것이다.