"""
MCP 캘린더 서버 메인 모듈
"""
from mcp.server.fastmcp import FastMCP
from datetime import datetime
from typing import List, Optional
from src.models import (
CalendarEventRequest,
CalendarEventResponse,
EventCategory,
EventStatus
)
from src.services import CalendarService
from src.exceptions import CalendarException, EventNotFound, UnauthorizedAccess, InvalidEventData
# MCP 서버 인스턴스 생성
mcp = FastMCP("ittae-calendar-mcp")
# 서비스 인스턴스
calendar_service = CalendarService()
# 기본 사용자 ID (실제 구현에서는 인증을 통해 획득)
DEFAULT_USER_ID = 1
@mcp.tool()
def get_all_events() -> List[CalendarEventResponse]:
"""
모든 캘린더 이벤트를 조회합니다.
"""
try:
result = calendar_service.fetch_events(DEFAULT_USER_ID)
if result.success and result.data:
return [calendar_service.to_response(event) for event in result.data]
return []
except Exception as e:
raise Exception(f"이벤트 조회 중 오류가 발생했습니다: {str(e)}")
@mcp.tool()
def get_event_by_id(event_id: int) -> CalendarEventResponse:
"""
ID로 특정 캘린더 이벤트를 조회합니다.
"""
try:
result = calendar_service.get_event_by_id(event_id, DEFAULT_USER_ID)
if result.success and result.data:
return calendar_service.to_response(result.data)
raise EventNotFound(event_id)
except CalendarException:
raise
except Exception as e:
raise Exception(f"이벤트 조회 중 오류가 발생했습니다: {str(e)}")
@mcp.tool()
def create_calendar_event(
title: str,
start_time: str,
duration: int,
category: str,
description: Optional[str] = None,
location: Optional[str] = None,
stamina_cost: int = 0
) -> CalendarEventResponse:
"""
새로운 캘린더 이벤트를 생성합니다.
Args:
title: 이벤트 제목
start_time: 시작 시간 (ISO 형식: 2025-08-02T10:00:00)
duration: 지속 시간(분)
category: 카테고리 (STUDY, WORK, REST, ACTIVITY)
description: 이벤트 설명 (선택)
location: 장소 (선택)
stamina_cost: 스태미나 소모량 (기본값: 0)
"""
try:
# 카테고리 유효성 검사
if category not in [cat.value for cat in EventCategory]:
raise InvalidEventData("category", category)
# 시간 파싱
try:
parsed_start_time = datetime.fromisoformat(start_time.replace('Z', '+00:00'))
except ValueError:
raise InvalidEventData("start_time", start_time)
request = CalendarEventRequest(
title=title,
description=description,
location=location,
start_time=parsed_start_time,
duration=duration,
category=EventCategory(category),
stamina_cost=stamina_cost
)
result = calendar_service.create_event(request, DEFAULT_USER_ID)
if result.success and result.data:
return calendar_service.to_response(result.data)
raise Exception(result.error or "이벤트 생성에 실패했습니다")
except CalendarException:
raise
except Exception as e:
raise Exception(f"이벤트 생성 중 오류가 발생했습니다: {str(e)}")
@mcp.tool()
def update_calendar_event(
event_id: int,
title: str,
start_time: str,
duration: int,
category: str,
description: Optional[str] = None,
location: Optional[str] = None,
stamina_cost: int = 0,
status: Optional[str] = None
) -> CalendarEventResponse:
"""
기존 캘린더 이벤트를 수정합니다.
Args:
event_id: 수정할 이벤트 ID
title: 이벤트 제목
start_time: 시작 시간 (ISO 형식: 2025-08-02T10:00:00)
duration: 지속 시간(분)
category: 카테고리 (STUDY, WORK, REST, ACTIVITY)
description: 이벤트 설명 (선택)
location: 장소 (선택)
stamina_cost: 스태미나 소모량 (기본값: 0)
status: 이벤트 상태 (PLANNED, COMPLETED, CANCELED) (선택)
"""
try:
# 카테고리 유효성 검사
if category not in [cat.value for cat in EventCategory]:
raise InvalidEventData("category", category)
# 상태 유효성 검사
event_status = None
if status and status not in [stat.value for stat in EventStatus]:
raise InvalidEventData("status", status)
elif status:
event_status = EventStatus(status)
# 시간 파싱
try:
parsed_start_time = datetime.fromisoformat(start_time.replace('Z', '+00:00'))
except ValueError:
raise InvalidEventData("start_time", start_time)
request = CalendarEventRequest(
title=title,
description=description,
location=location,
start_time=parsed_start_time,
duration=duration,
category=EventCategory(category),
stamina_cost=stamina_cost,
status=event_status
)
result = calendar_service.update_event(event_id, request, DEFAULT_USER_ID)
if result.success and result.data:
return calendar_service.to_response(result.data)
raise Exception(result.error or "이벤트 수정에 실패했습니다")
except CalendarException:
raise
except Exception as e:
raise Exception(f"이벤트 수정 중 오류가 발생했습니다: {str(e)}")
@mcp.tool()
def delete_calendar_event(event_id: int) -> str:
"""
캘린더 이벤트를 삭제합니다.
Args:
event_id: 삭제할 이벤트 ID
"""
try:
result = calendar_service.delete_event(event_id, DEFAULT_USER_ID)
if result.success:
return f"이벤트 ID {event_id}가 성공적으로 삭제되었습니다"
raise Exception(result.error or "이벤트 삭제에 실패했습니다")
except CalendarException:
raise
except Exception as e:
raise Exception(f"이벤트 삭제 중 오류가 발생했습니다: {str(e)}")
@mcp.tool()
def complete_event(event_id: int, stamina_after: int) -> CalendarEventResponse:
"""
이벤트를 완료 상태로 변경하고 완료 후 스태미나를 설정합니다.
Args:
event_id: 완료할 이벤트 ID
stamina_after: 완료 후 스태미나 수치
"""
try:
result = calendar_service.complete_event(event_id, DEFAULT_USER_ID, stamina_after)
if result.success and result.data:
return calendar_service.to_response(result.data)
raise Exception(result.error or "이벤트 완료 처리에 실패했습니다")
except CalendarException:
raise
except Exception as e:
raise Exception(f"이벤트 완료 처리 중 오류가 발생했습니다: {str(e)}")
@mcp.tool()
def get_events_by_category(category: str) -> List[CalendarEventResponse]:
"""
카테고리별로 이벤트를 조회합니다.
Args:
category: 카테고리 (STUDY, WORK, REST, ACTIVITY)
"""
try:
if category not in [cat.value for cat in EventCategory]:
raise InvalidEventData("category", category)
result = calendar_service.fetch_events(DEFAULT_USER_ID)
if result.success and result.data:
filtered_events = [
event for event in result.data
if event.category == category
]
return [calendar_service.to_response(event) for event in filtered_events]
return []
except CalendarException:
raise
except Exception as e:
raise Exception(f"카테고리별 이벤트 조회 중 오류가 발생했습니다: {str(e)}")
@mcp.tool()
def get_events_by_date(date: str) -> List[CalendarEventResponse]:
"""
특정 날짜의 이벤트를 조회합니다.
Args:
date: 날짜 (YYYY-MM-DD 형식)
"""
try:
try:
target_date = datetime.fromisoformat(date).date()
except ValueError:
raise InvalidEventData("date", date)
result = calendar_service.fetch_events(DEFAULT_USER_ID)
if result.success and result.data:
filtered_events = [
event for event in result.data
if event.start_time.date() == target_date
]
return [calendar_service.to_response(event) for event in filtered_events]
return []
except CalendarException:
raise
except Exception as e:
raise Exception(f"날짜별 이벤트 조회 중 오류가 발생했습니다: {str(e)}")
if __name__ == "__main__":
mcp.run()