datetime-mcp-server
datetime MCP Server
Model Context Protocol 서버로, 현재 날짜/시간 조회를 제공하며 TypeScript로 작성되었고 AWS Lambda에 배포되어 Streamable HTTP를 통해 서비스를 제공하도록 설계되었습니다.
기능 특징
두 개의 MCP 도구:
get_date,get_datetime, ISO 8601 형식 출력선택적 시간대 파라미터: IANA 시간대 이름(
Asia/Shanghai) 및 UTC 오프셋(+08:00) 지원, 기본값 UTC서드파티 시간 라이브러리 제로: 전적으로 Node.js 내장
Intl(ICU) 기반, moment/luxon/dayjs 의존성 없음Serverless 배포: 단일 파일 bundle(약 2MB) + Lambda Web Adapter, 콜드 스타트 이후 핫 인스턴스는 밀리초 단위 응답
최신 MCP 표준 준수: Streamable HTTP 전송, stateless 실행 모드, 프로토콜 버전 협상은 공식 SDK가 처리
Related MCP server: date-today-mcp
아키텍처
MCP Client ──HTTP POST /mcp──▶ Function URL ──▶ Lambda 函数
│
├─ LWA Layer (AWS_LAMBDA_EXEC_WRAPPER=/opt/bootstrap)
│ └─ 执行 zip 根目录 bootstrap → node dist/index.js
│
└─ Express app (端口 8080)
└─ POST /mcp → StreamableHTTPServerTransport
(stateless:每请求新建 transport,
enableJsonResponse 返回普通 JSON)핵심 메커니즘:
구성 요소 | 설명 |
Streamable HTTP | MCP가 현재 유일하게 권장하는 HTTP 전송; HTTP+SSE는 폐기됨 |
Stateless 모드 | 각 요청이 자체적으로 완결되고 세션을 유지하지 않으며, Lambda의 동결/해동 모델과 부합 |
| 응답이 SSE 스트림이 아닌 일반 JSON으로 나가며, Lambda 응답 버퍼링 제한을 회피 |
Lambda Web Adapter (LWA) | AWS 공식 공용 Layer로, Lambda invoke를 localhost:8080에 대한 실제 HTTP 요청으로 변환 |
| LWA가接管한 후의 실제 프로세스 진입점( |
MCP 도구
get_date
현재 달력 날짜를 ISO 8601 YYYY-MM-DD 형식으로 반환합니다.
파라미터 | 타입 | 필수 | 설명 |
| string | 아니요 | IANA 시간대 이름 또는 UTC 오프셋, 기본값 |
{"result":{"content":[{"type":"text","text":"2026-08-24"}]}}get_datetime
현재 날짜시간을 ISO 8601 오프셋 접미사 포함 형식으로 반환합니다(UTC 출력은 Z 사용).
{"result":{"content":[{"type":"text","text":"2026-08-23T14:30:00+08:00"}]}}오류 동작
시간대 누락 또는 빈 문자열 → UTC로 간주
비어 있지 않지만 잘못된 시간대 →
isError: true결과 반환, 안내 메시지 포함:
Error: Invalid IANA timezone "Mars/Olympus". Use a name like "UTC", "Asia/Shanghai" or "America/New_York".프로토콜 계층 오류(예: Accept 헤더 누락)는 SDK가 표준 JSON-RPC error로 반환
시간 출처 및 정밀도
시간은 실행 환경의 시스템 클럭(new Date())에서 읽습니다. 로컬은 로컬 머신 클럭(NTP 보정에 의존); Lambda에서는 호스트 클럭이 Amazon Time Sync Service(GPS/원자 시계 소스, PTP/NTP)에 의해 자동 동기화되어 오차가 밀리초 이내이며, 별도 구성이 필요 없습니다.
일부러 매 요청마다 외부 NTP를 조회하지 않습니다: AWS 클럭이 이미 지속적으로 동기화되어 있고, 외부 조회는 지연(+10~100ms)만 늘리고 네트워크 장애 지점과 속도 제한 문제를 도입할 뿐 정확도는 더 높아지지 않습니다.
출력 해상도는 초 단위까지 정밀합니다(밀리초는 절단됨). 시간대 변환은 순수 산술 연산입니다 — 모든 시간대가 동일한 절대 순간을 얻으며, 단지 벽시계 시간 표기만 다를 뿐입니다.
로컬 개발
요구 사항: Node.js ≥ 22.
npm install # 安装依赖
npm run dev # 构建 + 启动(默认 http://localhost:3000/mcp)
npm test # vitest 单测(14 个用例)
npm run typecheck # tsc --noEmit
npm run lint # eslint
npm run build # esbuild 打包为 CJS 单文件 dist/index.js로컬 수동 테스트(Accept 헤더는 두 미디어 타입을 모두 선언해야 합니다. 이는 SDK의 프로토콜 검증입니다):
curl -s http://localhost:3000/mcp \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"get_datetime","arguments":{"timezone":"Asia/Shanghai"}}}'공식 디버거도 사용 가능: npx @modelcontextprotocol/inspector, 연결 유형은 Streamable HTTP 선택.
참고: 로컬에
http_proxy환경 변수가 설정된 경우 curl에--noproxy '*'를 추가해야 합니다. 그렇지 않으면 localhost 요청이 프록시에 가로채져 502가 반환됩니다.
프로젝트 구조
├── src/
│ ├── time.ts # 核心纯函数:ISO 格式化、时区校验(可独立单测)
│ ├── server.ts # McpServer 实例 + 工具注册
│ └── index.ts # Express app + stateless transport 挂载 + 进程入口
├── test/
│ └── time.test.ts # 固定时间戳验证多时区/DST/半小时偏移/错误输入
├── build.mjs # esbuild 构建脚本(bundle: platform=node22, format=cjs)
├── bootstrap # LWA 进入口(zip 根目录,需可执行位 755)
├── deploy.sh # CLI 一键部署脚本
├── function.zip # 部署包(构建产物,不入库)
└── dist/index.js # 打包产物(约 2MB,零运行时依赖安装)기술 구현 핵심 포인트
시간대 포맷팅은 전부 Intl.DateTimeFormat으로 처리하며, 핵심 기법:
날짜:
en-CAlocale이 자연스럽게YYYY-MM-DD를 출력하므로 수동 조립 불필요오프셋 포함 시간:
formatToParts로 대상 시간대의 벽시계 시간 각 필드를 추출 →Date.UTC()로 이를 UTC인 것처럼 역산 → 원본 instant와의 차이로 분 단위 offset 계산 →±HH:MM또는Z로 포맷hourCycle: "h23": 일부 locale에서hour12: false가"24"시 경계 버그를 일으키는 것을 방지DST 및 특수 시간대: offset은 요청 시점 기준으로 계산되므로 일광 절약 시간 전환도 자연스럽게 정확(뉴욕
-04:00→-05:00), 30분/15분 단위 시간대(인도+05:30, 네팔+05:45, 채텀+12:45) 모두 단위 테스트로 커버Formatter 캐시: timezone별로
Intl.DateTimeFormat인스턴스를 캐시하여 핫 인스턴스에서 반복 호출 시 생성 오버헤드 제로시간대 검증: try/catch로
Intl.DateTimeFormat을 생성하고, 잘못된 값이RangeError를 던지면 유효하지 않은 것으로 간주. 새 ECMA-402는 ICU가 UTC 오프셋 문자열(예:+08:00)도 동시에 받아들이게 하며, 전체 체인 출력이 올바른지 검증하여 테스트에 포함
AWS Lambda에 배포
전제 조건: AWS CLI에 자격 증명이 구성되어 있어야 함; AWSLambdaBasicExecutionRole 권한이 포함된 실행 역할(없으면 아래 명령으로 생성 가능):
aws iam create-role --role-name datetime-mcp-role \
--assume-role-policy-document '{"Version":"2012-10-17","Statement":[{"Effect":"Allow","Principal":{"Service":"lambda.amazonaws.com"},"Action":"sts:AssumeRole"}]}'
aws iam attach-role-policy --role-name datetime-mcp-role \
--policy-arn arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole방법 1: CLI 스크립트
ROLE_ARN=arn:aws:iam::<账号ID>:role/datetime-mcp-role ./deploy.sh선택적 환경 변수: AWS_REGION(기본값 us-east-1), FUNCTION_NAME(기본값 datetime-mcp), ARCH(arm64/x86_64, 기본값 arm64).
스크립트 흐름: 리전의 최신 LWA Layer 버전 동적 조회 → esbuild 빌드 → bsdtar 패키징(시스템에 zip 명령 없음) → create/update 함수(nodejs22.x, 512MB, 타임아웃 10s) → 인증 없는 Function URL 생성 및 공개 호출 권한 부여 → endpoint 출력. 반복 실행 안전, 자동으로 업데이트 경로 진행.
방법 2: AWS 콘솔
로컬 패키징:
npm run build && bsdtar --format zip -cf function.zip bootstrap dist/index.jsLambda 콘솔 → Create function → Author from scratch
Runtime: Node.js 22.x; Architecture: arm64
Permissions: Create a new role with basic Lambda permissions
Code 탭 → Upload from → .zip file →
function.zip선택(내부 구조는 루트 디렉터리에bootstrap+dist/index.js가 있어야 함)Layers 영역 → Add a layer → AWS layers → LambdaAdapterLayerArm64(최신 버전 선택)
Configuration → Environment variables:
Key
Value
AWS_LAMBDA_EXEC_WRAPPER/opt/bootstrapPORT8080Configuration → General configuration → Memory 512 MB, Timeout 10 s
Configuration → Function URL → Create → Auth type NONE
콘솔에서 NONE 유형 Function URL을 생성하면 공개 호출용 리소스 정책이 자동으로 추가되므로
add-permission을 별도로 실행할 필요가 없습니다(CLI와의 차이점).
브라우저로 Function URL에 직접 접근하면 405가 반환되는 것이 정상입니다(POST만 허용). 이후 코드 업데이트: 다시 빌드/패키징한 후 Code 탭에서 재업로드하면 됩니다.
MCP 클라이언트 연동
opencode를 예로 들면, 구성에 remote MCP server를 추가합니다:
{
"mcp": {
"datetime": {
"type": "remote",
"url": "https://<identifier>.lambda-url.<region>.on.aws/mcp"
}
}
}설계 결정 기록
결정 | 결론 | 이유 |
언어 | TypeScript |
|
전송 모드 | Streamable HTTP + stateless + JSON response | Lambda에는 지속 프로세스가 없어 stdio 불가; 무세션 상태가 동결/해동 모델에 자연스럽게 부합; 순수 JSON이 SSE 스트리밍 제한 회피 |
SDK 버전 | 안정판 | 프로토콜 버전 협상을 올바르게 구현하여 구형 클라이언트와 하위 호환; 2026-07-28 개정판의 새 기능(TTL 캐시 등)은 정적 도구 목록에 무의미; beta 분할 패키지 API는 아직 불안정 |
시간대 구현 |
| 의존성 제로, Lambda에서 tzdata 추가 설치 불필요, 정밀도 충분 |
시간 출처 | 시스템 클럭이지 매번 NTP 조회가 아님 | AWS가 이미 서브밀리초 동기화 수행; NTP 외부 조회는 지연/장애 지점/비용만 증가시키고 정확도 이점 없음 |
배포 | 수동 CLI/콘솔 + LWA Layer | SAM/CDK/Terraform의 초기 투자 도입 없음; deploy.sh는 단지 명령 배치 처리일 뿐 IaC가 아님 |
문제 해결
현상 | 원인과 해결 |
| 요청에 Accept 헤더 누락. curl 테스트는 |
| LWA Layer 부착을 잊었거나 |
GET /mcp가 405 반환 | 예상된 동작. stateless 모드는 서버 푸시 스트림을 지원하지 않으며 POST만 수락 |
curl 로컬 테스트가 502 반환 및 응답이 매우 빠름 | 시스템 프록시가 localhost를 가로챔. |
Function URL 콜드 스타트 첫 요청이 느림 | Lambda 동결/해동 메커니즘 때문이며, 이후 요청은 밀리초 단위 |
패키징 시 | 로컬에 zip 패키지 미설치(Arch에서는 unzip과 별개 패키지). 본 프로젝트는 |
알려진 제한 사항 및 향후 개선
방어적 handler 미추가: 기본 Node.js 런타임에서(LWA 없이) 직접 실행하면 오류 메시지가 난해함. 안내용 자리표시자 handler를 내보내면 잘못된 구성 시 오류 경험을 개선할 수 있으며, 정상 경로에는 영향 제로
출력이 초 단위 정밀도까지만 제공, 밀리초 미노출
인증 및 속도 제한 미구현 — 공개 endpoint가 남용되어 호출량이 늘어날 수 있음(단회 비용은 0에 수렴하지만)
Intl.supportedValuesOf("timeZone")의 별칭 테이블이 파라미터 힌트에 사용되지 않아 오류 메시지에 예시 세 개만 제공SDK가 향후 안정판 1.x에서 분할 패키지(2026-07-28 개정판 지원)로 마이그레이션될 때 API 변경 추적 필요
License
This server cannot be deployed
Maintenance
Related MCP Connectors
Timezone MCP — wraps timeapi.io (free, no auth)
A real clock for AI agents: current time, timezone conversion, and DST facts from the IANA tzdb.
Current time in any IANA time zone, plus the full time-zone list. Via timeapi.io.
Related MCP Servers
- FlicenseAqualityNot gradedmaintenanceProvides timezone-aware date and time information with configurable time formats and timezone support. Enables users to get current date and time in their preferred timezone and format through simple MCP tools.2-
- AlicenseAqualityDmaintenanceProvides the current date in multiple formats (e.g., European, ISO, US) via a simple MCP tool.1ISC
- AlicenseAqualityDmaintenanceA simple MCP server that returns the current date and time with timezone information in ISO 8601, Unix timestamp, and human-readable formats.1MIT
- AlicenseNot gradedqualityDmaintenanceProvides time-related tools including current time retrieval, timezone conversion, time formatting, and timezone info via MCP.7 npmMIT