Skip to content

日別記事編集 MCP(Cursor) ​

日別記事の考察日・ホール・イベント・〇〇の日・ホール別考察・絵文字を、管理画面を開かずに Cursor エージェント(日別記事編集bot)から読み書きするための MCP です。WordPress の REST API daily-article/v1 を、権限を絞った日別記事編集bot ユーザーのアプリケーションパスワードで呼びます。管理者アカウントは渡しません。

関連: Issue #3872 / #3995(誕生日データ) / 設計 API-001-19 日別記事bot編集 REST / API-001-5 〇〇の日オプション / API-001-8 日付ホール別機種名 / ADM-035 権限管理画面

できること・できないこと ​

操作日別記事編集bot管理者
考察日の範囲で日別記事を一覧・1 件の編集項目を取得できるできる
考察日・ホール・イベント・〇〇の日・ホール別考察・絵文字の更新できるできる
イベント候補の取得、イベントマスタへの新規登録できるできる
下書き・承認待ちの日別記事をゴミ箱へ移すできるできる
前後 N 日の誕生日・紐付いた機種・ホール別の台数(去年の結果)を読むできるできる
声優名・キャラ名で誕生日を検索するできるできる
誕生日マスタ・作品名と機種の紐付けの編集できない(誕生日紐付け担当の birthday/v1)できる
本文・タイトル・ステータス・公開日時の変更、公開済み記事の削除できないできる
イベントの改名・削除、有料記事・機種マスタ・ほかの投稿タイプの編集できないできる

タイトルは考察日から自動で作り直されます(管理画面で保存したときと同じ)。保存後のキャッシュ無効化・考察テーブルの同期・絵文字テーブルの同期も管理画面保存と同じく走ります。

運用手順(WordPress 側) ​

bot が叩く WordPress(通常は本番)で、管理者が次の作業をします。

1. 日別記事編集bot ユーザーを作る ​

  1. 管理画面 → ユーザー → 新規追加
  2. ユーザー名は用途が分かる名前にする(例: daily-article-bot)。メールアドレスは管理者が受け取れるものにする
  3. 権限グループで「日別記事編集bot」(daily_article_editor_bot)を選んで追加する

日別記事編集bot ロールの Capability は ADM-035 権限管理画面 の「日別記事」で確認・変更できます。ゴミ箱への移動は trash_daily_article_drafts(既定で日別記事編集bot に付与)で、外すとゴミ箱移動だけ 403 になります。

2. アプリケーションパスワードを発行する ​

  1. 管理画面 → ユーザー → 日別記事編集bot ユーザーの編集画面
  2. 「アプリケーションパスワード」で名前(例: cursor-daily-article-tools)を入れて追加する
  3. 表示されたパスワード(xxxx xxxx xxxx xxxx xxxx xxxx)を控える。この画面を閉じると二度と表示されません

アプリケーションパスワードは HTTPS のサイトでのみ使えます(ローカルの HTTP では wp-config.php に define( 'WP_ENVIRONMENT_TYPE', 'local' ); が必要)。

3. 失効させる ​

bot をやめるときは、同じ画面で該当のアプリケーションパスワードを「取り消す」。

4. パスワードが漏れた疑いがあるとき ​

取り消しだけでは足りません。WordPress では本人のプロフィール編集(メールアドレス・ログインパスワードの変更)とアプリケーションパスワードの追加発行ができるため、漏れた資格情報で日別記事編集bot ユーザーが乗っ取られている可能性があります。

  1. 日別記事編集bot ユーザーのアプリケーションパスワードをすべて取り消す
  2. メールアドレスを確認したうえでログインパスワードをリセットする。確実にするなら、日別記事編集bot ユーザーを削除して作り直す(記事の作成者を別ユーザーに移す)
  3. サーバーの PHP エラーログで [bot-rest-audit] の行を確認する。いつ・どのユーザーが・どの記事のどの項目を変えたか(変更前後の文字数)、どの記事をゴミ箱へ移したか、どのイベントを登録したかが残る
  4. 心当たりのない変更があれば、該当記事のリビジョンや管理画面で内容を戻す。ゴミ箱へ移された記事はゴミ箱から復元する。不要なイベントはイベントマスタ管理画面で削除する

管理者権限への昇格はできないため、影響は上の表の「日別記事編集bot」の操作と、日別記事編集bot ユーザー自身のプロフィールに限られます。

設定(Cursor 側) ​

  1. .cursor/mcp.json.example を .cursor/mcp.json にコピーする(.cursor/mcp.json は gitignore。パスワードをコミットしない)
  2. daily-article-tools の env を書き換える
変数必須説明
WP_SITE_URLはいWordPress のサイト URL(例: https://example.com)。https:// 必須(http:// は localhost / 127.0.0.1 / [::1] / *.local / *.test のみ)。リダイレクトは追わないので、リダイレクト先の URL(www の有無も含む)を書く
DAILY_ARTICLE_WP_USERNAMEはい日別記事編集bot ユーザーのユーザー名
DAILY_ARTICLE_WP_APP_PASSWORDはい日別記事編集bot ユーザーのアプリケーションパスワード(スペース込みで可)

有料記事用の .env.local(WP_USERNAME / WP_APP_PASSWORD)、新台情報担当 MCP(NEW_MACHINE_WP_*)、外部データ取込 MCP(DATA_IMPORT_WP_*)とは別の変数名です。資格情報を混ぜないでください。

json
{
  "mcpServers": {
    "daily-article-tools": {
      "command": "node",
      "args": ["scripts/mcp-daily-article-tools.mjs"],
      "env": {
        "WP_SITE_URL": "https://example.com",
        "DAILY_ARTICLE_WP_USERNAME": "daily-article-bot",
        "DAILY_ARTICLE_WP_APP_PASSWORD": "xxxx xxxx xxxx xxxx xxxx xxxx"
      }
    }
  }
}

設定変更後は Cursor を再読み込みし、MCP daily-article-tools が有効になっていることを確認します。リポジトリで npm install 済み(@modelcontextprotocol/sdk)であることが前提です。

利用可能な Tool ​

MCP Tool name主な引数REST
daily_article_listdate_from, date_to, 任意 statusGET /daily-article/v1/articles
daily_article_getidGET /daily-article/v1/articles/{id}
daily_article_update_fieldsid, fields, 任意 expected_modifiedPOST /daily-article/v1/articles/{id}/fields
daily_article_list_eventshallGET /daily-article/v1/events
daily_article_register_eventhall, namePOST /daily-article/v1/events
daily_article_what_day_optionsdate または mmddGET /daily-article/v1/what-day-options
daily_article_kishu_names任意 dateGET /daily-article/v1/kishu-names
daily_article_trash_draftidPOST /daily-article/v1/articles/{id}/trash
daily_article_birthdaysdate, 任意 days / halls / include_last_yearGET /daily-article/v1/birthdays
daily_article_birthday_searchq, 任意 field / limitGET /daily-article/v1/birthdays/search
  • fields に入れられるキーは kousatsu_date / halls / daily_article_events / espasu_what_day_manual / island_pre / island_after / espasu_pre / espasu_after / bigapple_pre / bigapple_after / island_emoji / espasu_emoji / bigapple_emoji だけ。ほかのキーは MCP 側で送らずにエラーにする
  • daily_article_events は { "island": [12, 34] } のようにホールごとのイベント ID の配列。記事の halls に無いホールは指定できない
  • espasu_what_day_manual は daily_article_what_day_options が返した名前の配列
  • daily_article_birthdays は基準日の前後 days(0〜7、省略時 3)日の誕生日を返す。halls は省略時 island / espasu。台数の基準日はホールごとに date 以前でデータがある最新日(7 日までさかのぼる)で、count_reference_dates に入る。基準日が無いホールは count が null、機種が無ければ 0。include_last_year: true で各機種に 1 年前の同日の台数・差枚(last_year)が付く。BB/RB は返さない
  • daily_article_birthday_search は field が actor(声優名)/ chara(キャラ名)/ any(両方。省略時)。半角・全角スペースは無視する。台数が要るときは、返った month / day で daily_article_birthdays を呼ぶ

戻り値は REST のレスポンス JSON そのままです。HTTP エラー・通信失敗・設定不足のときは isError: true で {"error":"...","status":409,"data":{...}} のように返します。入出力とエラー条件の正本は API-001-19 です。

bot の作業の流れ ​

  1. daily_article_list で対象の記事を探し、daily_article_get で現在の値と modified_gmt を取る
  2. イベントを入れるときは daily_article_list_events で ID を探す。無ければ daily_article_register_event で登録して ID を得る
  3. 〇〇の日を入れるときは daily_article_what_day_options の候補から選ぶ
  4. daily_article_update_fields に変える項目だけを入れ、expected_modified に手順 1 の modified_gmt を渡す。続けて更新するときは、応答の modified_gmt を次の expected_modified に使う
  5. 409(記事が他の操作で更新されています)が返ったら、daily_article_get で取り直して内容を確かめてから再実行する。人が編集中の可能性があるので、上書きしてよいか迷うときは人間に報告する

HTTP 400 は入力不正(errors / unknown_keys を確認。何も更新されていない)、404 は記事が無い・日別記事でない、409 は上記の競合・公開済み記事の考察日の重複・ゴミ箱へ移せないステータス、429 はレート制限(項目更新 1 分 60 回、イベント登録・ゴミ箱移動 各 1 分 20 回、読み取り 1 分 120 回)です。読み取りの 120 回は記事の一覧・取得・イベント候補・誕生日(daily_article_birthdays / daily_article_birthday_search)の合計で数えるため、誕生日を日ごとに何度も呼ぶと記事の取得も 429 になります。429 は時間をおいて再実行します。503 は DB の読み取り失敗(0 件とは区別している)で、時間をおいて再実行します。

REST の例(curl) ​

MCP を使わずに動作確認するときの例です。

bash
SITE=https://example.com
AUTH='daily-article-bot:xxxx xxxx xxxx xxxx xxxx xxxx'

# 一覧
curl -sS -u "$AUTH" "$SITE/wp-json/daily-article/v1/articles?date_from=2026-10-01&date_to=2026-10-31&status=draft"

# 1 件取得
curl -sS -u "$AUTH" "$SITE/wp-json/daily-article/v1/articles/26916"

# 項目更新
curl -sS -u "$AUTH" -X POST "$SITE/wp-json/daily-article/v1/articles/26916/fields" \
  -H 'Content-Type: application/json' \
  -d '{"island_pre":"<p>考察</p>","daily_article_events":{"island":[12]},"expected_modified":"2026-10-03 01:02:03"}'

# イベント登録
curl -sS -u "$AUTH" -X POST "$SITE/wp-json/daily-article/v1/events" \
  -H 'Content-Type: application/json' -d '{"hall":"island","name":"周年"}'

# 誕生日(前後 3 日・アイランド/エスパス)
curl -sS -u "$AUTH" "$SITE/wp-json/daily-article/v1/birthdays?date=2026-10-10&days=3&halls=island,espasu"

# 誕生日(去年の結果付き)
curl -sS -u "$AUTH" "$SITE/wp-json/daily-article/v1/birthdays?date=2026-10-10&days=3&include_last_year=1"

# 声優名・キャラ名で検索(q は URL エンコードする)
curl -sS -u "$AUTH" "$SITE/wp-json/daily-article/v1/birthdays/search?q=%E8%8A%B1%E6%BE%A4&field=actor"

トラブルシューティング ​

症状確認
WP_SITE_URL / DAILY_ARTICLE_WP_USERNAME / ... を設定.cursor/mcp.json の daily-article-tools.env
HTTP 401(incorrect_password / invalid_username)ユーザー名・アプリケーションパスワードの誤り、取り消し済み、HTTP サイトでの利用
HTTP 401(ログインが必要です。)認証ヘッダーが PHP に届いていない。下の「401 / 403 の切り分け」を参照
HTTP 403(rest_forbidden)権限グループが「日別記事編集bot」か、ADM-035 で edit_daily_articles(ゴミ箱移動は trash_daily_article_drafts)が外されていないか
リダイレクトされましたWP_SITE_URL を location の URL(https / www の有無)に合わせる
HTTP 404(rest_no_route)テーマが本機能を含むバージョンか。パーマリンク設定が「基本」だと /wp-json/ が使えない場合がある
JSON 以外の応答WAF・メンテナンス画面・PHP エラー。body の先頭を確認。考察 HTML を WAF が弾く場合はサーバー側の WAF 設定を確認する

401 / 403 の切り分け ​

同じ資格情報で WordPress コアの /users/me を呼び、ログインできているかを確かめます。

bash
curl -sS -u "$AUTH" "$SITE/wp-json/wp/v2/users/me"
  • 401(rest_not_logged_in): 認証ヘッダーが PHP に届いていない。HTTP サイトで使っていないか、Apache の CGI / FastCGI 構成なら .htaccess に SetEnvIf Authorization "(.*)" HTTP_AUTHORIZATION=$1 等があるかを確認する
  • 200(日別記事編集bot ユーザーが返る): ログインはできている。権限グループと ADM-035 の Capability を確認する

実装メモ ​

  • 本体: scripts/mcp-daily-article-tools.mjs(テスト: npm run test:mcp-daily-article。npm test にも含まれる)
  • fields の許可キーは DailyArticleFieldsUpdateServiceInterface::ALLOWED_KEYS、一覧の 31 日上限は DailyArticleBotRestController::MAX_LIST_RANGE_DAYS と同期する
  • 誕生日の days 上限・limit 上限・field の値は DailyArticleBirthdayServiceInterface(MAX_DAYS / SEARCH_LIMIT_MAX / SEARCH_FIELDS)と同期する
  • 権限判定は REST 側(WordPressDailyArticleBotPermissionChecker)。MCP 側では権限を判定しない