mcpField
C
canai-mcp

canai-mcp

github.com/usewp開発069 回インストール1 週間前に更新
リポジトリ

canai-mcp について

canai-mcpは、CanAI MCPサーバーを通じてWordPressサイトのCanAIコンテンツを管理するスキルです。テンプレート、ページ、設定、メディアを扱い、FluentSnippetsの作成は対象外です。

主な機能

  1. 01MCPツール経由でCanAIコンテンツを読み書き
  2. 02テンプレート、ページ、設定の管理
  3. 03国際化とメディアへの対応
  4. 04サイトセットアップ手順の提供
  5. 05コンテンツ作業の範囲を限定

活用シーン

  1. 01CanAIテンプレートの読み取りや更新
  2. 02ページ作成とサイト設定
  3. 03サイトへのメディアアップロード
  4. 04CanAIサイト構成のセットアップ

MCP 対応

ユーザーが設定した完全なMCPエンドポイントを使い、CanAI MCPサーバー経由でWordPressとやり取りします。コンテンツ操作にはMCPツールを使い、コマンドラインや直接のREST呼び出しで代替しないよう定めています。

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/usewp/canai --skill canai-mcp

インストール後、Claude Code で/canai-mcpを入力して読み込まれたか確認します。

同梱ファイル

SKILL.md と一緒にインストールされる参考資料やスクリプト。

references/
FLUENTCART.md
MARKDOWN-FOR-AGENTS.md
READING-MODE.md
REFERENCE.md
STRUCTURE-NAVIGATION.md
WOOCOMMERCE.md

詳細

翻訳後に原文が更新されています。最新の内容は原文をご確認ください。

CanAI MCP スキル

CanAI を CanAI MCP サーバー(WordPress Abilities API + mcp-adapter)のみを通じて扱うエキスパートです。WordPress サイトはリモートの場合もローカルの場合もあります。このスキルからサイトの CanAI 関連データを読み取ったり変更したりする唯一の方法は、ユーザーの MCP 設定にある **WP_API_URL が、そのサイトの完全な MCP エンドポイント({site}/wp-json/mcp/wpcanai)を指すことです。

CanAI に登録された Twig 関数(組み込み機能のベンダー提供 Twig バージョンも含む)と WooCommerce のコンテキストについては、references/REFERENCE.md を参照してください。共通の Atomic Design ナビゲーション契約については、references/STRUCTURE-NAVIGATION.md を参照してください。ユーザーがブラウザーのリーディングモードでコンテンツを開くことを希望している場合は、references/READING-MODE.md を参照してください。

MCP は通信手段です — シェル操作やファイル編集は行いません

このスキルで使用できるのは CanAI MCP サーバーとそのツールのみです。ユーザーの WordPress とやり取りする際に、ターミナルの wp、curl、生の REST、リポジトリの編集で代用してはいけません。サイトはローカルでもリモートでも構いませんが、いずれの場合も MCP 経由でアクセスします。

FluentSnippets は、別途オプトインするスキル canai-yolo で扱います。このスキルはコンテンツ(テンプレート、ページ、ブログ投稿、設定、i18n、メディア、Tailwind)に特化しています。ユーザーがスニペットの作成を必要とする場合は、canai-yolo をインストールして使用してください。このスキルでスニペットのワークフローを独自に作り出してはいけません。これには、スニペットのデプロイループ(wpcanai-replace-in-snippet → sha256 check → purged、plugin v1.80.0)も含まれます。eval による回避策はありません。wpcanai/eval アビリティは plugin v1.59.0 で削除されたため、すべての機能は実際の wpcanai/* アビリティでなければなりません。


重大 — CanAI MCP サーバーのみを使用(厳守)

CanAI のコンテンツ作業(テンプレートやページの一覧表示/読み取り/書き込み、設定、ブートストラップ設定、スキャン、WooCommerce のページ ID、i18n、メディアなど)における、ユーザーの WordPress とのすべてのやり取りは、ホストが公開するツール(wpcanai-read-meta、wpcanai-write-meta、wpcanai-setup などのハイフン区切りの名前。設定済みサーバーのツール一覧を参照してください。多くの場合、canai-mcp または user-canai-mcp です)を使用し、必ず CanAI MCP サーバーを経由しなければなりません。WP-CLI、wp eval、直接 SQL、wp-admin に対するブラウザー自動化、またはライブサイトのデータを変更するためのワークスペース内 wp-content/ 配下のファイル編集は使用しないでください。

文書化された唯一の例外 — バイナリーアップロード。 MCP はファイルの転送には適していません(サイズと Base64 のオーバーヘッドがあるため)。そのため、メディアは MCP トランスポートで使用するものと同じ API キーを使い、CanAI のサイドロード REST ルート POST {site}/wp-json/wpcanai/v1/sideload 経由で送信します。詳しい手順は、以下の 「メディアのアップロード(バイナリーファイル)」を参照してください。サイトに対して curl を使用できるのは、これが唯一認められた場合です。それ以外はすべて引き続き MCP 経由で行います。

  1. MCP が提供するサイトの CanAI テンプレート HTML/CSS/JS/コンテキストを変更するために、wp-content/ 配下(またはリポジトリ内のどこであっても)のパスに対して Edit、Write、またはパッチの適用を使用しないでください。これらのファイルは、MCP を利用する作業でのライブ CanAI ストレージモデルではありません。
  2. CanAI コンテンツ(_canai_html、_canai_css、_canai_js、_canai_context、_canai_layout)の読み取りと書き込みはすべて、必ず CanAI MCP ツールのみを経由してください。
  3. これらのツールを呼び出すには、ホストの MCP 統合機能を使用してください。ユーザーがこのスキルまたは /canai-mcp を呼び出した場合、MCP 操作の代わりにシェルの wp、Python、未加工の HTTP、ローカルファイル編集を使用しないでください。(curl の例外は、文書化されたサイドロードルートのみです。以下の「メディアのアップロード」を参照してください。)
  4. ワークスペースでの Read/Grep/Glob の使用は、このスキル、プラグインのソース、またはドキュメントを読む場合に限ります。ライブのマイアカウント/プライバシー/テンプレート出力を「修正」するためには使用しないでください。ライブコンテンツについては、**wpcanai-resolve-content-id** または **wpcanai-list-pages** で正しい post_id を特定した後、**wpcanai-read-meta** を使用し、続いて **wpcanai-write-meta** を使用してください。

MCP ツールが利用できない場合、または失敗した場合は、MCP クライアントの設定(canai-mcp または設定済みのサーバー名、JWT_TOKEN、WP_API_URL)を確認するようユーザーに伝えてください。サイトコンテンツについて、WP-CLI、REST、ローカルファイル編集にひそかに切り替えないでください。


MCP ツールの呼び出し方法(すべてのクライアント)

これは、Cursor、Claude Desktop、Windsurf、Cline、VS Code 拡張機能など、MCP に対応するあらゆるホスト(IDE、CLI、エージェント)に適用されます。

  • CanAI ツールは、ホストの MCP ツール呼び出し機能(正確な API 名は異なります。例:CallMcpTool、use_mcp_tool、クライアントに表示されるネイティブ MCP ツール一覧)を通じて呼び出してください。ユーザーが設定した MCP サーバー名は、多くの場合 **canai-mcp** です。ホストによっては、サーバー名に接頭辞が付くことがあります(例:user-canai-mcp)。CanAI MCP 接続に対して、ホストに表示されるサーバー識別子を使用してください。
  • MCP クライアントがセッション、トランスポート、認証を処理します。シェル、Python、curl、または {site}/wp-json/mcp/wpcanai への未加工の HTTP を使って、MCP を再実装してはいけません。大きなペイロードも例外ではありません。wpcanai-write-meta を通じて _canai_html 全体を渡す方法は、サイズを問わず機能します。また、MCP エンドポイントへの独自の HTTP リクエストは、403 エラーになるだけです(セッション/認証がありません)。書き込み内容が大きすぎて全体を再送信したくない場合は、未加工の HTTP に切り替えず、対象箇所を変更する wpcanai-replace-in-meta を使用してください。
  • パラメーターを調べるために、ツール記述子の JSON ファイルを開いたり、REST ルートを調査したりしないでください。以下のインラインのツールリファレンスを使用してください。
  • ツール呼び出しが失敗した場合は、WordPress の CanAI → AI Client(エンドポイント URL、API キー)と MCP クライアントの設定を確認するようユーザーに依頼してください。独自の回避策を試みないでください。

ローカルスクリプトでの連携 — Python ではなく Node を使う

基本的なフローでは、ローカルスクリプトは一切必要ありません。サイトの読み書きにはMCP ツール呼び出し(ホストのネイティブな仕組み)を使い、HTML/CSS/JS の編集はエージェント内で行います(「最終的な文字列をエージェント内で組み立てる」)。シェルを呼び出す手順は 2 つだけです。バイナリーのサイドロード(後述)と、Tailwind のコンパイル(npx tailwindcss@^3。これも Node です)。

ローカルでの連携処理が本当に必要な場合(sha256 のハッシュ計算、一時ファイルの組み立て、インラインの tailwind.config の抽出、マルチパートアップロードなど)は、Node(node -e/npx)を使い、Python や pip でインストールしたツールは使わないでください。このスキルは npx skills add でインストールしたため、node と npx が PATH 上にあることは保証されています。python/python3/requests を使うと、環境に存在しない可能性があり、このスキルが前提としていない依存関係が追加されます。(これは、MCP のトランスポート自体をいかなる言語でも再実装してはならないという上記のルールとは別の話です。Node で一時ファイルのハッシュを計算するのは問題ありませんが、Node で …/wp-json/mcp/wpcanai への HTTP 通信を独自に実装することは禁止されています。)

前提条件(概要)

  • CanAI、wordpress/abilities-api、wordpress/mcp-adapter がインストールされた WordPress。
  • MCP エンドポイント: {site}/wp-json/mcp/wpcanai(WP Admin → CanAI → AI Client → Connections からコピーしてください)。
  • @automattic/mcp-wordpress-remote 用の JWT_TOKEN としての API キー(またはクライアントの環境設定)。

MCP クライアントのサーバー設定に追加します(例。キーはクライアントによって異なる場合があります)。

{
  "mcpServers": {
    "canai-mcp": {
      "command": "npx",
      "args": ["-y", "@automattic/mcp-wordpress-remote@latest"],
      "env": {
        "WP_API_URL": "https://example.com/wp-json/mcp/wpcanai",
        "JWT_TOKEN": "<paste-wpcanai-api-key>"
      }
    }
  }
}

重要: WP_API_URL には、サイトのホーム URL だけでなく、…/wp-json/mcp/wpcanai まで含む完全なパスを指定してください。AI Client → Connections に表示される値を使用してください。

**wpcanai-list-templates**(またはクライアントの CanAI サーバー用ツール一覧)で確認します。

CanAI の前提条件の設定(サイト上)

CanAI でページ全体を出力するには、サイトに以下が必要です。

  1. 最低限のテンプレート — 少なくとも 1 つのレイアウト(template_type が layout)、1 つのヘッダー(header)、1 つのフッター(footer)。レイアウトは {{ page_content }} を囲み、ヘッダー/フッターは {{ wpcanai_template('slug') }} で含めます。
  2. CanAI Starter(テーマスラッグ wpcanai-empty)— 最小構成の CanAI テーマをインストールして有効化します。プラグインに同梱されており、canai/theme/wpcanai-empty/ にあります。また、WP Admin → CanAI(ウェルカムページ)からもインストールできます。
  3. 同梱ライブラリ — CanAI → Settings で、Load Tailwind CSS Play CDN が Yes、Resource Source が Internal(同梱スクリプト。プラグイン v1.58.2 以降のデフォルト)になっていることを確認します。wpcanai-setup を使わない場合に設定してください(推奨セットアップでは自動的に有効になります)。必要に応じて Lucide と Alpine も有効にします。
  4. レイアウト内の Tailwind 設定 — レイアウトの _canai_html で、{{ wp_head() }} の後にある <script> 内に tailwind.config = { ... } を出力してください。これにより、CanAI が wp_head に追加する Tailwind スクリプトの後に実行されます。
<head>
  {# Meta & Viewport #}
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  {{ wp_head() }}
  {# Tailwind CSS Config #}
  <script>
    tailwind.config = {
      theme: {
        extend: {
          colors: {
            brand: { DEFAULT: '#0f766e', dark: '#115e59' },
          },
        },
      },
    }
  </script>
</head>
推奨セットアップ(MCP 呼び出し 1 回)

前提条件が不足している場合、またはユーザーから**「推奨セットアップをすべて実行」するよう求められた場合は、最初に **wpcanai-setup { } を呼び出してください。WPCanAi Starter(wpcanai-empty)をインストールして有効化し、(プラグインのデフォルトレイアウト用ヘルパーを使って)デフォルトレイアウトを作成し、基本的なヘッダー/フッターのテンプレートを追加して、ホームページを作成し、静的フロントページを設定し、**wpcanai_default_layout** を設定して、Tailwind CSS(wpcanai_tailwind_settings:load_tailwind = yes、source = plugin/内部の同梱 Play CDN)を有効にします。

WordPress/WooCommerce/CanAI のオプション(静的フロントページ、users_can_register、チェックアウト/アカウント登録、Tailwind の設定など)には、**wpcanai-read-settings**/**wpcanai-update-settings** を使ってください。wp_options に保存されているその他のプラグインオプションには、サイト所有者が CanAI → AI Client → Guardrails で許可リスト(読み取りリスト、自動適用リスト、任意の承認リスト)を設定した後、**wpcanai-get-option**/**wpcanai-update-options** を使ってください。CanAI のメタ情報を任意で設定できる新しいページには、**wpcanai-create-page** を使ってください。

メニュー — 2 つの場所のみ。ハードコードは禁止

ナビゲーションをハードコードしてはいけません。 ヘッダーでは {% for item in get_menu('primary')|filter(i => i.depth == 0) %}、フッターでは get_menu('footer') を使い、それ以外は使いません。<a href> のリストやフォールバックチェーン(2 つ目の get_menu() や、ループ内にハードコードしたリンクを置く {% else %} も含む)、その他のロケーション名、current_lang() による URL のプレフィックスは使用しません(URL とタイトルは get_menu() からローカライズされた状態で返されます)。リンク自体は wpcanai-write-menu で一度だけ記述します。ソースのマークアップにナビゲーションリストがある場合は、リンクを読み取り、write-menu で記述してから、そのリストを get_menu() のループに置き換えます。

ロケーションは primary と footer のみです(プラグイン v1.88.0)。3 つ目の名前(topnav、main、mobile、header)を決して作らないでください。write-menu は実行前にそれを拒否し("input[location] is not one of primary and footer")、wpcanai-scan はその名前を使う get_menu() 呼び出しを unknown_menu_location として報告します。ヘッダーのリンクを表示するモバイルドロワーでは、get_menu('primary') をもう一度ループします。言語によってリンクが異なる場合は、同じロケーションにその言語専用のメニューを用意します(プラグイン v1.89.0、「言語ごとのメニュー」を参照)。それでも 3 つ目のロケーションは作りません。

ワークフロー — ナビゲーションリストを 1 つずつ処理します。

  1. まず確認する — wpcanai-get-menus { } で各ロケーションに設定されている内容を確認できます(locations.primary / locations.footer。何も割り当てられていない場合は null)。
  2. リスト内のリンクを順番に読み取る。 リンクテキストは title です。このサイト内のページへのリンクは page(ID またはスラッグ)です。先にページを作成してください。何も解決されない page は unknown_page となり、何も書き込まれません。スラッグは slug_url() と同じように解決されます(プラグイン v1.96.0):同じスラッグを持つページが 1 つだけの場合は子ページのスラッグ単体で、それ以外の場合はパス("page": "gallery/theatrettes")または ID で指定します。複数のページで共有されているスラッグ単体は ambiguous_page となります(プラグイン v1.97.0)。それ以外のリンクは url です。ソースでリンクが新しいタブで開かれる場合にのみ "target": "_blank" を追加します。入れ子のリストは children です(最大 3 レベル)。リンクではないドロップダウンのトリガーやメガメニューの列見出しは、children を持ち、page と url のどちらも持たない項目です。リンクの 1 行の説明文は description、アイコンは icon(Lucide 名)、サムネイルは image(wpcanai-sideload-url で取得した添付ファイル ID)です。下記の「メガメニュー」を参照してください(プラグイン v1.89.0)。
  3. 書き込む — wpcanai-write-menu { "location": "primary", "items": [ { "title": "Home", "page": "home" }, { "title": "Services", "page": 42, "children": [ { "title": "Design", "page": "design" } ] }, { "title": "Docs", "url": "https://docs.example.com", "target": "_blank" } ] }。リストによってロケーション内の項目が置き換えられます。"merge": true を指定すると、同じタイトルの項目が更新され、残りの項目が追加されます。
  4. ヘッダーまたはフッターのテンプレートで、リストをループに置き換える:
    {# Navigation / Primary #}
    <nav aria-label="{{ t('Primary') }}">
      {% for item in get_menu('primary')|filter(i => i.depth == 0) %}
        <a href="{{ item.url }}" class="{{ item.active ? 'font-semibold' : '' }}"{% if item.target %} target="{{ item.target }}" rel="noopener"{% endif %}>{{ item.title }}</a>
      {% endfor %}
    </nav>
    
    トップレベル項目は |filter(i => i.depth == 0) でループします(プラグイン v1.89.0)。get_menu() はサブ項目も含め、すべての項目を 1 つのフラットなリストで返します。また、各項目には children も含まれます。ドロップダウンではトップレベル項目の中で item.children をループするため、フィルターを使わないと各サブ項目が 2 回表示されます(wpcanai-scan:menu_children_twice)。1 階層のみのメニューではフィルターによる変化はありませんが、後からサブ項目を追加した際に重複しないよう、必ず指定してください。<nav> と <a> にはソースのクラスを維持します。
  5. スキャンする — wpcanai-scan で、そのテンプレートに hardcoded_nav、unknown_menu_location、menu_fallback_chain、menu_children_twice、menu_not_assigned が表示されないことを確認します。
  • write-menu は、その場所にすでに割り当てられているメニューに書き込みます。名前の変更や置き換えは行いません。 primary に「Journal Primary」が設定されているサイトでは、「Journal Primary」のままです。メニューが割り当てられていない場所に限り、同じ呼び出しで Primary/Footer という名前の新しいメニューを作成して割り当てます。
  • canai-prepare の menus.json は、pages.json の隣に { "primary": [ … ], "footer": [ … ] } として配置されます。これはすでに write-menu の items 形式になっているため、そこに記載されたページが存在するようになってから、各キーにつき write-menu を1回呼び出します。page の値は pages.json のスラッグです。サイト上のスラッグが異なる場合(たとえば index がフロントページになった場合)は、作成したページの ID を渡してください。用意された HTML では各ナビゲーションの <nav data-canai-menu="primary">/"footer" にマークが付いています。ループで置き換えるのは、その <nav> 内のリンクです。
  • 翻訳のためにテンプレートを変更する必要はありません。 デフォルト以外の言語では、get_menu() は各 title と description を文字列ストアから取得し、各内部 url に言語プレフィックスを付けて返します。raw_title/raw_description には翻訳前のテキストが格納されます。メニューのタイトルと説明はほかの文字列と同じ方法で翻訳します。詳しくは ネイティブ文字列翻訳 をご覧ください。独自のメニューを持つ言語は例外です。詳しくは 言語別のメニュー をご覧ください。
  • アップグレードに関する注意。 プラグイン v1.88.0 より前に作成されたテンプレートでは、get_menu('wpcanai_primary')/get_menu('wpcanai_footer') が呼び出されます。どちらの名前も引き続き解決され、wpcanai-scan は各呼び出しを legacy_menu_location として報告します。wpcanai-replace-in-meta を使って名前を変更してください({ "post_id": <id>, "replacements": [{ "from": "get_menu('wpcanai_primary')", "to": "get_menu('primary')" }, { "from": "get_menu('wpcanai_footer')", "to": "get_menu('footer')" }] })。wpcanai-get-menus は、旧名で割り当てられたままのメニューを legacy_assignments に一覧表示します。
メガメニュー — 見出し、アイコン、説明、画像(プラグイン v1.89.0)

リッチなナビゲーションもメニューデータです。アイコンや説明文を含むメガメニューのリンクをハードコードしないでください。

  • 項目のフィールド。 title/page/url/target/classes のほか、write-menu の項目では description(1行のテキスト)、icon(Lucide アイコン名、[a-z0-9-])、image(添付ファイル ID。先に画像をサイドロードしてください)、attr_title(リンクの title 属性)を指定できます。get_menu() の項目には、これらに加えて id、parent、depth(トップレベルでは 0)が返されます。管理者は「外観」→「メニュー」で同じフィールドを編集できます(説明とタイトル属性は「表示オプション」の下、アイコンと画像は各項目にあります)。
  • 見出し。 children があり、page も url も指定されていない項目はリンクではなくラベルです。get_menu() は url: '' として返します。ドロップダウンのトリガーやメガメニューの列タイトルとして使います。子項目のない項目には、page/url のいずれかが必要です。
  • 書き込み — 1回の呼び出しで、最大3階層まで指定できます:
    { "location": "primary", "items": [
      { "title": "Services", "children": [
        { "title": "Build", "children": [
          { "title": "Websites", "page": "websites", "icon": "monitor", "description": "Fast sites, built to be edited." },
          { "title": "Shops", "page": "shops", "icon": "shopping-bag", "description": "WooCommerce stores that sell." } ] },
        { "title": "Grow", "children": [
          { "title": "SEO", "page": "seo", "icon": "search", "description": "Found by the people looking." } ] } ] },
      { "title": "Contact", "page": "contact" } ] }
    
  • レンダリング — トップレベルをフィルタリングし、見出しは <span>、リンクはアイコンと説明付きで表示します:
    {# Navigation / Primary #}
    <nav aria-label="{{ t('Primary') }}" class="flex items-center gap-6">
      {% for item in get_menu('primary')|filter(i => i.depth == 0) %}
        {% if item.children is empty %}
          <a href="{{ item.url }}" class="{{ item.active ? 'font-semibold' : '' }}"{% if item.target %} target="{{ item.target }}" rel="noopener"{% endif %}>{{ item.title }}</a>
        {% else %}
          <div class="relative" x-data="{ open: false }" @click.outside="open = false">
            <button type="button" @click="open = !open" :aria-expanded="open">{{ item.title }}</button>
            <div x-show="open" class="absolute left-0 mt-2 grid grid-cols-2 gap-8 rounded-xl bg-white p-6 shadow-lg">
              {% for column in item.children %}
                <div>
                  {% if column.url %}<a href="{{ column.url }}" class="font-semibold">{{ column.title }}</a>{% else %}<span class="font-semibold">{{ column.title }}</span>{% endif %}
                  {% for link in column.children %}
                    <a href="{{ link.url }}" class="mt-3 flex gap-3">
                      {% if link.image %}<img {{ image_attrs(link.image, 'src:thumbnail,alt') }} class="h-10 w-10 rounded object-cover">{% elseif link.icon %}<i data-lucide="{{ link.icon }}" class="h-5 w-5"></i>{% endif %}
                      <span>{{ link.title }}{% if link.description %}<small class="block text-slate-500">{{ link.description }}</small>{% endif %}</span>
                    </a>
                  {% endfor %}
                </div>
              {% endfor %}
            </div>
          </div>
        {% endif %}
      {% endfor %}
    </nav>
    
    ページではすでに lucide.createIcons() が1回呼び出されています。Alpine の x-show パネル内のアイコンは最初から DOM 内にあるため、レンダリングされます。
  • merge: true を指定すると、省略したフィールドの値は保持されます。""(または image の場合は 0)を指定すると、その値がクリアされます。
言語ごとのメニュー(プラグイン v1.89.0)
  • 複製せず、翻訳します。 ある言語で同じリンクを表示する場合は、メニューを1つにまとめます。タイトルと説明は文字列ストアを通じて翻訳され、内部 URL には言語プレフィックスが付きます(ネイティブ文字列翻訳を参照)。ほとんどの場合、これがデフォルトであり、適切な方法です。
  • リンクが異なる場合にのみ、言語別メニューを作成します — マレー語のみのページや、短いフッターなどです。wpcanai-write-menu { "location": "footer", "lang": "ms", "items": [ … ] } は、その場所に言語別メニューを書き込み、初回の呼び出し時にその言語のネイティブ名を使った Footer (Bahasa Melayu) を作成して、その言語に割り当てます。項目はその言語で書き込みます。get_menu() は項目を記述どおりに表示し(文字列ストアによる処理は行いません)、内部 URL には引き続きプレフィックスを付けます。
  • 3つ目の場所はありません。 言語別メニューは CanAI に保存される割り当てであり、登録された場所ではありません。Appearance → Menus → Manage Locations に表示されるのは引き続き Primary と Footer です。テンプレートは引き続き get_menu('primary') / get_menu('footer') を呼び出し、変更はありません。
  • 元に戻すには、wpcanai-assign-menu { "location": "footer", "lang": "ms", "menu_id": null } を使います。するとマレー語では、再び翻訳済みのデフォルトメニューが表示されます(言語別メニューは割り当てを解除された状態で保持されます)。assign-menu に menu_id を指定すると、言語(lang を指定しない場合は場所自体)が既存のメニューに割り当てられます。
  • コピーから始めます(v1.93.0):wpcanai-write-menu { "location": "footer", "lang": "ms", "copy_default": true } は、文字列ストアに翻訳があるラベルがすでに翻訳された状態で、デフォルトメニューから Footer (Bahasa Melayu) を作成します。その後、異なる項目を編集します。
  • 確認するには、wpcanai-get-menus を使います。languages.ms.footer はその言語のメニュー、または null(翻訳済みのデフォルトメニューを継承)です。(v1.93.0) 管理者は CanAI → Translations → Menus でも同じ操作を行えます。言語と場所ごとに、次の2つから選べます。
    • 「英語と同じメニューを使い、ラベルをBahasa Melayuにする」:ラベル数と、Appearance → Menus でメニューを開く「ラベルを翻訳」ボタンが表示されます。
    • 「リンクが異なる別のメニューを使う」:メニューを選択するか、「コピーから作成」を選びます。
  • 文字列。 ある場所に独自のメニューがある言語では、wpcanai-i18n-list-strings と Strings タブに、その場所のデフォルトメニューのタイトルと説明は表示されません。これらはその言語では表示されないためです。
HTML を CanAI に変換する(エージェントのワークフロー)

静的な HTML ファイル(例:index.html)を MCP ツール経由で CanAI に変換する場合:

canai-replicate の移行キットがソースの場合、output/push/*.json の成果物をプッシュしてください。output/pages/*.html/output/templates/*.html の生ファイルはプッシュしないでください。 canai-replicate の出力ファイルはすべて、単独で完結する完全な HTML ドキュメントです(設計上、<!DOCTYPE>/<html>/<head>/<body> を含んでおり、ローカルプレビュー用に file:// 経由で開けます)。CanAI 独自のレンダリング処理でも、_canai_html は独自のドキュメントシェルで囲まれます。そのため、キットファイルの生の内容をそのまま _canai_html に書き込むと、マークアップが二重になり、無効になります。<!DOCTYPE html> が 2 個、<html> が 2 個、<head>/</head> が 2 組、<body> が 2 個です(実環境で再現済み。dogfood A2、Defect #1 — CRITICAL)。まず "$HOME/.claude/skills/canai-replicate/bin/replica" pushprep <site> を実行してください。シェルとプレビュー用ライブラリのブロックが決定論的に取り除かれ、ファイルごとにプッシュ可能な { title, slug, template_type, html, css, js, warnings } JSON が output/push/<slug>.json に出力されます。その JSON の html/css/js を _canai_html/_canai_css/_canai_js に書き込み、新しい wpcanai-create-template 呼び出しでは、template_type が null でない場合に、その値を template_type 用語として使用してください。シェルを手作業で再度取り除かないでください。また、「ファイルが単純に見えるから」という理由で pushprep を省略しないでください。

header.json/footer.json では、それぞれの SLUG を必ず指定してください。 wpcanai_template('header')/('footer') は、タイトルや template_type 用語ではなく、作成されたテンプレート投稿の正確な slug で解決します。この 2 つは、タイトルが正確に header/footer と一致するか、その文字列に正確にスラッグ化されるタイトルを使用して wpcanai-create-template で作成してください。「Header — Acme migration」のような説明的なタイトルは、スラッグが単独の文字列から外れてしまいます。その結果、サイト全体のすべての {{ wpcanai_template('header') }} 呼び出し箇所で、どこにもエラーが出ないまま参照先が見つからなくなります(dogfood A2 Defect #7、実環境で再現済み)。次に進む前に、wpcanai-list-templates で返される slug が単独の文字列であることを確認してください。この 2 つは、それらを含むものより先にプッシュしてください。 CanAI のテンプレート関数を優先してください。ヘルパーで提供されるものをハードコードしないでください。 _canai_html/_canai_css/_canai_js を記述するときは、該当する登録済みの CanAI Twig 関数を使い、値やマークアップをハードコードしないでください。ハードコードされたアセット URL、内部リンク、ユーザー向け文字列は、相対パスの ./images/hero.jpg が 404 になるのと同じように、本番サイトで問題を起こします(ベースパスが違う、スラッグが移動している、パーマリンク形式ではない URL、翻訳されないなど)。ソース内の値は、次のヘルパーに対応させてください。

ソース内のハードコード代わりに使用
<img src="…">、srcset、CSS の url(…){{ image_attrs(id, 'src,alt') }}/{{ media_url(id, size) }}
内部リンクの <a href="/about">/href="about.html">スラッグで投稿を指定する場合は {{ slug_url('about') }}、その他のサイトパスには {{ turl('/path/') }}(turl('/') はホームページです)、ID で指定する場合は {{ turl(id_url(id)) }}/{{ turl(post_url()) }}/{{ turl(term_url(id)) }}。slug_url() と turl() は現在の言語に対応します
ヘッダー/フッターのナビゲーションリスト(<nav>、または <header>/<footer> 内のリンクを含む <ul>){% for item in get_menu('primary')|filter(i => i.depth == 0) %}/get_menu('footer') を使い、リンクは wpcanai-write-menu で一度だけ記述します(上記の メニュー を参照)。メガメニューのアイコン、説明文、見出しも含みます
言語プレフィックス付きの home_url()、base または current_lang() の URL 変数構築する必要はありません。turl()、slug_url()、get_menu() はそれぞれ現在の言語を自動的に適用します
ユーザー向けのリテラル文字列ページ内の文字列には {{ t('…') }}(ネイティブ文字列翻訳 を参照)、テーマやプラグインの gettext カタログにすでに含まれている文字列に限り {{ __('…') }}/{{ _x() }}/{{ _n() }}
動的にするべき静的な投稿内容/マークアップ{{ the_content() }}/{{ shortcode('[…]') }}
ページごとに重複している共通のヘッダー/フッター/パーシャル{{ wpcanai_template('slug') }}

値のハードコードが許されるのは、該当するヘルパーがない場合、または絶対 URL の外部リンクが実際に必要な場合(OG/Twitter メタ、Web マニフェストのアイコン、サードパーティーのオリジンなど)に限ります。画像(ステップ 5 とサイドロードの事前処理)と内部リンク(ステップ 6)は、このルールの具体例です。references/REFERENCE.md → メディアとリンク および 国際化(i18n) を参照してください。

  1. レイアウト _canai_html にリテラルの <title> を含めないでください。{{ wp_head() }} がドキュメントタイトルを出力します(WordPress / SEO プラグイン)。
  2. ページ単位の JavaScript(例:lucide.createIcons()、Alpine の初期化、カスタムハンドラー)は、インラインの <script> として _canai_html に置かず、_canai_js に置いてください。CanAI は wp_footer() で _canai_js を挿入します。_canai_js は Twig でレンダリングされます(_canai_html と同じエンジンです)。{# #} コメントは有効で、スクリプトの出力前に削除されます。内部名を記述するものには、/* */ よりもこちらを使用してください。レイアウト _canai_html でインラインに記述できる <script> は、{{ wp_head() }} の後に置く tailwind.config = { ... } だけです(CanAI が挿入する Tailwind スクリプトの後に実行されます)。
  3. レイアウトを再利用してください。 まず wpcanai-list-templates を呼び出します。既存のレイアウトがある場合は、その id を _canai_layout / 参照で再利用してください。ユーザーが 2 つ目のシェルを求めていない限り、レイアウトテンプレートを重複して作成しないでください。
  4. ページ本文とレイアウト: <main> / ページの主要マークアップは、wpcanai-create-page で作成した**page**に置きます(layout にはレイアウトテンプレート ID を設定してください)。wpcanai_template の layout 型として作成してはいけません。レイアウトテンプレートはドキュメントシェル({{ page_content }}、ヘッダー / フッターのインクルード)です。ページ本文の template_type は layout ではありません。
    • ネストされたページ(プラグイン v1.96.0)。 /gallery/auditorium/ のようなソースページは、トップレベルの gallery-auditorium ではなく、子ページです。まず親ページを公開状態で作成し(status: "publish" が既定値です)、その後、各子ページをパス順に parent: <parent id> と最後のパスセグメントを slug(auditorium)に指定して作成してください。/a/b/ を a-b というスラッグにまとめてはいけません。既存の親ページは再利用してください。wpcanai-list-pages { "all_pages": true }(プラグイン v1.97.0)で検索できます。これにより、プレーンな WordPress ページや下書きも含めたすべてのページが、path と status 付きで一覧表示されます。1.96.x ではこのフラグは無視され、status のない、公開済み CanAI ページだけが返されます。プレーンな WordPress ページまたは下書きの親ページについては、ユーザーに ID を尋ねてください。スラッグが gallery-2 として保存されたという create-page の警告は、gallery がすでに存在していたことを意味します。ページへのリンクには、返された ID を使う {{ turl(id_url(<id>)) }}(ページを移動しても追従します)、または返された slug(スラッグが重複している場合は path。ルール 6 を参照)を使う slug_url() を使用してください。url や、自分で組み立てたパスを貼り付けないでください。既存ページの移動、名前変更、スラッグ変更には wpcanai-update-page を使用してください。公開済みページを移動した後は、URL 変更に関する警告の説明に従って、旧パスを検索し修正してください(wpcanai-update-page を参照)。
  5. HTML を書く前に、ローカルメディアを自動検出してサイドロードしてください。 静的サイトのフォルダーでは、ほぼ必ずローカルアセット(./images/hero.jpg、assets/logo.svg、videos/intro.mp4、ファビコン、OG 画像、CSS の url(...) 背景など)が参照されています。HTML / CSS をそのまま送ると、公開サイトではこれらの参照がすべて 404 になります。wpcanai-create-page / wpcanai-write-meta を呼び出す前に、次のセクションの静的サイトアセットのサイドロード事前処理を実行してください。これにより、記述する HTML が相対パスや固定されたアップロード URL ではなく、{{ image_attrs(...) }} / {{ media_url(...) }} を使った ID 経由でメディアを解決できるようになります。
  6. 内部リンクを CanAI ヘルパーに書き換えてください(上記の原則の適用例です)。静的ソースには <a href="about.html"> / href="/about"> が残っていますが、公開サイトではベースパスが異なる、パーマリンクではない、スラッグが変更されるなどの理由で機能しません。各内部リンクを対応するヘルパーに書き換えてください。ページ間リンクには、対象ページ ID が分かっている場合(wpcanai-create-page または wpcanai-list-pages から取得)は {{ turl(id_url(<id>)) }}、そうでない場合は {{ slug_url('<slug>') }} を使います。子ページ(プラグイン v1.96.0)の場合、slug_url() はまず完全一致するパスを検索し、次に 1 ページだけが持つ完全一致のスラッグを検索します。slug_url('auditorium') は /gallery/auditorium/ を見つけ、ページを移動した後もそのページを検索し続けます。トップレベルページと子ページが同じスラッグを持つ場合、トップレベルページが優先されます(プラグイン v1.97.1 以降では、同じスラッグの下書きがあっても公開済みのトップレベルページが優先されます。公開済みページが 2 つある場合は、引き続き曖昧になります)。子ページ 2 つが同じスラッグを持つ場合、スラッグだけの形式では解決できず(プラグイン v1.97.0 以降、href="" となり、wpcanai-scan は link_unresolved と報告します)、パスを渡してください(slug_url('gallery/theatrettes'))。パスでのリンクはページの移動に追従せず(href="" として出力されるため)、ID を優先してください。ターム / アーカイブへのリンクには {{ turl(term_url(<term_id>)) }}、その他のサイトパスには {{ turl('/path/') }} を使います。ヘッダーまたはフッターのナビゲーションリストは、リンクごとに書き換えず、get_menu() のループにします(メニューを参照)。変更しないもの: 外部リンク / 絶対 URL(https://other.com)、プロトコル相対 URL(//cdn…)、アンカーのみのリンク(#section)。書き換えた内容をマニフェストに記載してください。
  7. セクションコメントを Twig のナビゲーションラベルに正規化してください。 canai-prepare が出力した HTML ナビゲーションコメントはすべて、{# Type / Short Label #} の正確な形式に変換してください。例:{# Section / Hero #}。HTML ナビゲーションコメントを _canai_html に残してはいけません(レンダリング結果を汚し、ページソースからの偵察に利用されるためです)。ランドマークの開始タグの直前には、対応する種類を必ず置いてください。<main> → Container、<section> → Section、<header> → Header、<footer> → Footer、<nav> → Navigation、<aside> → Sidebar です。管理された語彙と正確な変換方法については、references/STRUCTURE-NAVIGATION.md に従ってください。通常の実装メモには {# @dev … #} を使用してください。これらはエディターの Structure アウトラインには表示されません。canai-replicate の pushprep 成果物では、古い形式が使われている場合があります。書き込む前に正規化してください。
  8. 作成系の書き込み(create-page / create-template / implement / import / preset)後は、作業完了とする前に wpcanai-scan を実行し、コンテンツに関する検出事項を解消してください。 wpcanai-replace-in-meta によるピンポイント編集(クラス、文字列、URL)ではスキャンは不要なので、省略してください。missing_structure_comment、invalid_structure_comment、leaky_comment、leaky_secret はレイアウトの破損と同様に扱ってください。Twig のナビゲーションラベルを追加または修正し、HTML / /* */ コメントを Twig に変換して、メタデータから秘密情報らしいリテラルを削除してください。ナビゲーションに関する検出事項(hardcoded_nav、legacy_menu_location、unknown_menu_location、menu_fallback_chain、menu_children_twice、menu_not_assigned、hardcoded_lang_prefix、unlocalised_internal_href(プラグイン v1.88.0)、menu_children_twice(v1.89.0))も解消してください。また、ネイティブ i18n のサイトでは split_sentence(1 つの文が複数の t() に分割されています。結合してください。ネイティブ文字列翻訳の手順 2 を参照)と unsupported_t_markup(t() 内にブロック要素または閉じタグのないタグがあり、テキストとして表示されます。プラグイン v1.94.0)も解消してください。各メッセージに修正方法が記載されています。ワークフローについては、上記のメニューを参照してください。render_warning(v1.90.0)は、ページのレンダリング時に配列を出力した行を示しています。同じ方法で修正してください(wpcanai-scan のレンダリング警告を参照)。リンクに関する検出事項(プラグイン v1.97.0。wpcanai-scan のリンクに関する検出事項を参照)も解消してください。link_unresolved は、ページが 0 件または複数件一致するリテラルの slug_url() で、href="" としてレンダリングされます。複数件に一致する場合はパスを渡し、一致しない場合はスラッグを修正するか、turl(id_url(<id>)) を使ってください。link_moved は、移動済みページを指すリテラルの turl() / href パスです。メッセージに記載された turl(id_url(N)) に置き換えてください。サイト全体を対象に実行する場合は、下記のコメント / 秘密情報のセキュリティ一括チェックの手順を使用してください。
ブログ投稿(CanAI ページではありません)

ブログ投稿は通常の WordPress コンテンツであり、CanAI メタページではありません。wpcanai-write-post を使って、構造化されたブロックリストを渡して作成します。本文は、サイト所有者がブロックエディターで編集できるネイティブのブロックマークアップになり、追加作業なしでブログキットの blog-single-post テンプレートを通して表示されます。画像は先にサイドロードし、添付ファイル ID で参照してください。投稿には wpcanai-create-page や wpcanai-write-meta を使わないでください。

  • 投稿内のチャート(プラグイン v1.77.0)。 チャートは画像ではなくデータです。labels と最大 8 個の datasets を指定した chart ブロック(ブロック一覧を参照)を渡してください。9 つ目の系列はチャートを分割せず、「Other」にまとめてください。レンダリング済みのチャート画像をサイドロードすることは絶対に避けてください。目盛り、ツールチップ、表に「12」ではなく「12 ms」と表示されるよう、unit(ms、%、MB)を設定してください。すべてのチャートは、数値をアクセシブルなデータ表としても表示します(デフォルトでは折りたたまれた <details>。table: "open" で展開して表示し、"hidden" でスクリーンリーダーと Markdown のみに表示します)。そのため、JavaScript を使わない環境、リーダーモード、RSS、Accept: text/markdown の読者も、table ブロックを追加しなくても数値を確認できます。サイト所有者は Settings → Libraries → Chart.js でランタイムを無効にできます。設定項目は wpcanai_chart_settings(load_chart は auto|yes|no、source は plugin|cdn)で、**wpcanai-read-settings** で読み取り、**wpcanai-update-settings** で変更できる第一級の設定キーです。no にすると表だけが表示されるため、チャートが表示されないことを不具合として報告する前に、この設定を確認してください。
リーディングモード(Safari Reader / Firefox Reader View / Chrome)

ユーザーからページや投稿を「リーディングモードに対応」させるよう求められた場合(またはリーダービュー / Safari Reader について尋ねられた場合)は、まず references/READING-MODE.md の判定手順に従ってください。対象コンテンツを読み、リファレンスの表に照らして分類し、その形式ではリーディングモードを推奨できない場合(ランディングページ、一覧、WooCommerce の取引ページ、フォームページ、内容の薄いコピー)は、説明を添えて作業を中止してください。実質的な文章コンテンツの場合に限り、リファレンスのマークアップチェックリストに沿って進めてください(<h1>、<time>、rel="author" の署名欄を含む <article> を <main> 内に配置し、本文は <p> に入れ、ナビゲーション、関連コンテンツ、CTA は外に置きます)。リーディングモードを有効にするプラグイン設定やメタタグはありません。これはマークアップに基づくブラウザーのヒューリスティックにすぎないため、切り替え設定が存在すると説明しないでください。

エージェント向け Markdown(Accept: text/markdown)

サイトが AI エージェントやクローラーに Markdown を提供するかどうかをユーザーが尋ねた場合、ページをそれらが読み取れるようにしたい場合、Accept: text/markdown / ?format=markdown について質問された場合、またはその動作を無効にしたい場合は、references/MARKDOWN-FOR-AGENTS.md(プラグイン v1.76.0)に従ってください。CanAI のテイクオーバーページは Markdown リクエストに対し、レイアウトではなくページのコンテンツスロットを YAML フロントマターと変換済み本文として返します。そのため、Markdown をきれいに保つには、READING-MODE.md のマークアップ指針に従う必要があります。ネゴシエーションはリクエストごとに行われ、ブラウザーには見えないため、通常のアクセスには影響しません。唯一の制御項目はサイト全体に適用される wpcanai_markdown_negotiation オプション(デフォルトで有効)です。これは **wpcanai-read-settings** で読み取り、**wpcanai-update-settings** で変更できる第一級の設定キーであり、汎用オプション許可リストは使いません。WooCommerce のカート / チェックアウト / マイアカウント / 注文ページは常に HTML のままです。また、ページごとの上書きも、対象外ページで Markdown を強制する方法もありません。そのような方法があると説明しないでください。(v1.80.0)キャッシュ: Accept ヘッダーによって選択された Markdown 応答には Cache-Control: private, no-store(および LiteSpeed の no-cache)が送信されるため、ページキャッシュがブラウザーに Markdown を配信することはありません。?format=markdown はキャッシュ可能な URL であり、案内される rel="alternate" リンクもこの URL を指します。キャッシュが重要な場合は、クローラーとユーザーに ?format=markdown の URL を案内してください。

ページ形式マーク

すべてのページには format マークがあり、"twig"(デフォルトで、このスキルが作成する唯一の形式)または "blocks" のいずれかです。wpcanai-read-meta はデフォルトでこれを返し、wpcanai-list-pages の各行にも含まれます。blocks マークのページに html または js を書き込もうとすると、format_mismatch で拒否され、何も変更されません。このエラーにはサイト所有者に引き継ぐためのプロンプトが含まれます。再試行したり回避策を講じたりせず、サイト所有者に引き継いでください。blocks ページでは css のみ受け付けられます(プラグイン v1.67.0)。これはページのプレーン CSS スロットであり、_canai_css に書き込まれて、ヘッド内にそのまま出力されます。layout、context、tailwind_* も受け付けられます。

静的サイトのアセットをサイドロードする事前処理

ユーザーが /canai-mcp implement <folder> を実行したとき、静的サイトのインポートを依頼したとき、または canai-replicate の移行キットの output/push/*.json アーティファクトをプッシュしたときに実行されます(pushprep アーティファクトの html にも、ソースサイトの元の絶対 URL/ホットリンク画像 URL が含まれています。canai-replicate はサイドロードを行いません。これは他の静的サイトのインポートと同様、このスキルの役割です)。サイドロードしないままだと、ソースサイト独自のホットリンク保護が有効になった時点で画像が 404 になったり、壊れて表示されたりする可能性があります(実環境で確認済み:dogfood A2 Defect #6)。CanAI メタに HTML/CSS を書き込む前に、最初に一度だけ実行します。

  1. 参照されているアセットを列挙します。 Read/Grep/Glob を使ってフォルダーを調べ、参照先が指定されたフォルダー内のファイルであるものをすべて抽出します。最低限、以下を対象にします。

    • HTML 属性:src、(候補ごとの)srcset、<link rel="icon">/apple-touch-icon/manifest の href、<video poster>、<source src>、<object data>、<embed src>、OG/Twitter メタ(og:image、twitter:image)。
    • CSS:<style> ブロック、インラインの style="…"、およびフォルダー内のすべての .css ファイルにある url(...)。
    • JS:インラインの <script> と、明らかにアセットパスである場合のバンドル済み .js ファイル内の文字列リテラル(例:"./images/foo.png")。動的/テンプレート化された値は除外します。

    除外対象: 他のオリジンを指す絶対 http(s):// URL、プロトコル相対の //cdn…、data: URI、mailto:/tel:、アンカーのみの #…、およびフォルダー内に実在するファイルとして解決されないパス。解決後の絶対パスを基準に重複を除外します。

  2. マークアップを書き込む前に、意味のあるラスター画像をすべて事前確認します。 利用可能なローカル画像情報ツールで元の幅と高さを読み取り、ホストの画像ビューアーでピクセルを確認します。アスペクト比(width / height)と重要な領域(顔、頭、全身、同時に写る人物、手、製品、賞、画像内に埋め込まれた文字)を記録します。ロゴ、アイコン、装飾テクスチャ、object-contain で表示する画像では、被写体のトリミング分析は不要です。リモートの元画像については、可能であればサイドロード前に元画像/キャプチャしたアセットを確認します。確認できない場合は、破壊的なトリミングを推測で行わず、object-contain または元画像に近いアスペクト比を維持します。

    浅い固定 h-* のトリミングより、レスポンシブなアスペクト比コンテナーを優先します。縦長の元画像なら aspect-[4/5]、aspect-[3/4]、または aspect-square、正方形なら aspect-square または aspect-[4/3]、横長なら aspect-[16/9]、aspect-[3/2]、または aspect-[4/3] を使用します。object-cover を使う場合は、確認した構図を基準にします。目安として、顔写真/人物1人のポートレートには object-[center_30%](Y 軸 25~35%)、座っている2人には object-[center_58%](Y 軸 55~60%)、立っているグループには object-[center_20%]、物を持つ人物には object-[center_80%](Y 軸 75~85%)を使い、少なくとも aspect-[3/4] またはそれに近い高さを確保します。被写体が中央から外れている場合は、たとえば object-[80%_25%] のように両方の座標を指定します。これらは出発点であり、無条件に適用する既定値ではありません。

    デプロイ前に、幅の狭いモバイル表示と幅の広いデスクトップ表示の両方でトリミングをプレビューします。顔、頭、同時に写る人物、タスクに関係する物体が端に押し付けられることなく見える必要があります。必要に応じてブレークポイントごとにアスペクト比/焦点クラスを指定します。どちらのサイズでも適切なトリミングができない場合は、高さのあるコンテナー、または元画像に近いコンテナーを使用するか、object-contain を使います。

  3. 固有のファイルをそれぞれサイドロードします。POST {site}/wp-json/wpcanai/v1/sideload を使ってください(正確な curl については メディアのアップロードを参照してください)。各 id / source_url を記録できるよう、アップロードは一度に1件ずつ実行します。ソース HTML に alt が指定されている場合(<img alt="…">)は、適切な alt を渡してください。指定がない場合は省略します。同じバイト列を再アップロードしても問題ありません。WordPress はファイル名で重複を排除しますが、API は呼び出しごとに新しい添付ファイルを返すため、マップ内のキャッシュヒットが重要です。

  4. メディア id をキーにした書き換えマップを作成します — { <original-reference-string>: <media id> } とし、source_url の生値は使いません。キーには、ソースに記載された文字列をそのまま使います(例:./images/hero.jpg、images/hero.jpg、/images/hero.jpg はすべて同じアップロードの id にマッピングします)。CanAI はレンダリング時に id からライブ URL を解決するため、メディアが再生成されたりサイトが移転したりしてもページは動作しますが、固定された source_url ではそうなりません。(絶対 source_url を保持するのは、手順5の OG/マニフェストの例外だけです。)

  5. wpcanai-write-meta を呼び出す前に、HTML/CSS/JS をメモリ上で ID ベースのヘルパーに書き換えます。手順1でスキャンしたものと同じコンテンツ領域に、各領域に適した形式で適用します。

    • <img> → ハードコードされた src / alt を削除し、{{ image_attrs(<id>, 'src,alt') }} を展開します(寸法がわかっている場合は 'src,alt,width,height' を使います)。事前確認済みのアスペクト比コンテナや焦点位置ユーティリティを含め、既存のクラスやその他の属性は保持します。ページ下部にある画像には loading="lazy" を保持または追加します。
    • CSS の url(...)(_canai_css 内/インラインの style)、<source srcset>、<video poster>、<link rel="icon">/apple-touch-icon → {{ media_url(<id>, 'full') }}(Twig でレンダリングされる領域)。srcset の候補はそれぞれ個別に書き換え、1x/2x/480w の記述子を保持します。
    • OG/Twitter のメタタグ(og:image、twitter:image)とウェブマニフェストのアイコン → 絶対 source_url を保持します。これらは絶対 URL を必要とし、Twig で確実にレンダリングされるとは限らないため、ID ベースの書き換えに関する唯一の例外です。

    ディスク上のファイルは編集しないでください。書き換えはエージェント内で行い、書き換え後の文字列を html/css/js に渡します。

  6. 完了したらマニフェストをユーザーに報告します。元のパス → 寸法/アスペクト比 → フレーミングの選択 → メディア ID → 最終的な Twig 形式 の簡潔な表を作成します。ここで フレーミングの選択 にはコンテナのアスペクト比と焦点位置ユーティリティ(または object-contain)を記載し、最終的な Twig 形式 には書き換え後の {{ image_attrs(...) }}/{{ media_url(...) }}、または OG/マニフェストの例外における絶対 source_url を記載します。さらに、スキップした参照の件数と理由(外部、データ URI、ファイルなし)も記載します。ユーザーはこれを使って変換内容を抜き取り確認します。

冪等性。 ユーザーがインポートを再実行すると、エンドポイントはコンテンツハッシュで重複排除しないため、再アップロードが行われます。単発のインポートであれば問題ありません。同じフォルダーを以前のテンプレート作成にも使ったことが明らかであるなど、再実行の可能性を検出した場合はユーザーに警告し、再アップロードの代わりに wpcanai-list-media/wpcanai-get-media で既存の添付ファイルを再利用する方法を提示します。

この手順の対象外。 @font-face { src: url(...) } で読み込まれるフォントも同様に扱えるため、対象に含める必要があります。CSS-in-JS、webpack 形式のハッシュ付きバンドル、実行時の fetch でのみ参照されるアセットは静的に検出できません。ユーザーが手動で対応できるよう、これらはマニフェストの「スキップ」セクションに記載してください。

ショップとアーカイブのページネーション(サイト上の Twig)

ショップまたは商品カテゴリーのデリゲート(またはグローバルな WC テンプレート)の _canai_html を記述する場合は、注入された products と pagination を使用し、{{ wc_pagination()|raw }} または {{ wpcanai_pagination()|raw }} を出力します。カテゴリー/タグ/検索/作成者/アーカイブ(CPT アーカイブを含む)では、注入された posts と pagination を使用し、{{ the_posts_pagination()|raw }} または {{ wpcanai_paginate_links()|raw }} を使用します。同じ posts と pagination は、ブログインデックス(Settings → Reading →「Posts page」で設定するページ)にも注入されます。このページは通常の CanAI ページです(wpcanai-read-settings → page_for_posts で ID を確認できます)。そのページ自身の _canai_html が一覧をレンダリングします。このページのエディタープレビューでは空の状態が表示されます。これは、posts がライブリクエストでのみ存在するためです。「Your latest posts」フロントページモードはサポートされていません。データ構造とパーマリンクに関する注意事項の詳細は、references/REFERENCE.md を参照してください。

WooCommerce ブロック(カート/チェックアウト/注文完了/マイアカウント)のスタイリング

CanAI の wc_* Twig ヘルパー(wc_checkout_form()、wc_cart_totals()、マイアカウントのエンドポイント)は、ライブページがレンダリングされるまで HTML 構造を確認できない、WooCommerce の生の出力をそのまま返します。また、注文完了ページと注文支払いページでは、WooCommerce のゲスト向けメール確認ゲートによって詳細が隠されているため、単純に表示して確認することはできません。セレクターを推測しないでください。 これらのブロックをスタイリングする前に、次の手順を実行してください。

  1. スタイリングするコンテキストを指定して wpcanai-get-wc-css-reference を呼び出します(例:{ "context": "order-received" })。
  2. css_reference を確認します。これは、そのコンテキストのセレクターに対する WooCommerce 独自のデフォルトルールです。コンパイル済み CSS ではなく、生の SCSS ソースです。 Sass 変数、ミックスイン、& 親参照は解決されないまま返されます(例:darken($secondary, 10%)、@include …)。そのため、_canai_css に貼り付けるのではなく、実際の CSS に書き換えてください。インストール済みの WooCommerce に含まれる 4 つのスタイルシート(woocommerce.scss、woocommerce-layout.scss、woocommerce-smallscreen.scss、forms.scss)から抽出されるため、WooCommerce の更新後も正確な情報を得られます。記憶したクラス名に頼らず、ツールを再度呼び出してください。ルールは実際の祖先セレクターチェーンで囲まれて返されます(例:.woocommerce { table.shop_table { … } })。詳細度を合わせるには、チェーン全体を指定してください。@media only screen and (max-width: 768px) に囲まれたルールは woocommerce-smallscreen.scss 由来で、モバイル専用です。WooCommerce はファイル内ではなくエンキュー時にこのブレークポイントを適用するため、無条件に適用されるものとして扱わないでください。
  3. インストール先のファイルシステムにアクセスできる場合は、template_files のファイルを確認します。これらは信頼できるマークアップであり、テーマのオーバーライドが WooCommerce に同梱されたコピーより優先されます。パスは ABSPATH からの相対パスです。サンクスページの .woocommerce-order-overview__order/__date/__total のように、コアが提供してもスタイル設定されていないクラスも含まれます。templates_missing が空でない場合、コンテキストマップはインストール済みの WooCommerce と一致していません。そこに記載されたパスはコンテキスト相対です(例:order/order-details.php)。template_files とは意図的に形式が異なるため、両方のリストを単純に比較することはできません。
  4. third_party_hooks を確認します。各エントリ({hook, priority, callback, file, is_third_party})は、そのコンテキストに独自のマークアップを追加するプラグインを示しています(例:サンクスページにテーブルを追加する配達時間枠プラグイン)。スタイリングする前に file を読み込み、出力されるクラスを確認してください。代わりに "include_core": true を渡すと、WooCommerce 自身のものも含め、それらのフックに対するすべてのコールバックが返されます。各エントリには引き続き is_third_party フラグが付くため、サードパーティによる追加だけでなく、その領域にレンダリングされる内容全体を確認したい場合に便利です。
  5. サイトのデザインシステムに合わせて、ページ/テンプレートの _canai_css に(wpcanai-write-meta を使用して)対象セレクターにスコープしたオーバーライドを記述します。

css_fallback が "full-file" の場合、セレクターの抽出結果がありませんでした(WooCommerce の構造変更)。その場合はスタイルシート全体が返されているため、template_files で確認したクラスを検索してください。

これは読み取り専用の静的解析です。何もレンダリングされず、注文、カート、顧客のデータも読み取られません。


MCP ツールリファレンス(インライン)

機能 ID ではスラッシュを使用します。MCP ツール名ではハイフンを使用します(wpcanai/read-meta → wpcanai-read-meta)。

ツールのラベル(plugin v1.80.0、v1.81.0以降は7種類)

すべてのCanAIツールの説明は、ツールが触れる「CanAI自体のデータ以外のもの」を示す角括弧付きのプレフィックスから始まります。例:[fluent-snippets, filesystem] Update a snippet …。使い慣れていないツールを呼び出す前に確認してください。annotations.readonlyには書き込みの有無が引き続き示されます。また、uninstall-preset、restore-snapshot、restore-operationは破壊的な操作としてマークされています。

ラベルツールが読み書きする対象
canaiCanAI自体のデータ:wpcanai_template、_canai_*メタデータ、wpcanai_*オプション、スナップショット、プリセット
coreCanAI以外のWordPressデータ:投稿、固定ページ、投稿メタデータ、オプション、メディア
woocommerceWooCommerceの固定ページと設定
fluent-snippetsFluent Snippetsのファイルとインデックス(canai-yoloツール)
cacheページキャッシュプラグインのパージフック(LiteSpeed Cache、WP Rocket、W3 Total Cache、WP Super Cache、WP-Optimize — v1.82.0)
filesystemwp-content内のファイル(スニペットの保存先、アップロード)
httpサーバーからの送信リクエスト(サイドロード、プリセットのダウンロード、ネットワーク診断)

表示されるプレフィックスは、サイトに合わせて解決済みです。サードパーティーのラベル(woocommerce、fluent-snippets)は、そのプラグインが有効な場合にのみ表示されます。プラグインがなければ、ツール自体が登録されません。また、cacheはGuardrails → Page cache → purge after writes(wpcanai_purge_after_write、デフォルトで有効)が有効で、かつ対応するキャッシュプラグインが検出された場合にのみ表示されます(purge-cacheとdiagnosticsには常に付与されます)。そのため、write-metaは、素のサイトでは[canai]、LiteSpeedを使用しているサイトでは[canai, cache]となります。宣言されているラベル(plugin v1.81.0):

ラベルツール
canailist-templates、read-meta、grep-content、resolve-content-id、scan、get-pending、export、list-presets、list-snapshots、list-operations、get-snapshot、pin-snapshot、pin-current
canai, cachewrite-meta、replace-in-meta、create-template、import、restore-snapshot、restore-operation
canai, corelist-pages、list-media、get-media、read-settings、update-settings、get-option、update-options、uninstall-preset、get-menus、すべてのi18n-*ツール
canai, core, cachecreate-page、write-post、write-page、write-menu、assign-menu
canai, core, http, cachediagnostics
canai, core, woocommercesetup
canai, core, woocommerce, httpinstall-preset
core, cacheupdate-page
core, filesystem, httpsideload-url
core, filesystem, cacheupdate-media
woocommerceget-wc-page-ids、get-wc-css-reference
cachepurge-cache
fluent-snippetslist-snippets、get-snippet
fluent-snippets, filesystem, cachecreate-snippet、update-snippet、replace-in-snippet、set-snippet-status

cacheを持つ書き込みツールは、レスポンスに**purged**を返します。リクエストの終了時にパージがキューに登録される場合は{ "scope": "all"|"post", "plugin": "litespeed"|"wp-rocket"|"w3tc"|"wp-super-cache"|"wp-optimize" }、パージがない場合(自動パージがオフ、キャッシュプラグインがない、書き込みがない)はnullです。

必須要件(v1.82.0)。 touchesとは別に、ツールはmeta.mcp.requiresを宣言できます。これは、ツールが機能するために必要なラベルを示します。現在、cacheを必須とするのはpurge-cacheのみです。必須の連携機能が利用できないツールは、オフが推奨され、一覧に表示されません(次のツールの公開設定を参照)。

ツールの公開 — 表示されるツール(プラグイン v1.82.0)

tools/list でクライアントが取得する一覧は、「登録されているすべてのツール」ではありません。各ツールにはこのサイト向けの最新の推奨設定があり、所有者は AI Client → Tools でツールごとに設定を上書きできます(Auto / On / Off)。また、Follow recommendations スイッチによって、Auto が「推奨設定に従う」(新規インストールの場合)か、「オン」(≤1.81 からアップグレードしたサイトの場合。これにより設定は変更されません)かが決まります。上書き設定は常に優先されます。ルールは最初に一致したものが適用されます。

ルール非表示になる条件ツール
R1必須の連携機能がないキャッシュプラグインがない場合の purge-cache
R2常に — 翻訳ツールはオプトインですすべての i18n-* ツール(グループ Languages)
R3別の登録済み MCP 機能と重複する(サイトフィルター。デフォルトは空)get-option、list-media、get-media、sideload-url
R4セットアップが実行済み — 推奨のみで、自動適用はされませんsetup、プリセット、import、export

diagnostics と manage-tools は非表示になりません。非表示のツールを確認し、再び有効にするためのツールです。

表示されるはずのツールが一覧にありませんか? サイトにその機能がないと判断しないでください。wpcanai-manage-tools { "action": "list" } を呼び出すと、非表示のものも含め、登録済みのすべてのツールが exposed、override、recommendation、reason、rule とともに一覧表示されます。そのうえで、ユーザーが希望する場合は有効にし、再接続してください(HTTP クライアントは tools/list をキャッシュするため、再接続後に新しいツールが表示されます)。非表示のツールを直接呼び出すと tool_disabled エラーになり、メッセージに正確な manage-tools 呼び出しが示されます。

wpcanai-manage-tools(プラグイン v1.82.0)
  • 引数: { "action": "list" } または { "action": "set", "overrides"?: { "<short>": "on"|"off"|null }, "groups"?: { "<group>": "on"|"off"|null }, "follow_recommendations"?: bool, "apply_advisory"?: bool }。null を指定すると上書き設定が解除され、再び推奨設定が適用されます。groups は、Tools タブのグループに属するすべての登録済みツールに展開されます — Templates & code、Pages & posts、Media、Snippets、Languages、History & restore、Site & settings、WooCommerce(または languages などのスラッグ)。apply_advisory: true は R4 の推奨を off の上書き設定として書き込みます。不明なツール名 → invalid_tool(未登録ツールに対する既存の上書き設定は解除できる場合があります)。不明なグループ → invalid_group。diagnostics / manage-tools → exempt_tool。
  • 戻り値: { "follow_recommendations": bool, "tools": [{ "name", "group", "touches", "requires", "exposed", "override", "recommendation", "reason", "rule", "advisory", "bytes" }], "totals": { "exposed", "total", "exposed_bytes", "total_bytes", "exposed_tokens", "total_tokens" } }。set の後は "reconnect_required": true と note も返されます。bytes は説明と入力スキーマの合計であり、1 ターンあたりにツールが消費する量です。
  • 翻訳ツールはデフォルトで非表示です。 ユーザーが翻訳に関することを依頼した際、一覧に i18n-* ツールがない場合は、manage-tools list を実行し、このサイトで翻訳を有効にするかユーザーに確認してから、{ "action": "set", "groups": { "Languages": "on" } } を実行し、再接続して、以下の翻訳モデルルーターに進みます(言語がまだない場合、i18n-set-settings が最初の言語を追加します。この呼び出しの前にグループを有効にする必要があります)。上書き設定は、誰かが解除するまでリクエストや再接続をまたいで保持されます。
wpcanai-list-templates
  • 引数: { "status"?: string } — 投稿ステータスフィルター(デフォルトは "publish")。
  • 戻り値: オブジェクトのarray:id、title、slug、status、type(template_type の名前)、layout_id(整数または null)。
wpcanai-list-pages
  • 引数: { "all_pages"?: bool } — デフォルトは false です。(v1.97.0) all_pages: true を指定すると、ゴミ箱以外のあらゆるステータスの全ページ(投稿タイプ page)を、ID 順に一覧表示します。CanAI の行ではなく、通常の WordPress ページと下書きも含まれます。他の投稿タイプは返しません。
  • 戻り値: オブジェクトの array。id、title、post_type、(v1.97.0) status、layout_id(整数または null)、format("twig"|"blocks"。(v1.97.0) all_pages モードで CanAI を使って作成されていないページの場合は null)、css_lines / js_lines(ページ独自の _canai_css / _canai_js の空でない行数。0 = なし)、および (v1.96.0) slug、parent(ページ ID。0 = 最上位)、path(gallery/auditorium。先頭および末尾にスラッシュなし。スラッグなしで保存されたページの場合は '')、url。デフォルトでは、_canai_html を持つ投稿/ページ、またはブロックとしてマークされたページの URL が返されます。ページを作成する前に、all_pages: true と parent / path を使って既存の親ページ(通常のページや下書きも含む。入れ子ページを参照)を探します。また、子ページへのリンクには id / path を使います(ルール 6)。
  • デフォルトでは公開済みのページのみ。 all_pages を指定しない場合、ステータスが publish の投稿が返されます。wpcanai-create-page で status: "draft" を指定して作成したページは表示されません。all_pages: true を指定するか、ID で再取得してください。ページが失われたと決めつけないでください。
wpcanai-read-meta
  • 引数: { "post_id": int, "fields"?: ["html"|"css"|"js"|"context"|"layout"|"format"|"blocks"|"tailwind_build"|"tailwind_hash"|"asset_sources"|"render_warnings"] } — post_id は必須です。fields を省略すると、デフォルトの項目(html、css、js、context、layout、format)が読み込まれます。tailwind_build / tailwind_hash が必要な場合は、明示的に指定してください。ビルド CSS は大きくなることがあるため、デフォルトの項目には含まれていません。
  • 戻り値: html、css、js、context(文字列)、layout(整数)、format("twig"|"blocks")、blocks(文字列、生のブロックマークアップ)、tailwind_build(文字列)、tailwind_hash(文字列)、hashes(オブジェクト)のいずれかを含むオブジェクトです。
  • (v1.58.0)hashes。 fields に html、css、js のいずれかが含まれる場合、またはデフォルトで含まれる場合、レスポンスには hashes も含まれます。各コンテンツフィールドの SHA-1 が格納されます(例:{ "html": "<sha1>" })。メタデータのみの読み込み(html / css / js を指定しない場合)には含まれません。これらの値をそのまま wpcanai-write-meta の expected_hash に渡すと、この読み込み以降に該当フィールドが変更されていないことを条件に、次の書き込みを実行できます。
  • (v1.65.0)format と blocks。 format はデフォルトの項目に含まれます。blocks は生のブロックマークアップを返し、ブロックページでのみ有効です(それ以外の場合は format_mismatch)。ハッシュ化されません。
  • (v1.71.0)lines。 "lines": [from, to](1 始まり、両端を含む、最大 400 行)を指定すると、指定した単一のコンテンツフィールドの該当範囲のみが返されます。fields には html / css / js のいずれか 1 つだけを指定する必要があります(layout など、コンテンツ以外のフィールドはあわせて指定できます)。さらに line_counts: { html: 1240 } が追加されます。hashes は引き続きフィールド全体を対象に計算されるため、write-meta の expected_hash にそのまま使用できます。ドキュメント全体を取得する代わりに、grep-content の検索結果が示す範囲を調べるときに使用してください。lines と一緒に必ず fields を送信してください — { "post_id": 12, "fields": ["html"], "lines": [200, 260] }。fields を指定しないと、デフォルトで html、css、js が選択されるため、呼び出しは invalid_input lines applies to exactly one content field で失敗します。
  • (v1.80.0)hash_only。 "hash_only": true を指定すると、fields で指定されたコンテンツフィールド(html / css / js。何も指定しない場合は 3 つすべて)の hashes と line_counts、および format と layout のみが返されます — コンテンツは返されません。書き込みが反映されたことの確認や、write-meta の前に expected_hash を取得するための手軽な方法です。lines とは併用できません。
  • (v1.90.0)render_warnings。 デフォルトの項目には含まれません。fields: ["render_warnings"] を指定すると、過去 7 日間にページをレンダリングした際、この投稿自身の Twig が発生させた PHP 警告が返されます:[{ field, line, kind, message, excerpt, url, first_seen, last_seen }] — kind は expression(line とソース行を含む excerpt あり)または print(単独の {{ }} による出力。Twig ではどの出力か判別できないため、line と excerpt は null)です。1 つの投稿について、wpcanai-scan の render_warning 検出結果と同じデータが返されます。
  • (v1.89.3)不明な post_id。 read-meta、write-meta、replace-in-meta、grep-content は、invalid_post Invalid post_id: no post with ID 123 exists(または pass a positive post ID)を返します。wpcanai-list-pages / wpcanai-list-templates / wpcanai-resolve-content-id で ID を再解決してください。同じ ID で再試行したり、近い ID を推測して指定したりしないでください。
wpcanai-write-meta
  • 引数: { "post_id": int, "html"?: string, "css"?: string, "js"?: string, "context"?: string, "layout"?: int, "tailwind_build"?: string, "tailwind_hash"?: string, "confirm_truncate"?: bool, "expected_hash"?: object, "convert"?: bool, "ignore_syntax_error"?: bool } — post_id は必須です。更新するキーのみを含めてください。tailwind_build はページごとにプリコンパイルされた Tailwind CSS を _canai_tailwind_build に書き込み、tailwind_hash は入力ハッシュを _canai_tailwind_hash に書き込みます(この 2 つは対で指定します。Compile Tailwind for Production を参照)。
  • 戻り値: { "success": bool, "post_id": int } — Twig への変換パス(convert: true)では、converted: true と、ブロック本文が post_content に残り、レンダリングされないことを示す warnings 配列も返します。
  • 注意: html/css/js の文字列を完全な形で渡すことは、サイズにかかわらず完全にサポートされています。大きなページでも問題ありません。ペイロードが大きいからといって、MCP エンドポイントに対する生の HTTP/Python/curl に切り替えてはいけません(MCP セッション/認証がないため、403 になります)。特定の部分文字列(アセット URL、クラス名、文字列)のみを変更する場合は、以下の wpcanai-replace-in-meta を優先してください。
  • tailwind_build とソース編集を同時に行わないでください。 html/css/js と同じ呼び出しで書き込まれた tailwind_build には、書き込み前の epoch が記録され、保守的に古い状態のままになります。先にソースを書き込み、その後、別の呼び出しで tailwind_build/tailwind_hash を書き込んでください。
  • 空文字列の扱いは異なります。 write-meta はキーの有無(array_key_exists)を使うため、"html": "" を渡すとフィールドがクリアされます。create-template/create-page は !empty を使うため、空文字列は無視され、書き込まれません。コンテンツフィールドをクリアするには、write-meta を使用してください。
  • (v1.58.0)confirm_truncate(ブール値、省略可)— 1 KB 以上の html/css/js コンテンツを、元より 70%を超えて小さいペイロードに置き換える場合は、true が必須です。指定しない場合、その書き込みはエラーコード truncation_blocked(ステータス 409)で拒否され、何も書き込まれません(DB の変更も、スナップショットの取得も、操作の開始もありません。書き込み自体が行われないためです)。エラーメッセージには、フィールド名と正確な変更前/変更後のバイト数が示されます。意図した縮小であれば、同じ呼び出しを confirm_truncate: true として再送してください。意図したものでない場合は、先に投稿を wpcanai-read-meta で読み取ってください。ペイロードが誤っていた可能性があります。実際のブール値または文字列 "true" を渡してください。文字列 "false" と "0" は truthy ではなく、正しく「未確認」として扱われます。
  • (v1.58.0)expected_hash(オブジェクト、省略可)— フィールドごとに指定する楽観ロックです。例:{ "html": "<sha1>" }(スカラー値だけを指定することはできません。1 回の呼び出しで複数のフィールドを書き込めるためです)。そのフィールドが読み取り後に変更されていた場合、書き込みはエラーコード hash_mismatch(ステータス 409)で失敗し、何も書き込まれません。エラーには現在のハッシュが含まれるため、再読み込みして編集を適用し直し、再試行できます。ハッシュは wpcanai-read-meta の hashes オブジェクトから取得してください(上記を参照)。expected_hash を省略すると、以前と同じ最終書き込み優先の動作が維持されます。
  • (v1.58.0)ガードの順序: expected_hash は confirm_truncate より先に確認され、どちらも書き込み前に確認されます。古い読み取り状態は縮小による拒否より先に報告され、いずれかに該当すれば書き込み全体が拒否されます。
  • (v1.68.26)Twig 構文ガード。 変更された html/css/js フィールドは、スナップショットの取得や DB への書き込みの前に解析されます。Twig が無効な場合は、field、line、パーサーメッセージの詳細を含む twig_syntax_error(ステータス 422)が返され、何も書き込まれません。無効な中間状態を意図的に保存する場合に限り、ignore_syntax_error: true を使用してください。警告付きで書き込みは成功しますが、wpcanai-scan では引き続き問題として報告されます。
  • (v1.65.0 / v1.67.0)Blocks ページ。 blocks 形式のページに html または js を書き込むと、convert: true が指定されていない限り format_mismatch で拒否されます。convert: true を指定すると、ページは Twig に戻ります(v1.65.0)。blocks ページでは css のみの書き込みは受け付けられます(v1.67.0)。これはページのプレーン CSS 用スロットであり、_canai_css に書き込まれます。layout、context、tailwind_* は常に指定できます。
wpcanai-replace-in-meta
  • 引数: { "post_id": int, "field"?: "html"|"css"|"js", "replacements": [{ "from": string, "to": string }], "require_all"?: bool, "ignore_whitespace"?: bool, "regex"?: bool, "scope"?: { "text": string, "occurrence"?: int }, "dry_run"?: bool } — post_id と replacements は必須です。field のデフォルトは html です。replacements はペアが 1 つの場合も配列です — { "post_id": 12, "replacements": [{ "from": "a", "to": "b" }] }。トップレベルの from / to はありません。replacements を指定しない呼び出しは、スキーマ検証で replacements is a required property of input エラーになります。置換はサーバー側で順番に適用され、from が出現するすべての箇所が対象です。ドキュメントが通信経路を通ることはありません。(v1.71.0) ignore_whitespace を指定すると、from 内の各空白文字列が任意の空白文字列に一致します(インデントや折り返しの違いを許容します。to はそのまま書き込まれます)。regex は from を PCRE として、to を preg_replace のテンプレート($1)として扱います。ignore_whitespace とは併用できません。scope.text は、指定したテキストが文字どおり n 回目に出現する箇所の最も近い親要素に、すべての置換対象を限定します(occurrence のデフォルトは 1)。マークアップが分からなくても「conference と書かれた見出し」を指定できます。dry_run は件数と診断情報を計算しますが、何も書き込みません。require_all: true の場合、いずれかの from が 0 回しか一致しなければエラーになります(422。何も書き込まれません)。write-meta と同じ lang_mismatch のチェックが適用されます。
  • 戻り値: { "success": bool, "post_id": int, "field": string, "total": int, "dry_run": bool, "scope"?: { "text", "occurrences", "used", "start", "end", "line" }, "replacements": [{ "from", "to", "count", "suggestion"? }] }。require_all を指定しない場合、一致数が 0 のペアには、一致しなかった理由を説明する suggestion が含まれます。
  • (v1.71.0)不一致の診断。 require_all で不一致があると replace_no_match が返され、エラーメッセージそのものに診断情報が含まれます。MCP 経由ではメッセージだけが表示され、data は表示されません。"from" at index 1 not found, nothing written: <from>. Nearest match: <kind> at line 227, column 13 — <snippet> — <hint>. Pairs before it: 1 (counts: 3).(index は 0 始まりです。同じフィールドが REST 呼び出しでは data.failed_index、data.results、data.suggestion として存在します)。サーバーが確認する順の種類は次のとおりです。whitespace_only — 正確なバイト列の違いが、指定した範囲内の空白だけです。scope をそのままにして、同じ呼び出しに ignore_whitespace: true を指定して再試行してください。outside_scope — 文字列が存在するのは、scope.text で選択された要素とは別の要素だけです。別の要素から scope.text(または scope.occurrence)を選んでください。一致させるために**scope を外してはいけません**。誤った要素を書き換えてしまいます(対象が本当に指定範囲内の要素である場合は、範囲を指定した grep-content で正確なバイト列を再確認してください。空白や属性の順序の違いが原因である可能性があります)。partial は from の最長トークンを示します。none — フィールドまたは指定範囲内にアンカーがありません。post_id / field を確認してください。バイト列を「確認する」ためにドキュメントのローカルコピーへフォールバックしないでください。提案のスニペットと、max_length: 1000 を指定した範囲内の grep-content で正確な行を確認できます。
  • エラー: scope_not_found、scope_no_element(422。何も書き込まれません)、invalid_regex(パターンをコンパイルできません)、regex_error(422。何も書き込まれません。パターンはコンパイルできましたが、regex または ignore_whitespace のペアの実行中に PCRE エンジンがバックトラック/JIT の上限に達しました。パターンを簡素化してください。書き換えは適用されていません)、invalid_input(regex と ignore_whitespace を併用)。
  • 使用する場面: 既存の大きなページを対象に編集する場合(例:壊れたアセット URL の修正)。read-meta とページ全体を write-meta で書き戻す代わりに、小さな呼び出し 1 回で済みます。スラッシュに敏感なコンテンツ(.md\:flex のようにエスケープされた Tailwind セレクター)はそのまま保持されます。html / css / js を編集すると、write-meta と同様にレイアウトの Tailwind ビルドが古い状態になります。
wpcanai-create-template
  • Args: { "title": string, "type": string, "html"?: string, "css"?: string, "js"?: string, "layout"?: int } — title と type(template_type スラッグ)は必須です。
  • Returns: { "post_id": int, "slug": string }。
  • 型付き CPT テンプレート(v1.24+)。 公開済みの wpcanai_template で、template_type タームが single-<post_type> または archive-<post_type> の場合、その CPT のフロントエンドにおける個別表示/アーカイブ表示を担います。たとえば、service の詳細ページを担当するにはタイプ single-service、アーカイブを担当するには archive-service を指定して作成します。single-post(1.60.0 以降、事前に登録済み)は、WordPress エディターで作成されたブログ投稿にも同じように適用されます。prose コンテナー内の {{ the_content(post.post_content) }} を使い、型付きの single-* による置き換えでは post.featured_image.*、1.68.15 以降は post.author.* も利用できます。存在する場合にのみ適用されます。このようなテンプレートがない場合、CanAI はテーマとバイト単位で同一の出力にフォールバックします。固定ページは引き続き独自のメタパスを使用し、WooCommerce 商品は引き続き WC ブロックを使用します。
wpcanai-resolve-content-id
  • 引数: { "type": string } — type は必須です。基本タイプ(cart、checkout、my-account、shop、product-category、product、product-loop、404、search、category、tag、author、archive)、WooCommerce の 10 個のエンドポイントタイプ(order-received、order-pay、add-payment-method、orders、view-order、downloads、edit-account、edit-address、payment-methods、lost-password)、および (v1.50.0) wpcanai_template_types フィルターで登録されたタイプを受け付けます。
  • 登録タイプに関する注意点。 enum は、wp_abilities_api_init(init の優先度 1 から実行)で Ability が登録される際に構築されます。ファイルスコープで追加された wpcanai_template_types フィルター(ドキュメントに記載され、リゾルバー自身のドキュメントにも示されている方法)は、その時点で有効になっているため、追加したタイプは受け付けられます。デフォルトの優先度 10 で init コールバック内に追加した場合は、まだ登録されていないため、リゾルバー自体は正しく解決できても、スキーマによってタイプが拒否されます。ファイルスコープで登録してください。
  • 戻り値: { "content_post_id": int, "content_post_type": string, "rendering_mode": string, "template_post_id": int|null, "unreachable_post_id": int, "resolution_reason": string }。
  • (v1.50.0)content_post_id は実際にレンダリングする投稿です。 Delegate-body:レイアウトラッパーが適用されているため、WooCommerce ページが本文を提供します。content_post_id はそのページで、rendering_mode は page-rendered です。Template-body:そのタイプのテンプレートはレイアウトラッパーではないため、ページ全体を自らレンダリングします。content_post_id はテンプレートで、rendering_mode は template-rendered です。また、委譲先ページの _canai_html(存在する場合)はレンダリングされないコンテンツとして unreachable_post_id に報告されます。どちらが適用されたかは resolution_reason に記載されます。content_post_id を編集してください。v1.50.0 より前は、テンプレートがレンダリングされる場合でも委譲先ページが返されていたため、実際にはレンダリングされないメタデータを編集してしまう可能性がありました。
  • rendering_mode には 5 つの値があります — page-rendered(delegate-body)、template-rendered(template-body)、(v1.50.1) blank と none、および (v1.58.0) page-rendered-unwrapped です。Blank = レイアウトポインターが、すでに存在しない投稿を指しており、かつ委譲先ページ自体に _canai_html がないため、CanAI 固有のコンテンツは何もレンダリングされません。参照切れのポインターを修正してください(wpcanai-scan はこれを broken_layout として警告します)。Page-rendered-unwrapped(v1.58.0)= レイアウトポインターは参照切れですが、委譲先ページには独自の _canai_html があります。この場合、コンテンツが失われるのではなく、レイアウトのラッパーやクロームなしで CanAI がコンテンツを直接レンダリングします。content_post_id/content_post_type は page-rendered と同様に委譲先ページを示しますが、参照切れのポインターは修正する価値があります(wpcanai-scan は同じ broken_layout として警告します)。None = CanAI はこのタイプをまったくレンダリングしません。blank と none はどちらも content_post_id: 0 と content_post_type: "" を持ち、どちらに該当するかは resolution_reason に記載されます。v1.50.1 より前は、どちらも template-rendered/wpcanai_template と報告され、存在しないレンダラーを示していました。content_post_id がゼロ以外でない限り、この 2 つのフィールドを意味のある値として扱わないでください。v1.58.0 より前は、参照切れのレイアウトポインターは、委譲先ページの独自コンテンツがラッパーなしで実際にレンダリングされる場合でも、常に blank と報告されていました。
  • (v1.50.1)エンドポイントタイプは親のテンプレートを継承します。 WooCommerce エンドポイントがリクエストを処理するのは、そのタイプの公開済みテンプレートが存在する場合のみです。存在しない場合、リクエストは親タイプ(order-received/order-pay では checkout、ほかの 8 つでは my-account)を通じてレンダリングされます。resolve-content-id もこれに合わせて動作するため、独自テンプレートがないエンドポイントは、レンダリングできない委譲先ページではなく、template-body で親のテンプレートを報告します。v1.50.1 より前はそのページが報告されており、同じ投稿について wpcanai-scan 自身が出す unreachable_content 警告と矛盾していました。
wpcanai-scan
  • 引数: { } — 引数はありません。

  • 戻り値: severity、type、message、任意の post_id を含む指摘の array。

  • 指摘の種類(v1.50.0)。 WooCommerce ブロック(5 つの基本的な構造タイプ shop、product-category、cart、checkout、my-account)は、各タイプを実際のサイトの状態に照らして検証します。どちらの状態もエラーではありません。

    重大度種類意味
    warningunreachable_contentどちらの状態でもレンダリングされない、作成済みの _canai_html です。Template-body:テンプレートがレンダリングされる一方、WooCommerce ページにも本文が残っています。(v1.50.1) Delegate-body:タイプテンプレートがページ独自の _canai_layout によって置き換えられ、本文も残っています。使用されない投稿と、優先される投稿の両方を示します。
    warningbroken_layout(v1.58.0) レイアウトポインターが、すでに存在しない投稿を参照しています。CanAI はこれを「レイアウトなし」として解決し、委譲先ページ独自の _canai_html をラッパーなしで直接レンダリングします(レイアウトの装飾は付きません)。委譲先に本文があれば空白にはなりません。本文がなければ、CanAI 固有のコンテンツは何もレンダリングされません。いずれの場合も、参照切れのポインターを修正するかクリアしてください。v1.58.0 より前は、ページが常に空白でレンダリングされるという(現在は修正済みの)想定に基づき、critical でした。
    warningdelegate_mismatchテンプレートで宣言された _canai_delegate_page_id 自体に問題があります。投稿が存在しないか、page ではありません。状態とは独立して確認されます。宣言されたオーバーライドの破損自体が、解決された状態が delegate-body にならない理由である可能性があります。宣言されたオーバーライドを修正してください。現在解決される ID に付け替えないでください。その ID は同じ宣言値から派生しているため、両者を比較しても実際の不具合は見つかりません。
    warningempty_delegate_pageDelegate-body で、かつこのタイプの wpcanai_template が存在する場合:本文を保持するはずのページに _canai_html がありません。このタイプは意図的に設定されているため、空のページは実際の欠落を示します。
    warningduplicate_type_template同じ template_type タームを持つ公開済みテンプレートが複数あります。最初のテンプレートだけがレンダリングされ、残りは使われません。テンプレートは言語ごとに分けられません(1 つのタイプにつき 1 つのテンプレートがすべての言語に使用されます)。以前、言語ごとにコピーを保持していたサイトでは、これらのコピーがここに表示されます。余分なものをゴミ箱に移し、代わりに t() で文字列を翻訳してください。
    oktemplate_body_okTemplate-body:テンプレートがこのタイプを直接レンダリングします。下記の汎用スイープが出す template_ok とは異なります。同じ単語ですが、意味は異なります。
    okdelegate_okDelegate-body:ページにコンテンツと解決済みのレイアウトがあります。
    infono_template / wc_pageこのタイプには、まだ何も作成されていません。wpcanai_template がまったくない delegate-body(WC ページはサイトのレイアウト内でラップされてレンダリングされます。新規インストールでよく見られ、設定ミスではありません)か、状態が none(解決される WC ページがない)のどちらかです。
    infoleaky_comment (html)_canai_html に、ページのソース表示に露出する `` マーカーが残っています。セクションラベルを Twig {# #} に変換してください。
    warningleaky_comment (js)_canai_js に、インフラストラクチャのフィンガープリンティング(snippet/webhook/internal/…)のように見える /* */ ブロックがあります。_canai_js は Twig でレンダリングされるため、{# #} を使用してください。
    warningleaky_secret_canai_html / _canai_js / _canai_css に、シークレットのような形式のリテラル(API キー、Bearer トークン、秘密鍵 PEM など)が含まれています。デプロイ前にメタデータから削除してください。
    errortwig_syntax_error_canai_html、_canai_css、または _canai_js を Twig として解析できません。指摘には post_id、field、line、パーサーのメッセージが含まれます。ほかの構造に問題がないという指摘を信頼する前に、これを修正してください。
    汎用スイープ(_canai_html を持つすべての投稿と、product/404/search/category/tag/author/archive タイプの公開済み wpcanai_template)では、独自の検出結果として no_layout、no_content、no_layout_tpl、template_ok(テンプレートが _canai_html と _canai_layout の両方を持つ場合)が出力されます。この template_ok と、上記の WC ブロックの template_body_ok を混同しないでください。両方が 1 つのスキャン応答に同時に現れることがあり、それぞれ意味が異なります。WC ブロックがすでに同じ投稿を unreachable_content として報告している場合、no_layout は warning から info に格下げされ、unreachable_content を相互参照します(v1.50.0 の最終レビューで修正)。その投稿ではテンプレート本文の形状が有効になっているため、_canai_html はすでに到達不能です。また、_canai_layout を割り当てると形状がデリゲート本文に切り替わり、現在表示されているテンプレートが置き換わって、ライブ URL の表示内容が変わります。このようにフラグが付けられた投稿にレイアウトを割り当てるよう案内する前に、ユーザーがその形状変更を望んでいることを必ず確認してください。
  • ナビゲーションの検出結果(プラグイン v1.88.0)。 テンプレートまたはページごとに、一致箇所の line とともに報告されます。v1.88.0 以降、コンテンツスイープの対象には wpcanai_template の投稿も含まれるため、ヘッダー、フッター、レイアウトもチェックされます。hardcoded_lang_prefix と unlocalised_internal_href は、すべての行をメッセージに列挙する検出結果として投稿ごとに 1 件報告されます。これらは Menus と turl() の使用を徹底します:

    重要度種類検出条件修正方法
    warninghardcoded_nav固定された遷移先を持つ <a href> が2つ以上あり、get_menu( を含まない <nav>、または <header> / <footer> 内の <ul> / <ol>(ページ本文内の別のリンクリスト、item.url / post.url などのデータから生成されるリンク、breadcrumb / pagination / table of contents とラベル付けされたナビゲーションは検出されません)wpcanai-write-menu(primary または footer)でリンクを記述し、その場所で get_menu() をレンダリングします
    warninglegacy_menu_locationget_menu() が1.88より前のロケーション名で呼び出されているreplace-in-meta で get_menu('primary') / get_menu('footer') に名前を変更します(Menus のアップグレードに関する注意を参照)。古い名前でも引き続きレンダリングされます
    warningunknown_menu_locationget_menu() がその他の名前で呼び出されているprimary または footer を使用します。CanAI にほかのロケーションはありません
    infomenu_fallback_chainget_menu() の結果が is empty / is not empty で判定され、同じブロック内でもう一度 get_menu() が呼び出されている場合、または get_menu() の結果を反復処理する {% else %} 分岐にリンクがある場合ロケーションは1つだけで、フォールバックは不要です。2回目の呼び出しまたは {% else %} 分岐を削除し、write-menu でメニューを割り当てます
    warningmenu_children_twice (v1.89.0)filter( を使わずに get_menu()(またはそこから設定された変数)を反復処理し、その本体で項目の .children を読み取っているget_menu('primary')|filter(i => i.depth == 0) をループします。フラットリストにはすべてのサブ項目がすでに含まれています
    infomenu_not_assignedそのサイトで該当するロケーションにメニューがない状態で、get_menu('primary') / get_menu('footer') が呼び出されているwpcanai-write-menu { "location": …, "items": […] } でメニューを作成して割り当てます
    infohardcoded_lang_prefixcurrent_lang() == 'ms' ? '/ms' のように言語プレフィックスを手作業で作成している、href の先頭に current_lang() から設定した URL 変数を使用している、または設定済みの言語に対してリテラルの href="/ms/…" を使用している{{ turl('/path/') }} を使用します。現在の言語が自動的に適用されます。get_menu() の URL にはすでに言語が含まれています
    infounlocalised_internal_href複数の言語が設定されているサイトで、リテラルの href="/…" または href="{{ home_url(…) }}" を使用しているリンクを turl() で囲み、現在の言語に対応させます
  • レンダリング警告(プラグイン v1.90.0)。 render_warning(重要度 warning、post_id、field、line を含む)はコンテンツから読み取られるものではありません。ページのレンダリング時にテンプレート自体の Twig が PHP 警告を発生させると、CanAI が記録します。ほとんどの場合、原因は Array to string conversion、つまりリストを出力する {{ }} です。メッセージには行番号とそのソース、修正方法、最後に確認された場所が示されます。1つのキーを出力する({{ item.title }})、ループ処理する、または |join(', ') を適用します。単独の {{ items }} 出力では line は null です(Twig はどの出力か特定できません)。値がリストになっている {{ }} を見つけてください。read-meta(fields + lines)と replace-in-meta で修正します。フィールドを書き込むとそのフィールドの記録は消去されます。ページが再度レンダリングされた場合にのみ再び記録されるため、書き込み直後にリストが空でも、問題がないことの証明にはなりません。ユーザーにページを再読み込みしてもらってから、もう一度スキャンしてください。保存期間は7日間です。同じ記録は read-meta の fields: ["render_warnings"] と、wpcanai-diagnostics の render グループにも表示されます。

  • リンクの検出結果(プラグイン v1.97.0)。 各投稿の _canai_html から読み取られます。Twig コメントは無視されます(通常の href については HTML コメントも無視されますが、`` 内の {{ }} は引き続きレンダリングされるため、検出対象です)。重要度は warning で、post_id、field、line を含みます。slug_url() のリテラルごと、またはリンク先のパスごとに1件検出され、メッセージには該当するすべての行が列挙されます。

    種類発生条件修正方法
    link_unresolved実際の投稿に一致しない、または複数の投稿に一致するリテラルの slug_url('x') または slug_url('x', 'type')(異なる親の下に同じスラッグがある場合)です。どちらの場合も href="" が出力されます。複数の候補は path (ID n) として表示されます。ページの移動先がある場合は、現在の場所が示されますパスを渡す(slug_url('gallery/theatrettes'))か、turl(id_url(<id>)) を使います。一致するものがない場合は、スラッグを確認してください
    link_movedリテラルの turl('/a/b/') またはプレーンな href="/a/b/" のパスが、ページにも投稿にも該当しない一方で、末尾のセグメントが別のパスにある公開済みページ1件のスラッグと一致する場合です。ページの移動後もリンクが残っていますメッセージに示された turl(id_url(N)) にリンクを置き換え、replace-in-meta を使用してください

    link_moved は、移動したことが確実なページだけを報告します。報告されないもの:何も存在しないパス(タイプミス)、アーカイブ、ターム、ショップ、アカウントのパス、末尾のセグメントを複数の子ページが共有するパス(grep してください。wpcanai-update-page を参照)、サブディレクトリにインストールされたサイトで、その外部を指すプレーンな href(/blog/ にあるサイトの /about/ など)。先頭の /<lang>/ は、ルーターがリクエストを処理する方法と同じように、最初に取り除かれます。デフォルト以外の言語のプレフィックスだけが対象なので、英語がデフォルトのサイトにある /en/about/ は、そのまま検査されます(プラグイン v1.97.1)。(v1.97.1) インラインの <script> / <style> 内にあるプレーンな href は読み取られません。

  • v1.50.0 で削除: missing_delegate と stale_template_html。どちらも、テンプレートはページに委譲する空のマーカーでなければならないと判定していましたが、これは有効な形のひとつにすぎず、必須のルールではありません。正常に動作するサイトでも発生し、「空にするべき」という案内に従うと、描画コンテンツが削除されてしまいます。このチェックを再導入しないでください。

wpcanai-grep-content
  • 引数: { "pattern": string, "regex"?: bool, "fields"?: string[], "limit"?: int, "post_id"?: int, "max_length"?: int, "context_lines"?: int } — pattern は必須です。デフォルトではリテラルの部分文字列に一致します。regex: true を指定すると、pattern は PCRE として扱われます。fields は html|css|js のサブセットです(デフォルトではすべて)。limit は 1~100(デフォルトは 100)です。(v1.71.0) post_id を指定すると、検索対象が1つの投稿に限定されます(サイト全体の検索を省略します。ページがわかっている場合は必ず指定してください)。max_length(1~2000、デフォルトは 200)はスニペットの長さを制限します。context_lines(0~10)は前後の行を追加します。入力が不正な場合は invalid_regex / invalid_input エラーが返されます。
  • 戻り値: { "matches": [{ "post_id", "field", "line", "column", "line_length", "match", "before"?, "after"? }], "truncated": bool } — match はヒット位置を中心とした max_length バイトのウィンドウです(切り詰められた端は … で示されます)。line_length ≤ max_length の場合は行全体がバイト単位で正確に返され、そのまま replace-in-meta の from に貼り付けられます。長さが 300~400 バイトになることがある Tailwind の行でアンカーが必要な場合は、max_length: 1000 を渡してください。
  • 用途: 問題を起こすコメント、壊れたアセット URL、クラス名の変更など、すべての投稿を列挙して read-meta で1件ずつ確認するのが現実的でない場合に、サイト全体を検索します。返された一致箇所を使って wpcanai-replace-in-meta で修正します。
wpcanai-get-wc-page-ids
  • 引数: { } — 引数はありません。
  • 戻り値: { "cart": int, "checkout": int, "myaccount": int, "shop": int }(WooCommerce のページ ID。未設定の場合は 0)。
wpcanai-get-wc-css-reference
  • 引数: { "context": string, "include_core"?: boolean } — context は cart、checkout、order-received、order-pay、myaccount-dashboard、myaccount-orders、myaccount-view-order、myaccount-downloads、myaccount-edit-account、myaccount-edit-address、myaccount-payment-methods、myaccount-add-payment-method、myaccount-lost-password のいずれかです。include_core(デフォルトは false)を指定すると、third_party_hooks がコンテキストのフックに登録されたすべてのコールバックに拡張されます(WooCommerce コアを含みます)。各項目には引き続き is_third_party フラグが含まれます。lang パラメーターはありません。
  • 戻り値: css_reference(文字列 — コンパイル済み CSS ではなく、生の SCSS ソースです。Sass の変数/ミックスインは未解決のまま返されるため、貼り付けずに言い換えてください。インストール済み WooCommerce の woocommerce.scss、woocommerce-layout.scss、woocommerce-smallscreen.scss、forms.scss から取得した、そのコンテキストにおける WooCommerce のデフォルトルールです。詳細度を照合できるよう、完全な祖先セレクターの連鎖とともに出力されます。smallscreen ルールは @media only screen and (max-width: 768px) で囲まれて返されます)、css_fallback(null、またはセレクターの抽出で一致が見つからず、連結されたスタイルシート全体が返された場合は "full-file")、template_files(文字列配列、ABSPATH からの相対パス — 正式なマークアップのパスです。テーマによるオーバーライドが優先されます)、templates_missing(文字列配列、コンテキスト相対のパス。想定されているものの読み取りできないテンプレートです。template_files とは集合として比較できません。空でない場合、コンテキストマップがインストール済み WooCommerce と異なっています)、hooks_found(文字列配列 — テンプレートが実行するアクション)、third_party_hooks(オブジェクト {hook, priority, callback, file, is_third_party} — コア/テーマ/CanAI 以外のコールバックのみ。include_core が true の場合はすべてのコールバック)、context_echo({context, include_core})。
  • 読み取り専用。静的解析のみです。 レンダリングは行われず、注文/カート/顧客データも読み取りません。出力の使用方法については、上記の「WooCommerce ブロックのスタイリング」ワークフローのセクションを参照してください。
wpcanai-create-page
  • 引数: { "title": string, "slug"?: string, "status"?: string, "html"?: string, "css"?: string, "js"?: string, "layout"?: int, "parent"?: int, "menu_order"?: int } — title は必須です。オプションの _canai_* メタデータを指定して page 投稿を作成します。status のデフォルトは publish です。
  • (v1.96.0)parent / menu_order。 parent は、新しいページをその下にネストする既存ページの ID です。URL は /<parent path>/<slug>/ になり、slug は末尾のセグメントのみです(gallery/auditorium ではなく auditorium)。デフォルトの 0 はトップレベルです。menu_order は「ページ属性」→「順序」です(デフォルトは 0)。どちらも整数である必要があります。整数以外はツールの入力スキーマによって拒否され(ability_invalid_input。例:input[parent] is not of type integer.)、何も書き込まれる前に処理が中止されます。存在しない、ゴミ箱に移動されている、またはページではない parent を指定すると、同じく何も書き込まれる前に invalid_parent になります。ルール 4 の ネストされたページ を参照してください。
  • 戻り値: { "post_id": int, "slug": string, "format": string, "warnings": string[], "status": string, "parent": int, "path": string, "url": string }(status ~ url は v1.96.0 から)。path に先頭または末尾のスラッシュは含まれません(gallery/auditorium)。スラッグなしで保存された下書きでは '' です。新しいページへのリンクには、その ID(turl(id_url(<id>)))または返された slug/path に対する slug_url() を使用してください。url をテンプレートに貼り付けたり、自分で組み立てたパスを使ったりしないでください。
  • 警告(v1.96.0、致命的ではありません): 同じ親ページの下ですでに使われている、または予約済みのスラッグのため、WordPress がサフィックス付きのスラッグ(auditorium-2)を保存しました。リンクを作成する前に slug/path を読み戻してください。親ページが公開されていないため、訪問者はそのページを開けませんが、新しいページの URL は引き続きその親ページを経由します。下書きでは送信したスラッグが保持されます。ページの公開時に WordPress が重複を解消します。
wpcanai-update-page(プラグイン v1.96.0)

ページのタイトル、スラッグ、ステータス、親ページ、順序を更新します。MCP 経由で、ブロックエディターの「概要」および「ページ属性」パネルを操作します。

  • 引数: { "post_id": int, "title"?: string, "slug"?: string, "status"?: "draft"|"publish"|"pending"|"private"|"future", "parent"?: int, "menu_order"?: int } — post_idと、少なくとももう1つのフィールドが必須です。送信したフィールドのみが変更されます。parent: 0を指定すると、ページが最上位に移動します。slugはURLの最後のセグメントのみです。wpcanai-list-pages { "all_pages": true }(v1.97.0)でpost_idを確認してください。通常のページと下書きページが含まれます。
  • **(v1.97.0)future**は、予約済みページから取得したpreviousを再送できるよう受け付けられます。このツールは日付を受け取りません。WordPressでは、日付が未来の場合にのみページの予約状態が維持され、それ以外の場合はすぐに公開されます。日付が設定されていない下書きまたは保留中のページには現在時刻が設定されるため、futureを指定すると公開されます。同じルールにより、日付が未来のページではpublishがfutureになります。レスポンスのstatusは、ページに最終的に設定された状態です。
  • 本文には一切触れません。 post_contentとすべての_canai_*メタはそのまま維持されるため、Twig、ブロック、通常のWordPressページで同じように動作します。ページの名前変更、スラッグ変更、公開、非公開化、移動、並べ替えには、write-meta/write-pageではなく、このツールを使用してください。
  • エラー — 書き込み前にすべて拒否されます。 整数以外の値(および不明なステータス)は、ツールの入力スキーマによって拒否されます(ability_invalid_input、例:input[parent] is not of type integer.)。続いて、not_a_page(IDが存在しない、ゴミ箱に入っている、またはpageではない。ブログ投稿にはwpcanai-write-postを使用)、invalid_input(post_id以外のフィールドがない、titleまたはslugが空)、invalid_parent(親が存在しない、ゴミ箱に入っている、ページではない、またはそのページ自体)、parent_loop(409 — 指定した親がこのページの配下にあります。先にこのページを別の場所へ移動してください。WordPress単体では親を最上位に黙ってリセットし、成功を報告します)。
  • 返される値: { "post_id", "title", "slug", "status", "parent", "menu_order", "path", "url", "edit_url", "previous": { "title", "slug", "status", "parent", "menu_order", "url" }, "warnings": string[], "purged" }。previousには呼び出し前の値が格納されます。ページにスラッグがなかった場合(スラッグのない下書き)、slugは省略されます。また、**(v1.97.0)**ページにタイトルがない場合(またはタグやスペースのみなど、サニタイズ後に空になる場合)、titleも省略されます。
  • 元に戻すには、previousを再送します。 { "post_id": <id>, …previous }を指定すると、タイトル、スラッグ、ステータス、親、順序が元に戻ります。追加のurlキーは無視されます。スナップショットには_canai_*メタのみが保存され、WordPressのリビジョンに記録されるのはタイトルまでで、スラッグ、ステータス、親、順序は記録されません。そのため、元に戻す方法はpreviousのみです。ユーザーが問題ないと判断するまで保持してください。previous.statusがfutureの場合、そのまま再送されます(上記のfutureを参照)。1.96.xでは、previousに空のtitleやfutureステータスが含まれることがあり、どちらも再送すると拒否されます。これらは省略し、予約済みページの再予約は所有者にエディターで行ってもらってください。
  • 警告(処理は続行されます):
    • URLが変更されました — 公開中(または非公開)のページのスラッグまたは親が変更されました。いくつのサブページも一緒に移動したか、またメニュー、id_url()、スラッグのみのslug_url()リンクはページに追従する一方、slug_url()のパスリンクとハードコードされたパスは追従しないことが示されます。WordPressはページの古いURLからのリダイレクトを保存しません(コアの404推測機能を介してリダイレクトするサイトもありますが、当てにしないでください)。そのため、警告に示された方法でリンクを探してください。古いパスをスラッシュなし(gallery/auditorium)でwpcanai-grep-contentに指定すると、turl('/gallery/auditorium/')、href="/gallery/auditorium/"、slug_url('gallery/auditorium')(移動後にhref=""としてレンダリングされます)のすべてを検出できます。各該当箇所をwpcanai-replace-in-metaで修正してください。(v1.97.0)その後、wpcanai-scanで残っているものがlink_moved/link_unresolvedとして報告されます(wpcanai-scanのリンク検出結果を参照)。
    • 非公開になりました — 公開中(または非公開)のページが現在draft/pendingです。古いURLは訪問者が開けなくなりました。
    • スラッグに接尾辞が追加されました — WordPressが別のスラッグ(x-2)を保存しました。同じ階層のページがすでに使用しているか、予約済みのスラッグです。slugを送信していなくても発生することがあります。これは、コアが公開中ページのスラッグを保存のたびに、また下書きを公開する際に再確認するためです。
    • 親が公開されていません — wpcanai-create-pageと同様です。
  • キャッシュ。 公開中のページのスラッグまたは親を変更し、変更後も公開中である場合、ページキャッシュ全体が消去されます(purged.scope: "all")。キャッシュされたメニューやサブページのリンクはすべて古いURLを指すためです。タイトルの変更では、そのページのキャッシュが消去されます。**(v1.97.1)**同じルールがすべてのライター(wp-admin、write-post)に適用されます。URLの移動時にすべてのキャッシュが消去されるのは、階層型の投稿タイプ(ページ)またはメニュー内の投稿のみです。それ以外の投稿では、その投稿のキャッシュのみが消去されます。
wpcanai-write-post
  • 引数: { "post_id"?: int, "title"?: string, "blocks"?: object[], "slug"?: string, "status"?: string, "excerpt"?: string, "date"?: string, "featured_image"?: int, "categories"?: string[], "tags"?: string[] }。
    • 作成(post_id なし):title と blocks が必須です。更新(post_id を指定):送信したフィールドのみが変更されます。ID はページではなく投稿のものである必要があり、ページの ID を指定すると WP_Error('not_a_post') が返されます。
    • デフォルトのステータスは draft であり、publish ではありません。これは wpcanai-create-page とは異なります。所有者がまずエディターで文章を確認することを想定しています。有効な値:draft、publish、pending、private、future。
    • blocks は本文全体を置き換えます。部分編集には対応していません。リスト全体を再送してください。元に戻すには WordPress のリビジョンを使用します。
    • categories/tags には名前またはスラッグを指定できます。存在しないものは作成され、更新時にはセット全体が置き換えられます。
  • 戻り値: { "post_id": int, "slug": string, "status": string, "url": string, "edit_url": string, "block_count": int, "warnings": string[] }。warnings は致命的ではありません(解決できない埋め込みプロバイダー、代替テキストのない画像、クラシックエディターの本文を変換した場合)。致命的な問題がある場合は WP_Error が返され、何も書き込まれません。
  • ブログ投稿専用です。 固定ページには引き続き wpcanai-create-page と wpcanai-write-meta(CanAI HTML/CSS/JS メタ)を使用してください。write-post は投稿の post_content にネイティブブロックのマークアップを書き込み、CanAI メタには触れません。

ブロックの種類。 blocks 内の各項目は { "type": …, …fields } です。

typeフィールド
paragraphtext(必須)、align(left/center/right)
headingtext(必須)、level は 2~6(デフォルトは 2、level 1 は拒否されます — 投稿タイトルが h1 です)
listitems string[](必須)、ordered bool。フラットなリストのみ
quotetext または paragraphs string[](いずれか必須)、cite
imageid int(必須 — 添付ファイル ID)、alt、caption、size(デフォルトは large)、link(none/media/attachment)
codecode(必須)。リテラルテキストとしてエスケープされます。インライン HTML は使用できません
separator—
embedurl(必須、http/https)
tablerows string[][](必須、長方形)、header string[]
groupblocks(必須、子ブロック)、tag(div/section/header/footer/aside/main)、className
columnscolumns(必須、{ width?, className?, blocks } を 1~6 個)、className
buttonsbuttons(必須、{ text, url, style?: fill/outline, target?: _blank, rel?, className? })、className
coverimage(必須、添付ファイル ID)、overlay_opacity(10~100、10 刻み、デフォルトは 50)、blocks(必須)、className
media_textimage(必須、添付ファイル ID)、side(left/right)、blocks(必須)、className
spacerheight px(1~1000、デフォルトは 100)、className
chartchart(必須:bar/horizontal-bar/line/area/pie/doughnut/scatter/radar)、labels string[](scatter を除き必須 — データポイントごとに 1 つ)、datasets(必須、{ label, data } を 1~8 個。data は有限数値で構成し、欠損値には null を指定します。ラベルごとにちょうど 1 つ必要です。scatter の場合は [{ x, y }] で、labels は警告とともに無視されます。pie/doughnut ではデータセットがちょうど 1 つ必要です)、title、caption、unit(目盛り/ツールチップ/テーブルの接尾辞。例:ms、%)、stacked bool、y_min/y_max number、height px(160~800、デフォルトは 320)、source { text?, url? }(「Source: …」として表示)、table(デフォルトの collapsed/open/hidden)、className。文字列はプレーンテキストです(インライン HTML は除去されます)。シリアライズ後の labels + datasets は 64 KB までです。プラグイン v1.77.0。ブロックページでは wpcanai-write-page でも使用できます

embed を除くすべての種類で className を指定できます(ブロックのルート要素に Tailwind クラスを適用します)。embed は初回保存時に独自のクラスリストを書き換えます。ネストは最大 4 レベルです。ネスト内のエラーでは、ドット区切りの path(例:"2.blocks.0")が報告されます。 画像:まずサイドロードしてから、ID を渡します。 write-post が受け付けるのは添付ファイル ID のみです。URL やバイナリは受け付けません(cover と media_text も同じ ID を受け取り、解決できない場合は invalid_attachment を返します)。wpcanai-sideload-url を呼び出すか(または wpcanai-list-media で既存のものを検索し)、返された id を渡してください。

テキストフィールド内のインライン HTML は、<a href|title|rel|target>、<strong>、<em>、<b>、<i>、<code>、<br>、<s>、<sub>、<sup>、<kbd>、<mark> に制限されています。それ以外は拒否されず、黙って削除されます。段落内の <script> や <iframe> は、いずれにしても無効なブロックコンテンツです。Markdown は受け付けられず、write-post に生 HTML を使用する回避手段はありません。

wpcanai-get-menus(plugin v1.88.0)
  • 引数: { }。
  • 戻り値: { "locations": { "primary": { "menu_id", "name", "items" } | null, "footer": { … } | null }, "languages": { "<lang>": { "primary": { "menu_id", "name", "items" } | null, "footer": … } }, "menus": [{ "id", "name", "slug", "assigned_to": ["primary"], "languages": { "<lang>": ["footer"] } }], "legacy_assignments": { "<old location>": <menu id> }, "translations": { "<lang>": { "translated", "total" } } }。項目は { "id", "title", "type", "object", "object_id", "url", "target", "classes", "description", "attr_title", "icon", "image", "children" } で、生のままで翻訳されていません(見出しの url は "" です)。(v1.89.0) description/attr_title/icon/image(添付ファイル ID。ない場合は 0)。languages にはデフォルト以外のすべての言語が含まれ、翻訳されたデフォルトメニューを使用する場所は null になります。また、menus[].languages には言語ごとの割り当てが示されます。translations は、言語ごとに、その言語独自のメニューが設定されていない場所について、翻訳済みの項目タイトルと説明の数をカウントします。legacy_assignments には、1.88 より前の場所名で割り当てられたままのメニューが一覧表示されます(Menus のアップグレードに関する注記を参照)。
  • 各場所にすでに設定されているメニューを確認するため、write-menu の前に呼び出してください。
wpcanai-write-menu(plugin v1.88.0)
  • 引数: { "location": "primary"|"footer", "items": [{ "title": string, "page"?: int|string, "url"?: string, "target"?: ""|"_blank", "classes"?: string, "description"?: string, "attr_title"?: string, "icon"?: string, "image"?: int, "children"?: [ …same shape ] }], "merge"?: bool, "lang"?: string } — location と items は必須です。各項目には空でない title が必要で、page(公開されている任意の投稿タイプの投稿 ID またはスラッグ → スラッグが変更された場合も追従するページ項目)か url(サイト相対または絶対 URL のカスタムリンク)のいずれか一方のみを指定します。ただし、見出しは例外です。children を持つ項目ではどちらも指定できません(ラベルとして表示され、url: "" になります)。ネストは最大 3 階層です。
  • (v1.96.0)スラッグによる page。 まず完全なパス(gallery/auditorium)を照合し、次に公開されているページが 1 件だけ持つ単独のスラッグを照合します。これにより、子ページは自身のスラッグで解決され、移動後も引き続き解決されます。単独のスラッグが同じ場合は、トップレベルのページが優先されます(プラグイン v1.97.1 以降では、公開済みのページも下書きの同名ページより優先されます)。(v1.97.0) 複数の子ページで共有されている単独のスラッグは ambiguous_page になります。メッセージには各ページが path (ID n) の形式で表示されます。パスまたは ID を指定してください。他の投稿タイプへのフォールバックはなくなりました。ページに存在しないスラッグだけが他の公開投稿タイプで検索されます(そこで複数の投稿が見つかった場合も ambiguous_page になります)。該当するページがない場合は unknown_page になります。同じスラッグを持つ未添付画像によってページが隠されることはなくなりました。
  • (v1.89.0)リッチフィールド。 description(1 行)、attr_title、icon(^[a-z0-9-]{0,64}$ に一致する Lucide 名)、image(画像である添付ファイルの ID)。merge の場合、省略したフィールドは値を保持し、"" / 0 は値をクリアします。
  • (v1.89.0)lang — デフォルト以外の言語スラッグを指定すると、デフォルトのメニューではなく、その言語専用のメニューを指定した場所に書き込みます(言語ごとのメニューを参照)。最初の呼び出しでは Primary (<native name>) / Footer (<native name>) を作成し(同名のメニューがあれば再利用)、その言語に割り当てます。デフォルトの場所には影響せず、結果には lang が含まれます。デフォルトまたは不明なスラッグ、あるいはネイティブ翻訳のないサイトの場合は、invalid_lang になります。
  • (v1.93.0)lang とともに copy_default: true を指定すると(items は指定しません)、その言語専用のメニューを、指定した場所にデフォルトのメニューのコピーとして作成します。ラベル(タイトル、説明)には、保存済みの翻訳があればそれを使用します。ページ項目と投稿項目は元の投稿にリンクされたままとなり、それ以外の項目は現在の URL を持つカスタムリンクになります。その言語専用のメニューがすでに存在する場合は menu_exists(data: menu_id)、その場所にデフォルトのメニューがない場合は no_default_menu、lang が指定されていない場合は invalid_input を返します。それ以外のすべての呼び出しでは items が必須です。
  • デフォルトでは置き換えます。 指定した場所の項目は、指定された順序とネストどおりに items と完全に一致するようになります。items: [] を指定するとメニューが空になります。merge: true の場合は既存の項目を保持し、同じ親の下にある同じ title の項目を更新して、残りを追加します。
  • 場所ごとにメニューは 1 つです。 指定した場所にすでに割り当てられているメニューに書き込み、名前は変更しません。メニューが割り当てられていない場所に限り、新しいメニューを Primary / Footer という名前で作成し、その場で割り当てます。メニューを削除するツールはありません。メニューの削除は管理者が wp-admin で行います。
  • 書き込みの前にツリー全体を検証します。 primary / footer 以外の location は入力検証で拒否されます。エラーには、unknown_page(解決先がない page。先にページを作成してください)、ambiguous_page(v1.97.0。複数のページで共有されているスラッグ。パスまたは ID を指定してください)、invalid_item(title がない、page と url の両方が指定されている、children を持たない項目でどちらも指定されていない、target が "" / "_blank" 以外、icon が無効、image が画像添付ファイルではない)、too_deep(3 階層を超えている)があります。いずれの場合もメニューは変更されません。
  • 戻り値: get-menus が報告する指定場所の状態(menu_id、name、items)に加えて、written(書き込まれた項目数)と purged を返します。メニューはすべてのページに表示されるため、書き込みによってページキャッシュ全体が消去されます(ツールラベルを参照)。
wpcanai-assign-menu (plugin v1.89.0)
  • 引数: { "location": "primary"|"footer", "lang"?: string, "menu_id": int|null } — ロケーション(または lang を指定して、1つの言語のロケーション)に既存のメニューを割り当てます。IDは wpcanai-get-menus で一覧表示できます。メニュー項目は書き込みません。その場合は write-menu を使用してください。
  • menu_id: null にすると割り当てが解除されます。言語を指定した場合、その言語は翻訳済みのデフォルトメニューにフォールバックします(その言語独自のメニューは割り当て解除された状態で保持されます)。lang を指定しない場合、ロケーションにはメニューが割り当てられなくなり(v1.88 より前のエイリアスエントリーも同様です)、その後 wpcanai-scan は、表示箇所ごとに menu_not_assigned を報告します。
  • エラー: unknown_menu(IDがナビゲーションメニューではありません)、invalid_lang(デフォルトまたは不明なスラッグ)。戻り値: { "location", "lang" (ロケーション自体の場合は null), "menu_id", "name", "items", "purged" } — 直前に行った割り当てです。lang を指定した場合、その言語独自のメニューが返されます(解除後は menu_id: 0 ですが、言語には翻訳済みデフォルトメニューが表示されます)。lang を指定しない場合は、ロケーションのメニューが返されます。
  • サイト所有者が同等の操作を行う場所は、外観 → メニュー → メニューの位置を管理(デフォルト)と CanAI → 翻訳 → メニュー(言語ごと)です。
wpcanai-read-settings
  • 引数: { "keys"?: string[] } — すべての許可リスト対象オプションを読み取る場合は、keys を省略します。
  • 戻り値: オプションのキー → 値のオブジェクト。
  • 許可リスト対象キー: show_on_front、page_on_front、page_for_posts、blogname、blogdescription、users_can_register、wpcanai_default_layout、wpcanai_tailwind_settings(オブジェクト:load_tailwind は yes|no、source は cdn|plugin(v1.58.2 以降のデフォルトは plugin)、plugins は string[])、wpcanai_chart_settings(オブジェクト、plugin v1.77.0:load_chart は auto|yes|no(デフォルトは auto — Chart.js はチャートを表示するリクエストでのみ読み込まれます。yes は CanAI のすべてのページで読み込み、no は読み込みません。チャートは表のみ表示されます)、source は plugin|cdn(デフォルトは plugin)。部分オブジェクトは保存済みの設定と統合され、不明な値はデフォルト値として扱われます)、wpcanai_markdown_negotiation("1"|"0"。エージェント向け Markdown を参照)、wpcanai_purge_after_write("1"|"0"、plugin v1.80.0、デフォルトはオン — CanAI が書き込みを行った後にページキャッシュを消去します)、woocommerce_enable_signup_and_login_from_checkout、woocommerce_enable_myaccount_registration、woocommerce_cart_page_id、woocommerce_checkout_page_id、woocommerce_myaccount_page_id、woocommerce_shop_page_id。
wpcanai-update-settings
  • 引数: { "settings": { "<key>": <value>, ... } } — 許可リスト対象キーのみ(wpcanai-read-settings と同じ)。page_on_front を設定すると、該当する場合は show_on_front も "page" に設定されます。
  • 戻り値: { "success": bool, "updated": string[], "rejected": [{ "key", "reason" }] } — (v1.80.0) reason は not_whitelisted、invalid_type、invalid_value のいずれかです。何も更新されなかった場合、呼び出しは失敗し、エラーメッセージに拒否されたすべてのキーが列挙されます。一部のキーが設定された場合は success が true になり、残りは rejected に入ります。必ず確認してください。
  • (v1.80.0)ブール値。 オン/オフ設定(users_can_register、wpcanai_markdown_negotiation、wpcanai_purge_after_write、WooCommerce の登録切り替え設定)は、true/false、1/0、"1"/"0"、"yes"/"no" を受け付けます。このツールおよび他のすべてのツールのブール値入力では、文字列 "false" は false として扱われます。
  • 読み取り側は引き続き寛容です。 許可リストにないキーのみを指定して read-settings を実行すると、エラーにならず許可リスト全体が返されます。データを含むレスポンスが返ってきても、指定したキーの存在確認にはなりません。書き込み時に rejected を確認してください。
wpcanai-setup
  • 引数: { }。
  • 戻り値: { "success": bool, "steps": array } — 各ステップには action、status(done | skipped | error)、detail(推奨されるブートストラップの内容:テーマ、レイアウト、ヘッダー、フッター、ホームページ、フロントページのオプション、デフォルトのレイアウト、Tailwind の設定)が含まれます。
wpcanai-get-option
  • Args: { "name": string } — wp_options.option_name。
  • Returns: { "name": string, "value": mixed }。
  • Errors (v1.89.3): option_not_readable は、オプション名とその理由を示します。…は MCP の拒否リストに登録されており、MCP 経由では読み取れません — 中止してください。意図的にブロックされており、CanAI がサポートする設定については wpcanai-read-settings で確認できます。…は MCP の読み取り許可リストに登録されていません — CanAI → AI Client → Guardrails で追加する正確な名前をユーザーに伝え、確認後に一度だけ再試行してください。推測で繰り返し試さないでください。
  • Policy: 名前は WP Admin → CanAI → AI Client → Guardrails の読み取り許可リストに登録されている必要があります。拒否リストは危険なキー(例:active_plugins、cron)をブロックします。wpcanai_mcp_ で始まる名前は常にブロックされます(MCP の内部情報)。wp-config.php で WPCANAI_MCP_OPTIONS_UNRESTRICTED が定義されている場合、拒否リストにない他の名前はすべて読み取ることができます。
wpcanai-update-options
  • Args: { "options": { "<option_name>": <value>, ... } } — JSON で扱えるスカラー値と配列のみ。
  • Returns (applied): { "success": true, "status": "applied", "updated": string[], "pending_id": null }。
  • Returns (queued): { "success": true, "status": "pending_approval", "pending_id": string, "option_keys": string[], "message": string } — 管理者が WP Admin → CanAI → AI Client → Guardrails → Pending MCP option updates で Approve をクリックするまで、データベースへの書き込みは行われません。
  • Policy: Guardrails タブで、自動適用リストと承認が必要なリストを設定してください。名前が両方のリストにある場合は、承認が優先されます。リクエスト内に承認が必要なキーが1つでもある場合、options オブジェクト全体が1件の保留中リクエストとしてキューに登録されます。名前は少なくとも1つの書き込みリストに登録されている必要があります(制限なしモードの場合を除く)。拒否リストは get-option と同じです。
wpcanai-get-pending
  • Args: { "pending_id": string } — status が pending_approval の場合に wpcanai-update-options が返す値。
  • Returns: { "pending_id", "status": "pending"|"unknown", "created_at"?: int, "option_keys"?: string[], "message"?: string } — 承認、拒否、期限切れ、または無効な ID の後は unknown になります。wp-admin でユーザーに承認を依頼した後のポーリングに使用してください。
wpcanai-i18n-get-settings
  • Args: { }。
  • Returns: { "default": string, "languages": [{ "slug", "locale", "native_name", "hreflang" }], "enabled": bool, "site_locale": string } — デフォルト以外の言語が1つ以上設定されている場合、enabled は true です。(v1.92.0) locale は、その言語が選択されたときの WordPress ロケール(ms_MY)です。WordPress の一覧にない言語、および v1.92.0 より前に保存された言語の場合は "" です。(v1.95.0) site_locale は WordPress のサイトの言語(Settings → General。英語(米国)の場合は en_US)で、デフォルト言語は常にこれになります。これはルーターのプローブです。翻訳リクエストでは必ず最初に呼び出してください(翻訳モデルのルーターを参照)。
wpcanai-i18n-set-settings
  • Args: { "languages": [{ "slug"?: string, "locale"?: string, "native_name"?: string, "hreflang"?: string }], "default"?: string } — languages は必須です。言語リスト全体を置き換えます。 新しい言語だけでなく、すべての言語を含めてください。(v1.95.0) default は省略可能です。以下のデフォルトはサイトの言語を参照してください。

  • (v1.92.0) WordPress ロケールを渡してください。 各言語には slug または locale(WordPress ロケール:ms_MY、ja、pt_BR、en_US)が必要です。管理画面の言語選択と同様に、ロケールを指定すると、省略した値が自動的に補完されます。

    • slug:ISO 639-1 コード。同じコードを使用する別の言語がすでにある場合に限り、地域を付加します(pt の後に pt-pt)。
    • native_name:ロケールに対する WordPress 独自の言語名。en_US の場合は「English」です。
    • hreflang:言語コードのみ(ms)。同じコードを使用する別の言語がある場合は、言語コードと地域を指定します(pt-PT)。 つまり、{ "languages": [{ "slug": "en" }, { "locale": "ms_MY" }] } を指定すると、マレー語が ms / Bahasa Melayu / ms として追加されます。既存の言語で locale を省略した場合は、保存済みの値が保持されます。"locale": "" を指定するとクリアされます。ロケールがない場合、native_name と hreflang はスラッグを既定値とします。
  • (v1.92.0)エラー。 いずれのエラーでも設定は変更されず、error.data.value に問題のある入力が示されます。

    • unknown_locale:WordPress のロケールではありません。メッセージには近い候補が示されます(「th_TH is not a WordPress locale. Did you mean th?」)。
    • missing_slug:スラッグもロケールも指定されていない言語です。
    • invalid_hreflang:hreflang は正規化され(ms_my → ms-MY)、language[-Script][-REGION] の形式である必要があります。また、x-default は拒否されます。
    • duplicate_slug、duplicate_hreflang(大文字と小文字を区別しません)。
    • (v1.95.0) default_is_site_language:default がサイト言語のスラッグではありません(data:site_locale、default =必須のスラッグ、value)。invalid_default に代わるものです。
    • (v1.95.0) site_language_taken:2つ目の言語にサイト言語のロケールが指定されています。既定の言語を翻訳言語にすることはできません。
  • (v1.95.0)既定の言語はサイト言語です。 CanAI 独自の既定の言語はありません。既定の言語(URL プレフィックスなしで、ページの記述に使われる言語)は、WordPress のサイト言語である site_locale です。呼び出しでは、その言語の行が選択されます。

    • まずロケールで選択します(指定された値、またはそのスラッグに保存された値)。
    • 該当する行がなければ、ロケールのない行のうち、スラッグまたは hreflang がサイト言語と一致するものを選択します(en_US のサイトでの en など)。
    • それもなければ、警告を付けて WordPress のリストから先頭に追加します。

    default は検証用としてのみ渡してください。confirm_default_change(v1.92~v1.94)は無視されます。

  • (v1.95.0)既定の言語を変更するには、サイト言語を変更します。 すべての URL が移動します。新しい言語がプレフィックスのないアドレスを引き継ぎ、以前の既定の言語はプレフィックス付きのパス(/en/)に移ります。移動する内容をユーザーに伝え、事前に確認してください。 その後、次のいずれかを行います。

    • サイト所有者が 設定 → 一般 → サイト言語 で変更します。言語パックもインストールされます。
    • または、wpcanai-update-options { "options": { "WPLANG": "ms_MY" } } を呼び出します。これには、WPLANG が update-options の許可リストに含まれている必要があります(CanAI → AI Client → Guardrails。含まれていない場合、ツールは options_not_allowed を返します)。また、そのロケールの言語パックがインストールされている必要があります(インストールされていない場合、WordPress は以前の値を保持します)。英語(米国)には "" を使用します。

    どちらの方法でも CanAI は変更に追従します。新しいサイト言語が既定の言語になり(設定されていない場合は WordPress のリストから追加されます)、以前の既定の言語は翻訳言語として残り、削除される言語はありません。次に管理ページを開いたとき、サイト所有者には移動内容が通知されます。その後、wpcanai-i18n-get-settings を再度読み取ってください。

  • 戻り値: 更新後の設定(wpcanai-i18n-get-settings と同じ形式)。次の場合は warnings: string[] が含まれます。

    • (v1.95.0) サイト言語の行が追加された場合。
    • (v1.95.0) 保存によって既定の言語がサイト言語に変更された場合(v1.95.0 より前に保存され、サイト言語と一致しなかった設定。Languages タブで保存前に警告されるケース)。移動内容も示されます。
    • 削除された言語に翻訳が残っている場合。翻訳は保持され、同じスラッグを再度追加すると復元されます。
wpcanai-i18n-list-strings
  • Args: { "lang": string, "untranslated"?: bool, "search"?: string, "post_id"?: int, "layout"?: bool, "menus"?: bool, "location"?: "primary"|"footer" } — lang は必須です(翻訳列に入力されます)。untranslated: true を指定すると、翻訳が空の文字列のみを保持します。search はソーステキストに対する、大文字と小文字を区別しない部分文字列フィルターです。(v1.88.0) post_id を指定すると、その投稿(ページまたはテンプレート)で使われている文字列のみを保持します。layout: true を指定すると、そのレイアウトチェーン(レイアウト、ヘッダー、フッター)の文字列が追加されます。menus: true を指定すると、メニュー項目のタイトル(および説明、v1.89.0)が追加されます。location を指定すると、1つのロケーションのみが追加されます。(v1.89.0) lang で指定したロケーションにその言語独自のメニューが割り当てられている場合、そのメニューはすでにその言語で書かれているため、何も追加されません。
  • Returns: { "source": string, "translation": string, "post_ids": int[], "menus": string[], "tags": string[], "placeholders": string[] } の array。(v1.94.0) tags は翻訳で保持する必要があるトークンです(["strong"]、["a1","a2"]。ソースのインラインタグは属性なしで source に表示されます)。placeholders は保持する必要がある {names} です。post_ids は、_canai_html/_canai_js にその t() ソースを含む投稿です。(v1.88.0) menus は、そのソースを項目のタイトルまたは説明として使っているメニューのロケーション名です(["primary"]。該当するものがない場合は [])。メニューのみの行では post_ids: [] です。t() のソースは文字列インデックスから取得されます。コンテンツを変更した場合は、まず wpcanai-i18n-rescan を実行してください。メニューのタイトルは呼び出しごとに割り当て済みのメニューから読み取られます。
wpcanai-i18n-set-translations
  • Args: { "lang": string, "translations": { "<source>": "<translated>", ... } } — 1つの言語に対する一括アップサートです。lang は設定済みのデフォルト以外の言語である必要があります(デフォルト言語のテキストがソーステキストです)。空または null の翻訳はエントリーを削除します。(v1.94.0) 値では、ソースのタグ(<strong>…</strong>、<a1>…</a1>、<br/>)と {placeholders} を任意の順序で保持する必要があります(言語に合わせて移動できます)。それ以外のマークアップは取り除かれます。タグを削除、追加、重複、交差させた値や、プレースホルダーを削除した値は保存されません。結果の rejected: [{ "source", "reason" }] に記載されます(例:「Missing .」)。saved には、書き込まれたものだけがカウントされます。ソースキーは保存済みのソースと完全一致(文字単位で一致)する必要があります。前後の空白を削除したり、改行や折り返しを変更したりしないでください。
  • Returns: { "saved": int, "lang": string }。
wpcanai-i18n-get-media-map
  • Args: { "lang"?: string } — すべての言語を対象にする場合は省略します。
  • Returns: 言語スラッグをキーとするオブジェクト:{ "<lang>": { "<original_attachment_id>": <translated_attachment_id>, ... } } — {{ tmedia(id) }} で使われる、言語ごとの添付ファイルの置き換え情報です。
wpcanai-i18n-set-media-map
  • Args: { "lang": string, "map": { "<original_id>": <translated_id>, ... } } — 一括アップサートです。デフォルト以外の lang のみ指定できます。0/null はマッピングを削除します。
  • Returns: { "lang": string, "map": { ... } } — その言語の更新後のマップ全体です。
wpcanai-i18n-rescan
  • Args: { } — _canai_html/_canai_js を持つすべての投稿から文字列インデックスを再構築します。コンテンツを編集した後、および wpcanai-i18n-list-strings の実行前に実行してください。
  • Returns: { "posts_scanned": int }。
wpcanai-i18n-get-post-overrides
  • Args: { "post_id": int, "lang"?: string } — lang を省略すると、すべての言語のデータが返されます。
  • Returns: lang を指定した場合は { "post_id", "lang", "overrides": { title?, content?, excerpt?, fields?: {meta_key: value} } }、省略した場合は { "post_id", "overrides_by_lang": { "<lang>": {...} } }。空のオブジェクトはオーバーライドがないことを示します(デフォルト言語の値が表示されます)。
wpcanai-i18n-set-post-overrides
  • 引数: { "post_id": int, "lang": string, "overrides": { "title"?, "content"?, "excerpt"?, "fields"?: { "<meta_key>": "<value>" }, "seo_title"?, "seo_description"? } } — スパースアップサートです。送信されたキーのみが変更されます。値がnull/""の場合、そのオーバーライドは削除され、デフォルト言語にフォールバックします。最終的に空になった blob は行ごと削除されます。contentはHTMLを受け付けます(wp_kses_post)。title/excerptはプレーンテキストです。fieldsの値もプレーンテキストです。seo_title/seo_descriptionはプレーンテキストで、アクティブなSEOプラグインに渡されます(REFERENCE.md → i18n → SEO translationを参照)。デフォルト言語は拒否されます(invalid_lang)。投稿自体がデフォルトです。不明なトップレベルキーは黙って破棄されます。
  • 戻り値: { "post_id", "lang", "overrides": <resulting blob> }。
  • フィールドキーは書き込み時にsanitize_keyで正規化されます("My_Field"はmy_fieldとして保存/照合されます)。レンダリング時の読み取りパスでも同じように正規化されます。wpcanai-i18n-set-translationsのsavedは、書き込み済みのペアではなく、送信されたペア(削除対象やスキップされた非文字列を含む)の数をカウントします。
wpcanai-i18n-get-term-overrides
  • 引数: { "term_id": int, "lang"?: string }。
  • 戻り値: 投稿版と同じ形式で、blobキーはname/descriptionです。
wpcanai-i18n-set-term-overrides
  • 引数: { "term_id": int, "lang": string, "overrides": { "name"?, "description"?, "seo_title"?, "seo_description"? } } — 投稿版と同じスパース/削除/デフォルト言語のセマンティクスです。descriptionはHTMLを受け付けます(投稿のcontentと同様にwp_kses_post)。nameはプレーンテキストです。seo_title/seo_descriptionはプレーンテキストで、アクティブなSEOプラグインに渡されます(REFERENCE.md → i18n → SEO translationを参照)。
  • 戻り値: { "term_id", "lang", "overrides": <resulting blob> }。
グローバル文字列 — 投稿に紐づかないサイトレベルの文言

サイト名、キャッチフレーズ、アーカイブ/検索/404のSEOタイトルと説明文は、言語ごとのグローバル文字列ストア(オプションwpcanai_i18n_global_strings)に保存されます。これらは単一の投稿に属さないため、投稿/タームのオーバーライドとは別です。デフォルト以外の言語のフロントエンドでは、これらの文字列がoption_blogname/option_blogdescriptionとog:site_name(サイト名/キャッチフレーズ)、およびアーカイブコンテキストsearch、404、post_type:<slug>の<title>とメタディスクリプションに反映されます。タームアーカイブ(カテゴリー/タグ/カスタムタクソノミー)はグローバル文字列のコンテキストではありません。これらにはwpcanai-i18n-set-term-overridesを使用します。

wpcanai-i18n-get-global-strings
  • 引数: { "lang"?: string } — すべての言語を取得するにはlangを省略します。
  • 戻り値(lang指定時、対象言語のみ): { "lang": string, "strings": { "site_name": string, "tagline": string, "archive": { "<context>": { "title", "description" } } } }。未設定の場合、site_name/taglineは""です。戻り値(すべての言語、lang省略時): { "by_lang": { "<lang>": <blob> } }。読み取り専用です。
wpcanai-i18n-set-global-strings
  • 引数: { "lang": string, "values": object } — 両方必須です。valuesのキーは、site_name(プレーンテキスト)、tagline(プレーンテキスト)、archive(コンテキストから{ "title"?, "description"? }へのマップ)です。スパースアップサートです。送信されたキーのみが変更されます。値がnull/""の場合、そのキーは削除されます。最終的に空になったblobは言語の行ごと削除されます。デフォルト言語は拒否されます(invalid_lang)。サイト独自の名前/キャッチフレーズがデフォルトです。アーカイブコンテキストはsearch、404、post_type:<slug>(例:post_type:product)です。タームアーカイブ(カテゴリー/タグ/カスタムタクソノミー)はグローバル文字列のコンテキストではありません。これらの翻訳にはwpcanai-i18n-set-term-overridesを使用してください。
  • 戻り値: { "lang": string, "values": <resulting blob> }。
wpcanai-i18n-list-content
  • 引数: { "lang": string, "post_type"?: string, "untranslated"?: bool } — コンテンツ作業キューです。lang には、設定済みのデフォルト以外の言語を指定する必要があります(デフォルト言語はソースコンテンツです。指定すると invalid_input エラーになります)。attachment を除く公開投稿タイプの公開済み投稿が対象です(最大 1000 行。不明な post_type を指定すると [] を返します。投稿数の多いサイトでは post_type で絞り込んでください)。
  • 戻り値: { "id", "title", "post_type", "status", "kind", "done"?, "total"? } の array です。(v1.91.0) kind に応じて、投稿一覧の「翻訳」列および CanAI → 翻訳 → コンテンツと同じルールを適用します。
    • kind: "content"(CanAI Twig ページ以外のすべての投稿):オーバーライドの blob です。status は、blob がない場合は untranslated、投稿本文が空でないのに content のオーバーライドを含まない blob がある場合は partial、それ以外は translated です。
    • kind: "strings"(空白でない _canai_html を持つ CanAI Twig ページ):ページ独自の t() ソースです。レイアウトは対象外です。done / total は、翻訳済みの件数を表します。status は、すべて翻訳済みの場合は translated、一部の場合は partial、翻訳済みがない場合は untranslated、ページに t() 文字列がない場合は nothing です。タイトルのオーバーライドだけでは、Twig ページは「翻訳済み」になりません。
    • v1.91.0 より前は、すべての行で blob のルールが使われ、kind は含まれていませんでした。
    • untranslated: true を指定すると、未翻訳と一部翻訳済みの行だけを返します。nothing は返しません。
wpcanai-i18n-get-content
  • 引数: { "post_id": int, "fields"?: ["meta_key", ...], "lang"?: string } — post_id は必須です。fields には、含めるカスタムフィールドのメタキーを指定します(sanitize_key で正規化されます。オーバーライドの保存方法と同じです)。省略可能な lang(設定済みのデフォルト以外の言語)を指定すると、その言語の現在のオーバーライド blob も並べて比較できるよう返されます。
  • 戻り値: { "post_id": int, "post_type": string, "source": { "title": string, "content": string, "excerpt": string, "fields": { "<meta_key>": value }, "seo": { "title": string, "description": string } }, "overrides"?: { "<lang>": blob } } — source はデフォルト言語の RAW コンテンツです(タイトル/本文/抜粋は投稿から直接読み取り、指定されたカスタムフィールドはメタデータから読み取ります。seo は有効な SEO プラグインから読み取ったデフォルト言語の SEO タイトル/説明です)。読み取り専用です。これは、wpcanai-i18n-set-post-overrides を呼び出す前にエージェントが翻訳するソースを取得する、eval を使わない方法です。
プリセット — wpcanai-list-presets / wpcanai-install-preset / wpcanai-uninstall-preset
  • wpcanai-list-presets → { presets: [{ slug, title, description }] }。同梱パック(例:cpt-corporate、single-freelancer、blog-journal — ブログ全体を構成し、Blog ページが「Reading → Posts page」になります)は、完全なテンプレートスタック(レイアウト/ヘッダー/フッター+型付きの single-*/archive-* テンプレート)を構築します。
  • wpcanai-install-preset — { "slug": string, "set_front_page"?: bool, "clean_slate"?: bool, "adopt_woo_pages"?: bool, "trash_woo_pages"?: bool }。パックのテンプレートとページをインストールし、画像をサイドロードします。また、(v1.26+)ユーザー向けのコピーを {{ t('…') }} で囲み、languages() スイッチャーを追加します。ネイティブの i18n StringIndex が事前に読み込まれるため、インストール後に言語を設定した場合でも、wpcanai-i18n-list-strings にパックの文字列がすぐに表示されます。パックのテンプレートでは {{preset.image_id.<key>}} を参照して、tmedia() 用のサイドロード済み添付ファイル ID を取得できます。
    • ⚠ clean_slate: true は破壊的な操作です。 インストール前に、既存の CanAI テンプレートとページをすべてゴミ箱に移動し(ゴミ箱から復元できます)、プリセットの管理情報を消去します。この処理は、その後のインストールに失敗した場合でも実行されます。ユーザーの明示的な確認なしに、決して指定しないでください。MCP ホストでは、組み込みのガードなしで、このブール値がツールスキーマに公開されます。
    • adopt_woo_pages(デフォルトは true、v1.47.0)。 settings.woo_pages を持つパックは、重複ページ(checkout-2 など)を作成せずに、ストアの既存の WooCommerce ページにコンテンツを書き込みます。これにより、ページ ID とパーマリンクが維持され、すでに送信済みのメール内にある注文受領 URL も引き続き機能します。各ページの以前の CanAI メタデータはスナップショットとして保存され、wpcanai-uninstall-preset によって復元されます。ページ自体が削除されることはありません。false を指定すると、新しいページが強制的に作成されます(v1.47.0 より前の動作。以前に発行した注文リンクは機能しなくなります)。
    • trash_woo_pages(デフォルトは false)。 既存のショップ/カート/チェックアウト/マイアカウントのページをゴミ箱に移動し(ゴミ箱から復元できます)、パック独自のストアページにそれらのスラッグを割り当てます。この処理は引き継ぎより先に実行されるため、優先されます。引き継ぎ時には枠が空になっており、新しいページが作成されます。
  • wpcanai-uninstall-preset — { "slug": string }。パックのテンプレートとページを削除し、作成したナビゲーションメニューをすべて削除します。
wpcanai-export / wpcanai-import
  • wpcanai-export → CanAI のテンプレートとページ(メタデータ _canai_html/css/js/context/layout)をまとめた JSON バンドルで、各行の i18n_meta キーにより、すべての _canai_i18n_{lang} コンテンツ上書きデータを往復処理できます。(v1.39.0) コンパイル済みビルドがある行には、プリコンパイル済み Tailwind キャッシュ(_canai_tailwind_build / _hash / _built_at)も含まれます(空でないビルドのみエクスポートされ、ソース相対の _canai_tailwind_epoch スタンプは除外されます)。これにより、インポートしたレイアウトは Play CDN にフォールバックせず、インラインでレンダリングされます。メディアバイナリは含まれません — 添付ファイル ID には再サイドロード用のフラグが付けられ、自動的に再マッピングされることはありません。(v1.65.0) ブロックで作成したページも含まれません — エクスポート対象は _canai_html が空でない投稿のみのため、ブロックで作成したページ(本文が post_content にあるもの)はバンドルに含まれません。完全なバックアップ範囲を約束しないでください。(v1.80.0) { "summary": true } を指定すると、バンドルの代わりにインベントリを返します — { templates: [{ id, title, type, status, hashes }], pages: [{ id, title, format, hashes }], counts: { templates, pages, terms }, version }。これは read-meta が返すものと同じ SHA-1 hashes で、コンテンツは含まれません。2 つのサイトの比較や、取得前に変更内容を確認するために使用します。
  • wpcanai-import — { "data": object | "json": string, "dry_run"?: bool }。dry_run: true を指定すると、書き込みを行わずに変更内容を報告します。上書きデータはインポート時に再サニタイズされます。認証ゲート: インポートでは unfiltered_html レベルの書き込みが行われます。呼び出し元に unfiltered_html 権限(単一サイトでは管理者に付与されています)がない場合、またはリクエストに有効な CanAI API キー(Bearer / X-WPCanAI-API-Key)が含まれていない場合、403 forbidden が返されます。
  • (v1.97.0)パスによるページ指定。 バンドルはバージョン 1.1(wpcanai_export_version)です。各ページ行には post_name と並んで path(gallery/auditorium)が含まれ、委譲先ページの参照にも path が含まれます。インポートでは既存ページのみ更新され、新しいページは作成されません。WooCommerce のページ行は、まずストア内でそのタイプに該当するページと照合されます。それ以外の場合、バージョン 1.1 の行は完全一致する path のみと照合されます(他の場所にある同名ページを上書きする可能性があるため、単独のスラッグへのフォールバックはありません)。古いバンドルの行は、サイト内でどの親の下にあるかを問わず、そのスラッグを持つページが 1 つだけの場合に限り、スラッグで照合されます。ページが見つからない行、または(古いバンドルの場合)複数のページで共有されているスラッグを持つ行は、警告とともにスキップされます(pages_skipped にカウントされ、page_actions では action: "skipped")。パスが見つからない場合、同じスラッグを持つページが 1 つあれば、その場所が示されます。wpcanai-update-page でその場所に移動してから、もう一度インポートしてください。スラッグが共有されている場合は、各ページが path (ID n) の形式で一覧表示されます。その場合は、CanAI 1.97+ からもう一度エクスポートしてください。path を含む委譲先参照は、そのパスのみで解決されます。該当ページが見つからない場合、そのページは委譲先ページなしでインポートされ、その旨が警告されます。(v1.97.1) 古いバンドルの委譲先参照(path なし)は、スラッグを持つページまたは投稿が 1 つだけの場合に限り解決されます。インポート前に、wpcanai-create-page(parent を指定)で不足しているページを作成してください。
wpcanai-diagnostics
  • 引数: { "include_network"?: bool }(デフォルトは false)。→ wp-admin の Diagnostics ページを反映したヘルスレポートです(PHP/WP/プラグインのバージョン、機能とエンドポイントのチェック、Tailwind のビルド状況、WooCommerce の有無と HPOS の安全性に関する注記)。
  • include_network: true を指定すると、実際の外部 HTTP リクエスト(外向き HTTPS、REST ループバック、skills エンドポイントへの到達性、認証ヘッダーの引き継ぎ)も実行します。時間がかかります。推測で判断するのではなく、接続性や環境の問題が疑われる場合に使用してください。
  • (v1.80.0) page_cache チェック(プラグインの検出、パージフック、自動パージのオン/オフ)と、integrations グループ(ツールラベルごとに 1 行:有無、登録されたツール数、無効かどうか)が追加されました。(v1.82.0) integrations の各行では、ツール数を 宣言された ラベル(touches + requires)に基づいて集計します。そのため、統合機能が存在しなくても、それを宣言しているツールの数が報告され、hidden_by_recommendation も含まれます。
  • (v1.82.0) tools グループ:tool_budget(公開中と全ツールの数、バイト数、約トークン数)と tool_recommendations(「Follow recommendations」がオフで、推奨設定が現在の状態と異なる場合に警告します。fix は正確な manage-tools 呼び出しです)。languages グループ:languages(設定されているネイティブ言語、またはなし)と translation_tools(Languages ツールグループと、言語リストとの組み合わせ)があります。言語なし/オフ → パス(未使用)、言語なし/オン → 警告(i18n-set-settings で言語を追加するか、グループをオフにします)、言語あり/オン → パス、言語あり/オフ → 情報(正当なオーナーの選択です。翻訳が必要な場合は manage-tools で有効にします)。診断で翻訳に触れるのはここだけです。有効化しない限り、ほかの箇所では翻訳について言及されません。
  • (v1.90.0) render グループ:過去 7 日間にテンプレートでレンダリング警告が発生していない場合は pass の行を表示します。それ以外の場合は、各項目につき warning の行を 1 行ずつ表示します(新しい順、最大 10 件。その後に残りの件数を示す info の行を表示)。各行には post_id、field、line、edit_url が含まれます。これは wpcanai-scan が render_warning として報告する項目と同じです。
  • (v1.88.0) menus グループ:場所ごとに 1 行表示します。メニューが設定されている場合は、メニュー名と項目数を含む pass、何も割り当てられていない場合は warning(fix は write-menu 呼び出し)を表示します。また、以前の v1.88 より前の場所名で割り当てられたままのメニューを一覧表示する info の行も表示します。(v1.89.0) 言語メニューごとに、さらに info の行を 1 行表示します(「メニューの場所:footer(Bahasa Melayu)」)。
wpcanai-purge-cache(プラグイン v1.80.0、cache が必要)
  • サポート対象のキャッシュプラグインが有効な場合にのみ使用できます(LiteSpeed Cache、WP Rocket、W3 Total Cache、WP Super Cache、ページキャッシュがオンになっている WP-Optimize — v1.82.0)。該当するプラグインがなければ、オフが推奨され、通常はリストに表示されません(ルール R1)。パージするのは サードパーティのページキャッシュ のみです。CanAI の Twig コンパイルキャッシュと Tailwind ビルドは、ソースが変更されるたびに自動で無効化されるため、パージは不要です。
  • 引数: { "scope": "all"|"url"|"post", "target"?: string|int } — url の場合、target は URL(このサイト上の URL である必要があります)、post の場合は投稿 ID です。all の場合は無視されます。
  • 戻り値: { "purged": [{ "scope", "target" }], "plugin": "litespeed"|"wp-rocket"|"w3tc"|"wp-super-cache"|"wp-optimize"|null, "note"? }。plugin: null は、サポート対象のキャッシュプラグインが検出されなかったことを意味します。エラーではありません。その場合、機能するのは post のみ(WordPress の投稿キャッシュをクリア)で、all/url は注記を返します。
  • 通常は呼び出す必要はありません。 書き込み後のパージがオン(wpcanai_purge_after_write、デフォルトでオン)の場合、CanAI の書き込みはすべてリクエストの最後に 1 回パージします。テンプレートの変更ではサイト全体、ページの変更ではそのページがパージされ、書き込みの purged フィールドにその旨が示されます(ツールラベルを参照)。purge-cache を呼び出すのは、キャッシュプラグインが有効なのに purged が null だった場合(自動パージがオフ)、または書き込みの対象外だった URL(例:編集したページを一覧表示するアーカイブ)に限ってください。

Google Stitch の画面をデプロイする → canai-stitch スキル

Google Stitch のデプロイ(CanAI Stitch アドオンの wpcanai-stitch-connect、-list-projects、-set-projects、-list-screens、 -deploy-screen ツール)には、専用のスキル canai-stitch(npx skills add usewp/canai --skill canai-stitch)があります。 「Stitch をソースのままにする」か「CanAI に引き渡す」かの選択、stitch_page_edited ガードを使ったデプロイと再デプロイのワークフロー、忠実なコピーを CanAI の規約に合わせるためのクリーンアップについて解説しています。Stitch に関する依頼では必ず読み込んでください。オーナーから編集内容を置き換えるよう依頼されていない限り、wpcanai-stitch-deploy-screen に overwrite: true を指定しないでください。


メディアのアップロード

方法は 2 つあります。ソースに応じて選んでください。

ソース使用する方法理由
画像がすでに URL になっている(https://…/photo.jpg — 例:ソースサイト、CDN、レプリカのキャプチャからのホットリンク)MCP ツール wpcanai-sideload-urlサーバーがデータを取得するため、MCP 経由で送るのは小さな JSON のみです。API キー/ベース URL/curl は不要です。こちらを推奨します。
ローカルのバイナリファイル(ユーザーのマシン上にあるファイルで、公開 URL がないもの)sideload REST エンドポイント(以下)MCP はバイナリペイロード(サイズと Base64)の送信には適していません。
URL の画像 → wpcanai-sideload-url(推奨、MCP のみ)

MCP ツールを呼び出します。認証情報を用意する必要はなく、認証済みの MCP トランスポート経由で実行できます。

wpcanai-sideload-url {
  "url": "https://example.com/images/hero.jpg",
  "alt": "Front of the product",     // optional
  "attach_to": 0                      // optional parent post id
}

任意の項目:filename、title、caption、description。戻り値は { id, source_url, mime_type, filename, attached_to } です。_canai_html を記述するときは、返された id を {{ image_attrs(id, 'src,alt') }} / {{ media_url(id, 'full') }} 経由で使用してください(レンダリング時に解決されます。「CanAI のテンプレート関数を優先する」原則を参照)。絶対 URL が必要な場合(OG/Twitter メタ、Web マニフェスト)に限り、絶対 URL の source_url を使用してください。アセットがホットリンクされた URL であるレプリカ/インポートの処理には、この方法が適しています。

ローカルのバイナリファイル → sideload REST エンドポイント

MCP メッセージはバイナリペイロード(サイズと Base64 のオーバーヘッド)の送信には適していません。代わりに sideload REST エンドポイントを使用してください。MCP トランスポートと同じ CanAI API キーを使用できるため、別の認証情報は必要ありません。

  • エンドポイント: POST {site}/wp-json/wpcanai/v1/sideload
  • 認証: Authorization: Bearer <wpcanai_…API key>(または X-WPCanAI-API-Key:)。
  • 本文: multipart/form-data。必須フィールドは file=@<local-path> です。任意の項目:filename、title、alt、caption、description、attach_to(親投稿 ID)。
  • 戻り値: { id, url, source_url, mime_type, filename, title, alt, attached_to }。
  • API キーのユーザーに必要な権限: upload_files(管理者にはこの権限があります)。
ワークフロー(画像は 1 枚ずつ)

静的サイトのインポート(ユーザーが /canai-mcp implement <folder> などを実行した場合)では、確認せず、参照されているアセットを自動検出して、事前処理としてサイドロードしてください。「HTML を実装 → CanAI」の「静的サイトのアセットを事前処理でサイドロードする」を参照してください。以下の手順は、そのワークフロー以外で単一ファイルを都度アップロードする場合に適用します。

  1. ユーザーがローカルファイルのパスをまだ提示していない場合は、パスを尋ねます。

  2. 各ファイルをユーザーのマシンから 1 つずつ POST します。こうすることで、それぞれの id を取得できます。もっとも簡単な方法は curl で、これはシェルの使用が認められている唯一の例外です。

    curl -sS -X POST "{site}/wp-json/wpcanai/v1/sideload" \
      -H "Authorization: Bearer <API key>" \
      -F "file=@/abs/path/to/image.jpg" \
      -F "alt=Front of the product"
    

    Node のみのスタック(curl も Python もなく、Node 18 以降でグローバルに fetch/FormData/Blob を利用できる環境。「ローカルスクリプト用の補助コード」を参照)の場合、依存関係なしで同等の処理を実行できます。

    node -e "const fs=require('fs');const f=new FormData();f.append('file',new Blob([fs.readFileSync(process.argv[1])]),require('path').basename(process.argv[1]));f.append('alt',process.argv[2]||'');fetch(process.argv[3]+'/wp-json/wpcanai/v1/sideload',{method:'POST',headers:{Authorization:'Bearer '+process.argv[4]},body:f}).then(r=>r.text()).then(console.log)" /abs/path/to/image.jpg "Front of the product" "{site}" "<API key>"
    
  3. 返された id を取得し、既存の MCP ツール経由でテンプレートに渡します。固定 URL を使うより、_canai_html(wpcanai-write-meta)では {{ image_attrs(id, 'src,alt') }} / {{ media_url(id, 'full') }} を優先してください。絶対 URL が必要な場合(OG/Twitter メタ、Web マニフェスト)に限り、絶対 URL の url を使用してください。

ファイルを Base64 にエンコードして MCP ツールの引数に渡そうとしないでください。このエンドポイントを使用してください。

メディア SEO の整理(wpcanai-list-media / wpcanai-get-media / wpcanai-update-media)

名前の不適切なアップロードファイルや、代替テキストが設定されていないメディアを整理します(プラグイン 1.45.0 以降)。

  1. 監査 — wpcanai-list-media { "missing_alt": true } および/または { "generic_filename": true } (IMG_4521.jpg のようなカメラ/スクリーンショットのファイル名)。page/per_page でページネーションします。
  2. コンテキストの確認 — wpcanai-get-media { "id": 123 } は、すべてのメタデータに加えて、ファイルを参照しているページを示す used_in を返します。まず、それらのページを読んでください — 代替テキストとファイル名は、画像がそのページのコンテキストで何を示しているかを表す必要があります。古いファイル名から代替テキストを決めてはいけません。
  3. 修正 — wpcanai-update-media { "id": 123, "alt": "…", "title": "…", "filename": "red-leather-office-chair" }。
    • filename は拡張子を含まないスラッグです。ファイルとすべてのサイズバリエーションがディスク上でリネームされ、すべての内部参照が書き換えられます(post_content + _canai_html/_canai_css/_canai_js メタデータ)。
    • その後、古い URL は 404 になります(リダイレクトなし)— 変更後に used_in のページが正しく表示されることを再確認してください。
    • 空文字列を明示すると、フィールドがクリアされます。メタデータのみの更新ではファイルに変更を加えません。
    • ローカルファイルのみが対象です。外部ストレージ(S3/CDN)上のメディアでは file_missing が返されます。

重大:CanAI のストレージモデル

CanAI は Twig 本文に post_content を使用しません。_canai_html、_canai_css、_canai_js、_canai_context、_canai_layout には、wpcanai-read-meta/wpcanai-write-meta を使用してください。blocks とマークされたページは例外で、本文は post_content ですが、このスキルでは書き込みません。

重大:コンテンツの解決

WC のショップ/カート/チェックアウト/マイアカウント/商品カテゴリーのコンテンツは、多くの場合 WC ページにあります。商品/404/検索/アーカイブのコンテンツは、多くの場合 wpcanai_template にあります。誤った投稿を編集する前に、wpcanai-resolve-content-id を使用してください。


翻訳モデルの振り分け

CanAI の翻訳モデルは 1 つだけです。ネイティブ文字列翻訳(ページごとに 1 投稿、サイト全体の文字列テーブル、プラグイン 1.22.0 以降)です。言語ごとに投稿を複製する方法は CanAI のモデルではありません — テンプレートとページは言語ごとに分けられていません。投稿を対象とするツール(list-templates、read-meta、write-meta、create-page、write-post など)は lang 引数を受け取らず(誤って指定された引数は無視されます)、lang キーも返しません。lang が登場するのは i18n-* ツールだけで、その場合は翻訳対象の言語を示します。「このサイト/ページを翻訳して」「言語を追加して」「多言語化して」「ローカライズして」など、どのような翻訳依頼でも、まず設定を確認してください。

  1. (v1.82.0)i18n-* ツールはオプトイン方式で、デフォルトでは非表示です。 wpcanai-i18n-get-settings がツール一覧にない場合も、翻訳はサポートされていないと伝えないでください。wpcanai-manage-tools { "action": "list" } を実行し、このサイトで翻訳を有効にするかユーザーに確認してから、{ "action": "set", "groups": { "Languages": "on" } } を実行し、再接続してから続行してください。
  2. wpcanai-i18n-get-settings { } を呼び出します。
    • enabled: true → 以下のネイティブ文字列翻訳に従います。言語ごとの投稿を作成してはいけません。
    • enabled: false → ネイティブ翻訳はオフです。使用する言語をユーザーに確認します(例:WordPress ロケールの ms_MY)。デフォルトは常にサイト言語(site_locale、v1.95.0)で、ページがすでに書かれている言語にする必要があります。異なる場合はその旨を伝え、まずサイト言語を変更してもらってください(wpcanai-i18n-set-settings → デフォルトの変更はサイト言語の変更を意味します)。次に、言語ごとに locale を渡して wpcanai-i18n-set-settings で初期設定を行い、CanAI にスラッグ、名前、hreflang(v1.92.0)を設定させてから、ネイティブのワークフローを続行します。

ネイティブ文字列翻訳(単一投稿の i18n)

実行するタイミング: 上記のルーターがネイティブモデルと判定した場合です。ユーザーがサイトまたはページの翻訳、言語の追加、未翻訳箇所の補完、画像のローカライズを依頼し、wpcanai-i18n-get-settings が enabled: true を返した場合(または、初期設定を行った直後)に実行します。

モデル(プラグイン 1.22.0 以降): ページ/テンプレートごとに投稿は1つで、言語ごとのコピーは作成しません。ユーザーに表示される文字列は _canai_html / _canai_js 内で {{ t('…') }} にラップされ、サイト全体の文字列テーブルからレンダリング時に解決されます。デフォルト以外の言語はパスプレフィックス(/ms/plt/)付きで提供され、デフォルト言語にはプレフィックスが付きません。wp_head で hreflang の alternate が出力され、tmedia() マップによって言語ごとに添付ファイルが切り替わります。

ワークフロー
  1. 設定 — wpcanai-i18n-get-settings { } を実行します。言語が不足している場合や設定が誤っている場合は、ユーザーに確認してから wpcanai-i18n-set-settings で書き込みます(リスト全体が置き換えられるため、すべての言語を含めてください)。デフォルトはサイトの言語です(v1.95.0)。別の言語をデフォルトにするにはサイトの言語を変更する必要があり、変更するかどうかはユーザーが決めます。

  2. 作成の前提条件 — ユーザーに表示される文字列はすべて {{ t('…') }} にする必要があります。 インデックス化されて翻訳可能になるのは t() のソースだけです。ページにハードコードされた文字列がある場合は、先にラップします。wpcanai-read-meta → 置換ペアを作成 → wpcanai-replace-in-meta の順に実行します。例:{ "post_id": <id>, "replacements": [{ "from": ">Shop now<", "to": ">{{ t('Shop now') }}<" }] }(一致箇所が一意になるよう、周囲のマークアップも含めて指定します)。(v1.94.0)文ごとに t() を1つ使用し、その中にインラインタグを含めます。

    • {{ t('Get <strong class="font-semibold">free shipping</strong> today.') }} のように記述し、{{ t('Get') }} <strong>{{ t('free shipping') }}</strong> {{ t('today.') }} のようにはしません。言語によって語順が異なり、断片を並べ替えられないため、翻訳者には文全体が必要です。
    • 内側に含めるのはインライン要素(strong、em、a、span、br、code など)だけです。ブロック要素(p、div、li、見出し)は外側に置き、ブロックごとに t() を1つ使用します。
    • 動的な値は {placeholders} として渡す変数です。例:{{ t('Read our <a href="{url}">terms</a>.', {url: turl('/terms/')}) }}、{{ t('By {name}', {name: post.author.display_name}) }}。
    • 翻訳対象の属性(title、aria-label)には、それぞれ専用の t() を使用し、変数として渡します。
    • フレーズ全体にスタイルを適用する場合は、これまでどおりにします:<strong>{{ t('Best seller') }}</strong>。
    • 文字列キーには属性を含めません(Get <strong>free shipping</strong> today.)。そのため、スタイルを変更しても翻訳は維持されます。同じタグが繰り返される場合は番号を付けます(<a1>、<a2>)。

    断片の結合。 wpcanai-scan が split_sentence を報告した場合、文が分割されたままです。wpcanai-replace-in-meta で断片全体を1つの t() に結合し、タグをその中に含めます。その後、wpcanai-i18n-rescan を実行し、i18n-list-strings { "post_id": … } から取得した以前の断片の翻訳を参考に、新しいキーを翻訳します。以前の断片キーはテーブルに残りますが、使用されません。ネイティブ i18n が有効なサイトで新しいページを作成する場合は、最初からユーザーに表示される文字列を t() でラップします(Implement HTML → CanAI のヘルパーテーブルを参照)。

  3. インデックスを再構築する — コンテンツを編集した後は、wpcanai-i18n-rescan { } を実行します。wpcanai-i18n-list-strings が読み取るのはライブのメタデータではなくインデックスです。インデックスが古いと、古い文字列が表示されます。

  4. 翻訳 — デフォルト以外の言語ごとに、1 ページずつ:

    • wpcanai-i18n-list-strings { "lang": "ms", "post_id": 42, "layout": true, "menus": true, "untranslated": true } — そのページの t() 文字列、レイアウトの文字列(ヘッダー、フッター)、未翻訳のメニュー項目タイトルを取得します。まずはこちらを使います。サイト全体の一覧 { "lang": "ms", "untranslated": true } は、ユーザーがサイト全体を求めた場合にのみ使用してください。
    • すべての source を自分で翻訳します(エージェントが翻訳を作成します)。メニュー項目のタイトルと説明は通常の source です(menus が ["primary"] または ["footer"] の行)。同じ呼び出しで翻訳すると、get_menu() が翻訳済みの項目を表示します。テンプレートで t() を使う必要も、再スキャンもありません。その言語専用のメニューが設定されているロケーションは一覧に含まれません。そのメニューはすでにその言語で作成されているためです(「言語ごとのメニュー」を参照)。
    • 一括呼び出し 1 回で書き込みます:wpcanai-i18n-set-translations { "lang": "ms", "translations": { "Shop now": "Beli sekarang", "Add to cart": "Tambah ke troli" } }。
    • ユーザーが確認できるよう、source → 翻訳の表を報告します。修正も別の一括呼び出しで行います。
    • オーナーもページの「Translate strings」ボタンから同じ操作ができます(プラグイン v1.88.0):ページエディターの「Translations」ボックス、CanAI エディター、ブロックページの CanAI サイドボックス、およびフロントエンドの管理バーにある 「Translate this page」から利用できます。(v1.91.0)「All Pages」/「All Posts」/「All Templates」では、「Translations」列に言語ごとのチップが表示されます。MS は翻訳済み、MS• は一部翻訳済み、+ MS は未翻訳、— は翻訳対象なしを示します。各チップをクリックすると、該当するエディターが開きます。これにより「Translate: {language}」および「Translate strings」の行アクションは置き換えられました。該当ページに絞り込まれた「CanAI → Translations → Strings」が開き、レイアウトとメニューの切り替えもできます。手動で翻訳したいユーザーにはこちらを案内してください。
  5. コンテンツ(CPT/長文コンテンツ)— デフォルト以外の言語ごと:

    • wpcanai-i18n-list-content { "lang": "ms", "untranslated": true } → タイトル/本文/フィールドがデフォルト言語のまま表示される投稿の作業キューです。(v1.91.0) kind: "strings" の行は CanAI Twig ページです。上書きではなく、手順 4 に従って t() 文字列を翻訳してください(i18n-list-strings { "lang": "ms", "post_id": <id> } → i18n-set-translations)。以下の手順は kind: "content" の行を対象としています。
    • 投稿ごとに wpcanai-i18n-get-content { "post_id": <id>, "fields": ["subtitle", ...] } を実行すると、デフォルト言語の raw source(title、content、excerpt、名前付きカスタム fields)が返されます。翻訳したら、wpcanai-i18n-set-post-overrides { "post_id": <id>, "lang": "ms", "overrides": { "title": "…", "content": "…", "excerpt": "…", "fields": { "subtitle": "…" } } } を 1 回実行します。(i18n-get-content に "lang": "ms" を渡すと、既存の上書きも並べて確認できます。)専用の i18n ツールを使用し、コンテンツの翻訳に PHP eval を使わないでください。
    • ループ/アーカイブに表示されるタクソノミー用語:用語ごとに wpcanai-i18n-set-term-overrides を実行します。
    • 投稿ごとのカバレッジ表(id → 変更前/変更後のステータス)を報告します。URL は変わりません。上書きされたフィールドを使って、同じ投稿が /ms/… に表示されます(言語ごとのコピーは作成されません)。
    • SEO メタデータ(タイトル/説明)は、seo_title/seo_description の上書きキーで翻訳します。source は i18n-get-content で取得してください(SEO ブリッジの動作の詳細は references/REFERENCE.md を参照してください)。これらのキーが必要なのは、SEO 用の文言を表示されるタイトル/本文と異なるものにする場合のみです。それ以外の場合、デフォルト以外の言語のページでは、seo_title/seo_description は翻訳済みの title/name と excerpt/タグを除去した content から自動生成されます(説明は単語の区切りを維持しながら最大 160 文字に切り詰められます)。
  6. サイトレベルの文字列(サイト名/キャッチフレーズ/アーカイブの SEO)— デフォルト以外の言語ごと:

    • wpcanai-i18n-get-global-strings { "lang": "ms" } → 現在のサイト名、キャッチフレーズ、アーカイブマップを取得します。
    • 翻訳してから、1 回の呼び出しで書き込みます:wpcanai-i18n-set-global-strings { "lang": "ms", "values": { "site_name": "…", "tagline": "…", "archive": { "search": { "title": "…", "description": "…" }, "404": { "title": "…" } } } }。
    • アーカイブのコンテキストは search、404、post_type:<slug> です。カテゴリー/タグ/タクソノミーのタイトルはタームのオーバーライド(手順 5)であり、グローバル文字列ではありません。
  7. メディア(言語ごとに異なる画像が必要な場合) — wpcanai-sideload-url でローカライズしたアセットをアップロードし、マッピングします:wpcanai-i18n-set-media-map { "lang": "ms", "map": { "123": 456 } }。テンプレートでは、その画像を {{ tmedia(123) }} / {{ tmedia(123, 'large') }} で解決する必要があります。マッピングがない場合は元の画像にフォールバックします。通常の media_url() はマップを無視します。

  8. ブラウザーで確認する — 言語プレフィックス付き URL({site}/ms/<page-slug>/)にアクセスし、翻訳済みの文字列が表示されることを確認します。また、<head> にすべての言語の <link rel="alternate" hreflang="…"> が含まれていること、canonical と og:url に /ms/ プレフィックスが付いていること、og:site_name に翻訳済みのサイト名が表示され og:locale が言語と一致すること、languages() / lang_url() の切り替え用 URL にプレフィックスが付いていること、デフォルト言語にはプレフィックスが付かないことを確認します。

注意点
  • プレーンテキストの翻訳。 wpcanai-i18n-set-translations は値から HTML を取り除きます。マークアップは t() の中ではなく、テンプレート内の t() の周囲に記述してください。
  • キーは完全一致です。 翻訳が適用されるのは、t() のソースが文字単位で完全に一致する場合だけです。ソース文字列を渡し直す際に、トリミング、改行位置の変更、句読点の変更をしないでください。
  • デフォルト言語は読み取り専用です。 wpcanai-i18n-set-translations / wpcanai-i18n-set-media-map はデフォルト言語を拒否します。ソーステキストがデフォルトの表示内容です。
  • インデックスが古い場合。 _canai_html / _canai_js の編集後、一覧表示の前に再スキャンしてください。
  • 設定するまで何も行われません。 デフォルト以外の言語が設定されていない場合、プレフィックス付きルーティング/リダイレクト/hreflang は無効になり、t() / tmedia() は変更なしで値を返します。
  • コンテンツのオーバーライドはフロントエンド専用です。 管理画面、REST の編集コンテキスト、デフォルト言語では常に元の値が表示されます。/{lang}/ のフロントエンド表示のみが切り替わります。
  • 編集者は wp-admin でも翻訳できます。 プラグイン v1.70.0 以降、post.php?post=<id>&action=edit&lang=<slug>(「言語モード」)で開いたブロックエディターの投稿は、その言語のオーバーライド用 blob(wpcanai-i18n-set-post-overrides が書き込むものと同じ _canai_i18n_<lang> データ)を読み込み、保存します。ユーザーが「エディターで」翻訳を編集したと言った場合は、未翻訳だと決めつけず、wpcanai-i18n-get-content { "post_id": <id>, "lang": "<slug>" } で読み戻してください。
  • 検索で一致するのはデフォルト言語のテキストのみです — WP の検索はオーバーライド用 blob をインデックスしません。
Twig ヘルパー

t()、tmedia()、current_lang()、languages()、lang_url()、turl() — references/REFERENCE.md を参照してください(turl() は Media & Links の項にあります)。turl('/path/')(プラグイン v1.88.0)は現在の言語に対応する home_url() です。デフォルト言語では通常の URL が返され、ms では /ms/path/ が返されるため、内部リンクでプレフィックスが重複することはありません。turl(path, lang) は特定の言語を対象にします(切り替えの場合)。slug_url() も同様にローカライズされ、get_menu() の項目は title / description が翻訳され、url がローカライズされた状態で返されます。raw_title / raw_description には未翻訳のテキストが格納されます(各言語のメニューは記述されたとおりに表示されます)。current_language() と language_switcher() は引き続き current_lang() と languages() のエイリアスとして機能します(すでにこれらを使用しているテンプレート向けです)。新しいテンプレートではネイティブ名を使用してください。


よくあるワークフロー(汎用表記)

ツール名 + JSON 引数を使用し、ホストの MCP 呼び出し構文に合わせてください。

DevTools でスタイルを調整する(3 回の呼び出し)

ユーザーがブラウザーで試し、ページ URL、表示されているテキスト、プロパティ(例:「text conference、line-height を 1.15 に設定」)を渡してきます。ページ全体を読み取ったり、ローカルに複製したりせず、呼び出しは 3 回だけです。

  1. 投稿 ID を解決します。 wpcanai-list-pages {} を呼び出し、URL のスラッグ/タイトルを照合します(ホーム URL は静的フロントページです。タイトルが曖昧な場合は、wpcanai-read-settings { "keys": ["page_on_front"] } で page_on_front を読み取ります)。WooCommerce のルートの場合は、wpcanai-resolve-content-id { "type": "shop" } などを使います。
  2. 要素を見つけます。 wpcanai-grep-content { "post_id": <id>, "pattern": "conference", "max_length": 1000, "context_lines": 2 } → 行の match が、要素のクラスを含むバイト単位で正確な行です。
  3. その要素内だけを編集します。 wpcanai-replace-in-meta { "post_id": <id>, "scope": { "text": "conference" }, "replacements": [{ "from": "leading-none", "to": "leading-[1.15]" }], "require_all": true }。 要素にすでにユーティリティがある場合は、任意の値を指定できる Tailwind ユーティリティ(leading-[1.15]、text-[17px]、tracking-[-0.01em])を優先します。class 属性がない場合は、同じスコープ内で from: "<h1" → to: "<h1 class=\"leading-[1.15]\"" を使います。プロパティに対応するユーティリティがない場合に限り、field: "css" で CSS ルールを追加します。

ステップ 3 で replace_no_match が返された場合は、message(Nearest match: <kind> at line …)を確認します。whitespace_only の場合は、同じスコープ指定の呼び出しを ignore_whitespace: true を付けて再試行します。outside_scope の場合は、クラスが別の要素(通常は親要素)にあるため、その要素に含まれる scope.text を選びます。一致させるために scope を外してはいけません。そうすると別の要素が書き換えられます。対象がスコープ指定した要素で間違いない場合は、まず ignore_whitespace: true を試し、次にスコープを指定した grep-content(max_length: 1000)で正確なバイト列を確認します。scope.text には、要素自身の最初のテキストまたは属性値を指定します。サーバーはテキストの前にある最も近い開始タグをスコープにするため、子 <span> 内のテキストは見出しではなく span をスコープにします。単一プロパティの変更で、フィールド全体を read-meta してはいけません。その後に wpcanai-scan や Tailwind の再コンパイルは不要です(再実行するタイミングを参照)。

既存の HTML のセクションを編集または削除する
  1. ID を解決します — wpcanai-list-pages { } または wpcanai-resolve-content-id { "type": "shop" } などを使います。返された content_post_id(または list-pages の行の id)を使用します。
  2. 読み取ります — wpcanai-read-meta { "post_id": <id>, "fields": ["html"] }(必要に応じて他のフィールドも追加します)。
  3. 対象を絞った置換を優先します。 grep-content(post_id、max_length: 1000)で取得したバイト単位で正確な行を基準に {from, to} の組を作成し、require_all: true を指定して wpcanai-replace-in-meta を呼び出します。組が多い場合は、最初に dry_run: true を追加します。ソースで属性が複数行にまたがっている場合は、ignore_whitespace: true を使用します。
  4. 構造を書き換える場合に限り、全体を書き込む方法に切り替えます — wpcanai-read-meta { "post_id": <id>, "fields": ["html"] }(または一部の範囲:{ "post_id": <id>, "fields": ["html"], "lines": [from, to] })で読み取り、編集した後、expected_hash を指定して wpcanai-write-meta { "post_id": <id>, "html": "<full updated HTML>" } を呼び出します。
既存ページの壊れたアセット URL を修正する

公開中のページが誤ったアセット URL を参照している場合(インポート時に残った相対パスや、移転した CDN など)、ドキュメント全体を書き直さずに修正します。

  1. ID を解決する — wpcanai-list-pages / wpcanai-resolve-content-id → ページ/テンプレートの post_id。
  2. 公開 URL をメディアライブラリに取り込む — 不足しているアセットごとに、ホットリンク可能な URL は wpcanai-sideload-url を使います。ローカルのバイナリファイルは、sideload REST エンドポイント(POST {site}/wp-json/wpcanai/v1/sideload。許可されている curl はこれだけです。「メディアのアップロード」を参照)を使います。返された source_url をそれぞれ記録します。
  3. { <old/broken reference exactly as it appears> : <new source_url> } の形式で、置換元から置換先へのマップを作成します。HTML に現れる各バリエーション(./img/x.png、img/x.png、/img/x.png)を含めます。
  4. 適用する — wpcanai-replace-in-meta { "post_id": <id>, "field": "html", "replacements": [{ "from": "...", "to": "..." }, ...], "require_all": true }。影響を受けるフィールド/ページごとに 1 回呼び出します。それぞれの count を確認し(require_all により、一致しなかった from があればフラグが立ちます)、必要に応じて "field": "css" / "js" でも繰り返します。
新しいテンプレートを作成する
  1. wpcanai-create-template { "title": "...", "type": "component", "html": "...", "css": "..." }
  2. レイアウトが必要な場合:wpcanai-write-meta { "post_id": <new_id>, "layout": <layout_post_id> }
設定を診断する
  • wpcanai-scan { } — 委譲、レイアウト、テンプレート、構造ナビゲーション、コンテンツ漏えいの問題(missing_structure_comment / invalid_structure_comment / leaky_comment / leaky_secret)を検出します。
  • wpcanai-manage-tools { "action": "list" } — 非表示のものも含むすべてのツール、それぞれが非表示になっている理由、および公開対象セットのトークン予算を確認できます。wpcanai-diagnostics の tools と languages グループでも同じ情報を要約して確認できます。
コメント/シークレットのセキュリティ監査

一括インポートの後、サイトをユーザーに引き渡す前、またはソース表示からの漏えいを監査するようユーザーに求められたときに実行します。sideload の事前処理と同じ定型手順です。

  1. wpcanai-scan { } — 構造/コメント/シークレットに関する検出結果をすべて収集します(post_id と field を記録します)。
  2. 任意:サイト全体を grep する — スキャンで分類されない可能性のあるパターンを検索します。
    wpcanai-grep-content { "pattern": " または、Twig にある疑わしい /* … */ コメントを検索します。leaky_secret が検出された場合は、認証情報をメタデータから削除し、wp-config/環境変数/ブラウザーに表示されないサーバーサイドのスニペットに移します。
  3. 再スキャンする — leaky_* の検出結果がなくなるまで繰り返します。検出結果が残っている間は、タスクが完了したと主張してはいけません。

本番環境向けに Tailwind をコンパイルする

実行するタイミング: ユーザーが /canai-mcp optimize production、/canai-mcp compile tailwindcss、compile tailwind、build css、tailwind build と指示した場合、または公開サイト用に Tailwind を事前コンパイルするよう求めた場合に実行します。テンプレートを編集するたびに自動実行してはいけません。これは明示的にオプトインする最適化手順です。

実行内容: デフォルトの Tailwind 配信方法では、Play CDN(プラグインの tailwind.min.js または cdn.tailwindcss.com)を使い、ページを読み込むたびに訪問者のブラウザーで JIT コンパイルします。このワークフローでは、エージェント側で CSS を事前コンパイルし、メタデータとして保存します。ビルド済み CSS が存在する場合、サーバーはその CSS をインラインで出力し、Play CDN スクリプトを読み込みません。訪問者には静的 CSS が配信され、コンパイル処理はエージェント側で行われます。

ビルド単位はレイアウトです。 レイアウトごとに CSS バンドルを1 つコンパイルします。レイアウトに加えて、そのレイアウトを通じてレンダリングされるすべてのページ/テンプレートをスキャンし、レイアウトの投稿に保存します。サーバーはレンダリング時にレイアウトのビルド済み CSS を取得し(そのレイアウトの投稿は、それを使用するすべてのページでレンダリングされます)、1 回だけ出力します。これにより、ビルド済み CSS が各ページに散在せず、CanAI Editor のレイアウトの Compiled Tailwind CSS パネルにまとめて保存されます。

ストレージの仕様
メタキー保存場所目的
_canai_tailwind_buildレイアウト投稿(wpcanai_template、タイプ layout)レイアウトとそのすべての利用ページ向けにコンパイルされた CSS です。AssetManager::output_assets_head がこれを <style id="wpcanai-tailwind-css"> として出力し、そのレイアウトを通じてレンダリングされるページでは Play CDN をスキップします。
_canai_tailwind_hash同じレイアウト投稿任意。エージェント側でのみ使用します。 ビルドの生成に使用した入力の sha256 です。再コンパイルの前に比較し、変更がない場合はスキップします。古さの検出には必須ではありません。サーバーは _canai_tailwind_epoch を使って最新状態を追跡します(以下のサーバー側の最新状態管理を参照)。
_canai_tailwind_epoch同じレイアウト投稿書き込み時に自動設定される、ビルドのコンパイル時点のソースエポックです。サーバーはこれをグローバルエポックと比較して、古い状態かどうかを判断します。手動で設定する必要はありません。
_canai_tailwind_built_at同じレイアウト投稿Unix タイムスタンプです。空でない tailwind_build が書き込まれると wpcanai-write-meta が自動設定します(パネルの「N 分前にビルド済み」の表示に使用されます)。手動で設定する必要はありません。

空または未設定の場合、サーバーは Play CDN にフォールバックします。重複出力防止: レンダリング対象にビルドを持つレイアウトがある場合、サーバーはレイアウトのビルドのみを出力し、ページごとの _canai_tailwind_build は無視します。そのため、以前のページ単位のモデルでページに残された古いビルドが二重に出力されることはありません。wp-admin での手動編集も引き続き機能しますが、次回のコンパイルまでは実行時の CDN が使用されます。

サーバー側の最新状態管理(自動)。 tailwind_build が書き込まれるたびに、サーバーはビルドに現在のグローバルなソースエポック(_canai_tailwind_epoch)を記録します。また、テンプレートソース(_canai_html/css/js)または Tailwind 設定が変更されたとき、ブロックページの本文が変更されたとき(所有者による Gutenberg の保存、または MCP の書き込み)、あるいはページのフォーマットマークが変更されたときには、エポックを更新します。レンダリング時に、記録されたエポックが現在のエポックより古いビルドは古い状態として扱われ、古い CSS を出力する代わりに、ページは Play CDN にフォールバックします(正しい表示が保たれますが、処理は遅くなります)。そのため、再コンパイル前であっても編集後にページのスタイルが失われることはありません。これが、以下の tailwind_hash が現在は任意である理由です。正確性はもはやこれに依存しません。診断ページとレイアウトのコンパイル済み Tailwind CSSパネルには、古いビルドが表示されます。テンプレートを編集した後は、該当するレイアウトを再コンパイルしてインライン配信に戻してください。

ワークフロー
  1. Tailwind 設定を読み取って、プラグインのセットを取得します。

    wpcanai-read-settings { "keys": ["wpcanai_tailwind_settings"] }
    

    コンパイラーの呼び出し時には、返された plugins 配列(例:["forms", "container-queries"])を使用します。load_tailwind が no の場合は処理を停止し、先に有効化するようユーザーに伝えてください(コンパイル対象の Tailwind がありません)。

  2. レイアウトを列挙します — wpcanai-list-templates {} を実行し、type が layout の行を残します。各レイアウトがコンパイル対象です。

  3. 各レイアウトの利用ページを特定します — 有効なレイアウトがこのレイアウトになっているすべての投稿を対象とします。

    • wpcanai-list-pages {} と wpcanai-list-templates {} の行のうち、layout_id がこのレイアウトの ID と一致するもの。
    • WC デリゲートの本文:wpcanai-resolve-content-id { "type": "shop" }(および cart、checkout、my-account、product-category)を実行し、このレイアウトに解決される _canai_layout を持つ content_post_id を取得します。
    • このレイアウトが wpcanai_default_layout の場合、明示的な _canai_layout が設定されていない投稿も含めます(このレイアウトにフォールバックします)。
    • format が blocks の行(wpcanai-list-pages に含まれます)も利用ページです。本文は _canai_html ではなく post_content であり、Twig の本文と同様にスキャンされます(プラグイン v1.67.0 以降。それ以前は、サーバーがブロックページで Play CDN を強制的に使用していました)。
  4. ユニオンコンテンツを構築する — 1つの文字列に連結します。レイアウト自体の_canai_html、すべてのコンシューマーの_canai_html、{% include 'slug' %} / {{ wpcanai_template('slug') }}で参照されるすべてのパーシャル(ヘッダー/フッター — 各スラッグから投稿 ID を特定し、その_canai_htmlを読み込みます)、さらに、formatがblocksの各コンシューマーについて、wpcanai-read-meta { "post_id": <id>, "fields": ["blocks"] }から取得した未加工のブロックマークアップ(classNameの値はブロックコメントの JSON とレンダリング後のclass=""の両方に含まれるため、未加工のマークアップがスキャン対象として適切です)。blocks ページのcssスロットはユニオンに含めないでください — これは Tailwind のソースではなく、加工されずに出力される通常の CSS なので、スキャン対象のユーティリティクラスには影響しません。また、ページ上でcssのみを書き込んでもソースエポックは更新されないため、ステップ 5 でこれをハッシュ化すると、ドキュメント化された入力が変更されていてもビルドが「最新」と判定されてしまいます。レイアウト内のあらゆる場所で実際に使われているクラスを Tailwind スキャナーが検出できるよう、一時的な.htmlファイルに書き込みます。

  5. 入力をハッシュ化する — union_content + "\n--\n" + plugins.sort().join(",") + "\n--\n" + tailwind_versionの sha256 を計算します。レイアウトに既存の_canai_tailwind_hashがある場合は比較します。一致したら、そのレイアウトをスキップし、「スキップ(最新の状態)」としてカウントします。マシン間でハッシュが安定し(誤って v4 でビルドした場合も衝突せず無効化される)ように、tailwind_versionには実際にコンパイルで使用する v3 のバージョンを指定してください。ハッシュ化にはPython ではなく Node を使用します(ローカルスクリプト用の接続処理を参照)— 例:node -e "const c=require('crypto'),fs=require('fs');console.log(c.createHash('sha256').update(fs.readFileSync(process.argv[1])).digest('hex'))" hash-input.txt。

  6. Tailwind v3 でローカルコンパイルする(サーバーではコンパイルしません)。v4 ではなく Tailwind v3 を使用してください。実行時の配信には Tailwind v3 Play CDN を使用します。プラグイン v1.58.2 以降、これはデフォルトでassets/lib/tailwind/内のプリビルドバンドルを意味します(Resource Source = Internal)。サイト所有者がソースを外部に切り替えた場合のみ、cdn.tailwindcss.comが使われます。いずれの場合も v3 なので、プリコンパイラーも一致させるために必ず v3 を使用してください。どちらのランナーもnpm run buildなしで動作します。

    • npx tailwindcss@^3 -i input.css -o /dev/stdout --content "<temp.html>"。npx @tailwindcss/cliは使用しないでください — このパッケージは Tailwind v4 であり、ビルドが壊れます(以下の警告を参照)。
    • v3 リリースの Tailwind スタンドアロンバイナリ(例:v3.4.xタグのtailwindcss-macos-arm64 — Node 不要の単一実行ファイル)。明示的に v3 を固定してください。「latest」のバイナリは v4 です。 input.cssは@tailwind base; @tailwind components; @tailwind utilities;です。ステップ 1 のプラグインは、一時的なtailwind.config.js(plugins: [require('@tailwindcss/forms'), ...])で有効にします。レイアウトごとにビルドするため、Preflight/base はページごとではなく一度だけコンパイルされます。

    ⚠ v3 を使用してください。@tailwindcss/cliは絶対に使用しないでください。 Tailwind v4 では、この手順でビルドすると警告なしにモバイル専用ビルドが生成されます。v4 は--contentとtailwind.config.jsの両方を無視し、従来の@tailwind base/components/utilitiesディレクティブでは v4 のデフォルトテーマが読み込まれません。そのため、テーマトークンを必要とするユーティリティがすべて除外されます(--breakpoint-* → レスポンシブなmd:/lg:バリアントなし、--spacing → p-2なし、フォントサイズ → text-smなし)。flex/hiddenのような、テーマに依存しない静的ユーティリティだけが残ります。Tailwind v3 では、@tailwindディレクティブ、tailwind.config.js、--contentが正しく機能します。

  7. ビルドを検証してから、レイアウトに保存する。書き込む前に、コンパイルした CSS を簡易チェックしてください。サイズが十分にあり、少なくとも1つの@media (min-width:ルールを含んでいる必要があります(つまり、レスポンシブユーティリティが出力されている必要があります)。満たさない場合 — v4 でコンパイルしたときに典型的に見られる症状です — ビルドは壊れています(モバイル専用)。保存せず、レイアウトを失敗としてカウントし、動作中の Play CDN フォールバックをそのまま残してください(_canai_tailwind_buildが空または存在しない場合、サーバーは CDN を引き続き配信します)。検証に合格したら、wpcanai-write-metaで書き込みます。

    wpcanai-write-meta {
      "post_id": <layout_id>,
      "tailwind_build": "<full compiled CSS>",
      "tailwind_hash": "<sha256>"
    }
    

    その後、古いページごとのビルドをまだ保持しているコンシューマーでは、古いページごとのコンパイルによるものを削除します。wpcanai-write-meta { "post_id": <consumer_id>, "tailwind_build": "", "tailwind_hash": "" }。重複排除ガードはすでにこれらを無視しますが、削除しておくことでデータの整合性が保たれ、パネルにも正確な情報が表示されます。これらのフィールドを永続化するのは wpcanai-write-meta のみです。他の書き込み方法は使用しないでください。

  8. 報告 — X layouts compiled, Y skipped (already current), Z failed に加え、削除したコンシューマーのページごとのビルド数を報告します。失敗した場合は、レイアウト ID と理由を列挙します。

結果の確認

ブラウザーでフロントエンドのページを表示します。<head> に <style id="wpcanai-tailwind-css">…</style> が含まれ、<script src=".../tailwind.min.js"> は含まれていないことを確認します。Play CDN スクリプトがまだ表示される場合、ページのレイアウトに空の _canai_tailwind_build が設定されています(手順 7 で正しいレイアウト ID に書き込んだか再確認してください)。または、ページがコンパイルしたものとは別のレイアウトを使用しているか、load_tailwind が no になっています。

コンパイル済みレイアウトでレンダリングされたブロックページには、同じ <style id="wpcanai-tailwind-css"> が表示され、tailwind.min.js は含まれません。Twig の兄弟ページでは CDN が読み込まれないのに、ブロックページで読み込まれる場合は、ビルドが古くなっています。ブロックページの本文は Gutenberg で保存するたびに変更されるためです(再実行するタイミングを参照)。

再実行するタイミング

レイアウトのビルドへの入力が変更されたときは、いつでも再実行してください。対象には、レイアウト自体の _canai_html(インラインの tailwind.config = {…} ブロックも含みます。新しいトークンは、サーバーが Play CDN をスキップするため、コンパイル後は実行時に認識されません)、コンシューマーのページ/テンプレートの _canai_html、ブロックページの本文(所有者による Gutenberg での保存、または wpcanai-write-page)、ページの形式マークの変更、共有パーシャル(ヘッダー/フッター)、_canai_layout の割り当て、wpcanai_tailwind_settings のプラグインセットが含まれます。レイアウト単位のトレードオフに注意してください。**コンシューマーを1つ編集するだけで、レイアウト全体のビルドが古くなります。**手順 5 のハッシュチェックにより、「毎回再実行」しても低コストです。実際に変更があったレイアウトのみ再コンパイルされます。

**部分的な編集だけなら、再コンパイルは必須ではありません。**サーバーは各レイアウトのビルドに鮮度エポックを付与し、レンダリング時にソースに対して古くなったビルドを破棄して、Play CDN にフォールバックします(AssetManager::output_assets_head)。したがって、replace-in-meta の後もページは正しくレンダリングされ、新しい leading-[1.15] などのユーティリティは実行時 CDN から提供されます。コストは、次に意図的にコンパイルするまでページで CDN が読み込まれることだけです。コンテンツを少し調整するたびにではなく、本番環境向けの最適化をユーザーが求めたときに再コンパイルしてください。

コンパイル後の実行時の動作

レンダリングされたページのレイアウトに空でない _canai_tailwind_build がある場合、AssetManager::output_assets_head はそのビルドのみ(重複排除ガードを参照)に加えて、1行のシム <script id="wpcanai-tailwind-shim">window.tailwind=window.tailwind||{config:{}};</script> を出力します。このシムにより、レイアウトのインライン tailwind.config = { ... }({{ wp_head() }} の後)が TypeError を発生させず、安全にプロパティへ値を書き込めます。その結果、コンパイル後はこのブロックに視覚的な効果はありません。すべてのテーマトークンを、コンパイラーに渡す一時的な tailwind.config.js に含める必要があります。レイアウト投稿には _canai_css/_canai_js がなくてもビルド取得用のキューに追加されるため、ビルドのみを保持するレイアウトも正しく出力されます。

制限事項/注意点
  • レイアウトごとのユニオンサイズ。 レイアウトのビルドには、そのレイアウトを使うすべてのページで使用されるクラスが含まれるため、各ページに、ページごとに完全にビルドした場合より多くの CSS が含まれます。その代わり、シェル(ヘッダー/フッター/レイアウト)と Preflight を重複排除でき、CSS も 1 か所で確認できます。一般的なサイトであれば問題ありませんが、1 つのレイアウトがコンテンツの多様な大量のページに適用され、CSS のサイズが懸念になる場合は、それらのページを別のレイアウトに分けてください。
  • 再ビルドとの結び付き。 ビルドはレイアウト単位なので、コンシューマーを 1 つ編集するだけで、レイアウト全体のビルドが無効になります。ハッシュによるスキップで再コンパイルのコストは抑えられますが、ほとんどのコンテンツ編集後にレイアウトが再コンパイルされることを想定してください。
  • ページ単位ビルドからの移行。 既存のインストールでは、ページ上にビルドがあります。最初のレイアウト単位の再コンパイルが行われるまでは、重複排除ガードによって正しく処理されます(レイアウトのビルドがあればそれが優先され、なければページ単位のビルドが引き続き出力されます)。手順 7 のクリア処理で、順次削除されます。
  • 任意の値。 class="text-[#abc123]" は動作します(Tailwind JIT はリテラル文字列をスキャンします)。値が Twig ソース内のリテラルであり、レンダリング時に構築されるものではないことを確認してください。class="text-{{ color }}" は検出されません。これは、どの Tailwind ビルドでも同じ制約です。
  • レイアウト内のカスタム tailwind.config = {…}。 一時的な tailwind.config.js にも反映してください。そうしないと、コンパイル結果にそのトークンが含まれません。抽出は脆弱です。インラインブロックは JSON ではなく JS オブジェクトリテラルです(末尾のカンマ、シングルクォート、引用符のないキー、関数値、コメントが含まれる場合があります)。ローカルスクリプトの連携を参照し、Python ではなく Node で評価してください。一時的な tailwind.config.js に module.exports = { ... } を書き込み、コンパイラーに require() させます。これにより、オブジェクトリテラルはそれを記述したものと同じエンジンで解析されます。有効な設定を抽出できない場合は、トークンが欠けたビルドを黙って出荷せず、明示的にエラーにしてください。

HTML 操作のガイダンス

  • テンプレートでは、セクションの境界を示すナビゲーションコメントに **{# Type / Short Label #}** を使用します。これらのラベルを使って境界を見つけ、実装メモの {# @dev … #} は無視してください(コメントのルールについては REFERENCE.md を参照)。
  • ブロックを削除する: 開始位置の Twig コメント(または判別可能な開始位置)から、そのセクションの末尾(次の兄弟セクションのコメント、閉じる構造タグ、または include 行)までを削除します。
  • ブロックを編集する: そのセクション内のマークアップだけを置き換え、文字列の残りの部分は変更しないでください。
  • 最終的な文字列はエージェント内で構築し、**wpcanai-write-meta** に渡してください。外部スクリプトは不要です。

履歴/元に戻す(スナップショット)

CanAI のコンテンツを書き込むたびに、反映前の状態を専用テーブル(postmeta ではありません)にスナップショットとして保存します。これが元に戻すための手段です。以前の _canai_html を手作業で再構築しようとせず、書き込みに問題が起きたらすぐに利用してください。スナップショットを操作する管理画面はありません。これらのツールだけが利用可能です。

保存される内容。 対象となるメタキーは、次の 8 つだけです。_canai_html、_canai_css、_canai_js、_canai_layout、_canai_delegate_page_id、_canai_context、_canai_context_mode、_canai_format。それ以外は保存されません。post_title、post_content、添付ファイルも対象外です。また、ページのスラッグ、ステータス、親ページ、順序も対象外です。wpcanai-update-page はスナップショットを作成せず、レスポンス内の previous が元に戻すための情報です。

整合性のないスナップショットを復元すると、整合性のない状態が再現されます。 たとえば、ページが blocks とマークされている一方で、空ではない _canai_html(変換の中断や別の書き込み元によって残された古い Twig 本文)を保持しているときに取得したスナップショットを復元すると、両方の値がそのまま戻ります。ページは引き続きブロックエディターで開きますが、フロントエンドではブロックではなく古い Twig が表示されます。フロントエンドが post_content にフォールバックするのは、_canai_html が空の場合のみだからです。

取得されるタイミング。 wpcanai-write-meta、wpcanai-write-page(ブロックへの変換パスのみ。操作は mcp-write-page-convert)、wpcanai-replace-in-meta、wpcanai-create-template、wpcanai-create-page、wpcanai-setup、wpcanai-import、プリセットのインストール/アンインストール、コンテンツを書き換えるメディアの更新、および wp-admin エディターからのすべての保存時に、書き込み前のスナップショットが自動的に取得されます。通常の書き込みで「スナップショットを取得する」ツールを呼び出す必要はありません。明示的に操作する唯一のケースは、以下の wpcanai-pin-current です。

保持期間。 投稿ごとに、ピン留めされていないスナップショットを10 件までリングバッファーで保持します。11 回目の書き込みで最も古いスナップショットが削除されます。ピン留めされたスナップショットは削除対象外で、投稿ごとに最大 10 件までです。

操作。 1 回の論理的な書き込みの一部として取得されたスナップショットには、共通の operation_id が付与されます(例:14 件の投稿に適用されるプリセットのインストール)。14 件のスナップショットを 1 件ずつ復元するのではなく、wpcanai-restore-operation を使って全体をロールバックしてください。

キルスイッチ。 すべての wpcanai/* アビリティと同様に、これらのツールはそれぞれ、サイト所有者が CanAI → AI Client → Tools で無効にできます。無効化されたツールは、ツール一覧に表示されなくなります。

wpcanai-list-snapshots
  • 引数: { "post_id": int, "limit"?: int } — post_id は必須です。limit のデフォルトは 50 で、1~200 に制限されます。
  • 戻り値: { "snapshots": [{ "id": int, "post_id": int, "operation_id": string, "operation_type": string, "actor": string, "created_at": string, "pinned": bool, "label": string|null, "bytes": int }] } — 新しい順です。メタデータのみが返されます。ペイロードは意図的に除外されています。「どれを使うか」という質問に答えるために、約 7 KB の行を 10 件読み込んでコンテキストを消費することを避けるためです。
  • エラー: 呼び出し元がその投稿に対する edit_post 権限を持たない場合は forbidden。
wpcanai-get-snapshot
  • 引数: { "snapshot_id": int }。
  • 戻り値: 同じ要約フィールドに "payload" が追加されます。これは、取得された { "_canai_html": …, "_canai_css": …, … } マップです。null は「メタ行が存在しなかった」ことを示します。差分を確認する必要がある場合は、復元前にこれを読み取ってください。
  • エラー: wpcanai_snapshot_not_found、forbidden。
wpcanai-restore-snapshot
  • 引数: { "snapshot_id": int }。
  • 戻り値: { "success": true, "post_id": int }。
  • 現在の状態が先に取得されるため、復元操作自体も元に戻せます。誤ったスナップショットを復元しても致命的な問題にはならず、復旧できます。
  • エラー: wpcanai_snapshot_not_found、forbidden、forbidden_unfiltered_html — 復元では未加工の HTML/JS を投稿に書き戻すため、元の書き込みと同様に unfiltered_html が必要です(API キーを使う呼び出し元はこの条件を満たしますが、Cookie 認証の非管理者は満たさない場合があります)。
wpcanai-list-operations
  • 引数: { "limit"?: int } — デフォルトは 20 で、1~100 の範囲に制限されます。
  • 戻り値: { "operations": [{ "operation_id": string, "operation_type": string, "actor": string, "created_at": string, "posts": int }] } — 新しい順で、サイト全体が対象です(投稿ごとではありません)。posts は、その操作の対象となった投稿数です。
wpcanai-restore-operation
  • 引数: { "operation_id": string }。
  • 戻り値: { "success": bool, "restored": int, "failed": int, "post_ids": int[] }。
  • success は failed === 0 を意味し、restored > 0 ではありません。 15 件中 14 件をロールバックした場合は success: false が返されます。restored が 0 以外でも「完了」と見なさないでください。部分的な状態をユーザーに報告してください。
  • 書き込みを始める前に、影響を受けるすべての投稿の権限を確認します。途中で止まるロールバックは、開始しない場合よりも事態を悪化させます。
  • エラー: wpcanai_operation_not_found、forbidden(編集できない投稿の名前を示します)、forbidden_unfiltered_html。
wpcanai-pin-snapshot
  • 引数: { "snapshot_id": int, "pinned": bool, "label"?: string } — snapshot_id と pinned は必須です。label は任意で、ピン留め時のみ意味があります。
  • 戻り値: { "success": true, "snapshot_id": int, "pinned": bool }。
  • ピン留めすると、スナップショットがリングバッファによる削除の対象外になります(上限:投稿ごとに 10 件)。ピン留め解除は常に可能です。
  • エラー: wpcanai_snapshot_not_found、forbidden、wpcanai_snapshot_pin_failed。
wpcanai-pin-current
  • 引数: { "post_id": int, "label": string } — 両方とも必須です。label は空でない 191 文字以内の文字列にしてください(例:"fix broken product page — verified in browser")。
  • 戻り値: { "success": true, "snapshot_id": int, "pinned": true, "label": string, "created_at": string, "reused_existing": bool }。
  • ブラウザーで変更を検証した後に、このツールを呼び出してください。 スナップショットは通常、書き込み前にのみ取得されるため、動作を確認したばかりの状態をピン留めできる行はありません。誰かが再び投稿を編集するまで行は作成されず、その時点では確認済みの状態は以前の状態となり、すでに削除されている可能性があります。
  • 現在のコンテンツが最新のスナップショットと一致している場合は、重複を書き込まず、そのスナップショットをピン留めします(reused_existing: true)。
  • エラー: wpcanai_invalid_label(未指定または 191 文字超過)、wpcanai_post_not_found、wpcanai_not_canai_post(投稿に CanAI コンテンツが含まれていません。ピン留めすると「この投稿には何もなかった」という状態が検証済みとして記録されるためです)、forbidden、およびピン留め数が 10 件に達している場合の上限エラー。まず wpcanai-pin-snapshot でピン留めを解除してください。
復旧ワークフロー
  1. wpcanai-list-snapshots { "post_id": 123 } → 問題のある書き込みより前の行を探します(created_at / operation_type を確認してください)。
  2. 任意:wpcanai-get-snapshot { "snapshot_id": 456 } → ペイロードが復元したいコンテンツであることを確認します。
  3. wpcanai-restore-snapshot { "snapshot_id": 456 }。
  4. ブラウザーでページを確認します。
  5. wpcanai-pin-current { "post_id": 123, "label": "known-good after rollback" } を呼び出し、復元した状態が削除されないようにします。

複数の投稿に損害が及んでいる場合(プリセットのインストールまたはインポートの失敗など)は、すぐに wpcanai-list-operations → wpcanai-restore-operation を実行してください。


アクションルーター(クイックリファレンス)

目的ツール
テンプレートを一覧表示するwpcanai-list-templates
CanAI ページを一覧表示する(下書きを含むすべてのページ:all_pages: true)wpcanai-list-pages
フィールドを読み取るwpcanai-read-meta
フィールドを書き込むwpcanai-write-meta
新しいテンプレート投稿を作成するwpcanai-create-template
新しいページを作成する(CanAI メタ付き)wpcanai-create-page
ページの名前変更、スラッグ変更、公開、移動を行う(形式を問わない)wpcanai-update-page
2 つのメニュー(プライマリ、フッター)を読み取るwpcanai-get-menus
メニューリンクを設定または変更するwpcanai-write-menu(Menusを参照)
ロケーションまたは言語を既存のメニューに割り当てる、または割り当てを解除するwpcanai-assign-menu
WP/WC/CanAI オプションを読み取るwpcanai-read-settings
WP/WC/CanAI オプションを更新するwpcanai-update-settings
任意の wp_option を読み取る(許可リスト)wpcanai-get-option
任意の wp_options を更新する(許可リスト/承認)wpcanai-update-options
保留中のオプション更新を確認するwpcanai-get-pending
推奨サイトのブートストラップを行うwpcanai-setup
タイプに対応する投稿 ID を確認するwpcanai-resolve-content-id
WC ページ IDwpcanai-get-wc-page-ids
診断するwpcanai-scan
_canai_* メタを grep するwpcanai-grep-content
環境/ネットワークを診断するwpcanai-diagnostics
ページキャッシュを消去する(書き込み時の purged で対象にならなかった場合)wpcanai-purge-cache
コンテンツなしのインベントリ+ハッシュwpcanai-export { "summary": true } / wpcanai-read-meta { "hash_only": true }
プリセットを一覧表示/インストール/削除するwpcanai-list-presets / wpcanai-install-preset(⚠ clean_slate) / wpcanai-uninstall-preset
CanAI コンテンツをエクスポート/インポートするwpcanai-export / wpcanai-import
不適切な書き込みを元に戻す(1 件の投稿)wpcanai-list-snapshots → wpcanai-restore-snapshot
操作全体を元に戻す(複数の投稿)wpcanai-list-operations → wpcanai-restore-operation
1 件のスナップショットのペイロードを読み取るwpcanai-get-snapshot
検証済みコンテンツのチェックポイントを作成するwpcanai-pin-current(ピン留め/解除:wpcanai-pin-snapshot)
FluentSnippets(オプトイン)、wpcanai-replace-in-snippet を含むcanai-yolo スキル — ここでは説明していません
本番環境用に Tailwind を事前コンパイルするwpcanai-write-meta と tailwind_build + tailwind_hash を使用(Compile Tailwind for Productionを参照)
サイトを翻訳する(ネイティブ i18n)Native string translation ワークフローを参照
ネイティブ i18n 言語を読み取る/設定するwpcanai-i18n-get-settings / wpcanai-i18n-set-settings
翻訳可能な文字列を一覧表示する(1 ページ:post_id)wpcanai-i18n-list-strings
翻訳を書き込む(一括)wpcanai-i18n-set-translations
言語ごとの画像(tmedia)wpcanai-i18n-get-media-map / wpcanai-i18n-set-media-map
文字列インデックスを再構築するwpcanai-i18n-rescan
投稿コンテンツのオーバーライド(CPT/長文形式)wpcanai-i18n-get-post-overrides / wpcanai-i18n-set-post-overrides
ターム名/説明のオーバーライドwpcanai-i18n-get-term-overrides / wpcanai-i18n-set-term-overrides
コンテンツ翻訳の作業キューwpcanai-i18n-list-content
翻訳する投稿のソースを読み取るwpcanai-i18n-get-content
サイト名/キャッチフレーズ/アーカイブ SEO(グローバル文字列)wpcanai-i18n-get-global-strings / wpcanai-i18n-set-global-strings

セキュリティ

  • API キーはパスワードと同様に扱い、使用しない場合は AI Client → Connections で無効化してください。
  • ローカル以外のサイトでは HTTPS を優先してください。

コメント