ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

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

MCP Time Server 实战指南:时间查询与时区转换工具的实现、配置与调试 MCP Time Server 实战指南时间查询与时区转换工具的实现、配置与调试【免费下载链接】serversModel Context Protocol Servers项目地址: https://gitcode.com/GitHub_Trending/se/servers在基于 Model Context ProtocolMCP构建的服务器集合仓库中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 [ mcp1.23.0, pydantic2.0.0, tzdata2024.2, tzlocal5.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_timeget_current_time用于获取指定时区的当前时间。必填参数只有一个timezonestringIANA 时区名例如America/New_York、Europe/London。返回结果由 Pydantic 模型TimeResult见 server.py约束包含四个字段timezone时区名、datetimeISO 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:0001:00, is_dst: false }源码实现细节业务逻辑在TimeServer.get_current_time()中server.pydef 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( timezonetimezone_name, datetimecurrent_time.isoformat(timespecseconds), day_of_weekcurrent_time.strftime(%A), is_dstbool(current_time.dst()), )几个值得注意的实现点时区校验走get_zoneinfo统一入口。该函数server.py捕获ZoneInfo构造失败并抛出 MCP 标准协议错误McpError(ErrorData(codeINVALID_PARAMS, ...))错误信息形如Invalid timezone: No time zone found with key Invalid/Timezone——这与 time_server_test.py 中的断言完全一致说明非法时区不会导致进程崩溃而是以协议级错误返回给客户端。夏令时由dst()直接计算is_dst字段让 LLM 能明确知道目标时区当前是否处于夏令时避免模型自行猜测。星期几以英文全称返回%A→Monday等对多语言模型而言是更无歧义的表示。核心工具二convert_timeconvert_time将某一时间点从一个时区转换到另一个时区必填参数三个source_timezonestring源 IANA 时区名timestring24 小时制时间格式必须为HH:MMtarget_timezonestring目标 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:0009: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, tzinfosource_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 Nepals UTC5:45 time_diff_str f{hours_difference:.2f}.rstrip(0).rstrip(.) h这样尼泊尔Asia/KathmanduUTC5:45会得到4.75h而非四舍五入的5h印度UTC5:30、伊朗UTC3:30等半小时偏移时区也不会失真。3. 错误路径与测试一一对应。time_server_test.py 中专门参数化了三类失败场景源时区非法、目标时区非法、时间格式非法如25:00分别断言抛出McpError或ValueError与源码中的异常分支严格对应。本地时区自动探测与 --local-timezone 覆盖Time Server 的一个重要设计是当用户提问现在几点而没指明时区时模型应当使用服务器所在主机的本地时区。这一能力分两层实现第一层时区探测函数get_local_tzserver.pydef 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.pytimezone: { type: string, description: fIANA 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_time的source_timezone与target_timezone描述同样嵌入了本地时区名。这是用工具描述做提示工程的一个典型范例服务器无需额外代码仅靠描述文本就能让 LLM 正确地默认使用本地时区。命令行参数 --local-timezone覆盖机制的入口在init.pyparser argparse.ArgumentParser( descriptiongive a model the ability to handle time queries and timezone conversions ) parser.add_argument(--local-timezone, typestr, helpOverride 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-timezoneAmerica/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 hosts 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方式二pippip 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()。两条路径汇聚到同一个argparseasyncio.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中。使用 uvxcontext_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 列出的四类典型自然语言问法恰好覆盖了两个工具的能力边界What time is it now?未指定时区模型将依据工具描述中使用本地时区What time is it in Tokyo?对应get_current_timetimezoneAsia/TokyoWhen its 4 PM in New York, what time is it in London?对应convert_timeConvert 9:30 AM Tokyo time to New York time对应convert_time注意需把 12 小时制换算为 24 小时制09:30再传参。这两个工具在注册时还携带了ToolAnnotationsserver.pyreadOnlyHintTrue、destructiveHintFalse、idempotentHintTrue、openWorldHintFalse即向客户端声明这些工具是只读、可重复执行、无副作用的客户端可据此放心地自动调用而无需二次确认。测试体系用 freeze_time 锁住时间验证边缘场景Time Server 的测试文件 time_server_test.py 是理解其边界行为的好入口。测试借助freezegun把系统时钟冻结在特定时刻再断言转换结果从而把时间正确性变成可回归验证的属性。覆盖的边缘场景包括DST夏令时切换前后如2024-03-31欧洲/美国已入夏令时、2024-01-01为冬令时验证is_dst与偏移量正确欧洲与美国 DST 结束时间不同步2024-10-28欧洲已回到冬令时UTC1而纽约仍在夏令时UTC-4时差为-5.0h2024-11-04之后双方都回到冬令时时差变为-6.0h45 分钟偏移尼泊尔Asia/KathmanduUTC5:45双向转换分别得到4.75h/-4.75h半小时 DST 跳变Lord Howe 岛夏令时仅 30 分钟冬令时为 UTC10:30半小时/历史偏移时区伊朗Asia/TehranUTC3:30、委内瑞拉 2016 年切换前的America/CaracasUTC-4:30跨日期变更线23:00的华沙时间转太平洋阿皮亚UTC13后日期进位到次日基里巴斯 UTC14 是全球最早进入新一天的时区同样验证日期进位时差为零的特殊案例南极 Troll 站在夏季与欧洲 DST 时区同为 UTC2时差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 模型并序列化为文本。它对时区问题的处理有几个可复用的设计IANA 时区名 zoneinfo作为时区表示与计算基础非法值以INVALID_PARAMS协议错误返回而非崩溃tzlocal 自动探测 --local-timezone覆盖 UTC 兜底的三级时区解析链并覆盖到 Docker 的LOCAL_TIMEZONE环境变量把探测到的本地时区动态写进工具描述让 LLM 在用户未指定时区时自动选对默认值时差格式化区分整数与小数小时正确处理 30/45 分钟偏移时区。如需扩展可以在此基础上增加新的时间相关工具例如按日历规则计算下一个工作日的时区本地时间并参照 time_server_test.py 的 freeze_time 参数化写法补充回归用例。【免费下载链接】serversModel Context Protocol Servers项目地址: https://gitcode.com/GitHub_Trending/se/servers创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进