Appearance
おすすめ台担当 MCP(Cursor)
ホールの台データ(差枚・G 数)と機種別・末尾の集計、カレンダー(〇〇の日・イベント・入替)を読んでおすすめ台の案を作り、下書きとして保存するための MCP です。WordPress の REST API recommend/v1 を、おすすめ台担当ロールのユーザーのアプリケーションパスワードで呼びます。編集用・取込用・運用確認用・X告知用・管理者のアカウントは渡しません。
関連: Issue #3900 / 設計 API-009 おすすめ台 REST / ADM-035 権限管理画面 / ADM-037 おすすめ案承認
できること・できないこと
| 操作 | おすすめ台担当 |
|---|---|
| 台データ(日付・台番号・機種 ID・機種名・差枚・G 数)の取得。期間指定・1 台の履歴・1 か月ずつの一括取得 | できる |
| 機種別サマリ・末尾集計・カレンダー(曜日・〇〇の日・イベント・入替)の取得 | できる |
おすすめ案(ホール×対象日、最大 20 台)の下書き保存・上書き(draft の間だけ)と、保存済みの案・対象日の結果の取得 | できる |
| おすすめ案の承認・却下 | できない |
| 記事への反映・公開 | できない |
| BB / RB の取得 | できない |
案の下書き保存以外の書き込み(記事・取込・新台情報・自分のプロフィールやアプリケーションパスワードの変更を含む。アプリケーションパスワード経由の REST(/wp/v2/* を含む)・XML-RPC と wp-admin) | できない |
| wp-admin を開く(ダッシュボード・記事一覧・プロフィールを含む) | できない |
案の承認・却下は、管理者が管理画面の「サイト運営 > おすすめ案承認」(ADM-037、manage_options)で行います。承認・却下済みの案は bot から上書きできません(409)。
おすすめ台担当ロール(recommend_picker)が持つのは WordPress の read と read_recommend_data・write_recommend_draft だけです。wp-admin を開くとサイトトップへリダイレクトされます。
WordPress は read だけのユーザーにも自分のプロフィール編集とアプリケーションパスワードの発行を許すため、recommend_picker を持ち、他には REST 用 bot ロールしか持たないユーザーには BotWriteGuardHooks で次の制限をかけています(詳細は API-009 の「WordPress コア経由の書き込みの遮断」)。
- REST は GET / HEAD / OPTIONS と POST
/recommend/v1/drafts以外を 403(/wp/v2/postsの作成、/wp/v2/users/meの更新、アプリケーションパスワードの発行・削除を含む)。他の bot ロールも持たせた場合は、そのロールに許された書き込みも通る - 自分自身に対する
edit_userとアプリケーションパスワードの発行・編集・削除の権限を外す(管理者による取り消しはできる) - XML-RPC にはログインできない
この制限がかかるのは recommend_picker と他の REST 用 bot ロールだけを持つユーザーです。それ以外のロール(購読者・編集者など)を足すと制限が外れるため、兼用しないでください。
ログインパスワードには注意してください。 上の制限はアプリケーションパスワードでのアクセスと wp-admin が対象です。ログインパスワードで wp-login からブラウザにログインすると、フロントの会員プロフィール・退会フォームやコメント投稿などは使えてしまいます。おすすめ台担当ユーザーのログインパスワードは長いランダムな値にし、どこにも保存せず使わないでください。bot にはアプリケーションパスワードだけを渡します。
運用手順(WordPress 側)
bot が叩く WordPress(通常は本番)で、管理者が次の作業をします。
1. おすすめ台担当ユーザーを作る
- 管理画面 → ユーザー → 新規追加
- ユーザー名は用途が分かる名前にする(例:
recommend-bot)。メールアドレスは管理者が受け取れるものにする - 権限グループで「おすすめ台担当」を選んで追加する
- パスワードは長いランダムな値を生成したまま、控えずに追加する(ログインパスワードは使わない。bot にはアプリケーションパスワードだけを渡す)
bot ごと・用途ごとにアカウントを分けてください。 運用確認bot・X告知担当などのアカウントに read_recommend_data / write_recommend_draft を足して兼用しないこと。おすすめ台担当ロールの Capability は ADM-035 権限管理画面 で確認できます(既定は「おすすめ台用データの取得」「おすすめ案の下書き保存」のみ)。
2. アプリケーションパスワードを発行する
おすすめ台担当ユーザーは wp-admin を開けないため、管理者が発行します。
- 管理者でログイン → 管理画面 → ユーザー → おすすめ台担当ユーザーの編集画面
- 「アプリケーションパスワード」で名前(例:
cursor-recommend-tools)を入れて追加する - 表示されたパスワード(
xxxx xxxx xxxx xxxx xxxx xxxx)を控える。この画面を閉じると二度と表示されません
アプリケーションパスワードは HTTPS のサイトでのみ使えます(ローカルの HTTP では wp-config.php に define( 'WP_ENVIRONMENT_TYPE', 'local' ); が必要)。
3. 本番で権限を確かめる
ユーザーを作ったら、そのアプリケーションパスワードで次の 2 つを確かめます(マージ後に人間が行う)。
bash
SITE=https://example.com
AUTH='recommend-bot:xxxx xxxx xxxx xxxx xxxx xxxx'
# 200 になる
curl -sS -o /dev/null -w '%{http_code}\n' -u "$AUTH" "$SITE/wp-json/recommend/v1/coverage"
# 403 になる(コア REST での記事作成は拒否される)
curl -sS -o /dev/null -w '%{http_code}\n' -u "$AUTH" -H 'Content-Type: application/json' \
-d '{"title":"test","status":"draft"}' "$SITE/wp-json/wp/v2/posts"4. 失効させる
bot をやめるときは、管理者が同じ画面で該当のアプリケーションパスワードを「取り消す」。
5. 漏れた疑いがあるとき
漏れたパスワードでできるのは、台データ・集計の読み取りとおすすめ案の下書き保存だけです(記事は変えられず、案も承認しなければ使われません)。それでも念のため、次のどちらかを行ってください。
- アプリケーションパスワードをすべて取り消し、ログインパスワードもリセットする
- おすすめ台担当ユーザーを削除して作り直す(確実)
偽の案が保存された可能性がある場合は、管理者が ADM-037 で draft の案を確認し、不要なら却下します。
設定(Cursor 側)
.cursor/mcp.json.exampleを.cursor/mcp.jsonにコピーする(.cursor/mcp.jsonは gitignore。パスワードをコミットしない)recommend-toolsのenvを書き換える
| 変数 | 必須 | 説明 |
|---|---|---|
WP_SITE_URL | はい | WordPress のサイト URL。https:// 必須(http:// は localhost / 127.0.0.1 / [::1] / *.local / *.test のみ)。リダイレクトは追わないので、リダイレクト先の URL(www の有無も含む)を書く |
RECOMMEND_WP_USERNAME | はい | おすすめ台担当ユーザーのユーザー名 |
RECOMMEND_WP_APP_PASSWORD | はい | おすすめ台担当ユーザーのアプリケーションパスワード(スペース込みで可) |
RECOMMEND_EXPORT_DIR | いいえ | recommend_export が NDJSON を書き出すディレクトリ。省略時は OS の一時ディレクトリ配下の recommend-export。相対パスは MCP サーバーの起動ディレクトリ基準で絶対パスにする。ディレクトリは 0700、ファイルは 0600 で作る。既存のディレクトリは自分の所有で 0700(他ユーザーに権限なし)でなければ書き出さない(Windows では権限の確認を省く) |
他の MCP(OPS_STATUS_WP_*・X_ANNOUNCE_WP_* 等)とは別の変数名です。資格情報を混ぜないでください。
json
{
"mcpServers": {
"recommend-tools": {
"command": "node",
"args": ["scripts/mcp-recommend-tools.mjs"],
"env": {
"WP_SITE_URL": "https://example.com",
"RECOMMEND_WP_USERNAME": "recommend-bot",
"RECOMMEND_WP_APP_PASSWORD": "xxxx xxxx xxxx xxxx xxxx xxxx"
}
}
}
}設定変更後は Cursor を再読み込みし、MCP recommend-tools が有効になっていることを確認します。リポジトリで npm install 済み(@modelcontextprotocol/sdk)であることが前提です。
利用可能な Tool
| MCP Tool name | 主な引数 | REST |
|---|---|---|
recommend_coverage | 任意 hall、任意 days(1〜92) | GET /recommend/v1/coverage |
recommend_units | hall, date_from, date_to(最大 31 日)、任意 page, per_page(≤2000) | GET /recommend/v1/units |
recommend_unit_history | hall, dainum、任意 days(1〜180) | GET /recommend/v1/units/{dainum}/history |
recommend_export | hall, year、任意 cursor(YYYY-MM)、任意 since(Y-m-d) | GET /recommend/v1/export |
recommend_kishu_daily | hall, date_from, date_to(最大 92 日)、任意 kishu_ids(整数配列 ≤50) | GET /recommend/v1/kishu-daily |
recommend_end_number | hall, date_from, date_to(最大 31 日) | GET /recommend/v1/end-number |
recommend_calendar | hall, date_from, date_to(最大 92 日、先の日付も可) | GET /recommend/v1/calendar |
recommend_drafts_get | hall, target_date | GET /recommend/v1/drafts |
recommend_draft_save | hall, target_date, method_version, items(1〜20 件)、任意 note | POST /recommend/v1/drafts |
戻り値は REST のレスポンス JSON そのままです。ただし recommend_export だけは、1 か月分の NDJSON(数 MB になり得る)を RECOMMEND_EXPORT_DIR の {hall}-{YYYY-MM}.ndjson(since の月は {hall}-{YYYY-MM}-since-{Y-m-d}.ndjson。その後の月は月全体なので通常の名前)に書き出し、応答では ndjson の代わりにファイルのパス file を返します(count・month・next_cursor はそのまま)。同じホール・月を取り直すと上書きします。-since- 付きのファイルは since ごとに別名で増えるので、手元のデータに取り込んだら消してください。タイムアウトは recommend_export だけ 180 秒、他は 30 秒です。HTTP エラー・通信失敗・設定不足のときは isError: true で {"error":"...","status":403,"data":{...}} のように返します。429 のときは Retry-After ヘッダーの秒数を retry_after に入れます。入出力とエラー条件の正本は API-009 です。
案を作る流れ
recommend_coverageで、ホールの台データが最新日まで揃っているかを確かめる(missing_datesには休業日も入る)- 過去のデータを手元で分析するときは、
recommend_exportをnext_cursorがnullになるまで呼び、書き出された NDJSON を読む。1 分あたり 20 回までで、429 になったらretry_after秒待つ。毎日の更新は月を丸ごと取り直さず、sinceを渡して差分だけ取る(月をまたぐときはnext_cursorと同じsinceを渡して続ける)。/exportは今日までを返すため、取り込み途中の日の行とsamai_stepは途中の値のことがある。sinceは前回取り込んだ日の翌日ではなく、前回の取得時点でrecommend_coverageのlatest_dayだった日の翌日(取り込みが終わっていた日の翌日)にし、それより後の日(部分的に取り込まれていた日)は次回もう一度取り直して上書きする - 直近の傾向は
recommend_units・recommend_kishu_daily(必要な機種だけkishu_idsで絞る)・recommend_end_number・recommend_calendarで取る。気になる台はrecommend_unit_historyで入れ替え(kishu_changed)がないかを見る。台データの各行のsamai_step(150/50/1)はその日のホールの差枚の刻みで、50や150の日の差枚は丸めた値なので、刻みの違う期間をまたいで差枚を比べるときに使う - 案を
recommend_draft_saveで保存する。台番号と機種 ID はホールの最新取込日のデータと一致している必要がある(入れ替え前の台番号は 400)。method_versionには選定ロジックの版を入れ、ロジックを変えたら上げる - 同じホール・対象日を作り直すときは、そのまま
recommend_draft_saveを呼べば上書きされる(result: updated、revisionが 1 増える)。409(承認済み・却下済み)なら作り直さない - 対象日のあとで
recommend_drafts_getを呼ぶと、各台にresult(kishu_id・samai・g)が付く。result.kishu_idが案と違えば台が入れ替わっているので、成績の評価から外す
1 ユーザーあたり GET は 1 分 120 回、recommend_export は 1 分 20 回、保存は 1 分 20 回までです(最初の呼び出しから 60 秒の固定ウィンドウ)。超えると 429 になり、Retry-After(秒)が付きます。数えた呼び出しには X-RateLimit-Limit・X-RateLimit-Remaining も付きます。
REST の例(curl)
bash
SITE=https://example.com
AUTH='recommend-bot:xxxx xxxx xxxx xxxx xxxx xxxx'
# 台データが揃っているか
curl -sS -u "$AUTH" "$SITE/wp-json/recommend/v1/coverage?hall=island&days=14"
# 期間内の台データ(1 ページ 500 件)
curl -sS -u "$AUTH" "$SITE/wp-json/recommend/v1/units?hall=island&date_from=2026-09-01&date_to=2026-09-30"
# 案の保存
curl -sS -u "$AUTH" -H 'Content-Type: application/json' \
-d '{"hall":"island","target_date":"2026-10-05","method_version":"v1","items":[{"rank":1,"dainum":"1001","kishu_id":11,"score":0.9,"reason_code":"trend","reason_text":"直近 7 日の平均差枚が上昇"}]}' \
"$SITE/wp-json/recommend/v1/drafts"トラブルシューティング
| 症状 | 確認 |
|---|---|
WP_SITE_URL / RECOMMEND_WP_USERNAME / ... を設定 | .cursor/mcp.json の recommend-tools.env |
HTTP 401(incorrect_password / invalid_username) | ユーザー名・アプリケーションパスワードの誤り、取り消し済み、HTTP サイトでの利用 |
HTTP 403(rest_forbidden、権限がありません) | 認証ヘッダーが PHP に届いていない、または権限グループが「おすすめ台担当」でない・ADM-035 で「おすすめ台用データの取得」「おすすめ案の下書き保存」が外されている |
| HTTP 400(台番号・機種が最新取込日と一致しない) | recommend_units で最新取込日の台番号と機種 ID を取り直して案を作る |
| HTTP 409(案が承認済み・却下済み / 台データが 1 日も無い) | 承認・却下済みの案は上書きできない。台データが無いホールは取込を待つ |
| HTTP 429 | GET 1 分 120 回・export 1 分 20 回・保存 1 分 20 回を超えた。retry_after(Retry-After)秒待つ |
| HTTP 503 | DB の読み書きの一時的な失敗。時間をおいて同じ内容で呼び直す |
NDJSON の書き出しに失敗しました | RECOMMEND_EXPORT_DIR に書き込めるか。symlink は不可。自分の所有で chmod 700 の実ディレクトリを指定する |
リダイレクトされました | WP_SITE_URL を location の URL(https / www の有無)に合わせる |
HTTP 404(rest_no_route) | テーマが本機能を含むバージョンか。パーマリンク設定が「基本」だと /wp-json/ が使えない場合がある |
JSON 以外の応答 | WAF・メンテナンス画面・PHP エラー。body の先頭を確認 |
403 の切り分けは 新台情報担当 MCP の「403 の切り分け」 と同じ手順(/wp/v2/users/me で認証できているかを見る)です。
実装メモ
- 本体:
scripts/mcp-recommend-tools.mjs(テスト:npm run test:mcp-recommend。npm testにも含まれる) - 上限値(案の最大 20 台、
kishu_idsの最大 50 件)はRecommendationDraftConstants::MAX_ITEMS・RecommendSummaryRestController::KISHU_IDS_MAXと同期する - 期間の上限・案の中身の詳しい検証・権限判定・レート制限は REST 側(
RecommendRestHandler/WordPressRecommendPermissionChecker)。MCP 側では形式だけを確かめる