
"지난 일주일간의 데이터를 분석해달라”는 요청을 할 경우, 일부 LLM은 실제 현재 시점을 기준으로 판단하기보다, 모델이 학습을 마친 과거의 특정 시점을 암묵적 ‘현재’로 간주하는 경향을 보이기도 한다. 이로 인해 시간 범위가 왜곡되거나, 사용자의 의도와 다른 기준 시점이 적용되는 문제가 발생할 수 있다.
또한 “지금부터 5분 후에 실행해달라”와 같은 지연 실행 요청에 대해서도, LLM은 물리적 시간의 흐름이나 시스템 수준의 경과 시간을 자율적으로 인지하지 못하기 때문에 해당 명령을 독립적으로 수행할 수 없다. 다시 말해, 일반적인 LLM은 시간의 경과를 추적하거나 미래 시점을 예약하는 기능을 내재적으로 갖고 있지 않다.
이러한 한계를 보완하기 위해, 실제 시스템 시각(예: 대한민국 서울 기준)과 단조 증가 시간(monotonic time)을 명시적으로 참조하고, 상대적·절대적 시간 계산을 수행할 수 있는 Time-aware MCP(Model Context Protocol)를 파이썬으로 구현해보고자 한다.

✅ 대한민국 서울(Asia/Seoul) 기준 현재 시각 인지
✅ 자연어 기반 시간 계산 (100일 전, 지난 일주일 등)
✅ 상대 시간(1시간 후) 예약 개념 인지
✅ Monotonic clock 기반 시간 지연 인식
✅ MCP의 Prompts / Resources / Tools 구조 명시
✅ LM Studio → Local LLM → MCP 연결
| Tool | 기능 |
|---|---|
| get_current_time | 서울 기준 현재 시각 반환 |
| calculate_datetime | 기준일 기준 날짜 계산 |
| get_relative_range | 지난 n일 범위 계산 |
| schedule_task | 상대 시간 이후 실행 시간 계산 |
import sys
import json
import asyncio
import datetime
import time
from datetime import timezone, timedelta
# ===============================
# CONFIG
# ===============================
KST = timezone(timedelta(hours=9))
SERVER_BOOT_TIME = datetime.datetime.now(KST)
MONOTONIC_START = time.monotonic()
# ===============================
# TOOLS
# ===============================
TOOLS = [
{
"name": "get_current_time",
"description": "Return current time in Asia/Seoul and elapsed monotonic seconds.",
"inputSchema": {
"type": "object",
"properties": {}
}
},
{
"name": "calculate_datetime",
"description": "Calculate datetime relative to base date.",
"inputSchema": {
"type": "object",
"properties": {
"base": {"type": "string", "description": "today or specific ISO datetime"},
"days_offset": {"type": "integer"},
"hours_offset": {"type": "integer"}
}
}
},
{
"name": "get_relative_range",
"description": "Return date range for last N days based on today.",
"inputSchema": {
"type": "object",
"properties": {
"days": {"type": "integer"}
},
"required": ["days"]
}
},
{
"name": "schedule_task",
"description": "Calculate future execution time after given seconds.",
"inputSchema": {
"type": "object",
"properties": {
"delay_seconds": {"type": "integer"}
},
"required": ["delay_seconds"]
}
}
]
# ===============================
# TOOL IMPLEMENTATION
# ===============================
def now_kst():
return datetime.datetime.now(KST)
def monotonic_elapsed():
return time.monotonic() - MONOTONIC_START
async def handle_request(request):
method = request.get("method")
id_ = request.get("id")
# 1️. Initialize
if method == "initialize":
return {
"jsonrpc": "2.0",
"id": id_,
"result": {
"protocolVersion": "2025-06-18",
"capabilities": {
"tools": {},
"resources": {},
"prompts": {}
},
"serverInfo": {
"name": "time-aware-mcp",
"version": "2.0.0"
}
}
}
# 2️. tools/list
elif method == "tools/list":
return {
"jsonrpc": "2.0",
"id": id_,
"result": {
"tools": TOOLS
}
}
# 3️. tools/call
elif method == "tools/call":
params = request.get("params", {})
name = params.get("name")
arguments = params.get("arguments", {})
# -----------------------
if name == "get_current_time":
return {
"jsonrpc": "2.0",
"id": id_,
"result": {
"content": [
{
"type": "text",
"text": json.dumps({
"current_time_kst": now_kst().isoformat(),
"elapsed_seconds": monotonic_elapsed()
}, indent=2)
}
]
}
}
# -----------------------
elif name == "calculate_datetime":
base = arguments.get("base", "today")
days = arguments.get("days_offset", 0)
hours = arguments.get("hours_offset", 0)
if base == "today":
base_dt = now_kst()
else:
base_dt = datetime.datetime.fromisoformat(base).astimezone(KST)
result_dt = base_dt + datetime.timedelta(days=days, hours=hours)
return {
"jsonrpc": "2.0",
"id": id_,
"result": {
"content": [
{
"type": "text",
"text": result_dt.isoformat()
}
]
}
}
# -----------------------
elif name == "get_relative_range":
days = arguments.get("days")
end = now_kst()
start = end - datetime.timedelta(days=days)
return {
"jsonrpc": "2.0",
"id": id_,
"result": {
"content": [
{
"type": "text",
"text": json.dumps({
"start": start.isoformat(),
"end": end.isoformat()
}, indent=2)
}
]
}
}
# -----------------------
elif name == "schedule_task":
delay = arguments.get("delay_seconds")
execution_time = now_kst() + datetime.timedelta(seconds=delay)
return {
"jsonrpc": "2.0",
"id": id_,
"result": {
"content": [
{
"type": "text",
"text": json.dumps({
"execute_at": execution_time.isoformat(),
"delay_seconds": delay
}, indent=2)
}
]
}
}
return None
async def main():
loop = asyncio.get_event_loop()
while True:
line = await loop.run_in_executor(None, sys.stdin.readline)
if not line:
break
try:
request = json.loads(line)
response = await handle_request(request)
if response:
print(json.dumps(response), flush=True)
except Exception as e:
print(f"ERROR: {str(e)}", file=sys.stderr)
if __name__ == "__main__":
asyncio.run(main())