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Ноль сторонних библиотек времени: полностью на встроенном
Intl(ICU) Node.js, без зависимостей moment/luxon/dayjsServerless-развёртывание: один bundle-файл (~2 МБ) + 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 | Единственная рекомендуемая в настоящее время HTTP-транспортировка MCP; HTTP+SSE устарел |
Stateless-режим | Каждый запрос самодостаточен, сессии не поддерживаются, что соответствует модели заморозки/разморозки Lambda |
| Ответ идёт обычным JSON, а не потоком SSE, что обходит ограничения буферизации ответов Lambda |
Lambda Web Adapter (LWA) | Официальный публичный Layer AWS, преобразующий вызов Lambda в реальный HTTP-запрос к localhost:8080 |
Скрипт | Настоящая точка входа процесса после接管 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
Источник времени и точность
Время считывается из системных часов среды выполнения (new Date()). Локально — это часы машины (зависит от калибровки NTP); на Lambda часы хоста автоматически синхронизируются Amazon Time Sync Service (источник GPS/атомные часы, PTP/NTP), погрешность в пределах миллисекунд, никакой настройки не требуется.
Намеренно не выполняется внешний NTP-запрос при каждом вызове: часы AWS уже постоянно синхронизированы, внешний запрос лишь добавит задержку (+10~100 мс), внесёт сетевую точку отказа и проблемы с лимитами, а точность выше не станет.
Разрешение вывода — до секунды (миллисекунды отбрасываются). Преобразование часовых поясов — чистая арифметика: все часовые пояса получают один и тот же абсолютный момент, различается лишь представление в местном времени.
Локальная разработка
Требуется 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 должен объявлять оба типа media — это проверка протокола 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-CAестественным образом выводитYYYY-MM-DD, ручная сборка не нужнаВремя со смещением:
formatToPartsберёт поля местного времени целевого пояса → с помощьюDate.UTC()обратно вычисляет его как UTC → вычитает из исходного момента, получая смещение в минутах → форматирует как±HH:MMилиZhourCycle: "h23": позволяет избежать граничного бага с часом"24", который возникает сhour12: falseв некоторых локаляхDST и особые часовые пояса: смещение вычисляется на момент запроса, переход на летнее время обрабатывается корректно (Нью-Йорк
-04:00→-05:00), пояса с получасовым/четвертьчасовым сдвигом (Индия+05:30, Непал+05:45, Чатем+12:45) покрыты юнит-тестамиКэш форматтеров: экземпляры
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, 512 МБ, таймаут 10 с) → создание Function URL без аутентификации и разрешение публичного вызова → вывод endpoint. Повторный запуск безопасен, автоматически идёт по пути обновления.
Способ 2: Консоль AWS
Локальная упаковка:
npm run build && bsdtar --format zip -cf function.zip bootstrap dist/index.jsКонсоль Lambda → 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
При создании Function URL типа NONE через консоль ресурсная политика публичного вызова добавляется автоматически, выполнять
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 и др.) бессмысленны для статического списка инструментов; API бета-раздельных пакетов нестабилен |
Реализация часовых поясов |
| Ноль зависимостей, не нужно доустанавливать tzdata на Lambda, точности достаточно |
Источник времени | Системные часы, а не NTP при каждом запросе | AWS уже выполняет субмиллисекундную синхронизацию; внешний NTP добавляет задержку/точки отказа/затраты без выигрыша в точности |
Развёртывание | Ручной CLI/консоль + LWA Layer | Без первоначальных вложений в SAM/CDK/Terraform; deploy.sh — просто пакетная обработка команд, не IaC |
Устранение неполадок
Симптом | Причина и решение |
| В запросе отсутствует заголовок Accept. Для теста curl нужен |
| Забыли подключить LWA Layer или не задали |
GET /mcp возвращает 405 | Это ожидаемо. Stateless-режим не поддерживает серверные push-потоки, принимается только POST |
Локальный тест curl возвращает 502, ответ мгновенный | Системный прокси перехватил localhost. Добавьте |
Первый запрос к Function URL на холодном старте медленный | Это следствие механизма заморозки/разморозки Lambda, последующие запросы — миллисекунды |
При упаковке ошибка | На машине не установлен пакет zip (в Arch это отдельный пакет от unzip). В проекте уже используется |
Известные ограничения и будущие улучшения
Защитный handler не добавлен: при запуске в среде выполнения Node.js по умолчанию (без LWA) сообщение об ошибке невнятное. Можно экспортировать подсказывающий handler-заглушку для улучшения опыта при ошибочной конфигурации; на нормальный путь это не влияет
Вывод только с точностью до секунды, миллисекунды не раскрываются
Нет аутентификации и ограничения частоты — публичный endpoint могут злоупотреблять, накручивая вызовы (хотя стоимость одного вызова стремится к нулю)
Таблица алиасов
Intl.supportedValuesOf("timeZone")не используется для подсказок параметров, в сообщении об ошибке приведены лишь три примераПри будущем переходе SDK со стабильной версии 1.x на раздельные пакеты (поддержка редакции от 2026-07-28) потребуется отслеживать изменения API
Лицензия
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