지난 포스트에서는 MCP(Model Context Protocol)이 무엇인지, 그리고 MCP가 최근 화제가 되는 이유에 대해서 다뤘습니다.
해당 내용에 대해서 관심 있으신 분들은 지난 포스트 글을 참고해주시기 바랍니다.
MCP(Model Context Protocol)이 뭐길래?
인공지능(AI) 기술의 발전과 함께, AI 시스템이 외부 데이터와 효과적으로 연동되는 것이 점점 더 중요한 이슈로 떠오르고 있습니다. 기존에는 AI 모델이 내부적으로 훈련된 데이터만을 기반으로
dytis.tistory.com
이번 포스트에서는 그럼 MCP는 어떻게 활용할 수 있는건데?를 위하여, 간단한 실습을 하고자 합니다.
Claude desktop에 Naver 검색 능력 입히기를 주제로 실습해보겠습니다.
Claude desktop 설치
Claude 데스크톱은 Claude AI 챗봇의 기능을 사용자의 컴퓨터로 직접 제공하는 Windows 및 macOS 전용 애플리케이션으로, 워크플로와의 원활한 통합과 보다 빠르고 집중적인 상호작용을 가능하게 해줍니다. (아직은 베타로 제공중입니다.)
Claude 데스크톱은 아래 링크에서 다운받을 수 있습니다.
Download Claude
Download Claude for your desktop or mobile device.
claude.ai
앱을 다운받은 후, 구글 계정 또는 본인의 계정을 사용해서 로그인을 하면, 아래처럼 Chat 인터페이스를 기본으로 하는 간단한 claude app을 확인할 수 있습니다.
먼저, 여기서 기본적인 claude app의 성능을 테스트해보기 위하여, Naver OpenAPI를 사용해서 영등포구청 맛집을 찾아줘라는 질문을 던져보면 다음과 같은 답변을 합니다.
죄송합니다만, 저는 Naver OpenAPI에 직접 접근하거나 실시간으로 맛집 정보를 검색할 수 있는 기능이 없습니다. 제 지식은 2024년 10월까지의 정보로 제한되어 있으며, 현재 외부 API나 인터넷에 연결할 수 없습니다.
영등포구청 주변 맛집을 찾으시려면 다음과 같은 방법을 이용해보시길 권장드립니다:
1. 네이버 지도 앱이나 웹사이트에서 직접 "영등포구청 맛집" 검색
2. 카카오맵, 구글맵 등의 지도 서비스 이용
3. 망고플레이트, 다이닝코드와 같은 맛집 추천 앱 사용
4. 네이버 블로그나 카페에서 최근 영등포구청 맛집 리뷰 검색
혹시 Naver OpenAPI를 직접 활용하는 방법에 대해 알고 싶으시다면, 제가 API 연동 방법이나 코드 예시를 안내해드릴 수 있습니다. 원하시는 정보가 있으면 말씀해주세요.
그러면 여기서 이제 우리의 claude app에게 Naver 검색 기능을 주입해봅니다.
Develop MCP Server (FastMCP)
지난 포스트에서도 공유드렸듯이 python에서는 fastmcp라는 라이브러리를 통해서 MCP Server를 쉽게 구축할 수 있습니다.
https://github.com/jlowin/fastmcp
GitHub - jlowin/fastmcp: The fast, Pythonic way to build Model Context Protocol servers 🚀
The fast, Pythonic way to build Model Context Protocol servers 🚀 - GitHub - jlowin/fastmcp: The fast, Pythonic way to build Model Context Protocol servers 🚀
github.com
아래와 같이 FastMCP 서버를 초기화하고, 그 다음에는 필요한 엔티티에 데코레이터를 달아주는 방식으로 구현되어 있습니다.
# demo.py
from fastmcp import FastMCP
mcp = FastMCP("Demo 🚀")
특히, 구현상에서의 MCP는 3가지의 주요한 구성요소가 있습니다:
리소스 (Resource)
리소스는 AI 모델에게 필요한 정보와 컨텍스트를 제공합니다. REST API의 GET 엔드포인트와 유사합니다. 데이터를 제공하지만 상당한 계산을 수행하거나 부작용이 있어서는 안 됩니다.
ex) File contents, Database schemas, API responses, System information
@mcp.resource("config://app")
def get_config() -> str:
"""Static configuration data"""
return "App configuration here"
동적으로 인자 전달:
@mcp.resource("users://{user_id}/profile")
def get_user_profile(user_id: str) -> str:
"""Dynamic user data"""
return f"Profile data for user {user_id}"
도구 (Tool)
도구는 AI 모델이 외부 시스템에서 작업을 수행할 수 있게 합니다. 리소스와 달리 도구는 계산을 수행하고 side effect가 있을 것으로 예상됩니다. REST API의 POST 엔드포인트와 유사합니다.
@mcp.tool()
def calculate_bmi(weight_kg: float, height_m: float) -> float:
"""Calculate BMI given weight in kg and height in meters"""
return weight_kg / (height_m ** 2)
http 요청 예시)
import httpx
@mcp.tool()
async def fetch_weather(city: str) -> str:
"""Fetch current weather for a city"""
async with httpx.AsyncClient() as client:
response = await client.get(
f"https://api.weather.com/{city}"
)
return response.text
복잡한 입력 처리 예시)
from pydantic import BaseModel, Field
from typing import Annotated
class ShrimpTank(BaseModel):
class Shrimp(BaseModel):
name: Annotated[str, Field(max_length=10)]
shrimp: list[Shrimp]
@mcp.tool()
def name_shrimp(
tank: ShrimpTank,
# You can use pydantic Field in function signatures for validation.
extra_names: Annotated[list[str], Field(max_length=10)],
) -> list[str]:
"""List all shrimp names in the tank"""
return [shrimp.name for shrimp in tank.shrimp] + extra_names
프롬프트 (Prompt)
프롬프트는 AI 모델이 리소스와 도구를 활용하여 일관되고 유용한 응답을 생성하도록 안내합니다. 프롬프트는 재사용 가능한 템플릿이며, 이는 서버에 인코딩된 "모범 사례"와 같습니다. 프롬프트는 문자열만큼 간단할 수 있습니다.
@mcp.prompt()
def review_code(code: str) -> str:
return f"Please review this code:\n\n{code}"
from fastmcp.prompts.base import UserMessage, AssistantMessage
@mcp.prompt()
def debug_error(error: str) -> list[Message]:
return [
UserMessage("I'm seeing this error:"),
UserMessage(error),
AssistantMessage("I'll help debug that. What have you tried so far?")
]
FastMCP에서는 이미지나 Context에 대한 처리도 가능합니다.
Image
from fastmcp import FastMCP, Image
from PIL import Image as PILImage
@mcp.tool()
def create_thumbnail(image_path: str) -> Image:
"""Create a thumbnail from an image"""
img = PILImage.open(image_path)
img.thumbnail((100, 100))
# FastMCP automatically handles conversion and MIME types
return Image(data=img.tobytes(), format="png")
@mcp.tool()
def load_image(path: str) -> Image:
"""Load an image from disk"""
# FastMCP handles reading and format detection
return Image(path=path)
Context
Context 객체는 도구와 리소스에 MCP 기능에 대한 액세스를 제공합니다. 이를 사용하려면 fastmcp.Context로 주석이 달린 매개변수를 추가합니다.
from fastmcp import FastMCP, Context
@mcp.tool()
async def long_task(files: list[str], ctx: Context) -> str:
"""Process multiple files with progress tracking"""
for i, file in enumerate(files):
ctx.info(f"Processing {file}")
await ctx.report_progress(i, len(files))
# Read another resource if needed
data = await ctx.read_resource(f"file://{file}")
return "Processing complete"
SQLite Explorer 예시
from fastmcp import FastMCP
import sqlite3
mcp = FastMCP("SQLite Explorer")
@mcp.resource("schema://main")
def get_schema() -> str:
"""Provide the database schema as a resource"""
conn = sqlite3.connect("database.db")
schema = conn.execute(
"SELECT sql FROM sqlite_master WHERE type='table'"
).fetchall()
return "\n".join(sql[0] for sql in schema if sql[0])
@mcp.tool()
def query_data(sql: str) -> str:
"""Execute SQL queries safely"""
conn = sqlite3.connect("database.db")
try:
result = conn.execute(sql).fetchall()
return "\n".join(str(row) for row in result)
except Exception as e:
return f"Error: {str(e)}"
@mcp.prompt()
def analyze_table(table: str) -> str:
"""Create a prompt template for analyzing tables"""
return f"""Please analyze this database table:
Table: {table}
Schema:
{get_schema()}
What insights can you provide about the structure and relationships?"""
날씨 도구 예시
from mcp.server.fastmcp import FastMCP
# MCP 서버 초기화
mcp = FastMCP("weather-service")
@mcp.resource()
async def get_locations(self) -> list:
"""사용자가 자주 검색하는 위치 목록을 반환합니다"""
return ["Seoul", "New York", "London", "Tokyo"]
@mcp.tool()
async def get_weather(self, location: str) -> dict:
"""특정 위치의 날씨 정보를 가져옵니다"""
api_key = "your_api_key"
url = f"https://api.weather.com/v1/location/{location}?apiKey={api_key}"
async with httpx.AsyncClient() as client:
response = await client.get(url)
return response.json()
@mcp.tool()
async def save_favorite_location(self, location: str) -> bool:
"""사용자의 즐겨찾는 위치를 저장합니다"""
# 데이터베이스에 저장하는 로직
return True
@mcp.prompt("weather_forecast")
def weather_forecast_prompt(location: str, forecast: dict) -> str:
return f"""
{location}의 날씨 예보:
현재 온도: {forecast['current']['temperature']}°C
날씨 상태: {forecast['current']['condition']}
오늘의 예상 최고/최저 기온: {forecast['today']['high']}°C / {forecast['today']['low']}°C
내일 예보: {forecast['tomorrow']['condition']}, {forecast['tomorrow']['high']}°C / {forecast['tomorrow']['low']}°C
"""
# MCP 서버 실행 - 지속적인 연결 유지
if __name__ == "__main__":
mcp.run(transport='stdio')
Naver OpenAPI MCP
Naver OpenAPI
다시 본론으로 돌아와서, Naver에서는 자사의 다양한 검색 서비스를 OpenAPI로 제공하고 있습니다.
https://developers.naver.com/docs/common/openapiguide/README.md
API 공통 가이드 - Open API 가이드
API 공통 가이드 네이버 오픈API는 네이버 플랫폼의 기능을 외부 개발자가 쉽게 이용할 수 있게 웹 또는 SDK 형태로 공개한 기술들입니다. 네이버 오픈API로 활용할 수 있는 기술에는 네이버 로그인
developers.naver.com
네이버가 엄청 다양한 서비스를 제공하고 있는 큰 회사이다보니, OpenAPI를 제공해주는 목록도 굉장히 많습니다.
그 중에서 검색 서비스의 경우 하루 25,000회의 호출 제한이 있지만 네이버의 다양한 검색 서비스를 무료로 사용해볼 수 있습니다.
실행 준비
이러한 네이버의 OpenAPI를 MCP Server로 구현해둔 작업물이 있어, 이것을 활용해볼 예정입니다.
https://github.com/pfldy2850/py-mcp-naver
GitHub - pfldy2850/py-mcp-naver: python MCP NAVER
python MCP NAVER. Contribute to pfldy2850/py-mcp-naver development by creating an account on GitHub.
github.com
(맞습니다. 제가 만들었습니다..)
이 MCP 서버는 네이버에서 제공하는 검색 관련 OpenAPI들을 모두 사용할 수 있도록 래핑해둔 서버입니다.
먼저, 위 API를 사용하기 위해서 아래 링크에 들어가 api 사용 신청을 해야 합니다.
https://developers.naver.com/apps/#/register=datalab
애플리케이션 - NAVER Developers
developers.naver.com
신청이 완료되었으면, client id와 client secret을 확인할 수 있고, 이는 추후 mcp server 등록시 넣어주기 위해 환경변수로 작성합니다
export NAVER_CLIENT_ID=<YOUR NAVER CLIENT ID>
export NAVER_CLIENT_SECRET=<YOUR NAVER CLIENT SECRET>
이렇게 준비가 완료되었으면 위의 레포의 내용대로 설치합니다.
uv pip install mcp-naver
uv run python -m mcp-naver.hosts.claude_desktop \
-e NAVER_CLIENT_ID=$NAVER_CLIENT_ID \
-e NAVER_CLIENT_SECRET=$NAVER_CLIENT_SECRET
위의 명령어들을 다 실행했으면, 적용을 위해서 claude app을 종료하고 재시작합니다.
익숙한 Chat 인터페이스의 오른쪽 아래에 망치 아이콘과 함께 MCP tools를 14개 사용할 수 있다고 표시가 되어 있습니다.
이를 클릭해서 보면, Naver OpenAPI MCP 서버에서 제공하는 도구 목록이 보이는 것을 확인할 수 있습니다.
적용된 mcpServers에 대한 설정파일은 macOS는 ~/Library/Application Support/Claude/claude_desktop_config.json 경로에 있고, Windows는 %APPDATA%\Claude\claude_desktop_config.json 경로에 있으니 직접 파일을 수정할 수도 있습니다.
적용 결과
그러면 아까와 같이 Naver OpenAPI를 활용해서 영등포구청역 맛집을 검색해줘 라고 물어보겠습니다.
claude app은 필요한 내용을 찾기 위해 Naver OpenAPI의 search_local (지역 검색) tool을 활용하여, 영등포구청역 맛집 이라는 검색어를 생성해 검색합니다.
{
`query`: `영등포구청역 맛집`,
`display`: 10
}
{
"lastBuildDate":"Thu, 20 Mar 2025 22:29:01 +0900",
"total":5,
"start":1,
"display":5,
"items":[
{
"title":"민똣",
"link":"https:\/\/www.instagram.com\/mintot_official",
"category":"음식점>베트남음식",
"description":"",
"telephone":"",
"address":"서울특별시 영등포구 당산동1가 55 1층",
"roadAddress":"서울특별시 영등포구 당산로10길 28-1 1층",
"mapx":"1268989296",
"mapy":"375213933"
},
{
"title":"당산오돌 본점",
"link":"https:\/\/blog.naver.com\/skill8114",
"category":"한식>육류,고기요리",
"description":"",
"telephone":"",
...
}
그러고 나서, 더 많은 맛집 정보를 찾아보겠다는 말과 함께 search_blog(블로그 검색)에 영등포구청역 맛집 추천 이라는 쿼리를 직접 생성하여 검색합니다.
{
`query`: `영등포구청역 맛집 추천`,
`display`: 5
}
{
"lastBuildDate":"Thu, 20 Mar 2025 22:29:06 +0900",
"total":4805,
"start":1,
"display":5,
"items":[
{
"title":"[<b>맛집<\/b>] <b>영등포구청역 맛집<\/b> | 무제한 샤브샤브 <b>맛집 추천<\/b>... ",
"link":"https:\/\/blog.naver.com\/hayoungju98\/223795238282",
"description":"❗️영업 시간 매일 11:00 ~ 21:00 주말에만 브레이크 타임 15:00 ~ 17:00 외관 <b>영등포구청역 맛집<\/b> 샤브올데이 당산점의 외관. 생각보다 가게가 꽤 커서 놀랐다. 그리고 더 놀란건 가게 앞 웨이팅 ㅠㅋㅋㅋ 예약도... ",
"bloggername":"Zero Blog",
"bloggerlink":"blog.naver.com\/hayoungju98",
"postdate":"20250313"
},
{
"title":"<b>영등포구청역 맛집 추천<\/b> 고기집 동래정",
"link":"https:\/\/blog.naver.com\/wj2796\/223530123811",
"description":"내부 및 인테리어 영등포구청역 맛집 매장으로 들어서면 이렇습니다. 아담하면서 깔끔한 분위기가... 걸 추천드린다니 기대가 된답니다. 3. 메뉴 <b>영등포구청역맛집 추천<\/b> 동래정은 하이볼도 있는데요. 오후 3시... ",
그런 다음, 위의 검색 내용을 토대로 영등포구청역 주변 맛집 리스트를 다시 재생성하여 답변을 생성합니다.
영등포구청역 맛집에 대한 정보를 찾아드리겠습니다. Naver OpenAPI를 통해 검색해보겠습니다.
더 많은 맛집 정보를 찾아보겠습니다.
영등포구청역 맛집을 Naver OpenAPI를 통해 검색한 결과, 다음과 같은 추천 맛집들을 찾았습니다:
### 영등포구청역 주변 맛집 리스트
1. **민똣** (베트남음식)
- 주소: 서울특별시 영등포구 당산로10길 28-1 1층
- 인스타그램: @mintot_official
2. **당산오돌 본점** (육류, 고기요리)
- 주소: 서울특별시 영등포구 국회대로37길 6 1층
- 블로그: blog.naver.com/skill8114
3. **88냉삼 영등포구청직영점** (돼지고기구이)
- 주소: 서울특별시 영등포구 당산로32길 1-6
4. **연탄불** (육류, 고기요리)
- 주소: 서울특별시 영등포구 양산로17길 17
5. **팬팬떡볶이** (분식, 떡볶이)
- 주소: 서울특별시 영등포구 영등포로 143 1층
- 인스타그램: @panfan1997
### 블로그에서 추천하는 맛집
1. **샤브올데이 당산점** (무제한 샤브샤브)
- 영업시간: 매일 11:00 ~ 21:00 (주말 브레이크 타임 15:00 ~ 17:00)
- 가게가 크고 웨이팅이 있는 인기 맛집
2. **동래정** (고기집)
- 아담하고 깔끔한 분위기의 매장
- 하이볼 메뉴도 있음
3. **대박곱창구이** (곱창전문점)
- 오동통한 소곱창이 특징
- 천엽, 선지 해장국 서비스 제공
- 메뉴: 마늘/황소/모둠 곱창, 특양/대창/막창 구이 등
- 모둠곱창 250g 25,000원
4. **미락카츠 본점** (돈카츠 전문점)
- 영등포구청 인근 카츠 맛집
이 중에서 더 자세한 정보나 특정 음식 종류에 대해 알고 싶으시면 말씀해주세요!
추가
Naver OpenAPI를 사용해달라는 지시어를 제외하더라도, 스스로 MCP를 잘 찾아서 사용합니다.
Q: 영등포구청역 맛집 찾아줘
A:
영등포구청역 주변 맛집을 찾아드리겠습니다.
영등포구청역 주변의 추천 맛집들을 소개해드립니다:
### 지역 맛집 리스트
1. **민똣** - 베트남 음식 전문점
- 주소: 서울특별시 영등포구 당산로10길 28-1 1층
- 인스타그램: mintot_official
2. **당산오돌 본점** - 육류, 고기요리 전문점
- 주소: 서울특별시 영등포구 국회대로37길 6 1층
- 블로그: skill8114
3. **88냉삼 영등포구청직영점** - 돼지고기구이 전문점
- 주소: 서울특별시 영등포구 당산로32길 1-6
4. **연탄불** - 육류, 고기요리 전문점
- 주소: 서울특별시 영등포구 양산로17길 17
5. **팬팬떡볶이** - 떡볶이 전문점
- 주소: 서울특별시 영등포구 영등포로 143 1층
- 인스타그램: panfan1997
### 블로그 추천 맛집
1. **샤브올데이 당산점** - 무제한 샤브샤브 전문점
- 영업 시간: 매일 11:00 ~ 21:00 (주말 브레이크 타임 15:00 ~ 17:00)
- 특징: 가게가 크고 인기가 많아 웨이팅이 있을 수 있음
2. **동래정** - 고기집
- 특징: 아담하고 깔끔한 분위기, 하이볼 메뉴도 있음
3. **대박곱창구이** - 소곱창 전문점
- 특징: 통통한 마늘 소곱창이 유명하며, 천엽/선지 해장국 서비스 제공
- 메뉴: 모둠곱창 250g 25,000원
- 마늘/황소/모둠 곱창, 특양/대창/막창 구이 등 다양한 메뉴 보유
4. **미락카츠 본점** - 카츠 전문점
- 영등포구청 카츠 맛집으로 유명
맛집을 더 자세히 알아보시거나 특정 음식 종류에 대한 추천이 필요하시면 말씀해주세요!
Q: 요즘 생수 가격이 얼마정도 해?
A:
현재 한국의 생수 가격에 대해 알아보겠습니다.
현재 한국의 생수 가격을 검색해 보니 다양한 종류와 용량별로 가격 차이가 있습니다. 주요 생수 가격은 다음과 같습니다:
1. 소용량 생수 (500ml)
- 동원샘물 무라벨 500ml x 20병: 4,500원 (병당 약 225원)
- 삼다수 500ml 무라벨 x 20개: 5,400원 (병당 약 270원)
- 노브랜드 미네랄워터 500ml x 20입: 3,800원 (병당 약 190원)
- 스파클 무라벨 500ml x 80개: 12,890원 (병당 약 161원)
2. 대용량 생수 (2L)
- 동원샘물 무라벨 2L x 6개: 1,500원 (병당 약 250원)
- 삼다수 2L 무라벨 x 6개: 3,500원 (병당 약 583원)
- 스파클 무라벨 2L x 24개: 9,290원 (병당 약 387원)
3. 기타 생수
- 몽베스트 330ml: 8,300원 (구체적인 개수는 명시되지 않음)
- 자연드림 JUST 기픈물 종이팩 1,000ml x 12입: 16,150원~17,950원 (팩당 약 1,350~1,500원)
일반적으로 브랜드, 용량, 구매 수량에 따라 가격이 다양하게 형성되어 있으며, 대형마트나 온라인 플랫폼별로도 가격 차이가 있습니다. 무라벨 제품이 많이 출시되어 있고, 대량 구매 시 병당 가격이 더 저렴한 경향이 있습니다.
결론
이번 포스트에서는 Claude Desktop App을 설치하고, Naver OpenAPI MCP Server를 활용하여 claude app에 naver 검색 능력을 활용할 수 있도록 실습을 진행하였습니다. 그 과정에서 FastMCP를 활용하여 Python MCP Server를 쉽고 간단하게 작성하는 방법에 대해서 간략하게 다뤘습니다.
MCP가 오픈소스로 공개되고 나서, 많은 참여자들이 MCP Server 개발에 참여하여 생태계를 폭발적으로 성장시키고 있는 중입니다. 이러한 상황에서 활용할 수 있는 많은 MCP를 적재적소에 활용해서 개인의 요구사항에 맞는 적절한 LLM agent를 만들기도 쉬워지는 것 같습니다.
물론 MCP는 아직 초기 단계이기 때문에 과장된 기대라는 시각도 있지만, 실질적인 사례가 쌓이고 있다는 점에서 단순한 유행보다는 AI 기술의 다음 단계를 여는 중요한 전환점이 될 가능성이 높아 보입니다. 앞으로 MCP가 AI 생태계에 어떤 변화를 가져올지 지속적으로 주목할 필요가 있을 것 같습니다.
'AI > 어플리케이션 개발' 카테고리의 다른 글
Cursor에서 MCP (Model Context Protocol) 사용하기 (0) | 2025.04.03 |
---|---|
MCP(Model Context Protocol)이 뭐길래? (0) | 2025.03.20 |
LangChain을 활용한 Tool Calling # 4 (3) | 2024.11.29 |
LangChain을 활용한 Tool Calling # 3 (0) | 2024.11.29 |
LangChain을 활용한 Tool Calling # 2 (3) | 2024.11.29 |