mcpField
K
kicksmash

kicksmash

リモート
kicksma.shゲーム・エンタメ01 日前に更新
リポジトリ公式サイト

kicksmash について

kicksmashは、共有リンクでパデルの試合を運営するオープンソースのモバイルファーストなWebアプリです。アプリのインストールやアカウント登録は不要で、個人やエージェント向けに参加受付、スケジュール、選手レベル、主催者管理の機能を備えています。

主な機能

  1. 01試合リンクの作成と共有
  2. 02試合への参加と名簿管理
  3. 03アメリカーノ形式の組み合わせ作成
  4. 04選手レベルと範囲の設定
  5. 05バランスの取れたチームの提案

活用シーン

  1. 01グループチャットでパデルの試合を企画
  2. 02参加登録やキャンセル待ちを調整
  3. 03アメリカーノ形式の大会を運営
  4. 04選手レベルに応じて参加者を編成

MCP 対応

アシスタント向けに /mcp のMCPサーバーとREST APIを提供します。OpenAPI仕様も公開し、URLによるエージェントの設定に対応しています。

設定

リモートサーバーはインストール不要です。クライアントを選んで設定をコピーするか、ワンクリックで導入できます。

エンドポイントhttps://kicksma.sh/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 設定に貼り付けて再起動します。認可が必要なサーバーは初回接続時にログインを求めます。

ツール

15

このツールにパラメーターはありません。

詳細

Kicksmash — 1つのリンクでパデルの対戦をマッチング

CI License: Apache-2.0

パデルの試合を手軽に開催できる、モバイルファーストのウェブアプリです。アプリのインストールも、アカウントも、パスワードも不要です。試合を作成して、kicksma.sh/{code} をWhatsAppやTelegramで共有すれば、友達はリンクをタップして名前を一度入力するだけで参加できます。

  • スタック: Next.js 15(App Router、TypeScript)・Supabase Postgres + Drizzle・Resend・next-intl(EN/RU/ES)・Tailwind v4・Vercel(Cron + OG images)。
  • Apache License 2.0 の下でオープンソースとして公開されています。自分用のコピーを動かし、その上に構築し、プルリクエストを送ってください。CONTRIBUTING.md と SECURITY.md をご覧ください。
  • 本人確認: 名前を一度入力すると、署名付きの httpOnly Cookie(1 年間)にプレイヤー UUID が保存され、localStorage には復元シールが保存されます(サーバーはそのプレイヤーと用途に対してシールに署名します。Cookie を紛失した場合はシールから復元され、プレイヤー ID だけから復元されることはありません)。デバイス間での利用:すべてのプレイヤーには非公開の個人リンク(/p/{token}。マイマッチ、すべてのメール、カレンダーの招待に表示)があり、どのデバイスでもサインインできます。以前使ったことのあるメールアドレスがあれば、6 桁のコードで履歴を復元し、そのメールアドレスを共有するすべての本人確認情報を統合できます。ホーム画面のショートカットを開くと個人リンクが表示され、カレンダーの予定やメールには非公開のイベントリンク(/p/{token}/{code}:デバイスにサインインし、マッチを開きます)が含まれます。新しく追加されたメールアドレスには個人リンクが送信されます(マッチ中ならカレンダーの招待に、それ以外の場合はメール単独で送信)。トークンは 12 文字です。以前の 32 文字のトークンも、遅延短縮後は previous_token として引き続き機能します。メールアドレスは変更できますが、一度設定すると空欄にはできません。以前のアドレスは recovery_email として保持されるため、どちらのアドレスに復元コードを送ってもプレイヤーのアカウントに戻れます。マイマッチの「このリンクをメールで送信」では、個人リンクをメールで送信します(ほかに、ネイティブ共有、コピー、QR を利用できます)。パスキー(DECIDING ルール 39):参加後、「この電話でログイン状態を保持」を選ぶと、電話のパスワードマネージャーにパスキーが保存されます。それ以降は、「Face ID または指紋でサインイン」でその電話のどのブラウザーにもサインインでき、まずマッチページの参加フォームにある名前欄の下に常時表示される「以前にプレイしたことがありますか?」から利用できます。名前だけでのセッションではパスキーは保存されません。サインイン時に統合されるのは、そのブラウザーが作成した同じ名前の未登録レコードのみです。Kicksmash は公開鍵、ラベル、2 つの日付(passkeys)を保持します。マイマッチには「ログイン情報を記憶している電話」が表示され、「削除」できます。
  • 通知、静かな時間帯、受信トレイ: プレイヤーへのすべての通知は、1 つのゲート(src/lib/domain/notices.ts)を通ります。まずマイマッチの受信トレイに保存されます(90 日間、新しい順、プレイヤーの言語で表示)。その種類の通知が有効な場合(7 種類あり、クラブの新着マッチ以外はすべて有効)、かつ静かな時間帯でなければ、設定された通知チャネルに送信されます。ただし、マッチ開始まで 3 時間以内の場合は例外です。静かな時間帯に保留された通知は、終了時に毎時の cron から、各チャネルにつき 1 通の短いメッセージとして送信されます。
  • プッシュ通知リマインダー: 各マッチの 1 時間前に Web Push(VAPID)で通知します。プレイヤーが有効にしたすべてのデバイスに送信されます(iPhone の場合はホーム画面のアプリから)。/api/cron/push は Supabase の pg_cron + pg_net により 5 分ごとに呼び出されます(Vercel Hobby の cron は 1 日 1 回です)。VAPID_PUBLIC_KEY、VAPID_PRIVATE_KEY、VAPID_SUBJECT を設定してください。
  • リンク: /{code}(4 文字、公開)・/{code}/i/{6}(個人用の招待)・/{code}/manage/{10}(主催者用の秘密リンク)。
  • エージェントを前提に設計されたオープンなサービスです。 公開 REST API(/api/openapi.json に OpenAPI 3.1 を公開)、URL を指定するだけでどのアシスタントにも追加できる /mcp の MCP サーバー、即時にセルフサービスで取得できるキー、署名付き Webhook、llms.txt、/.well-known/mcp.json、AI クローラーを歓迎する robots.txt、AGENTS.md、インストール可能なスキル(npx skills add evhg/padel-matchup)、グループおよび会場ごとのカレンダーフィードを提供します。公開データは CC BY 4.0 です。/developers と /agents をご覧ください。
  • メールアドレスはどこでも任意です。 RESEND_API_KEY がなくても、メール機能を非表示にした状態でアプリをすべて利用できます。
  • プライバシーの概要と詳細: /about には、保存される情報、決して行わないこと、退会方法を簡潔に記載しています。フッターの目立たないリンクからアクセスできる /privacy と /terms には、Cookie、処理事業者、コードで適用される数値(src/lib/legal.ts)を含む詳細を記載しています。主催者が開始するメール(招待、招待リマインダー)には、署名付きのワンタップ式 /unsubscribe リンクが含まれます。プレイヤーが自分のメールアドレスを再度追加すると、オプトアウトは解除されます。マイマッチの下部にある「アカウントを削除」では、個人データ(名前、メールアドレス、電話番号、個人リンク、連携済みの Telegram、Discord、LINE アカウント、公開ページ、パスキー)を消去し、今後の試合の参加枠を解放し、プレイヤー自身が主催する今後のマッチをキャンセルします。また、コーチの場合は今後のレッスンをキャンセルし(各生徒にコーチの本名を伝えます)、コーチページとその全情報を削除します。過去のスコアには「削除済みプレイヤー」と表示されます。アカウントは残したままコーチページだけを削除したいコーチは、設定の下部にある「コーチページを削除」を利用できます。ただし、今後のレッスンが残っている場合は削除できません。
  • レベル: すべてのプレイヤーは、自分のパデルレベルを 1 回設定できます(0~7 を 0.25 刻み。パデルアプリで使われている尺度)。マイマッチから設定するか、レベル範囲を指定したマッチで初めて求められたときに設定します。レベルは、名簿、順位表、チーム選択画面など、名前の横に小さなチップで表示されます。結果に応じてレベルが調整されます。主催者が 2 対 2 のスコアを確定するか、トーナメントを終了すると、イベントごとに 1 回、小幅な Elo 方式の変動(マッチごとに最大 ±0.10、トーナメントごとに最大 ±0.12)が適用され、記録されます(「マッチ後に 3.25 → 3.30」)。レベル未設定のプレイヤーは変動せず、計算対象にもなりません。主催者はマッチまたはトーナメントごとにレベル範囲を設定できます。プリセットは Bronze 1.0–2.5、Silver 2.5–3.5、Gold 3.0–4.5、Platinum 4.5+、または最小値と最大値を指定するカスタム範囲です。範囲内のプレイヤーは通常どおり参加できます。範囲外のプレイヤーは参加を申請し、主催者が承認(参加枠に登録。満員の場合は待機リストに追加)または却下します。回答は参加バーとアクティビティフィードに表示されます。予約済みまたは招待済みのプレイヤーと主催者には、範囲は適用されません。4 人全員のレベルが設定されている場合、スコアパネルはバランスの取れたチーム(レベル差が最小のチーム)を提案します。マイマッチには、プレイ数、勝利数、勝率、表彰台入り回数の統計欄が追加されます。
  • 不正利用の制限(特に記載のない限り UTC の 1 日あたり。人間には余裕を持たせ、スクリプトには厳しく設定):1 IP あたりの新規本人確認情報 40 件、プレイヤーあたりのマッチ 20 件、主催者あたりの招待 40 件、プレイヤーあたりの 1 時間ごとの参加 30 件、メールアドレス変更 10 回、個人リンクのメール送信 5 回、1 IP あたりの復元コード 20 件、1 IP あたりのブラウザークラッシュレポート 60 件。カウンターは metrics_daily に保存され、追加のインフラは不要です。制限に達すると「リクエストが多すぎます」と表示され、それ以外の処理は行われません。

クイックスタート(設定不要、約2分)

pnpm install
pnpm dev

これだけです。DATABASE_URL を設定しない場合、アプリは ./.pglite に組み込みの PGlite データベースを起動し、マイグレーションを適用して、サンプルイベントを2件登録します。

URL表示内容
http://localhost:3000/PLAY参加者2人、予約済み招待1件、空き1枠の試合
http://localhost:3000/PAST主催者が確認した3セットのスコアが記録された終了済みの試合
http://localhost:3000/new試合を作成

.env.example を .env にコピーすると、設定を変更できます。すべてのフロー(参加、ウェイトリスト、招待、スコア、「自分の試合」、OG プレビュー)は、キーを設定しなくてもエンドツーエンドで動作します。

pnpm test        # vitest: slot-claim concurrency, invite transitions, score-lock rules, reminders, identity, americano rotation, rate limits, opt-outs, account deletion
pnpm typecheck
pnpm lint
pnpm build
pnpm e2e         # the sixteen Playwright journeys against a fresh production build (`node scripts/suites.mjs --why` says which a change can break); first time: pnpm exec playwright install chromium

pnpm e2e は、使い捨ての PGlite データベース、ダミーの Resend キー(メール UI は有効になり、送信は問題なく失敗します)、生成された VAPID キーを使ってポート3001で next start を起動し、すべての e2e/*.mjs テストスイートを実行します。SHOTS=./shots を指定するとページ全体のスクリーンショットを保存し、PW_CHROMIUM=/path/to/chromium を指定すると、事前にインストールされたブラウザーを使用します。GitHub Actions は、プッシュとプルリクエストのたびに、型チェック、Lint、PGlite および実際の Postgres サービス上での vitest、ビルド、e2e テストスイートを実行します(.github/workflows/ci.yml)。

テストはデフォルトでインメモリの PGlite 上で実行されます。実際の Postgres(実際の並行処理)に対してテストを実行するには、TEST_DATABASE_URL に使い捨て可能なデータベースを指定してください。各テストファイルの実行前に、その public スキーマが削除されます。

TEST_DATABASE_URL=postgres://postgres:postgres@localhost:5432/padel_test pnpm test

環境変数

本番環境で必須の変数は、データベース URL の1つだけです。それ以外はすべて安全なデフォルト値が設定されています。

変数必須目的
DATABASE_URL✅Supabase の Transaction pooler 接続文字列(ポート 6543)。Supabase の Connect ダイアログに表示される内容をそのまま指定します。POSTGRES_URL(Vercel ⇄ Supabase の連携)と SUPABASE_DB_URL も使用できます。空の場合は、ローカル開発専用の組み込み PGlite データベースが使われます。
DATABASE_PASSWORDURL に [YOUR-PASSWORD] が残っている場合URL 内のパスワードに置き換え、自動的にパーセントエンコードします。
DIRECT_DATABASE_URLいいえpnpm db:migrate と pnpm db:generate 用の直接接続 URL(ポート 5432)です。POSTGRES_URL_NON_POOLING も使用できます。
AUTO_MIGRATEいいえfalse にすると、アプリは初回接続時にマイグレーションを適用しません。このセーフティネットは新しいデータベース専用です。本番環境では、Migrate ワークフローから各マイグレーションを適用します(AGENTS.md のルール 7)。
APP_BASE_URLいいえVercel の本番ドメインがデフォルトです。ローカル環境やその他のホストでは設定してください。NEXT_PUBLIC_APP_BASE_URL は同じ値をブラウザー側で使用するためのものです。
SESSION_SECRET推奨ID クッキーに署名します。設定しない場合、データベース URL から安定したシークレットが生成されます。
CRON_SECRET推奨/api/cron/* と一度限りのセットアップルートを保護します。設定すると、Vercel が自動的に送信します。
RESEND_API_KEYいいえカレンダーへの招待、通知、リマインダーなど、すべてのメールを有効にします。
EMAIL_FROMいいえデフォルトは Kicksmash <matches@<your domain>> です。ドメインは Resend で認証済みである必要があります。
RESEND_WEBHOOK_SECRETいいえResend からの受信 Webhook を検証します。これにより、feedback@ または claude@ への返信がメモになります。
OUTREACH_FROMいいえアウトリーチメールの差出人欄です。デフォルトは Claude at Kicksmash <claude@<your domain>> です。
VAPID_PUBLIC_KEY / VAPID_PRIVATE_KEY / VAPID_SUBJECTいいえプッシュ通知によるリマインダーを有効にします(npx web-push generate-vapid-keys)。
TELEGRAM_BOT_TOKEN / TELEGRAM_WEBHOOK_SECRET / TELEGRAM_BOT_USERNAMEいいえTelegram ボットと Telegram サインインを有効にします。GET /api/telegram/setup(Bearer CRON_SECRET)で Webhook を一度登録してください。ユーザー名のデフォルト値は src/lib/config.ts にあるものです。ボット自体を有効にするかどうかはトークンで決まります。
TELEGRAM_MINIAPP_SLUGいいえMini App の短縮名です。カードから Mini App に直接リンクできるようになります。設定しない場合、ボットのカードは代わりにウェブサイトへリンクします。アプリが存在するまでは、この設定が適切です。
TELEGRAM_OWNER_IDいいえオーナーの Telegram ID です。提案、クラブ申請、下書きはワンタップで回答できるよう、この ID に送信されます。また、管理用デスクはこのアカウントでのみ開けます。
DISCORD_BOT_TOKEN / DISCORD_PUBLIC_KEYいいえDiscord ボットを有効にします。GET /api/discord/setup(Bearer CRON_SECRET)でコマンドとインタラクション URL を一度登録してください。インストールリンクが返されます。
DISCORD_APPLICATION_IDいいえ設定されていない場合は、ボットトークンから読み取ります。
DISCORD_INVITE_URLいいえコミュニティページにサーバーを表示します。
WHATSAPP_TOKEN / WHATSAPP_PHONE_ID / WHATSAPP_NUMBER / WHATSAPP_VERIFY_TOKEN / WHATSAPP_APP_SECRETいいえWhatsApp のスレッド機能を有効にします。カードを送るチャンネルではなく、一度に 1 人とやり取りする機能です。Meta の Groups API で作成できるのは、招待リンクから参加するビジネス自身のグループだけです。グループの定員は最大 8 人で、公式ビジネスアカウントが必要です。また、メッセージは編集できません。そのため、ボットがすでにチームが使っているチャットに参加することはできません。ボットが作成したグループは、オーナーが Meta 側で手順を完了するまで待機します(ROADMAP.md)。Meta の Webhook を /api/whatsapp/webhook に設定してください。GET リクエストは WHATSAPP_VERIFY_TOKEN を使ってサブスクリプションのハンドシェイクに応答し、すべての POST リクエストは WHATSAPP_APP_SECRET で署名されていない場合、拒否されます。WHATSAPP_NUMBER はユーザーがメッセージを送る番号で、主催者が自分のグループに貼り付ける wa.me のリンクを作成します。
WHATSAPP_TEMPLATES_PER_DAYいいえWhatsApp が UTC の 1 日に送信できるメッセージテンプレートの数です。WhatsApp を連絡手段に指定しているプレイヤーに対し、試合の変更、空き枠、スコアの確認、結果カードを送信します。デフォルトは 50 です。0 にすると無効になり、通知はメールまたはプッシュ通知で送信されます。Meta は、24 時間のオープンウィンドウ外に配信した各テンプレートに対して課金します。この設定はコストを抑えるためのものです(docs/OPERATING.md)。
WHATSAPP_WABA_IDいいえWhatsApp Business Account の ID です(電話番号 ID ではありません)。Meta に登録済みのテンプレートを一覧表示し、data/whatsapp-templates.json をもとに不足分を作成するため、scripts/whatsapp-templates.mjs だけが読み取ります。
LINE_CHANNEL_TOKEN / LINE_CHANNEL_SECRETいいえLINE ボットを有効にします。タイのチームのグループチャットにカードを送信します。LINE の Webhook を /api/line/webhook に設定してください。すべての配信は、LINE_CHANNEL_SECRET で署名されていない場合、拒否されます。LINE では送信済みメッセージを編集できないため、プレイヤーが気づくような変更があった場合にのみカードを再送します。また、回答可能な項目には、従量課金のプッシュ通知ではなく、無料の返信機能を使います。
PASSPORT_PRIVATE_KEY / PASSPORT_PUBLIC_KEYいいえプレイヤーパスポートの署名に使う Ed25519 ペアです(それぞれ生の 32 バイトの 16 進数)。設定しない場合、パスポートには alg: "none" が含まれます。
ANTHROPIC_API_KEYいいえ傾聴デスクでの返信案と、オーナーがメモ用に受け取る提案を作成します。
ANTHROPIC_WORKSPACE_IDいいえキー自体にワークスペースが設定されていない場合に使用するワークスペースです。その場合、Messages API の各呼び出しで anthropic-workspace-id ヘッダーとして送信されます。Claude Console の「Settings」から「Workspaces」を開くと確認できます。wrkspc_ で始まる ID です。ワークスペース内で作成したキーの場合は、空のままにしてください。キーにワークスペースが設定されておらず、この項目にも ID がない場合は、無効なキーと同様に拒否されます(docs/OPERATING.md)。
LISTEN_MODELいいえこれらの下書きの作成に使うモデルです。デフォルトは claude-sonnet-5 で、src/lib/ops/anthropic.ts でも価格が設定されています。
ANTHROPIC_MONTHLY_CAP_USDいいえアプリが超過しないようにする上限額です。米ドルで指定します。デフォルトは 20 です。
ANTHROPIC_ADMIN_KEYいいえAdmin API キーです。これにより、サービスボードは独自の推定値ではなく、実際の使用額を読み取ります。
SCRAPE_DISABLEDいいえカンマ区切りのプラットフォーム ID(playtomic,matchi)または all を指定します。対象プラットフォームでは、空いているコート時間を 15 分ごとに読み取る処理が停止します(DECIDING のルール 35)。Vercel の設定は次回のデプロイ時に反映されます。POST /api/admin/metrics で scrape_off_<platform> の行を追加する場合は、デプロイは不要です(docs/OPERATING.md)。空の場合、読み取り機能があり、ブロック後の休止中でないすべてのプラットフォームを読み取ります。
TAVILY_API_KEYいいえリサーチデスクで使用します。都市ごとのクラブとコーチを調べ、回答ページの根拠情報を取得します。
REDDIT_CLIENT_ID / REDDIT_CLIENT_SECRET / REDDIT_USERNAME / REDDIT_PASSWORDいいえ承認済みの返信を、プロジェクトのアカウントから Reddit に投稿できるようにします。設定しない場合、「Approve」はコピーして貼り付ける操作になります。
GOOGLE_SERVICE_ACCOUNT_JSONいいえSearch Console へのアクセス権を持つサービスアカウントです。サービスボードがインプレッション数とクリック数を読み取れるようになります。
INDEXNOW_KEYいいえページが変更された時点で、その変更を Bing と Yandex に通知します。
BACKUP_GITHUB_TOKEN / BACKUP_GITHUB_REPOいいえ毎晩のデータベーススナップショットを、指定したリポジトリにコミットします。
UPTIME_REPOいいえGitHub Actions の稼働状況プローブの owner/repo です。サービスボードが未解決のインシデントを読み取れるようになります。
OPERATOR_VERCEL_TEAM / OPERATOR_VERCEL_PROJECTいいえVercel プロジェクトを指定します。キーが不足している場合、設定画面へのリンクとともに報告できるようになります。
シークレットを生成します:openssl rand -base64 32。上記の表と .env.example は、コード内の process.env の読み取り箇所から node scripts/gen-docs.mjs によって生成されます。また、どちらかが古くなると tests/docs.test.ts が失敗するため、コードが読み取る環境変数がドキュメントに記載されないことはありません。デプロイの状態はいつでも /api/health で確認できます(シークレットは返されません)。

本番環境のセットアップ

手順は docs/DEPLOY.md にあります。Supabase、Vercel、ドメイン、Resend、Cron ジョブについて、ブラウザーだけで進める方法と CLI を使う方法を説明しています。ローカルでアプリを実行する際に必要なものはありません。

npm パッケージ

純粋なエンジンは、信頼できる唯一の情報源となるよう src/lib/domain から生成され、パッケージとして公開されます。

  • @erikv69/americano:americano 全体を生成する buildSchedule({ names | players, courts, rounds, seed }) に加えて、ラウンドごとのプランナー(planRound、planMexicanoRound、planKingRound)、履歴、順位表を提供します。
  • @erikv69/levels:レベル帯、プリセット、範囲に加えて、levelFit、balancedTeams、matchDeltas、tournamentDeltas を提供します。

pnpm packages:build は packages/*/src と packages/*/dist を再生成します(どちらも gitignore 対象です)。tests/packages.test.ts は CI の実行ごとにパッケージをビルドします。リリースするには、packages/<name>/package.json のバージョンを更新してビルドし、そのディレクトリから npm publish を実行します。

コミュニティ

質問、アイデア、「API を使って何か作りました」という報告は GitHub Discussions へどうぞ。不具合は issue で報告してください。Discord サーバーもあり(リンクは /developers にあります)、ボットが 1 時間に 1 回ほど質問に回答します。Telegram ボットと Reddit アカウントも、それぞれの場所で利用者に回答しています。コードとロードマップはここで公開しています。

自分でデプロイする

Kicksmash は、Next.js プロジェクト 1 つと Postgres データベース 1 つで構成される Apache-2.0 のソフトウェアです。クラブ、都市、国で運用できます。docs/DEPLOY.md に、Vercel ボタン、Docker ビルド、手順が記載されています。 上記の環境変数一覧が設定のすべてであり、テストでその状態が維持されるようになっています。

製品に組み込まれたルール

  • マッチ=ちょうど4人です。 トーナメント=主催者が定める定員(4〜64人、4人単位)で行うアメリカーノです。第1ラウンドは4人以上の任意の名前で開始し(アメリカーノとメキシカーノでは、コートに入れなかった人が順番に休み、全員が1回休むまでは誰も2回休みません。キング・オブ・ザ・コートでは、引き続き4人単位の名前が必要です。予約済みで未承諾の名前も人数に含まれ、承諾時に実際のプレイヤーと統合されるプレースホルダープレイヤーが作られます)、まだ空いている枠はすべて締め切られるため、トーナメントの参加者はその時点で参加しているプレイヤーちょうどになります。主催者は当日にラウンドを生成します(パートナーを入れ替え、休憩を公平に割り振り、コートは1〜N面)。コートはプレイヤーに紐づき(4人につき1面)、実際の名前を付けられます。参加者が4人単位なら、スケジュールは厳密に成立します(プレイヤー数−1ラウンドですべてのペアが一度パートナーになり、次のラウンドでは第1ラウンドが再現されます)。スコア入力済みでも最新ラウンドを削除できます(確認あり)。ラウンドが作成されると、参加者は各コートのポイントをすぐに入力できます(開始時刻を待つ必要はありません)。順位表はリアルタイムで更新され、主催者が確定するとロックされます。
  • すばやく作成: クイック選択チップには主催者自身の履歴のみが表示されます(参加者が2人以上いる予約済み席から、よく使う曜日・時間帯を取得し、該当するものが1つになるまで履歴全体を対象にします。次回の該当日時に反映され、デフォルトの日付には最もよく使われる日時が設定されます)。キーボードのGoキーは次のフィールドへ移動するだけで、マッチを作成することはありません。会場は任意(「コート未定」)で、コート欄も任意です(コート名はページ、共有テキスト、メール、カレンダーのタイトルに表示されます)。ランディングページ自体がフォームです。結果が入力されると「もう一度プレイ」が表示され、上にある1つのコントロールで設定した当日・時刻にマッチを複製します(デフォルトでは1週間後)。そのマッチの各プレイヤーには「参加しますか?」メッセージが1回だけ届きます(DECIDING rule 37)。ホーム画面への追加プロンプトを使うと、主催者は個人用リンクへのアプリのようなショートカットを作成できます。
  • 予約情報を貼り付け: プラットフォームのアプリ(Playtomic、MATCHi、The Padel Societyなど)ですでに予約したコートは、リンクまたは共有テキストを貼り付けるとマッチになります。最初のページで「予約済みですか?貼り付けてください」を選ぶと作成フォームが開き、クラブ、日付、時刻、長さ、コート、料金、プレイヤーが入力され、予約情報にない項目や確認が必要な項目が示されます。予約情報の料金がプレイヤー1人あたりと明記されている場合のみ「プレイヤー1人あたりの料金」に入力され、それ以外の料金はコートの合計額として1行で表示され、プレイヤーごとに請求されることはありません。ボットとのプライベートチャットでは、読み取った内容が返され、文面にクラブ、日付、時刻が含まれていれば「マッチを作成」を1回タップするだけでマッチを作成できます。Kicksmashが貼り付けられたリンクを読み取るのは1回だけです。チャットアプリのプレビュー用ロボットと同じ方式です(src/lib/booking/unfurl.ts、DECIDING rule 35)。プラットフォームへのサインインも、予約や支払いも行いません。
  • 支払い状況: マッチに料金が設定されている場合、参加者全員に同じ名前の一覧が表示されます。プレイヤーは「支払いました」をタップし、主催者は受け取ったことをタップして記録できます。主催者はその記録を取り消すこともできます。フラグは1つではなく2つです。「支払いました」と「受け取りました」は、別々の人による異なる申告であり、後者を記録できるのは主催者だけです。Kicksmashを通じてお金がやり取りされることも、何かがアップロードされることもありません。アプリは双方が申告した内容を記録するため、チャットで催促する必要がありません。
  • 満員の場合: イベントごとに、離脱(辞退、削除、招待の辞退)時に自動昇格する待機リスト、または受付の完全締め切りを設定できます。空席ができ、その席を待っている人がいない場合(離脱による空席、または誰も申し込まなかった席)で、マッチまでの時間が90分〜48時間の場合、その席に参加できる人に通知を1回送ります。対象は、その時間と場所でプレイを希望した人、グループの他のメンバー、過去60日以内に参加済みマッチで参加者の誰かとプレイした人、そしてマッチがクラブの掲示板に掲載されている場合は、その会場で過去90日以内にマッチをした人または今後マッチの予定がある人です。各人には、利用できるチャンネルで1回通知します。まずカードから1回タップで「参加します」と返せるプライベートTelegramメッセージを送り、利用できなければマッチリンク付きメール、それも利用できなければプッシュ通知を送ります。1マッチにつき全期間を通して1回のみで、レベル範囲内のプレイヤーに限り、通知が届くチャンネルがある人を対象に最大40人まで送ります。主催者と、直前に離脱した人には送りません。グループに属さないプライベートマッチでは、参加者の過去のパートナーにのみ通知し、それ以外の人には送りません。
  • 参加は先着順で、アトミックに処理されます。変更ごとにイベントの行がロックされ、1つのUPDATE … WHERE id = (SELECT … LIMIT 1)でちょうど1つの枠を確保するため、最後の枠を2人が同時にタップしても適切に処理されます(12件の並列参加でテスト済み)。
  • 予約枠には個人用招待リンクが付与されます。主催者がロースターの空き枠をタップすると、その場で展開され(名前、任意の電話番号/メールアドレス、「完了」)、メールアドレスがある場合は招待がすぐに送信されます(「招待メールを送信しました」)。メールアドレスがない場合、行に「招待はまだ承諾されていません」と表示され、「今すぐ招待」「プレイヤーを削除」を選べます。他の人が空き枠をタップすると、その場で参加します。保留中の名前にメールアドレスも電話番号もない場合でも、通常のマッチリンクからその枠に入れます。参加フォームに名前を入力すると、2回のタップで「Dannyの予約枠を引き継ぐ」が表示されます(DECIDING rule 34)。すでにマッチに参加しているプレイヤーは、他の人の個人用リンクを使えません。ページにはその枠の人が誰かが表示され、サーバーはタップを拒否します。予約したプレイヤーのメールアドレスが別のIDですでに判明している場合、招待ページでは確定前に「以前一緒にプレイしましたか?コードで復元」を提示します。予約フォームに既知のメールアドレスまたは電話番号を入力すると、そのプレイヤーの名前が候補として表示されます。既知のメールアドレスは再入力を求めず、代わりに編集ボタンと、アクティビティメールのオン/オフ切り替えが表示されます(参加・離脱・返信のお知らせ、ラインナップ変更、スコアのリマインダー)。参加したマッチのカレンダー招待は必ず送信されます。時刻変更とキャンセルのお知らせも送信されますが、「マイマッチ」の「お知らせ」でマッチ変更をオフにした場合は送信されません。
  • アクティビティフィードは閲覧者の視点で表現します。「あなたがZedを追加しました」と「ZedがErikによって追加されました」のように表示します。ポップアップは一切ありません。iOSではキーボードが開くとオーバーレイが画面外に移動します。
  • 最新情報を受け取り、カレンダーを自動更新。 チャンネルが設定されていないメンバーが参加した時点で、「最新情報を受け取る」が、どこで通知を受けたいか尋ねます。選択肢はWhatsApp(電話番号が設定されている場合)、Telegram、メールで、プッシュ通知はありません。TelegramとWhatsAppは、すでに使っているアプリを1回タップして開きます(アカウントまたは電話番号とウェブ上のプレイヤーを紐づける署名付きリンク)。メールでは、参加が確定しているプレイヤーに実際の招待メールを送信し(変更やキャンセルに合わせて更新されます)、待機リストの人には個人用リンクを送信します。招待は空席ができたときに有効になります。カードには送信先が表示されます。1つ紐づけると、カードは「Telegramで最新情報を受け取れます ✓」という控えめな1行表示になり、再度尋ねることはありません。WhatsAppを使うプレイヤーには、マッチの変更、空席(「参加します」をタップすると参加)、スコア確認、結果カードが、Meta承認済みの4種類のユーティリティテンプレート(data/whatsapp-templates.json)で送信され、1日あたりWHATSAPP_TEMPLATES_PER_DAY件までです。テンプレートを送れない場合はメール、次にプッシュ通知に切り替えます。単一マッチではGoogle/Appleのボタンは表示しません。これらを使うと、更新できない未連携のコピーが作られるためです。チャットを使うプレイヤーのカレンダーには、代わりに本人のマッチのみを含むフィード(/p/{key}/calendar.ics、30日前から、招待と同じUIDとフィールド、キャンセル済みマッチはキャンセル済みと表示)を使用します。webcal://として提供され、パソコンではGoogle Calendar経由でも利用できます。キーは個人用トークンのハッシュなので、このアドレスから誰かがサインインすることはありません。招待エントリには、短いプライベートイベントリンク(kicksma.sh/p/{12 chars}/{code})を1つだけ記載し、参加者が確定した後はプレイヤー一覧も記載します。フィードエントリには公開マッチリンクを記載します。
  • ラインナップ確定: すべての枠への参加・確認が完了すると、タイトルに- COMPLETE、説明にプレイヤー名を追加してカレンダーエントリを再送します(誰かが離脱すると元に戻ります)。アプリからメッセージを送ることはありません。主催者には、ローカライズ済みの文面があらかじめ入力されたWhatsApp/Telegramの転送ボタンが表示されます。辞退された枠は空席になります。
  • 連絡先一覧: あなたのイベントに参加または招待されたことがある人全員の一覧で、メールアドレス/電話番号は自動的に再利用されます。
  • 会場の記憶: 最後に使用した会場があらかじめ入力され、以前の会場はすべてコンボボックスに表示されます。自由入力したテキストは新しい会場として追加されます。
  • タイムゾーン: Vercelのx-vercel-ip-timezoneをデフォルトとして使用します(取得できない場合はブラウザーのタイムゾーンを使用)。編集でき、UTCで保存されます。
  • スコア: 開始後は参加者なら誰でも入力できます。プレイヤー同士で修正できます。主催者が一度入力または編集するとロックされます(「主催者が確認済み」)。1〜3セットで、任意のチーム割り当てに対応し、結果はマイマッチに勝敗として表示されます。
  • 主催者アクセス=作成者のCookie または10文字の管理リンク(イベントごとのhttpOnly Cookieを設定します)。
  • Telegramボット(@kicksmash_bot)、控えめな設計: パデルのグループチャットに追加すると、チャット内に各マッチのカードを1つだけ表示します。カードにはタイトル、時刻、会場、レベル範囲、レベル付きロースター、残り枠、2つのボタン「参加します」「参加できません」が表示されます。タップすると、ウェブボタンと同じコード経路で参加または離脱します(主催者への通知、カレンダー招待、待機リストへの昇格、Webhook)。カードはその場で編集され、再投稿されることはありません。ボットが新しいメッセージを投稿するのは4回だけです。カード本体(/newでチャットに紐づく作成リンクを発行し、ボットが管理者であるかプライバシーモードがオフなら、貼り付けられたkicksma.shリンクがカードになります。/match CODEは常に使用できます)、短い「ラインナップ確定」通知、開始約1時間前のリマインダー(5分間隔のプッシュCron経由)、主催者が確定した後の結果画像です。チャットごとに英語またはロシア語を使用します(/lang)。タップした人は、名だけでプレイヤーになります。「マイマッチ」でTelegramサインイン(Login Widget)すると、そのアカウントをブラウザー上のIDにリンクまたは統合できます。カードへの返信「+1」または「参加できません」も2つのボタンと同じ動作をし、ボットはメッセージではなくリアクションで応答します。プライバシーモードはオンのままです。ボットが通常のメッセージを読むのは、クルー専用のグループに限ります。グループ管理者が、クルーページに表示されるクルー管理者向けリンク「このクルーのTelegramグループを運営する」を使ってクルーに紐づけたグループで、ボットが管理者であり、その案内がピン留めされている場合です。ここではタップしなくても、「参加」「不参加」「木曜午後7時にRawaiで参加する人は?」(チャットですでに分かっているコート)を英語、ロシア語、スペイン語で利用できます。グループ管理者が/quietを実行すると、ボットは単語に反応しなくなります。ボットが保持するのは名、Telegramユーザー名、参加・不参加の状況、コート、スコアのみで、メッセージの内容は保持しません。モジュール:src/lib/telegram/。
  • Discordボットも同じ方針: HTTPインタラクションのみを使用し(ゲートウェイプロセスなし)、同じサーバーレス関数上で動作します。/newはチャンネルに紐づく作成リンクを発行し、/match CODEはカードを投稿します。2つのボタンで共通の処理を通じて参加・離脱でき、全員に表示されるカードがその場で更新されます。「ラインナップ確定」通知、リマインダー、結果はそれぞれ1回だけ投稿されます。/askはパデルまたはKicksmashに関する質問にその場で回答し、1時間ごとの処理では、ボットが参加しているサーバーの新着メッセージを読み、質問らしいものを残して返信で回答します(独自のコミュニティなのでタップは不要です。共通の日次予算が適用され、返信のたびに回答ページが増えます)。チャンネルごとに/lang en|ruを設定します。モジュール:src/lib/discord/。
  • リスニングデスク(役立つ返信を作成し、勝手には投稿しません): アプリは毎時、パデルの運営方法について質問している人がいる公開フィード(Algolia経由のHacker News、r/padel、RSS経由のReddit検索)を読み、直近1週間の投稿を保持します。簡易判定(パデル+運営に関する意図)を行い、モデルに対してスレッドの言語で、厳格なトーンルールに従った返信を作成するよう求めます。最初に質問へ回答し、誇張せず、質問の解決に役立つ場合に限りkicksma.shに触れ、開発元であることを明かします。返信案はTelegramでオーナーに送られ、1日最大6件の承認/スキップ/編集ボタンが表示されます。承認すると、プロジェクトのアカウントでRedditに投稿します(Redditのキーがなければ手動コピー用として印を付けます)。/admin/listenがデスクです(Telegramサインイン経由でオーナーのみ利用できます)。返信案とトークンの日次上限により、上限付きAPIキーを安全に保ちます。承認済みの返信は、いつまでも役立つ回答ページ(/answers/{slug})になり、質問は一般化して書き換えられ、QAPage JSON-LDを含み、サイトマップに掲載され、すぐに公開されます。日曜のTelegramダイジェストには、その週の一覧と、新しい各ページをワンタップで非公開にするボタンが表示されます。モジュール:src/lib/listen/。
  • フィードバックと決定者: TelegramまたはDiscordの/feedback、ウェブフォーム/feedback、feedback@宛てのメールは、フィードバックとして記録されます。送信者には率直なお礼をすぐに返します(内容を確認したこと、何かを実装した場合に知らせることを伝え、それ以外は約束しません)。実際のフィードバックが届くと、オーナーにはTelegramで提案がすぐに届きます。docs/DECIDING.mdのルールによる判定、変更内容と変更箇所、規模(小・中・大)、コードを読まずに出した所要時間の見積もり、必要なもの、推奨事項を記載し、「実装」または「見送り」で締めくくります。オーナーの承認がない限りフィードバックから何かを実装することはなく、送信者には実際にリリースした内容だけを知らせます。ストアが初めて検出した本番エラーも、最初に確認された時点でオーナーに1行で通知されます。1時間あたり数件までです。フィードバックとエラーがトリガーであり、日次処理はありません。
  • 埋め込みとoEmbed: /embed/board/{slug}と/embed/match/{code}はiframe対応のビューです(ヘッダーなし、新しいタブでkicksma.shを開き、「kicksma.shから配信中」フッターを表示)。会場掲示板には「この掲示板を埋め込む」の下に埋め込み用コードが表示されます。/api/oembed?url=…&format=jsonはoEmbedプロバイダーであり、マッチページと掲示板ページは<link rel="alternate" type="application/json+oembed">でoEmbedを案内します。そのため、WordPress、Discourse、Ghost、Notionではリンクを貼り付けるとライブカードが展開されます。ヘルパー:src/lib/embed.ts。
  • 独自にデプロイ: このアプリはPostgresデータベースを備えた単一のNext.jsプロジェクトです。Dockerfileでスタンドアロンイメージをビルドします。READMEの環境変数テーブルが設定項目のすべてであり、それが守られていることをテストで確認します。すべてApache-2.0です。あなたのクラブ、都市、国で実行してください。
  • トーナメント形式: アメリカーノ方式のローテーションに加えて、メキシカーノ(第1ラウンドはランダム、その後は順位に応じてコートを割り当て、各コートで1位+4位対2位+3位。次のラウンドはすべてのスコア入力を待って開始)と、キング・オブ・ザ・コート(勝者は上のコートへ、敗者は下のコートへ移動し、最上位コートの勝者と最下位コートの敗者はその場に残り、毎ラウンドパートナーを入れ替え、最後にいたコートに応じて順位を付ける)も選べます。トーナメント作成時にチップ1つで選択し、第1ラウンド開始までは変更できます。エンジン:src/lib/domain/formats.ts。
  • 主催者が確認したレベル: 結果を確定すると、主催者に折りたたまれた「レベルを確認」行が表示されます。一緒にプレイした人のレベルを、1人ずつ、または一括でタップして確認します。確認済みのレベルはチップの横に✓が表示され、半段階未満の変動では確認済みのままです。
  • ランキング(オプトイン、デフォルトでオフ): /v/{slug}/rankingでは、クラブの過去90日間の確定済み結果を順位付けします(勝利3ポイント、引き分け1ポイント、トーナメントの表彰台は3/2/1ポイント)。/phuketと/singaporeでは、都市内の全クラブを対象に同様の順位付けを行い、オープンなマッチも一覧表示します。「ランキングに表示」をオンにしたプレイヤーのみが表示されます(「マイマッチ」またはランキングページで1回タップ)。都市一覧:src/lib/domain/cities.ts。
  • レベル(0〜7): プレイヤーは一度だけ、0.25刻みで自分のレベルを申告します(バンドごとにまとめられた通常の選択欄と短いガイド)。レベルはロースター、順位表、スコアパネルで名前の横に表示されます。結果に応じて調整されます。主催者が2対2のマッチスコアを確定するか、トーナメントを確定すると、Elo方式の変動がイベントごとに1回、レベルが設定された各プレイヤーに適用されます(レベル差1つは約10対1の勝算に相当し、マッチあたり最大±0.10、トーナメントあたり最大±0.12)。レベルの情報源は「結果により調整」に変わり、「マイマッチ」には最大件数が設定されたログと最新の変動が表示されます。レベル未設定のプレイヤーは変動せず、計算にも含まれません。
  • レベル範囲: マッチまたはトーナメントには、ブロンズ(1.0〜2.5)、シルバー(2.5〜3.5)、ゴールド(3.0〜4.5)、プラチナ(4.5以上)、または任意の最小〜最大範囲を設定できます。範囲はヒーロー領域にチップとして表示されます。範囲内の人は通常どおり参加します(参加フォームでレベルを一度入力)。範囲外の場合はボタンが参加をリクエストに変わり、主催者にはレベル付きのリクエストが表示され、承認(席への割り当てまたは待機リストへの登録)または拒否できます。リクエストした人には参加バーに回答が表示され、カレンダー招待または短いメールが届きます。主催者が予約した招待と昇格は範囲の制限を受けません。また、主催者(オーナー、2026年10月11日)と一緒に、スコア入力済みの終了したマッチにすでに参加したプレイヤーも制限を受けません。バランスの取れたチーム: 4人全員のレベルが設定されている場合、スコアパネルには差が最小となる2対2のチーム分けが提案されます。
  • 「マイマッチ」の統計欄: プレイ数、勝利数、勝率、表彰台(確定済みのチーム結果と確定済みの順位表から集計)。
  • クラブページと創設クラブ: /clubs/claimから、会場掲示板を運営するクラブが掲示板を申請できます(名称、世界中のどの都市・国でも入力可能、予約ページ、ウェブサイト、コート数、クラブ紹介文)。申請はセルフサービスで、独自の確認手順があります。申請者はクラブでの役割を申告し、業務連絡先を入力します。クラブ独自ドメインの業務用メールアドレスは、その場で6桁のコードにより確認され、それ以外の方法はオーナーが手動で確認します。その後、Telegramを1回タップするとページが公開され、申請者にはTelegramまたはメールで結果が届きます。保留中の申請は申請者以外には一切表示されません。公開後、/v/{slug}には「Playtomic/MATCHi/Playbypointで予約…」と表示されます(リンクからプラットフォームを識別し、そのクラブのすべてのマッチにも表示)。ウェブサイト、紹介文、バッジのほか、クラブがすでに持っているフィードを共有した場合は本日の空きコートも表示されます。共有できるフィードは予約カレンダー(.ics。営業時間内で予約が重なる時間を差し引いたコート数を空きとして計算)または空き時間のJSONリストで、1時間ごとに更新されます。クラブがフィードを共有しておらず、予約リンク(ない場合はウェブサイト)がKicksmashに読み取り機能のあるプラットフォーム(Playtomic、MATCHi、Book & Go)にある場合、Kicksmashはそのプラットフォームの公開クラブページから3日先までの空きコート時間を読み取ります。読み取り可能なすべてのクラブについて、当日は15分ごと、次の2日間は約1時間ごとに読み取ります。ページとAPIにはプラットフォーム名が表示されます(DECIDING rule 35:サインイン、予約、支払いは行いません。プレイヤーはプラットフォーム上で予約・支払いをします。KicksmashBotが読み取る内容は/aboutに記載されています)。クラブが共有するフィードが常に優先されます。2〜6時間後に空きコートがある場合、そのクラブでその日、その時間帯にプレイを希望したプレイヤーに、利用できるチャンネルで1回通知します。ボタンを1つタップすると、そのクラブと時間が入力されたマッチフォームが開きます(希望1件につき1日1コート)。ディレクトリに掲載されたクラブの申請が拒否された場合、拒否理由が「クラブではない」または「重複」以外なら、その掲載内容のまま新しい管理リンクで再掲載されます。各都市の最初の10クラブは創設クラブとなり、今後もすべて無料で利用できます。/clubsには公開中のクラブが都市別に一覧表示されます。GET /api/v1/clubs?city=phuketとGET /api/v1/clubs/{slug}から同じ情報を公開し、MCPツールはfind_clubsです。クラブは個人用リンク(「マイマッチ」にも表示)からページを管理します。モジュール:src/lib/domain/clubs.ts、src/lib/booking/(プラットフォームの検出、クラブのフィード、scrape.tsの15分間隔の読み取り、adapters/内のプラットフォームごとの読み取り機能)。
  • プレイヤーパスポート(オプトイン、デフォルトでオフ): プレイヤーは「マイマッチ」から/u/{slug}の公開ページをオンにできます(名、バンドと主催者確認済みマーク付きのレベル、プレイ数/勝利数/勝率/表彰台、プレイするクラブを掲載)。また、署名付きレベル文書/u/{slug}/passport.jsonを利用できます。正規化JSONに対するEd25519署名で、公開鍵は/.well-known/kicksmash-passport.json、@erikv69/levelsのverifyPassportで検証でき、有効期限は90日です。プロフィール一覧はありません。自分のデータをダウンロード(/api/me/export)すると、保有するすべての情報を含むJSONファイルが1つ生成されます。ただし、トークン、管理リンク、パスキーの認証情報IDと公開鍵は含まれません(電話番号にはラベルと日付が表示されます)。レベルのインポート: レベル選択欄を開くと「別のアプリにレベルがありますか?」が折りたたまれて表示され、Playtomic(同じ0〜7)、1〜10のクラブ向け尺度、5段階のシステムを、変換方法を公開したうえで0〜7に対応付けます。対応表は/levelsにあります。モジュール:src/lib/domain/passport.ts(純粋なWebCrypto)、src/lib/domain/profile.ts。
  • 結果カード: マッチにスコアが入力されるか、トーナメントのスコア付きラウンドが作成されると、「結果を共有」から/{code}/cardが開きます。WhatsAppとTelegramでは、生成画像付きでリンクが展開されるページです(勝者側を強調表示したスコア欄、または順位表の上位5人)。画像は長押しして保存でき、共有ボタンと、次の操作につながる「自分のマッチを企画する」も表示されます。
  • グループ: プレイヤーが2人以上いるマッチで「このクルーをグループにする」を選ぶと、マッチの設定(会場、形式、定員、レベル範囲、タイムゾーン)をデフォルトとして引き継ぎ、マッチのプレイヤーをメンバーとするグループ(/g/{6 chars})が作成されます。タップした人が管理者になります。リンクを知っている人は誰でも参加できます(名前のみ)。グループのマッチに参加するか、招待を承諾すると、その人もグループメンバーになります。グループページのメンバー一覧、GET /api/v1/groups/{code}、MCPツールget_groupでは、閲覧者にメンバーの名と人数が表示されますが、レベルは表示されません(src/lib/domain/groupAccess.tsのcanSeeMemberLevels)。マッチのロースターには、他のすべてのマッチページと同様に、そのマッチで席を確保したプレイヤーのレベルが表示されます。管理者は参加リクエストをオンにできます(オーナーの決定E、2026年10月9日)。新規参加者は任意のメモを添えてリクエストでき、管理者にはTelegram、メール、またはプッシュ通知が届きます。リクエストはグループページにも表示され、管理者は承認または拒否できます。本人にも結果が届きます。拒否された人は7日後に再度リクエストできます。このようなグループでは、グループのマッチの席に座ってもメンバーにはなりません(joinGroupはviaとしてself、match、adminを受け取ります)。すべてのメンバーがグループページから次のマッチを作成できます。あらかじめ入力された作成フォーム(次の週次枠を含む)が開き、マッチはグループに紐づけられます。他の全メンバーには個人用リンク付きのメールとプッシュ通知が届きます。管理者は週次枠を設定できます。毎時のCronが数日前に次のマッチを作成し(先行日数は1〜14日、デフォルトは5日)、グループの作成者の席を確保し、全員に通知します。同じ開催回が2回作成されることはありません。グループのマッチの下には、過去90日間にスコア入力済みのマッチが2件以上ある場合、小さなシーズン順位表が表示されます。プレイした各メンバーを名で表示し、プレイ数、勝利数、連勝数(3連勝から🔥)を記載します。勝利数の多い順です。「マイマッチ」には、参加しているグループと、それぞれの次回マッチが表示されます。
  • 会場掲示板(オプトイン、デフォルトでオフ): 主催者は、会場が設定されたマッチの作成時または編集時に「会場掲示板に表示」を選択できます。掲載対象の今後のマッチ(キャンセルされていないもの)が/v/{venue-slug}に表示されます(会場名の公開ページで、残り枠とレベル範囲を表示)。マッチには「…の掲示板に掲載中」チップが表示されます。/v/{slug}/posterは掲示板へのQRコード付きの1ページ印刷用ポスターです(「…で開催される参加者募集中のパデルマッチをスキャン」)。掲示板が空の場合の表示とフッターからは、会場があらかじめ入力され、掲載がオンになった作成フォームへ移動できます。スラッグはASCII文字のみで、会場名を変更すると同期して更新されます。会場を削除するとマッチは掲示板から外れます。
  • アメリカーノ生成ツール: /americanoは公開され、インデックス登録可能なページです。ブラウザー上で同じローテーションエンジンを実行し、プレイヤー(または貼り付けた名前)、コート、ラウンドを設定できます。参加者が4人単位の場合は正確なローテーション、それ以外の場合は公平な休憩になります。印刷用スタイルシートを備えています。「Kicksmashで実際に運営」を選ぶと、スケジュールを本物のトーナメントに引き継げます。名前も引き継がれ(/?type=tournament&capacity=N&s=gen&names=…)、「あなたのアメリカーノを実際に開催」の下に作成フォームが開いて、引き継いだプレイヤー名が表示されます。トーナメントが作成された時点で、各プレイヤーには招待リンク付きの予約枠が作られます(seatNames)。貼り付けたリストは64人までで、エンジンが扱える上限です。どちらの生成ページにもフィードバックへのリンクがあります。sitemap.xmlには、すべての言語で/、/play、/americano、/about、/privacy、/termsが、都市、クラブ、コーチ、シリーズ、回答ページとともに掲載されます。robots.txtではクローラーのアクセスを許可し、個人用ページ、管理ページ、招待ページ、共有ページ、カードページはインデックス対象外にします。

API、MCP サーバー、Webhook

サイト上で公開されているすべての情報はプログラムやアシスタントから利用でき、作成フォームで行うすべての操作は、1 回の呼び出しで実行できます。

項目場所
REST 読み取り(キー不要)GET /api/v1/matches/{code}、/api/v1/boards/{slug}、/api/v1/groups/{code}、/api/v1/schedule?players=8&courts=2
REST 書き込み(キーは任意。キーなしの場合はアドレスごとにレート制限)POST /api/v1/matches、POST /api/v1/matches/{code}/join
キーPOST /api/v1/keys → 即時発行され、表示されるのは 1 回のみです。Authorization: Bearer ks_live_…
Webhook(キー必須)POST /api/v1/webhooks に url、events、任意の filter を指定します。X-Kicksmash-Signature: t=…,v1=… で署名され、毎時の cron によってバックオフ付きで再試行されます。
MCPPOST /mcp(ストリーム可能な HTTP、ステートレス JSON)。試合とグループ:about_kicksmash、get_match、find_matches、get_group、generate_schedule、create_match、join_match、create_api_key。クラブとシリーズ:find_clubs、find_series。コーチング:find_coaches、coach_slots、request_coach、book_lesson、cancel_lesson。モデルリファレンスと OpenAPI ドキュメントを含むリソースもあります。tests/docs.test.ts は、この行の内容が src/lib/api/mcp.ts より遅れている場合に失敗します。
ディスカバリー/llms.txt、/llms-full.txt、/.well-known/mcp.json、/api/openapi.json、/developers、/agents、robots.txt では AI クローラーが明示的に許可されています。
フィード/g/{code}/calendar.ics、/v/{slug}/calendar.ics、プレイヤー本人専用の非公開フィード /p/{key}/calendar.ics

公開データの形式(src/lib/api/serialize.ts)に含まれるのは名とレベルのみです。メールアドレス、電話番号、個人用トークン、管理リンクは一切含まれません。エラーは { error: { code, message, hint, status } } という形式で、すべてのヒントに次に行うべき操作が記載されています。制限は src/lib/domain/ratelimit.ts に定義されています。

レジストリとマーケットプレイス

リポジトリのルートにある server.json は、公式 MCP レジストリ(名前空間 io.github.evhg)のマニフェストです。リポジトリの所有者は、1 つのコマンドで公開できます:mcp-publisher login github && mcp-publisher publish。公開リポジトリのスキルインデックスが skills/kicksmash/SKILL.md を取り込みます。Smithery、Glama、mcp.so では、それぞれの Web フォームからリモート URL https://kicksma.sh/mcp を登録できます。

管理ダッシュボード

/admin は、一般公開されている読み取り専用の利用状況ページです。正常性の行(データベース、メール、プッシュ通知、両方の cron、本日のエラー)を含むヒーロー表示、スパークライン付きの統計タイル、無料プランの上限に対するメーター(Supabase 500 MB、Resend 3,000/月および 100/日)、7 日/30 日/90 日の傾向グラフ(増加数、参加数、送信メッセージ、エラー、データベースサイズ、合計)が表示されます。カウンターは metrics_daily に保存されます(アプリが値を加算し、毎時の cron がスナップショットを記録します)。個人データは表示されません。エラーは件数のみを記録し、内容は記述しません。失敗するサーバーアクション、失敗する cron の各ステップ、ブラウザーのクラッシュ画面(レート制限付きの /api/client-error)がそれぞれカウンターを加算するため、ここにある唯一のアラートは正常性の行です。Hobby プランでは Vercel の帯域幅に関する公開 API がないため、ダッシュボードへのリンクを表示します。

プロジェクト構成

src/app/                 routes: / · /[code] · /[code]/share · /[code]/card (+ opengraph-image) · /[code]/i/[invite] · /[code]/manage/[manage] · /g/[code] (+ calendar.ics) · /v/[slug] (+ /poster, /ranking, calendar.ics) · /phuket · /singapore · /clubs (+ /claim) · /v/[slug]/manage/[token] · /api/v1/clubs (+ /[slug]) · /u/[slug] (+ passport.json) · /api/me/export · /.well-known/kicksmash-passport.json · /api/telegram/{webhook,setup,login} · /api/discord/{interactions,setup} · /admin/listen · /answers (+ /[slug]) · /embed/{board,match}/… · /api/oembed · /me · /p/[token] (+ /calendar, calendar.ics) · /about · /privacy · /terms · /play · /americano · /developers · /agents · /mcp · /api/v1/* · /api/openapi.json · /llms.txt · /.well-known/mcp.json · /unsubscribe · /admin · /api/cron/{hourly,push} · /api/client-error · robots.txt · sitemap.xml
src/app/[code]/opengraph-image.tsx   link preview (Inter w/ Cyrillic, organizer's language)
src/actions/             server actions (identity incl. restore codes, events, slots, scores)
src/lib/domain/          pure business logic, driver-agnostic (events, slots, scores, reminders, queries)
src/lib/notify.ts        every outbound email; safe no-op without RESEND_API_KEY
src/lib/calendar.ts      Google Calendar URL + RFC 5545 .ics builder (also served at /{code}/calendar.ics); inviteFields, what every entry says
src/lib/calendarFeed.ts  a player's own feed: its key, its matches, its .ics
src/lib/domain/identity.ts personal tokens, email one-time codes, identity merge
src/lib/domain/{ratelimit,optouts,anonymize}.ts   abuse ceilings, unsubscribe list, account deletion
src/lib/domain/{levels,requests,rating}.ts   level maths (ranges, presets, balanced teams, deltas), join requests, result-based adjustment
src/lib/domain/groups.ts   groups, membership (joinGroup with via), asks to join, weekly slots (recurrenceDue / autoCreateGroupMatches)
src/lib/domain/groupAccess.ts   pure: who sees members' levels, what a join does, when a declined person may ask again
src/lib/domain/venueBoard.ts   venue slugs, the public board query, listing toggle
src/lib/api/               public REST (operations, serialize, keys, webhooks, openapi), MCP server (mcp.ts), model-facing docs (docs.ts)
src/lib/channels/          one card algorithm (cards.ts) with Telegram and Discord as adapters over it
src/lib/domain/facts.ts    the append-only fact log every view is a query over
skills/kicksmash/SKILL.md  installable skill for coding agents; AGENTS.md at the root for agents working on this repo
src/lib/domain/{levels,rating,requests}.ts       level scale, presets, fit, balanced teams, Elo-style deltas; join requests
src/lib/alerts.ts        error counters for the admin health row
src/db/schema/           the tables, one file per domain (enums, players, events, demand, groups, clubs, coaching, channels, api, ops)
src/db/                  driver factory (postgres-js | PGlite), seed
drizzle/                 generated SQL migrations
messages/{en,ru,es}.json all UI, share and email copy (identical key sets, typed in global.d.ts)
tests/                   vitest against PGlite (or TEST_DATABASE_URL)
e2e/                     Playwright journeys + runner (pnpm e2e)
.github/workflows/ci.yml typecheck · lint · schema vs migrations · vitest (PGlite + Postgres) · build · e2e (2 shards)
scripts/                 gate.sh (the pre-push gate), check-migrations.sh, gen-docs.mjs (this README's env table)

スキーマの変更:該当ドメインのファイルを src/db/schema/ で編集 → pnpm db:generate → drizzle/ をコミット → 変更が main に反映されると Migrate ワークフローが適用します(AGENTS.md ルール 7)。bash scripts/check-migrations.sh は、2 つの内容が一致しない場合に失敗します。

コメント

MCP は初めてですか?

プロトコルの概要から最初のサーバーの導入まで、よくある疑問にまとめて答えます。

AI アプリケーションが外部のツールやデータソースに統一的な方法で接続するためのオープンなプロトコルです。クライアントはモデルとの対話を担い、MCP サーバーはツール・リソース・プロンプトテンプレートを提供します。標準メッセージでやり取りするため、1 つのサーバーを複数のクライアントで再利用できます。