mcpField
H
HammerTime

HammerTime

远程服务
hammertime.cyou通信协作142 天前更新

关于 HammerTime

HammerTime 提供用于生成 Discord 时间戳和管理账户设置的 REST API 与无状态 MCP 服务。它以与机器人相同的选项和已保存设置提供时间戳命令。

主要功能

  1. 01生成多种格式的时间戳
  2. 02计算过去或未来的时间
  3. 03转换 Discord snowflake ID
  4. 04读取和更新机器人设置
  5. 05查看账户密钥和服务状态

适用场景

  1. 01为 Discord 活动创建时间戳
  2. 02计算相对当前时间的日期和时间
  3. 03根据 snowflake ID 查询时间戳

MCP 相关

无状态 Streamable HTTP MCP 服务位于 `/mcp`,使用与 REST API 相同的 API 密钥。其工具对应 HammerTime 机器人的命令。

配置

远程服务无需本地安装。选择你使用的客户端,复制配置或使用一键安装。

服务地址https://api.hammertime.cyou/mcp
一键复制安装命令
Claude CodeTerminal
Cursor.cursor/mcp.json
VS Code.vscode/mcp.json
Claude Desktopclaude_desktop_config.json
Windsurf~/.codeium/windsurf/mcp_config.json

命令在终端中运行;JSON 粘贴到对应客户端的 MCP 配置后重启。需要授权的服务会在首次连接时提示登录。

全部服务地址
Streamable HTTPhttps://api.hammertime.cyou/mcp需要授权

工具

详细介绍

HammerTime API

HammerTime 的公共 REST API 和 MCP 服务。每个端点和每个 MCP 工具都是同名的机器人命令,每个输入都是同名的命令选项,因此 POST /at 会接收 year、month、day、hour、minute、timezone、format 等参数,并像机器人一样,根据账号已保存的设置进行解析。逻辑由 @went.tf/hammertime-core 实现,机器人也使用它。

POST /at、/at12、/iso、/unix、/in、/ago、/add、/subtract、/snowflake命令,权限范围 timestamps:create
GET/PATCH /settings账号的机器人设置,权限范围 settings:read / settings:write
GET /me、GET /health正在使用的密钥和服务状态
POST /mcpMCP 服务(Streamable HTTP,无状态),使用相同的 API 密钥
GET / 和 GET /generated/api-docs.jsonSwagger UI 和 OpenAPI 文档

使用情况的记录方式与机器人相同(使用相同的计数器,来源标记为 api 或 mcp),除非账号关闭了遥测;回复中也会像机器人回复一样附带运行次数。

密钥

用户可在 HammerTime 网站的个人资料页面创建密钥,每个密钥对应一个关联的 Discord 账号。将密钥作为 Authorization: Bearer htk_... 发送。机器人有自己的服务密钥,会通过 X-Discord-User-Id 指定每个请求对应的账号;该密钥仅可用于命令的 REST 端点,不能用于 /mcp。

MCP 客户端需配置 /mcp 的 URL 和请求头,例如:

{ "mcpServers": { "hammertime": { "type": "http", "url": "https://<host>/mcp", "headers": { "Authorization": "Bearer htk_..." } } } }

将机器人作为客户端

机器人可以让此 API 处理其时间戳命令(在机器人的 .env 中配置 COMMANDS_API_URL 和 COMMANDS_API_KEY,参见 .env.example)。机器人会发送用户提供的选项,并使用带有 X-Discord-User-Id 的服务密钥,然后将答案转换为 Discord 回复。此时 API 已记录该次运行,回复中也附带了相关说明,因此机器人不会重复记录。当 API 停机、响应缓慢(COMMANDS_API_TIMEOUT_MS)、拒绝机器人请求或对其限流时,机器人会完全按照此前的方式自行处理命令,包括记录该次运行,因此此服务发生故障时,除了日志中的警告外不会造成任何影响。对于 API 拒绝的输入,机器人会照常措辞回复并记录。

速率限制

每个密钥平均每分钟可发起 RATE_LIMIT_PER_MINUTE(60)个请求,突发请求上限为该值的一半;REST 端点与 /mcp 共用限额。机器人的服务密钥会按其所服务的 Discord 账号分别限流,因此一个高频用户不会耗尽其他用户的配额。每个经过身份验证的请求的响应都会包含 RateLimit-Limit、RateLimit-Remaining 和 RateLimit-Reset;被拒绝的请求会返回带有 Retry-After(秒)的 429 响应。/health 和文档不受限流。

客户端地址每分钟身份验证失败 AUTH_FAILURES_PER_MINUTE(20)次后,在等待期结束前会被拒绝,且在此之前不会进行任何查询,因此大量伪造密钥不会各自导致 Laravel 应用执行一次查询。来自该地址的有效密钥也必须等待;这可能影响共用地址的无辜用户,因此应将该限额设得宽松一些。

在反向代理后方,所有客户端都会使用代理的地址,因此它们会被算作同一个客户端。将 CLIENT_IP_HEADER 设置为代理写入客户端地址的请求头,并确保代理会覆盖该请求头(nginx:proxy_set_header X-Real-IP $remote_addr;),否则任何人都能选择自己的计数地址。后台不会运行任何任务来清理限额记录:只有在使用时才会更新对应的计数桶,而 Redis 会在其再次被填满后自行清除该记录。

在 Redis 中计数

pm2.json 会运行两个工作进程(见下文)。如果不做处理,每个工作进程都会单独计数,因此一个密钥最多可以发起的请求数会达到工作进程数乘以 RATE_LIMIT_PER_MINUTE,某个地址的认证失败次数也是如此。配置 Redis,让它们统一计数: 可以配置 REDIS_URL,也可以配置 REDIS_HOST、REDIS_PORT、REDIS_USERNAME、REDIS_PASSWORD、REDIS_DB 和 REDIS_TLS(见 .env.example),这样所有工作进程共享限额。

  • 一个 Lua 脚本会为令牌桶补充令牌、检查令牌并扣除令牌,因此两个工作进程不会同时取走最后一个令牌。键的格式为 <REDIS_KEY_PREFIX><kind>:<key or address>(hammertime-api:requests:key:42),并会自动过期。为 API 单独设置一个数据库(REDIS_DB);前缀可确保即使 Laravel 应用也共用该 Redis,其键也不会受到影响。
  • 请求等待 Redis 的时间不会超过 150 ms。如果 Redis 没有响应,该请求会像未使用 Redis 时一样,按工作进程分别在内存中计数:限额稍微宽松一些,也好过限流器导致 API 宕机。日志会说明这一情况,每分钟最多记录一次;Redis 恢复后,计数会重新写入 Redis。
  • REDIS_TLS=true 适用于连接另一台机器上的 Redis;在本机上无需启用。

以集群模式运行

pm2 会运行两个共享端口的服务工作进程(pm2.json 中的 exec_mode、instances、wait_ready 和 kill_timeout)。工作进程开始监听后会发送 ready,并在收到 SIGINT 时等待正在处理的请求完成后再停止,这样部署时就可以逐个替换工作进程,而不会中断请求(src/graceful-shutdown.ts,机器人也采用相同方式)。请求所需的数据不会从前一个工作进程保留。密钥缓存按工作进程分别维护,因此每个工作进程每隔 KEY_CACHE_TTL_SECONDS 会向 Laravel 应用验证一次密钥;除非配置了 Redis,否则限额也按工作进程分别计算。

数据存放位置

本服务不存储任何数据。Laravel 应用(SledgeHammerTime)负责管理密钥、设置和遥测数据;本服务使用 INTERNAL_API_SECRET 调用其 /internal 端点(见该仓库中的 docs/api.md)。已验证的密钥会缓存 KEY_CACHE_TTL_SECONDS,因此被撤销的密钥在这段时间内仍可使用。

生成的部分

src/generated/commands.ts(请求架构、选项类型和英文文本)由机器人的 src/commands.json 及其 en-US 语言区域生成,docs/openapi/api-docs.json(位于仓库根目录)则由路由生成。 两者都会提交到仓库;如果文件过期,测试会失败:

pnpm --filter @went.tf/hammertime-api run generate                       # after changing commands.json or the locale
pnpm --filter @went.tf/hammertime-api run generate -- --commands <path>  # from another commands file
pnpm --filter @went.tf/hammertime-api run generate -- --check            # write nothing, fail when out of date
pnpm --filter @went.tf/hammertime-api run openapi:sync [-- --check]      # after changing routes or their descriptions

运行

pnpm run build:core && pnpm --filter @went.tf/hammertime-api run build
cp packages/api/.env.example packages/api/.env    # fill it in, git ignores it
node packages/api/dist/src/server.js    # reads packages/api/.env by itself

在服务器上,pm2 会运行本服务(pm2.json 中的 HammerTimeBot:Api),但前提是 packages/api/.env 文件存在,见 deploy.conf。(该文件以前是仓库根目录下的 .env.api,部署时会自动移动此类文件。)请先部署 Laravel 应用,因为本服务会调用其内部端点。本服务只监听 127.0.0.1:3010,因此 nginx 必须将其代理到一个主机名:虚拟主机配置文件为 nginx/api.hammertime.cyou.conf,其中的文件头包含一次性设置说明。

测试

在根目录运行 pnpm run test:packages(core 和 API)。测试会在进程内运行应用,并使用内存后端;MCP 测试使用真实的 MCP SDK 客户端。

在 MCP Registry 中上架

server.json 是 MCP Registry 中列出的内容:MCP 服务的端点,以及它需要以 bearer token 的形式提供 API 密钥。Registry 只保存元数据,因此无需发布软件包。

名称为 io.github.WentTheFox/hammertime。.github/workflows/publish-mcp.yml 中的工作流使用 GitHub Actions 的 OIDC 令牌进行身份验证,以证明该仓库属于相应所有者,因此无需在任何地方存储密钥。

发布一个版本:

  1. 提高 packages/api/package.json 和 packages/api/server.json 中的 version。两者不一致时,测试会失败。
  2. 合并后为其打标签:git tag mcp-v0.2.0 && git push origin mcp-v0.2.0。

该工作流会测试软件包、检查标签是否与 server.json 一致、使用 mcp-publisher validate 验证,并发布。每个版本只能发布一次。若要在不发布的情况下试用该文件,请在 packages/api 中运行 mcp-publisher validate。

评论

支持 Markdown

第一次接触 MCP?

从协议是什么,到在客户端里装上第一个服务,这几条回答覆盖了大多数入门问题。

一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。