Skip to main content
Glama
timeaissr

datetime-mcp-server

by timeaissr

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 的冻结/解冻模型契合

enableJsonResponse: true

响应走普通 JSON 而非 SSE 流,避开 Lambda 响应缓冲限制

Lambda Web Adapter (LWA)

AWS 官方公共 Layer,把 Lambda invoke 翻译成对 localhost:8080 的真实 HTTP 请求

bootstrap 脚本

LWA 接管后的真正进程入口(node dist/index.js);配置里的 handler 字段只是占位符

MCP 工具

get_date

返回当前日历日期,ISO 8601 YYYY-MM-DD。

参数

类型

必填

说明

timezone

string

否

IANA 时区名或 UTC 偏移量,默认 "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 完成,核心技巧:

  1. 日期:en-CA locale 天然输出 YYYY-MM-DD,无需手工拼装

  2. 带偏移量的时间:formatToParts 取目标时区的墙上时间各字段 → 用 Date.UTC() 把它当作 UTC 反算 → 与原始 instant 相减得到分钟级 offset → 格式化为 ±HH:MM 或 Z

  3. hourCycle: "h23":避免 hour12: false 在某些 locale 下产生 "24" 小时的边界 bug

  4. DST 与特殊时区:offset 按请求时刻计算,夏令时切换自然正确(纽约 -04:00→-05:00),半小时/刻钟时区(印度 +05:30、尼泊尔 +05:45、查塔姆 +12:45)均被单测覆盖

  5. Formatter 缓存:按 timezone 缓存 Intl.DateTimeFormat 实例,热实例上重复调用零构造开销

  6. 时区校验: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

方式一: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。重复运行安全,自动走更新路径。

方式二:AWS 控制台

  1. 本地打包:

    npm run build && bsdtar --format zip -cf function.zip bootstrap dist/index.js
  2. Lambda 控制台 → Create function → Author from scratch

    • Runtime:Node.js 22.x;Architecture:arm64

    • Permissions:Create a new role with basic Lambda permissions

  3. Code 页签 → Upload from → .zip file → 选择 function.zip(内部结构必须是根目录 bootstrap + dist/index.js)

  4. Layers 区 → Add a layer → AWS layers → LambdaAdapterLayerArm64(选最新版本)

  5. Configuration → Environment variables:

    Key

    Value

    AWS_LAMBDA_EXEC_WRAPPER

    /opt/bootstrap

    PORT

    8080

  6. Configuration → General configuration → Memory 512 MB、Timeout 10 s

  7. Configuration → Function URL → Create → Auth type NONE

控制台创建 NONE 类型 Function URL 时会自动添加公开调用的资源策略,无需再执行 add-permission(这是与 CLI 的差异)。

浏览器直接访问 Function URL 返回 405 属正常(仅允许 POST)。以后更新代码:重新构建打包后在 Code 页签重新 Upload 即可。

MCP 客户端接入

以 opencode 为例,在配置中添加 remote MCP server:

{
  "mcp": {
    "datetime": {
      "type": "remote",
      "url": "https://<identifier>.lambda-url.<region>.on.aws/mcp"
    }
  }
}

设计决策记录

决策

结论

理由

语言

TypeScript

Intl 内建时区支持零依赖;Lambda 冷启动最快(~100-200ms vs Python ~300-800ms);bundle 小。Python 优势是装饰器写法更简洁

传输模式

Streamable HTTP + stateless + JSON response

Lambda 无持久进程,stdio 不可行;无会话状态天然匹配冻结/解冻模型;纯 JSON 避开 SSE 流式限制

SDK 版本

稳定版 @modelcontextprotocol/sdk@1.x(1.30.0)

正确实现协议版本协商,向后兼容旧客户端;2026-07-28 修订版的新特性(TTL 缓存等)对静态工具列表无意义;beta 拆分包 API 未稳定

时区实现

Intl 而非第三方库

零依赖、Lambda 上无需补装 tzdata、精度足够

时间源

系统时钟而非每次查 NTP

AWS 已做亚毫秒同步;NTP 外查增加延迟/故障面/成本且无准确度收益

部署

手动 CLI/控制台 + LWA Layer

不引入 SAM/CDK/Terraform 的前期投入;deploy.sh 仅是命令批处理,非 IaC

故障排查

现象

原因与解决

Not Acceptable: Client must accept both application/json and text/event-stream

请求缺 Accept 头。curl 测试需带 -H "Accept: application/json, text/event-stream";真实 MCP 客户端自动携带

Handler 'handler' missing on module 'index'

忘记挂 LWA Layer 或漏设 AWS_LAMBDA_EXEC_WRAPPER,运行时按默认路径找 handler 导致。补齐 Layer 和环境变量即可

GET /mcp 返回 405

符合预期。stateless 模式不支持服务端推送流,仅接受 POST

curl 本地测试返回 502 且响应极快

系统代理拦截了 localhost。加 --noproxy '*'

Function URL 冷启动首个请求慢

Lambda 冻结/解冻机制所致,后续请求毫秒级

打包时报 zip: command not found

本机未装 zip 包(Arch 上与 unzip 是两个包)。本项目已改用 bsdtar(libarchive 自带),保留权限位

已知限制与未来改进

  • 防御性 handler 未添加:若裸跑于默认 Node.js 运行时(无 LWA),报错信息晦涩。可导出一个提示性的占位 handler 改善误配置时的报错体验,对正常路径零影响

  • 输出仅到秒级精度,未暴露毫秒

  • 未做鉴权与限流——公开 endpoint 可能被滥用刷调用量(虽然单次成本趋近于零)

  • Intl.supportedValuesOf("timeZone") 的别名表未用于参数提示,错误信息只给了三个示例

  • SDK 未来从稳定版 1.x 迁移到拆分包(支持 2026-07-28 修订版)时需跟进 API 变更

License

ISC

Related MCP Connectors

Related MCP Servers

  • F
    license
    A
    quality
    Not graded
    maintenance
    Provides 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
    -
  • A
    license
    A
    quality
    D
    maintenance
    A simple MCP server that returns the current date and time with timezone information in ISO 8601, Unix timestamp, and human-readable formats.
    1
    MIT
  • A
    license
    Not graded
    quality
    D
    maintenance
    Provides time-related tools including current time retrieval, timezone conversion, time formatting, and timezone info via MCP.
    7 npm
    MIT