news 2026/9/5 22:13:10

MCP Time Server 实战指南:时间查询与时区转换工具的实现、配置与调试

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
MCP Time Server 实战指南:时间查询与时区转换工具的实现、配置与调试

MCP Time Server 实战指南:时间查询与时区转换工具的实现、配置与调试

【免费下载链接】serversModel Context Protocol Servers项目地址: https://gitcode.com/GitHub_Trending/se/servers

在基于 Model Context Protocol(MCP)构建的服务器集合仓库中,Time MCP Server 是一个典型的"小而完整"的参考实现:它用不到 220 行核心代码,向大语言模型(LLM)暴露了获取当前时间与跨时区时间转换两个能力,并自动探测系统时区。读完本文,你将掌握mcp-server-time的两个工具的参数语义与返回结构、--local-timezone时区覆盖机制的底层实现,以及在 Claude.app、Zed、VS Code 等客户端中完整的配置方式,还能了解如何用 MCP Inspector 调试该服务器,以及其测试用例如何覆盖 DST、半小时时区偏移等边缘场景。

项目定位与总体架构

Time MCP Server 的包名为mcp-server-time(当前仓库中版本为 0.6.2,要求 Python >= 3.10,采用 MIT 许可证),源码位于 src/time 目录。从 pyproject.toml 可以看到它的全部运行时依赖:

dependencies = [ "mcp>=1.23.0", "pydantic>=2.0.0", "tzdata>=2024.2", "tzlocal>=5.3.1", ]

这四个依赖各司其职:mcp提供 MCP 协议的Server抽象与 stdio 传输层;pydantic用于定义结构化的返回模型;tzdata提供 IANA 时区数据库(保证在无系统时区数据的容器/平台上也能解析时区);tzlocal用于探测宿主机的本地时区。

从源码结构看,整个服务器由三个文件组成:

  • server.py:核心实现,包含时区解析、两个工具的业务逻辑与 MCP 路由分发;
  • init.py:定义命令行入口main(),解析--local-timezone参数后启动异步服务;
  • main.py:两行的模块启动器,使python -m mcp_server_time可以运行。

传输层采用 stdio(标准输入输出):serve()函数在 server.py 末尾通过stdio_server()打开读写流并调用server.run(),客户端(Claude、Zed、VS Code 等)以子进程方式拉起服务器,经 stdin/stdout 交换 JSON-RPC 消息。

核心工具一:get_current_time

get_current_time用于获取指定时区的当前时间。必填参数只有一个:

  • timezone(string):IANA 时区名,例如America/New_YorkEurope/London

返回结果由 Pydantic 模型TimeResult(见 server.py)约束,包含四个字段:timezone(时区名)、datetime(ISO 8601 格式、精确到秒、带偏移量)、day_of_week(星期几英文名)、is_dst(是否处于夏令时)。

调用示例(来自 src/time/README.md):

{ "name": "get_current_time", "arguments": { "timezone": "Europe/Warsaw" } }

响应:

{ "timezone": "Europe/Warsaw", "datetime": "2024-01-01T13:00:00+01:00", "is_dst": false }

源码实现细节

业务逻辑在TimeServer.get_current_time()中(server.py):

def get_current_time(self, timezone_name: str) -> TimeResult: """Get current time in specified timezone""" timezone = get_zoneinfo(timezone_name) current_time = datetime.now(timezone) return TimeResult( timezone=timezone_name, datetime=current_time.isoformat(timespec="seconds"), day_of_week=current_time.strftime("%A"), is_dst=bool(current_time.dst()), )

几个值得注意的实现点:

  1. 时区校验走get_zoneinfo统一入口。该函数(server.py)捕获ZoneInfo构造失败并抛出 MCP 标准协议错误McpError(ErrorData(code=INVALID_PARAMS, ...)),错误信息形如Invalid timezone: 'No time zone found with key Invalid/Timezone'——这与 time_server_test.py 中的断言完全一致,说明非法时区不会导致进程崩溃,而是以协议级错误返回给客户端。
  2. 夏令时由dst()直接计算is_dst字段让 LLM 能明确知道目标时区当前是否处于夏令时,避免模型自行猜测。
  3. 星期几以英文全称返回%AMonday等),对多语言模型而言是更无歧义的表示。

核心工具二:convert_time

convert_time将某一时间点从一个时区转换到另一个时区,必填参数三个:

  • source_timezone(string):源 IANA 时区名;
  • time(string):24 小时制时间,格式必须为HH:MM
  • target_timezone(string):目标 IANA 时区名。

调用示例:

{ "name": "convert_time", "arguments": { "source_timezone": "America/New_York", "time": "16:30", "target_timezone": "Asia/Tokyo" } }

响应结构由TimeConversionResult模型(server.py)定义,包含源、目标两个TimeResult,以及一个人类可读的时差字符串:

{ "source": { "timezone": "America/New_York", "datetime": "2024-01-01T12:30:00-05:00", "is_dst": false }, "target": { "timezone": "Asia/Tokyo", "datetime": "2024-01-01T12:30:00+09:00", "is_dst": false }, "time_difference": "+13.0h" }

源码实现细节

TimeServer.convert_time()的实现(server.py)有几个关键的工程细节:

1. 以"今天的日期"补齐时间。用户只传入HH:MM,实现先用datetime.strptime(time_str, "%H:%M")严格解析(失败时抛出Invalid time format. Expected HH:MM [24-hour format]),再把当前日期与解析出的时分拼合成一个带时区信息的datetime,因此转换结果会附带完整日期——这对跨日期变更线(date line)的场景尤为重要:

now = datetime.now(source_timezone) source_time = datetime( now.year, now.month, now.day, parsed_time.hour, parsed_time.minute, tzinfo=source_timezone, ) target_time = source_time.astimezone(target_timezone)

2. 时差支持非整数小时。时差通过两个时区的utcoffset()相减计算,并对半小时/45 分钟偏移做了特殊格式化:

if hours_difference.is_integer(): time_diff_str = f"{hours_difference:+.1f}h" else: # For fractional hours like Nepal's UTC+5:45 time_diff_str = f"{hours_difference:+.2f}".rstrip("0").rstrip(".") + "h"

这样尼泊尔(Asia/Kathmandu,UTC+5:45)会得到+4.75h而非四舍五入的+5h,印度(UTC+5:30)、伊朗(UTC+3:30)等半小时偏移时区也不会失真。

3. 错误路径与测试一一对应。time_server_test.py 中专门参数化了三类失败场景:源时区非法、目标时区非法、时间格式非法(如25:00),分别断言抛出McpErrorValueError,与源码中的异常分支严格对应。

本地时区自动探测与 --local-timezone 覆盖

Time Server 的一个重要设计是:当用户提问"现在几点"而没指明时区时,模型应当使用服务器所在主机的本地时区。这一能力分两层实现:

第一层:时区探测函数get_local_tz(server.py):

def get_local_tz(local_tz_override: str | None = None) -> ZoneInfo: if local_tz_override: return ZoneInfo(local_tz_override) # Get local timezone from datetime.now() local_tzname = get_localzone_name() if local_tzname is not None: return ZoneInfo(local_tzname) # Default to UTC if local timezone cannot be determined return ZoneInfo("UTC")

优先级为:命令行显式覆盖 >tzlocal探测系统时区(如Europe/Paris)> 回退到UTC。这个回退链在 time_server_test.py 中被完整覆盖,包括:覆盖值有效/无效、tzlocal返回None时回退 UTC、Windows 平台时区名(如Pacific Standard Time)被tzlocal转换为 IANA 名等场景。

第二层:把探测结果注入工具描述,引导 LLM 行为。serve()中,list_tools返回的get_current_time参数描述是动态拼接的(server.py):

"timezone": { "type": "string", "description": f"IANA timezone name (e.g., 'America/New_York', 'Europe/London'). Use '{local_tz}' as local timezone if no timezone provided by the user.", },

也就是说,如果服务器运行在America/Chicago的机器上,模型看到的 schema 描述里会直接写明"用户未提供时区时请使用America/Chicago"。convert_timesource_timezonetarget_timezone描述同样嵌入了本地时区名。这是"用工具描述做提示工程"的一个典型范例:服务器无需额外代码,仅靠描述文本就能让 LLM 正确地默认使用本地时区。

命令行参数 --local-timezone

覆盖机制的入口在init.py:

parser = argparse.ArgumentParser( description="give a model the ability to handle time queries and timezone conversions" ) parser.add_argument("--local-timezone", type=str, help="Override local timezone") args = parser.parse_args() asyncio.run(serve(args.local_timezone))

按 src/time/README.md 的说明,只需在客户端配置的args列表中加入--local-timezone即可强制指定"本地时区",例如:

{ "command": "python", "args": ["-m", "mcp_server_time", "--local-timezone=America/New_York"] }

这对容器部署特别有用。查看 Dockerfile 可以看到镜像层的对应设计:镜像通过环境变量LOCAL_TIMEZONE(默认UTC)在 ENTRYPOINT 中拼装--local-timezone参数:

# Set the LOCAL_TIMEZONE environment variable ENV LOCAL_TIMEZONE=${LOCAL_TIMEZONE:-"UTC"} # when running the container, add --local-timezone and a bind mount to the host's db file ENTRYPOINT ["mcp-server-time", "--local-timezone", "${LOCAL_TIMEZONE}"]

因此 Claude.app 中的 Docker 配置里才会出现-e LOCAL_TIMEZONE这样的写法(见下文配置章节)。值得注意的是:覆盖值若为非法时区名,ZoneInfo构造会直接抛异常——测试 test_get_local_tz_with_invalid_override 验证了这一点。

安装与运行

README 给出两种安装方式,二者最终运行的是同一个入口点。

方式一:uv(推荐)

无需显式安装,直接使用uvx以隔离环境运行:

uvx mcp-server-time

方式二:pip

pip install mcp-server-time

安装后可作为 Python 模块运行:

python -m mcp_server_time

这里有个细节可以印证:pyproject.toml 声明了控制台脚本入口:

[project.scripts] mcp-server-time = "mcp_server_time:main"

uvx mcp-server-time执行的就是init.py 中定义的main();而python -m mcp_server_time走的是main.py(其内容仅from mcp_server_time import main; main())。两条路径汇聚到同一个argparse+asyncio.run(serve(...))流程,行为完全一致。

客户端配置

以下配置示例全部继承自 src/time/README.md,按客户端分别说明。

Claude.app

使用 uvx:

{ "mcpServers": { "time": { "command": "uvx", "args": ["mcp-server-time"] } } }

使用 Docker(通过环境变量指定本地时区):

{ "mcpServers": { "time": { "command": "docker", "args": ["run", "-i", "--rm", "-e", "LOCAL_TIMEZONE", "mcp/time"] } } }

使用 pip 安装:

{ "mcpServers": { "time": { "command": "python", "args": ["-m", "mcp_server_time"] } } }

Zed

添加到 Zed 的settings.json中。使用 uvx:

"context_servers": [ "mcp-server-time": { "command": "uvx", "args": ["mcp-server-time"] } ],

使用 pip 安装:

"context_servers": { "mcp-server-time": { "command": "python", "args": ["-m", "mcp_server_time"] } },

VS Code

手动安装时,按下Ctrl + Shift + P并输入Preferences: Open User Settings (JSON),在用户设置(JSON)中加入下列配置块;也可以把配置放到工作区的.vscode/mcp.json文件中以便在团队间共享。注意:使用mcp.json文件时,顶层需要mcp键。

使用 uvx:

{ "mcp": { "servers": { "time": { "command": "uvx", "args": ["mcp-server-time"] } } } }

使用 Docker:

{ "mcp": { "servers": { "time": { "command": "docker", "args": ["run", "-i", "--rm", "mcp/time"] } } } }

Zencoder

按 README 步骤:打开 Zencoder 菜单(...)→ 从下拉菜单选择Agent Tools→ 点击Add Custom MCP→ 填入名称与下述服务器配置,并点击Install按钮:

{ "command": "uvx", "args": ["mcp-server-time"] }

调试与本地构建

MCP Inspector 调试。对 uvx 安装:

npx @modelcontextprotocol/inspector uvx mcp-server-time

如果在源码目录中开发(本仓库的src/time目录):

cd src/time npx @modelcontextprotocol/inspector uv run mcp-server-time

构建 Docker 镜像

cd src/time docker build -t mcp/time .

该 Dockerfile 采用两阶段构建:第一阶段基于官方 uv 镜像,利用uv.lock锁定依赖(uv sync --locked)并借助 build cache 挂载加速;第二阶段切换到轻量的python:3.12-slim-bookworm,仅复制生成的虚拟环境,并把/app/.venv/bin置于PATH首位,最终由 ENTRYPOINT 以--local-timezone参数启动。

交互示例与典型问法

README 列出的四类典型自然语言问法,恰好覆盖了两个工具的能力边界:

  1. "What time is it now?"(未指定时区,模型将依据工具描述中使用本地时区);
  2. "What time is it in Tokyo?"(对应get_current_timetimezone=Asia/Tokyo);
  3. "When it's 4 PM in New York, what time is it in London?"(对应convert_time);
  4. "Convert 9:30 AM Tokyo time to New York time"(对应convert_time,注意需把 12 小时制换算为 24 小时制09:30再传参)。

这两个工具在注册时还携带了ToolAnnotations(server.py):readOnlyHint=TruedestructiveHint=FalseidempotentHint=TrueopenWorldHint=False,即向客户端声明这些工具是只读、可重复执行、无副作用的,客户端可据此放心地自动调用而无需二次确认。

测试体系:用 freeze_time 锁住时间验证边缘场景

Time Server 的测试文件 time_server_test.py 是理解其边界行为的好入口。测试借助freezegun把系统时钟"冻结"在特定时刻,再断言转换结果,从而把"时间正确性"变成可回归验证的属性。覆盖的边缘场景包括:

  • DST(夏令时)切换前后:如2024-03-31欧洲/美国已入夏令时、2024-01-01为冬令时,验证is_dst与偏移量正确;
  • 欧洲与美国 DST 结束时间不同步2024-10-28欧洲已回到冬令时(UTC+1)而纽约仍在夏令时(UTC-4),时差为-5.0h2024-11-04之后双方都回到冬令时,时差变为-6.0h
  • 45 分钟偏移:尼泊尔Asia/Kathmandu(UTC+5:45),双向转换分别得到+4.75h/-4.75h
  • 半小时 DST 跳变:Lord Howe 岛夏令时仅 +30 分钟,冬令时为 UTC+10:30;
  • 半小时/历史偏移时区:伊朗Asia/Tehran(UTC+3:30)、委内瑞拉 2016 年切换前的America/Caracas(UTC-4:30);
  • 跨日期变更线23:00的华沙时间转太平洋阿皮亚(UTC+13)后日期进位到次日,基里巴斯 UTC+14 是"全球最早进入新一天"的时区,同样验证日期进位;
  • 时差为零的特殊案例:南极 Troll 站在夏季与欧洲 DST 时区同为 UTC+2,时差+0.0h

此外,test_convert_time_errors验证了三种参数错误路径(源/目标时区非法、25:00等非法时间格式),test_get_local_tz_*系列则验证了上文所述的本地时区探测回退链。整体来看,测试与源码中的异常分支、格式化逻辑一一对应,是维护时区类工具时值得借鉴的用例设计方式。

小结

Time MCP Server 展示了 MCP 参考实现的标准形态:以 stdio 为传输层,用Server抽象注册list_tools/call_tool两类处理器,工具参数全部通过 JSON Schema 描述,返回结构化 Pydantic 模型并序列化为文本。它对时区问题的处理有几个可复用的设计:

  1. IANA 时区名 +zoneinfo作为时区表示与计算基础,非法值以INVALID_PARAMS协议错误返回而非崩溃;
  2. tzlocal 自动探测 +--local-timezone覆盖 + UTC 兜底的三级时区解析链,并覆盖到 Docker 的LOCAL_TIMEZONE环境变量;
  3. 把探测到的本地时区动态写进工具描述,让 LLM 在用户未指定时区时自动选对默认值;
  4. 时差格式化区分整数与小数小时,正确处理 30/45 分钟偏移时区。

如需扩展,可以在此基础上增加新的时间相关工具(例如按日历规则计算下一个工作日的时区本地时间),并参照 time_server_test.py 的 freeze_time 参数化写法补充回归用例。

【免费下载链接】serversModel Context Protocol Servers项目地址: https://gitcode.com/GitHub_Trending/se/servers

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/5 22:12:36

Ice 配置教程:30 分钟从安装到自定义菜单栏布局

Ice 配置教程:30 分钟从安装到自定义菜单栏布局 【免费下载链接】Ice Powerful menu bar manager for macOS 项目地址: https://gitcode.com/GitHub_Trending/ice/Ice Ice 是一款 macOS 菜单栏管理工具:它能把你不常用的菜单栏图标收进"隐藏…

作者头像 李华
网站建设 2026/9/5 22:12:06

数字音频时钟同步揭秘:BNC 50欧与单晶铜时钟线的重要性

发烧时钟线、BNC 50欧、单晶铜这些关键词,经常出现在数字音频系统的升级讨论里。但它们解决的问题并不是“换一根更贵的线”,而是数字设备之间的时钟同步与信号完整性问题。对使用独立解码器、CD转盘、音频界面或专业声卡的人来说,时钟线是否…

作者头像 李华
网站建设 2026/9/5 22:10:58

自校正最小方差控制:从MATLAB仿真到工程实践

简介:本资源是一套面向自动控制专业学习者与工程实践者的MATLAB自校正控制(STC)算法实现包,聚焦最小方差控制(MVC)这一经典自适应策略,适用于工业过程控制、机器人伺服系统等需在线参数调整的动…

作者头像 李华
网站建设 2026/9/5 22:10:52

大学生新电脑开荒指南:先理顺系统与文件管理,再谈必装软件

每年八月下旬到九月中旬,一批刚拿到录取通知书的准大学生会同时面对两个新东西:大学生活,以及一台属于自己的新电脑。和高中机房统一配置的老机器不同,这台电脑从开机进入桌面那一刻起,几乎所有的软件决策都要自己做。…

作者头像 李华