HammerTime について
HammerTimeは、Discordのタイムスタンプ生成とアカウント設定管理を行うREST APIおよびステートレスなMCPサーバーです。ボットと同じオプションや保存済み設定でタイムスタンプコマンドを実行できます。
主な機能
- 01複数形式のタイムスタンプ生成
- 02過去・未来の時刻計算
- 03Discord snowflake IDの変換
- 04ボット設定の読み取りと更新
- 05アカウントキーとサービス状態の確認
活用シーン
- 01Discordのイベント用タイムスタンプ作成
- 02現在を基準にした日時の計算
- 03snowflake IDからタイムスタンプを確認
MCP 対応
ステートレスなStreamable HTTP MCPサーバーを`/mcp`で提供し、REST APIと同じAPIキーを使用します。ツールはHammerTimeボットのコマンドに対応しています。
設定
リモートサーバーはインストール不要です。クライアントを選んで設定をコピーするか、ワンクリックで導入できます。
https://api.hammertime.cyou/mcpclaude mcp add --transport http wentthefox-hammertime https://api.hammertime.cyou/mcp{"mcpServers":{"wentthefox-hammertime":{"type":"http","url":"https://api.hammertime.cyou/mcp"}}}Cursor で開く{"servers":{"wentthefox-hammertime":{"type":"http","url":"https://api.hammertime.cyou/mcp"}}}VS Code で開く{"mcpServers":{"wentthefox-hammertime":{"type":"http","url":"https://api.hammertime.cyou/mcp"}}}{"mcpServers":{"wentthefox-hammertime":{"serverUrl":"https://api.hammertime.cyou/mcp"}}}コマンドはターミナルで実行し、JSON は各クライアントの MCP 設定に貼り付けて再起動します。認可が必要なサーバーは初回接続時にログインを求めます。
https://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 /mcp | MCP サーバー(Streamable HTTP、ステートレス)です。同じ API キーを使用します |
GET / および GET /generated/api-docs.json | Swagger UI と OpenAPI ドキュメントです |
アカウントでテレメトリをオフにしていない限り、使用状況はボットと同じように記録され(同じカウンターを使い、ソースは api または mcp になります)、応答にはボットと同様に実行回数が含まれます。
キー
ユーザーは HammerTime のウェブサイト(プロフィールページ)でキーを作成します。各キーは 1 つの 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 が拒否した入力は、ボットがこれまでどおりメッセージを作成して記録します。
レート制限
各キーは、REST エンドポイントと /mcp を合わせて、平均して RATE_LIMIT_PER_MINUTE 件(60 件)のリクエストを送信でき、バースト上限はその半分です。ボットのサービスキーは、処理対象となる Discord アカウントごとに制限されるため、1 人の利用者がほかの利用者の枠を使い切ることはありません。認証済みリクエストのすべての応答には RateLimit-Limit、RateLimit-Remaining、RateLimit-Reset が含まれ、拒否された場合は Retry-After(秒)を含む 429 が返されます。/health とドキュメントは制限されません。
認証に AUTH_FAILURES_PER_MINUTE 回(20 回)失敗したクライアントアドレスは、照会が行われる前に、待機時間が経過するまで拒否されます。これにより、偽のキーを大量に送信しても、Laravel アプリでキーを照会するコストが発生しません。そのアドレスから有効なキーを使った場合でも待機が必要になるため、共有アドレスを使う無関係なユーザーに影響することがあります。したがって、数値は余裕を持って設定してください。
リバースプロキシの背後では、すべてのクライアントがプロキシのアドレスを使うため、全員が 1 つとしてカウントされます。CLIENT_IP_HEADER に、プロキシがクライアントのアドレスを設定し、必ず上書きするヘッダーを指定してください(nginx の場合:proxy_set_header X-Real-IP $remote_addr;)。そうしないと、誰でも自分がカウントされるアドレスを選べてしまいます。制限の後処理を行うバックグラウンド処理はありません。バケットは使用時にのみ更新され、Redis は再びいっぱいになると個別に忘却します。
Redis でのカウント
pm2.json は 2 つのワーカーを実行します(以下を参照)。何も設定しない場合、それぞれが個別にカウントするため、キーごとのリクエスト数はワーカー数に RATE_LIMIT_PER_MINUTE を掛けた数まで増える可能性があり、アドレスごとの認証失敗回数も同様です。Redis を設定して、これらをまとめてカウントしてください。REDIS_URL を設定するか、REDIS_HOST と REDIS_PORT、REDIS_USERNAME、REDIS_PASSWORD、REDIS_DB、REDIS_TLS を設定します(.env.example を参照)。これにより、すべてのワーカーで制限が共有されます。
- 1 つの Lua スクリプトでバケットを補充し、確認してから消費するため、2 つのワーカーが両方とも最後のトークンを取得することはありません。キーは
<REDIS_KEY_PREFIX><kind>:<key or address>(hammertime-api:requests:key:42)で、自動的に有効期限が切れます。API には専用のデータベース(REDIS_DB)を割り当ててください。プレフィックスがあるため、Redis を共有している場合でも Laravel アプリのキーは安全です。 - リクエストが Redis からの応答を待つ時間は、最大でも 150 ms です。応答がない場合、そのリクエストは Redis を使わない場合と同様に、ワーカーごとにメモリ上でカウントされます。レートリミッターが原因で API が停止するより、制限が少し緩いほうがよいためです。この場合、ログに記録されますが、記録は最大でも 1 分に 1 回です。Redis が復旧すると、再び Redis でカウントします。
REDIS_TLS=trueは別のマシンにある Redis 用です。このマシン上の Redis では不要です。
クラスターとしての実行
pm2 は pm2.json の exec_mode、instances、wait_ready、kill_timeout の設定に従い、ポートを共有する 2 つのワーカーとしてサーバーを実行します。ワーカーはリッスンを開始すると ready を送信し、SIGINT を受け取ると処理中のリクエストを完了してから停止します。これにより、デプロイ時にリクエストを取りこぼすことなく、ワーカーを 1 つずつ入れ替えられます(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 でホスト名にプロキシする必要があります。vhost は nginx/api.hammertime.cyou.conf にあり、ヘッダーに初回セットアップの手順が記載されています。
テスト
ルートから pnpm run test:packages を実行します(core と API)。アプリはインメモリバックエンドに対してプロセス内で実行され、MCP テストでは実際の MCP SDK クライアントを使用します。
MCP Registry への登録
server.json には、MCP Registry に掲載される MCP サーバーのエンドポイントと、API キーをベアラートークンとして要求することが記載されています。レジストリが保持するのはメタデータのみなので、公開するパッケージはありません。
名前は io.github.WentTheFox/hammertime です。.github/workflows/publish-mcp.yml のワークフローは GitHub Actions の OIDC トークンで認証します。このトークンにより、リポジトリがその所有者のものであることが証明されるため、シークレットをどこにも保存する必要はありません。
バージョンを公開するには:
packages/api/package.jsonとpackages/api/server.jsonの両方でversionを上げます。両者が異なる場合、テストは失敗します。- マージしてから、タグを付けます:
git tag mcp-v0.2.0 && git push origin mcp-v0.2.0。
ワークフローはパッケージをテストし、タグが server.json と一致することを確認してから、mcp-publisher validate で検証し、公開します。
バージョンは一度だけ公開できます。公開せずにファイルを試すには、packages/api で mcp-publisher validate を実行します。
コメント
AI アプリケーションが外部のツールやデータソースに統一的な方法で接続するためのオープンなプロトコルです。クライアントはモデルとの対話を担い、MCP サーバーはツール・リソース・プロンプトテンプレートを提供します。標準メッセージでやり取りするため、1 つのサーバーを複数のクライアントで再利用できます。