Skip to main content
Glama
main.pyโ€ข6.3 kB
from mcp.server.fastmcp import FastMCP import os from dotenv import load_dotenv from typing import Dict, Any, List # Import local modules from cgm.dexcom import DexcomClient from cgm.nightscout import NightscoutClient from nutrition.database import FoodDatabase from community.search import HybridSearchClient from treatment.calculator import calculate_bolus # Load environment variables load_dotenv() # Initialize MCP Server mcp = FastMCP("T1D Manager") # Initialize Services food_db = FoodDatabase() search_client = HybridSearchClient() @mcp.tool() def get_recent_cgm(dexcom_username: str, dexcom_password: str, region: str = "OUS") -> str: """ Get real-time CGM readings directly from Dexcom Share. This requires the user's Dexcom account credentials. Args: dexcom_username: Dexcom account ID (email or username) dexcom_password: Dexcom account password region: Account region ('OUS' for Korea/International, 'US' for USA). Default is 'OUS'. """ if not dexcom_username or not dexcom_password: return "Error: Dexcom ID and Password are required." try: # Initialize Dexcom Client (Stateless) client = DexcomClient(dexcom_username, dexcom_password, region) # Get data # Fetching a bit of history to calculate delta readings = client.get_readings(minutes=30, max_count=2) if not readings: return "No recent data found from Dexcom." latest = readings[0] # Calculate delta if possible delta_str = "" if len(readings) > 1: diff = latest['sgv'] - readings[1]['sgv'] sign = "+" if diff > 0 else "" delta_str = f"[Delta: {sign}{diff}]" result = f"### ๐Ÿฉธ ์‹ค์‹œ๊ฐ„ ๋ฑ์Šค์ฝค ํ˜ˆ๋‹น\n" result += f"- **{latest['sgv']}** mg/dL ({latest['direction']}) {delta_str}\n" result += f"- ์ธก์ • ์‹œ๊ฐ„: {latest['time']}\n" return result except Exception as e: return f"Dexcom Error: {str(e)}" @mcp.tool() def calculate_insulin_dosage(current_bg: int, target_bg: int, isf: int, carbs: int, icr: int) -> str: """ Calculate suggested insulin bolus (Correction + Meal). ALWAYS returns educational explanation detailing the calculation. """ result = calculate_bolus(current_bg, target_bg, isf, carbs, icr) output = f""" ## ๐Ÿ’‰ ์ธ์А๋ฆฐ ๊ณ„์‚ฐ ๊ฒฐ๊ณผ **์ด ๊ถŒ์žฅ ์šฉ๋Ÿ‰: {result['units']:.1f} ๋‹จ์œ„** {result['explanation']} {result['educational_content']} {result['markdown_table']} """ return output @mcp.tool() def search_nutrition_info(food_name: str) -> str: """ Search for carbohydrate content of a food item. """ info = food_db.search(food_name) if info: return f"### ๐ŸŽ {info['name']}\n- **ํƒ„์ˆ˜ํ™”๋ฌผ**: {info['carbs']}g ({info['unit']})\n- **์ฐธ๊ณ **: {info['desc']}" else: return f"'{food_name}'์— ๋Œ€ํ•œ ์˜์–‘ ์ •๋ณด๋ฅผ ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค." @mcp.tool() def search_diabetes_community(query: str) -> str: """ Search Naver Blogs and Kakao Web for patient experiences and tips. Use this for finding non-medical life tips (e.g. snacks, patches, travel). """ results = search_client.search_hybrid(query) if not results: return "๊ฒ€์ƒ‰ ๊ฒฐ๊ณผ๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค." output = f"### ๐Ÿ” '{query}' ์ปค๋ฎค๋‹ˆํ‹ฐ ๊ฒ€์ƒ‰ ๊ฒฐ๊ณผ\n" for item in results: icon = "๐ŸŸข" if item['source'] == "Naver Blog" else "๐ŸŸก" output += f"- {icon} [{item['title']}]({item['link']})\n" return output @mcp.tool() def activate_sick_day_mode(symptoms: str = "๊ฐ๊ธฐ ๊ธฐ์šด") -> str: """ Activate 'Sick Day Rules' when the user feels unwell. Returns specific guidelines for managing T1D during illness. Args: symptoms: User's reported symptoms (e.g., "cold", "fever"). """ return f""" ### ๐Ÿšจ ์•„ํ”ˆ ๋‚ (Sick Day) ๋ชจ๋“œ ์‹œ์ž‘ ์–ด๋จธ๋‹ˆ, ๋งŽ์ด ํŽธ์ฐฎ์œผ์‹ ๊ฐ€์š”? ('{symptoms}') ๋ชธ์ด ์•„ํ”„๋ฉด ์ŠคํŠธ๋ ˆ์Šค ํ˜ธ๋ฅด๋ชฌ ๋•Œ๋ฌธ์— **ํ˜ˆ๋‹น์ด ํ‰์†Œ๋ณด๋‹ค ์˜ค๋ฅผ ์ˆ˜ ์žˆ์–ด์š”.** **โœ… ์ง€๊ธˆ ์ง€์ผœ์ฃผ์„ธ์š”:** 1. **ํ˜ˆ๋‹น ์ฒดํฌ**: ํ‰์†Œ๋ณด๋‹ค ์ž์ฃผ (2~4์‹œ๊ฐ„ ๊ฐ„๊ฒฉ) ํ™•์ธํ•ด์ฃผ์„ธ์š”. 2. **์ธ์А๋ฆฐ**: ์‹์‚ฌ๋ฅผ ๋ชป ํ•˜์…”๋„ **๊ธฐ์ € ์ธ์А๋ฆฐ์€ ์ ˆ๋Œ€ ์ค‘๋‹จํ•˜๋ฉด ์•ˆ ๋ฉ๋‹ˆ๋‹ค.** 3. **์ˆ˜๋ถ„ ์„ญ์ทจ**: ํƒˆ์ˆ˜๋ฅผ ๋ง‰๊ธฐ ์œ„ํ•ด ๋ฌผ์„ 1์‹œ๊ฐ„์— ํ•œ ์ปต์”ฉ ๊ผญ ๋“œ์„ธ์š”. ๐Ÿ’ง 4. **์‘๊ธ‰ ์ƒํ™ฉ**: ๊ตฌํ† ๊ฐ€ ๋ฉˆ์ถ”์ง€ ์•Š๊ฑฐ๋‚˜ ์ˆจ์‰ฌ๊ธฐ ํž˜๋“ค๋ฉด ๋ฐ”๋กœ ๋ณ‘์›์— ๊ฐ€์…”์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์ œ๊ฐ€ ๋” ์ž์ฃผ ์ƒํƒœ๋ฅผ ์—ฌ์ญค๋ณผ๊ฒŒ์š”. ๋ฌด๋ฆฌํ•˜์ง€ ๋งˆ์‹œ๊ณ  ํ‘น ์‰ฌ์„ธ์š”. ํž˜๋‚ด์„ธ์š”! ๐Ÿ’– """ @mcp.tool() def get_glucose_status_with_empathy(dexcom_username: str, dexcom_password: str, region: str = "OUS") -> str: """ Check current glucose with a warm, empathetic persona. Analyzes trends and gives context (e.g., "It seems to be stable"). """ cgm_result = get_recent_cgm(dexcom_username, dexcom_password, region) # Simple logic to add empathy based on the result string using keyword matching # In a real scenario, LLM does this, but we can hint strongly in the return value msg = cgm_result + "\n\n" msg += "--- \n**๐Ÿค– AI ์ฝ”๋ฉ˜ํŠธ**:\n" if "Error" in cgm_result: msg += "์–ด๋จธ๋‹ˆ, ์—ฐ๊ฒฐ์— ์ž ์‹œ ๋ฌธ์ œ๊ฐ€ ์ƒ๊ธด ๊ฒƒ ๊ฐ™์•„์š”. ์ธํ„ฐ๋„ท ์—ฐ๊ฒฐ์„ ํ•œ๋ฒˆ ํ™•์ธํ•ด์ฃผ์‹œ๊ฒ ์–ด์š”?" elif "No recent data" in cgm_result: msg += "๋ฐ์ดํ„ฐ๊ฐ€ ์•„์ง ์•ˆ ๋„˜์–ด์™”๋„ค์š”. ์„ผ์„œ๊ฐ€ ์กฐ๊ธˆ ๋ฉ€๋ฆฌ ์žˆ๋‚˜์š”?" else: # Extract number roughly for logic (This is a naive parsing for demo) # Real logic should happen in get_recent_cgm or here by calling client directly # But to avoid re-calling, we rely on the string output or LLM's interpretation. # Let's trust LLM to convert this data into empathy, # BUT we provide the 'Persona Instruction' as a distinct return block. msg += "์–ด๋จธ๋‹ˆ, ์‹์‚ฌํ•˜์‹  ๊ฒŒ ์†Œํ™”๋˜๊ณ  ์žˆ๋‚˜์š”? " msg += "์ˆ˜์น˜๊ฐ€ ์•ˆ์ •์ ์ด๋ผ๋ฉด ๋ฌด๋ฆฌํ•˜์ง€ ๋งˆ์‹œ๊ณ  ํŽธ์•ˆํ•˜๊ฒŒ ๊ณ„์„ธ์š”. " msg += "ํ˜น์‹œ ์กฐ๊ธˆ ๋†’๋”๋ผ๋„ ๊ต์ • ์ธ์А๋ฆฐ์ด ๋„์™€์ค„ ๊ฑฐ๋‹ˆ๊นŒ ๋„ˆ๋ฌด ๊ฑฑ์ • ๋งˆ์‹œ๊ณ ์š”. ๐Ÿต" return msg # ... existing tools ...

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/JunHyungKang/t1d-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server