2026/06/13

LLM 에 MCP 를 연동하는 방법 — 날짜 서버로 배우는 Model Context Protocol

LLM(대형 언어 모델) 은 기본적으로 텍스트를 받아 텍스트를 내뱉는 시스템입니다. 하지만 외부 데이터에 접근하거나, 실시간 정보를 얻거나, 파일 시스템을 조작하려면 도구(Tool)가 필요합니다. MCP(Model Context Protocol) 는 LLM 이 외부 도구와 안전하게 통신할 수 있게 하는 표준 프로토콜입니다.

이 글에서는 "오늘 날짜를 알려주는 MCP 서버"를 예로 들어, MCP 서버를 직접 코딩하고, 설정하고, LLM 이 이를 호출하는 전체 과정을 단계별로 설명합니다.


1. MCP(Model Context Protocol) 란?

MCP 는 Anthropic 이 제안한 개방형 프로토콜로, LLM 애플리케이션과 외부 데이터 소스·도구들을 연결하는 표준 인터페이스입니다. USB 포트가 다양한 주변기기를 연결하듯, MCP 는 어떤 LLM 이든 어떤 MCP 서버든 연결할 수 있게 합니다.

MCP 가 없으면 각 LLM 플랫폼마다 전용 플러그인을 만들어야 합니다. MCP 가 있으면 한 번 만든 서버가 Claude, Cursor, Hermes Agent 등 모든 LLM 플랫폼에서 바로 동작합니다.

MCP 의 핵심 구성 요소

  • MCP Host: LLM 을 실행하는 애플리케이션 (예: Hermes Agent, Claude Desktop, Cursor)
  • MCP Server: 외부 도구나 데이터를 제공하는 서버 (예: 오늘 날짜를 반환하는 서버)
  • Transport: 호스트와 서버 간 통신 방식 (stdio, HTTP/SSE)
  • Tools: 서버가 호스트에 제공하는 기능 목록 (예: get_today)

MCP 가 해결하는 문제

  • 재사용: 한 번 작성한 MCP 서버가 모든 LLM 플랫폼에서 동작
  • 보안: LLM 은 MCP 서버를 통해만 외부에 접근 — 직접 네트워크/파일시스템 접근 불가
  • 표준화: JSON-RPC 기반의 통일된 프로토콜 — 각 플랫폼별 커스텀 플러그인 불필요

2. MCP 서버 코드 작성하기 — 날짜 서버 만들기

먼저 Python 으로 "오늘 날짜를 알려주는" MCP 서버를 직접 만들어보겠습니다. mcp Python 패키지를 사용하면 몇 줄로도 서버를 만들 수 있습니다.

2-1. 환경 준비

먼저 MCP SDK 를 설치합니다.

pip install mcp

2-2. 날짜 서버 전체 코드

#!/usr/bin/env python3
"""
today_mcp_server.py — 오늘 날짜를 알려주는 MCP 서버

사용법:
  python today_mcp_server.py        # stdio 모드로 실행
  python today_mcp_server.py --http  # HTTP 모드로 실행 (포트 8090)
"""

import argparse
from datetime import datetime
from zoneinfo import ZoneInfo
from mcp.server import Server

app = Server("today-server")

# ── 비즈니스 로직 ──────────────────────────────────────────
def get_today(timezone: str = "Asia/Seoul") -> dict:
    """지정된 시간대의 현재 날짜와 시간을 반환합니다."""
    try:
        tz = ZoneInfo(timezone)
        now = datetime.now(tz)
        weekdays = ["Monday", "Tuesday", "Wednesday", "Thursday",
                    "Friday", "Saturday", "Sunday"]
        return {
            "datetime": now.strftime("%Y-%m-%d %H:%M:%S"),
            "timezone": timezone,
            "weekday": weekdays[now.weekday()],
            "date_only": now.strftime("%Y-%m-%d"),
            "time_only": now.strftime("%H:%M:%S")
        }
    except Exception as e:
        return {"error": str(e)}

def get_formatted_date(format_str: str, timezone: str = "Asia/Seoul") -> str:
    """strftime 포맷으로 날짜를 반환합니다."""
    try:
        tz = ZoneInfo(timezone)
        now = datetime.now(tz)
        return now.strftime(format_str)
    except Exception as e:
        return f"Error: {e}"

# ── MCP 서버 ───────────────────────────────────────────────
@app.tool()
def get_today_tool(timezone: str = "Asia/Seoul") -> dict:
    """현재 날짜와 시간을 반환합니다.

    Args:
        timezone: 시간대 (기본값: Asia/Seoul)
                 예: America/New_York, Europe/London, UTC

    Returns:
        날짜, 시간, 요일, 시간대 정보
    """
    return get_today(timezone)

@app.tool()
def get_formatted_date_tool(format_str: str, timezone: str = "Asia/Seoul") -> str:
    """지정된 포맷으로 날짜를 반환합니다.

    Args:
        format_str: strftime 포맷 (예: "%Y년 %m월 %d일")
        timezone: 시간대 (기본값: Asia/Seoul)

    Returns:
        포맷팅된 날짜 문자열
    """
    return get_formatted_date(format_str, timezone)

# ── 실행 ───────────────────────────────────────────────────
if __name__ == "__main__":
    parser = argparse.ArgumentParser(description="Today MCP Server")
    parser.add_argument("--http", action="store_true", help="Run in HTTP mode")
    args = parser.parse_args()

    if args.http:
        from mcp.server.http import run_server_as_async_http_app
        import http.server
        import threading

        # Simple HTTP server for testing
        from wsgiref.simple_server import make_server
        from mcp.server.http import run_server_as_async_http_app

        async def main():
            app_instance = run_server_as_async_http_app(app)
            httpd = make_server("localhost", 8090, app_instance)
            print("MCP HTTP Server running on http://localhost:8090")
            httpd.serve_forever()

        import asyncio
        asyncio.run(main())
    else:
        from mcp.server.stdio import stdio_server
        import asyncio

        async def main():
            async with stdio_server() as (read, write):
                await app.run(read, write)

        asyncio.run(main())

코드 설명

이 코드는 크게 두 부분으로 나뉩니다.

1) 비즈니스 로직 (상단)

  • get_today(): 지정된 시간대의 현재 날짜/시간을 반환
  • get_formatted_date(): strftime 포맷으로 사용자 정의 날짜 형식 반환
  • 에러 처리: 잘못된 시간대 이름도 try/except 로 안전하게 처리

2) MCP 서버 (하단)

  • @app.tool(): 함수를 MCP 도구로 등록 — LLM 이 호출할 수 있음
  • stdio 모드: 기본 방식. 호스트가 서버를 자 프로세스로 실행하고 stdin/stdout 으로 통신
  • HTTP 모드: 서버를 별도 HTTP 엔드포인트로 실행 — 원격 호스트에서 연결 가능

테스트 해보기

이렇게 하면 서버를 거치지 않고 함수가 정상 동작하는지 확인할 수 있습니다.

# 서버를 거치지 않고 직접 테스트
from today_mcp_server import get_today, get_formatted_date

print(get_today())
# {"datetime": "2025-06-13 14:30:00", "timezone": "Asia/Seoul", ...}

print(get_formatted_date("%Y년 %m월 %d일 (%A)"))
# 2025년 06월 13일 (Friday)

3. MCP 서버 설정하기

서버 코드를 만들었다면, 이제 LLM 애플리케이션에 연결해야 합니다. Hermes Agent 를 예로 들어 설명합니다.

3-1. config.yaml 에 등록

~/.hermes/config.yaml 파일에 mcp_servers 섹션을 추가합니다.

mcp_servers:
  today:
    command: python
    args:
      - ~/Developer/scripts/today_mcp_server.py

여기서 today 는 서버 이름입니다. 이 이름이 도구 이름 앞에 붙습니다:

  • 서버의 get_today 도구 → today.get_today(로 인식됨)
  • 서버의 get_formatted_date 도구 → today.get_formatted_date(로 인식됨)

3-2. 설정 옵션 전체

mcp_servers:
  today:
    command: python                    # 실행할 명령어
    args:                              # 명령어 인수
      - ~/Developer/scripts/today_mcp_server.py
    env:                               # 환경 변수 (선택)
      API_KEY: "sk-xxx"
    timeout: 30                        # 연결 타임아웃 (초)
    disabled: false                    # 서버 비활성화

3-3. HTTP 방식 서버도 지원

stdio 대신 HTTP 로 통신하는 원격 MCP 서버도 연결할 수 있습니다.

mcp_servers:
  remote-server:
    url: http://your-server.com/mcp    # HTTP 엔드포인트

3-4. Hermes Agent 재시작

설정을 추가한 후 Hermes Agent 를 재시작하면:

  • 자동 감지: config.yaml 의 mcp_servers 를 읽어 MCP 서버 프로세스를 시작
  • 도구 등록: 서버가 제공하는 도구 목록(LLM 이 볼 수 있는 설명 포함)을 가져옴
  • 즉시 사용: 사용자가 대화를 시작하면 LLM 이 MCP 도구들을 사용할 수 있음
  • 에러 피드백: 서버가 시작 실패하면 Hermes Agent 가 콘솔에 에러를 출력
  • 핫 리로드: 서버 코드를 수정하면 다음 Agent 세션에서 자동으로 반영

4. LLM 이 MCP 를 호출하는 전 과정

이제 가장 중요한 부분입니다. 사용자가 "오늘 날짜 알려줘"라고 입력했을 때, 내부에서 어떤 일이 일어나는지 단계별로 추적해보겠습니다.

단계 1: 사용자 입력

메시지가 LLM 애플리케이션(Hermes Agent) 에 전달됩니다.

"오늘 날짜 알려줘"

단계 2: LLM 이 도구 사용 여부를 판단

LLM 은 다음 정보를 함께 받습니다:

  • 사용자의 질문
  • 등록된 MCP 도구 목록 (이름 + 설명 + 파라미터)
  • 시스템 프롬프트 (도구 사용 가이드라인)

단계 3: LLM 이 도구 호출을 결정

LLM 은 "오늘 날짜"라는 키워드를 보고 get_today 도구를 사용해야 한다고 판단합니다.

Tool: today.get_today
Description: 현재 날짜와 시간을 반환합니다.
Parameters:
  - timezone: 시간대 (기본값: Asia/Seoul)

단계 4: Hermes Agent 가 MCP 서버에 요청 전달

LLM 이 도구 호출을 결정하면, Hermes Agent 가 실제 MCP 서버에 요청을 보냅니다. JSON-RPC 2.0 프로토콜을 사용합니다.

Tool Call:
  tool_name: today.get_today
  arguments: {"timezone": "Asia/Seoul"}

이 요청은 MCP 서버 프로세스의 stdin 으로 전달됩니다.

실제 JSON-RPC 메시지는 다음과 같습니다:

// Hermes Agent → MCP Server
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "get_today",
    "arguments": {"timezone": "Asia/Seoul"}
  }
}

// MCP Server → Hermes Agent
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "content": [{
      "type": "text",
      "text": "{"datetime": "2025-06-13 14:30:00", "timezone": "Asia/Seoul", "weekday": "Friday", ...}"
    }],
    "isError": false
  }
}

단계 5: 결과 LLM 에 전달

MCP 서버의 응답을 LLM 에 다시 전달합니다.

Tool Result (today.get_today):
현재 시간: 2025-06-13 14:30:00
시간대: Asia/Seoul
요일: Friday

단계 6: LLM 이 최종 답변 생성

LLM 은 도구 결과를 바탕으로 자연스러운 답변을 생성합니다.

"지금 한국 시간으로 2025년 6월 13일 금요일 오후 2시 30분입니다."

전체 흐름 다이어그램

┌─────────────┐     ┌──────────────────┐     ┌──────────────────┐
│   사용자    │────▶│  Hermes Agent     │────▶│  LLM (qwen3.6)   │
│             │     │                  │     │                  │
│ "오늘 날짜   │     │  MCP 서버 관리    │     │  1. 질문 분석     │
│  알려줘"    │◀────│  프로세스 실행    │◀────│  2. 도구 호출     │
│             │     │                  │     │     결정          │
└─────────────┘     └────────┬─────────┘     └──────────────────┘
                             │
                    ┌────────▼─────────┐
                    │  MCP Server      │
                    │  (today)         │
                    │                  │
                    │  get_today()     │
                    │  실행            │
                    └────────┬─────────┘
                             │
                    ┌────────▼─────────┐
                    │  JSON-RPC 응답   │
                    │                  │
                    │  {"datetime":    │
                    │   "2025-06-13    │
                    │   14:30:00", ...}│
                    └────────┬─────────┘
                             │
                    ┌────────▼─────────┐
                    │  Hermes Agent    │
                    │                  │
                    │  결과를 LLM 에   │
                    │  다시 전달       │
                    └────────┬─────────┘
                             │
                    ┌────────▼─────────┐     ┌─────────────┐
                    │  LLM             │────▶│   사용자    │
                    │                  │     │             │
                    │  최종 답변 생성   │     │ "지금 한국  │
                    │                  │     │  시간으로    │
                    └──────────────────┘     │  2025년 6월 │
                                            │  13일 금요일 │
                                            │  오후 2시    │
                                            │  30분입니다" │
                                            └─────────────┘

5. MCP 서버 작성 시 주의사항

5-1. docstring 은 LLM 이 보는 설명서

MCP 서버에서 docstring은 단순 주석이 아닙니다. LLM 이 도구 사용 여부를 결정하는 유일한 정보원입니다.

@app.tool()
def get_today(timezone: str = "Asia/Seoul") -> str:
    """현재 날짜와 시간을 반환합니다.

    Args:
        timezone: 시간대 (기본값: Asia/Seoul)
                 예: America/New_York, Europe/London, UTC

    Returns:
        현재 날짜와 시간 문자열
    """

docstring 이 명확해야 LLM 이 언제 이 도구를 호출해야 하는지 정확히 알 수 있습니다. 모호한 설명은 LLM 이 도구를 사용하지 않거나 잘못된 파라미터로 호출하는 원인이 됩니다.

5-2. 입력 파라미터 정의하기

@app.tool()
def search_files(directory: str, pattern: str) -> list:
    """지정된 디렉토리에서 패턴에 맞는 파일 목록을 반환합니다.

    Args:
        directory: 검색할 디렉토리 경로
        pattern: 파일 이름 패턴 (예: "*.py", "test_*")

    Returns:
        일치하는 파일 경로 목록
    """
  • 타입 힌트 필수: str, int, bool 등 명확한 타입 지정
  • 기본값 설정: 가능한 한 파라미터에 기본값 제공
  • Args 설명: 각 파라미터의 용도와 예시를 docstring 에 포함

5-3. 에러 처리는 반드시

@app.tool()
def get_weather(city: str) -> str:
    """도시의 현재 날씨를 반환합니다."""
    try:
        response = requests.get(f"https://api.weather.com/{city}")
        response.raise_for_status()
        return json.dumps(response.json(), ensure_ascii=False)
    except Exception as e:
        return f"날씨 조회 실패: {e}"

에러가 발생해도 서버가 크래시되지 않도록 try/except로 감싸야 합니다. 에러 메시지도 LLM 에 전달되므로, 의미 있는 에러 메시지를 반환하세요.

5-4. 보안 — 환경 변수는 명시적으로 전달

mcp_servers:
  weather:
    command: python
    args: [~/scripts/weather_server.py]
    env:
      WEATHER_API_KEY: "***"  # API 키는 여기에서 전달

API 키나 비밀번호는 코드에 하드코딩하지 말고 env 옵션으로 전달하세요. config.yaml 은 버전 컨트롤에 포함되지 않도록 .gitignore 에 추가하세요.


6. 실전 — 다른 MCP 서버 예시

파일 시스템 서버

mcp_servers:
  filesystem:
    command: npx
    args:
      - "-y"
      - "@modelcontextprotocol/server-filesystem"
      - "/Users/mulpats/Developer"  # 접근 허용 디렉토리

지정된 디렉토리 내의 파일을 읽고, 검색하고, 목록을 확인할 수 있습니다. 접근 허용 디렉토리만 지정해야 보안상 안전합니다.

GitHub 서버

mcp_servers:
  github:
    command: npx
    args:
      - "-y"
      - "@modelcontextprotocol/server-github"
    env:
      GITHUB_PERSONAL_ACCESS_TOKEN: "ghp_xxx"

GitHub 리포지토리의 코드를 읽고, 이슈를 확인하고, PR 을 검토할 수 있습니다.

시간대 서버 (uvx)

mcp_servers:
  timezone:
    command: uvx
    args:
      - "mcp-server-timezone"

uvx 로 설치 없이 바로 실행할 수 있는 MCP 서버도 많습니다. uvx는 Python 패키지를 가상 환경에서 즉시 실행하는 도구입니다.


7. 문제 해결 가이드

MCP 도구가 인식되지 않음

# config.yaml 의 mcp_servers 섹션 확인
cat ~/.hermes/config.yaml | grep -A 5 mcp_servers

# Hermes Agent 재시작 후 로그 확인
hermes start  # 또는 재시작
  • config.yaml 의 YAML 들여쓰기가 정확한지 확인 (스페이스 2개 사용)
  • 서버 경로를 절대 경로로 변경해보기
  • Hermes Agent 를 재시작했는지 확인

"MCP SDK not available" 오류

# Python 환경 확인
which python
python --version

# MCP SDK 설치 확인
pip show mcp
  • pip install mcp 로 최신 버전 설치
  • 가상 환경이 활성화되어 있는지 확인
  • Python 버전이 3.9 이상인지 확인

서버 연결 실패

# 서버 직접 실행 (디버깅)
python ~/Developer/scripts/today_mcp_server.py

# HTTP 모드로 실행 테스트
python ~/Developer/scripts/today_mcp_server.py --http
# 브라우저에서 http://localhost:8090 접속
  • 서버를 직접 실행해서 에러 메시지 확인
  • 포트가 이미 사용 중인지 확인 (lsof -i :8090)
  • 방화벽 설정 확인 (macOS: 시스템 설정 → 네트워크 → 방화벽)

요약

  • MCP 는 LLM 과 외부 도구를 연결하는 표준 프로토콜
  • Python mcp 패키지로 몇 줄로도 MCP 서버 작성 가능
  • config.yaml 에 등록하면 Hermes Agent 가 자동으로 관리
  • LLM 은 도구 설명(docstring) 을 보고 언제 호출할지 결정
  • JSON-RPC 2.0 으로 호스트와 서버 간 통신
  • 에러 처리와 보안(API 키 관리) 을 반드시 고려

댓글 없음:

댓글 쓰기

SK하이닉스 ADR 상장 이후 한국 증시 급락의 원인 분석

분류: 증시분석 SK하이닉스 ADR 상장 이후 한국 증시 급락의 원인 분석 지난 7월 10일, SK하이닉스가 미국 나스닥에 미국주식예탁증서(ADR)를 상장했다. 공모가 149달러로 상장한 하이닉스 ADR은 첫 거래일인 13일 기준 약 168달...