mcpField
C
canai-yolo

canai-yolo

github.com/usewp开发046 次安装3 天前更新
代码仓库

关于 canai-yolo

canai-yolo 是一项可选技能,通过 CanAI MCP 工具管理 FluentSnippets 中的 PHP、CSS 和 JS 代码片段。它专注于片段操作与部署防护,仅用于用户明确要求的片段工作。

主要功能

  1. 01列出并读取代码片段
  2. 02创建和更新代码片段
  3. 03带防护措施发布片段
  4. 04通过哈希校验替换代码
  5. 05检查存储代码哈希与缓存状态
  6. 06说明 CanAI 语言处理方式

适用场景

  1. 01检查站点专用代码片段
  2. 02创建或更新 FluentSnippets 代码
  3. 03发布用户要求的 PHP 片段
  4. 04验证片段写入与缓存状态

MCP 相关

它使用 CanAI MCP 服务,地址为 `{site}/wp-json/mcp/wpcanai`,并通过 API key 认证。文档介绍了管理 FluentSnippets 的工具,这些工具仅在 FluentSnippets 插件启用时注册。

安装

复制下面的命令,在终端中运行。

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

安装后在 Claude Code 中输入/canai-yolo确认已加载。

详细介绍

CanAI YOLO — FluentSnippets MCP 工具

高风险。需主动启用。 此技能介绍的 MCP 工具可以在 WordPress 网站上永久发布 PHP。只有用户明确要求处理代码片段时,才安装并调用此技能。

处理模板、页面、国际化、媒体、设置和 Tailwind 时,请改用 canai-mcp。除非用户要求使用此技能,否则不要在普通的 /canai-mcp 内容会话中混用 YOLO 工作流。

传输方式与 canai-mcp 相同:使用同一个 CanAI MCP 服务({site}/wp-json/mcp/wpcanai)和 API 密钥。能力 ID 使用斜杠;MCP 工具名称使用连字符。


严重警告——何时使用此技能

仅在以下情况使用 canai-yolo:

  1. FluentSnippets(easy-code-manager)——通过专用 MCP 工具列出、读取、创建、更新或发布网站的 PHP、CSS 或 JS 代码片段。

没有 eval 逃生通道。 wpcanai/eval 能力已在插件 v1.59.0 中移除——WP.org 全面禁止 eval()——且不再读取 WPCANAI_ENABLE_EVAL。每项能力都是真正的能力;没有任何备用方案。如果某个工作流似乎需要 eval,正确的做法是请求添加缺失的能力,而不是临时变通。计划推出一个可选启用的附加插件(specs/2026-08-20-canai-eval-addon-design.md)。


FluentSnippets(PHP/CSS/JS 代码片段)

网站专用的 PHP、CSS 和 JS 通常存放在 FluentSnippets 中,而不是插件代码里。工具:wpcanai-list-snippets、wpcanai-get-snippet、wpcanai-create-snippet、wpcanai-update-snippet、wpcanai-replace-in-snippet、wpcanai-set-snippet-status。这些工具的描述以 [fluent-snippets] / [fluent-snippets, filesystem] 开头(如果网站在写入后会清除页面缓存,还会带有 cache)——参见 canai-mcp 中的 工具标签。

仅凭架构定义不易看出的关键规则:

  1. 只有在 FluentSnippets 处于启用状态时才会出现这些工具。 如果 easy-code-manager 未启用,这六个代码片段工具就不会注册——你不会在工具列表中看到它们。

  2. 规范格式(插件 v1.80.0)。 发送 PHP 时,正文中不要包含 <?php——写入时会去除开头的 <?php 和空行(不再拒绝),读取时也会去除。末尾换行会保留;末尾的 ?> 会被移除。sha256 / bytes / lines 是基于代码经过 get-snippet 返回后的原样内容计算的,因此比较时请对该字符串计算哈希,而不是对本地文件计算。css/js 不得自行包裹在 <style> / <script> 标签中。PHP 会在保存前进行语法检查并试运行一次:不得重新声明已存在的函数或类(用 function_exists / class_exists 做保护),不得打印输出、在顶层使用 return,也不得调用尚未加载的内容(请使用钩子)。

  3. 创建的默认状态为草稿。 wpcanai-create-snippet 会写入草稿,除非你传入 status: "published"(v1.80.0);此时会先检查 PHP 语法,若存在语法错误则不会写入任何内容。响应中的 status 是回读的结果——Fluent Snippets 自带的 auto_publish 仍可能会将草稿发布。

  4. 只有允许列表中的分组才能成功写入。 管理员可在 CanAI → AI Client → Guardrails → FluentSnippets group allowlist 中指定 Agent 可写入的代码片段分组。允许列表为空(默认状态)意味着所有代码片段写入都会被拒绝(snippet_writes_disabled)。写入不在允许列表中的分组会返回 snippet_group_not_allowed。除非用户另有指定,否则优先使用分组 AI。读取(list/get)始终允许,不受允许列表影响。

  5. 更新采用稀疏方式。 调用 wpcanai-update-snippet 时只发送要更改的字段;服务端会读取当前代码片段,将你的更改合并到其完整元数据中,再写回。省略某个字段会保留其当前值——不能只发送 code 来清空 group/priority/run_at/created_at。

  6. run_at 因类型而异。 PHP → all|backend|frontend;php_content → shortcode|wp_head|wp_body_open|wp_footer|before_content|after_content;css → wp_head|admin_head|everywhere;js → wp_head|wp_footer|admin_head|admin_footer。无效的类型与运行位置组合会在调用 FluentSnippets 前被拒绝(invalid_snippet_type / invalid_run_at)。

  7. 上游问题(此处未修复)。 CSS 的 everywhere 实际上不会在管理界面加载(插件中的拼写错误:everywehere),而 PHP 的 frontend 不会被强制限制(会在所有位置运行)——由于这两个值是 FluentSnippets 界面提供的选项,因此仍会被接受。不确定时,优先使用 all / backend / wp_head。

  8. 重新启用。 如果某个代码片段发生致命错误,FluentSnippets 会自动将其禁用,并将 has_error 设为 true。除非传入 "reactivate": true,否则更新会被拒绝(snippet_has_error);传入该参数会清除错误并应用更新——因此,常规编辑不会悄悄重新启用已知有问题的代码。

  9. 错误信息会说明原因(v1.80.0)。 如果 Fluent Snippets 拒绝保存,错误信息会返回 "{title} on line {line}: {reason} Fix: {fix}"——重试前请先阅读原因和修复建议;不要再次发送相同代码。

通过 wpcanai-update-snippet 的 group 将代码片段移至其他分组时,当前分组和目标分组都必须在允许列表中。

部署循环(plugin v1.80.0)

无需重新发送,即可编辑在线代码片段,并在不使用浏览器的情况下确认:

  1. 替换 — wpcanai-replace-in-snippet { "file_name", "replacements": [...], "require_all": true, "expected_sha256"?: "<sha256 from your last read>" }。require_all 会在未找到匹配项时不写入任何内容,并指出最接近的匹配项;如果你读取代码片段后有人对其进行了更改,expected_sha256 会以 hash_mismatch(409)拒绝写入。不确定锚点是否正确时,先使用 dry_run: true。仍需通过 wpcanai-update-snippet 重写整个正文(使用相同的 expected_sha256)。
  2. 检查 — 响应中包含代码实际存储后的 sha256、bytes 和 lines,这些值是在写入后回读得到的。将 sha256 与预期代码的哈希值(基于上述规范化形式)进行比较。之后,或在其他会话中,wpcanai-get-snippet { "file_name", "fields": ["hash"] } 会返回相同字段,但不返回代码。
  3. 缓存 — 响应中的 purged 表示是否有待处理的页面缓存清除:{ "scope": "all", "plugin": "…" } 表示在请求结束时清除整个站点的缓存(代码片段会在每个页面上运行);null 表示未安排任何清除操作。仅当缓存插件处于启用状态且 purged 为 null(写入后清除缓存的功能已关闭),或针对写入范围以外的 URL 时,才调用 wpcanai-purge-cache(在 canai-mcp 中)。
在代码片段中编写自定义 REST 路由

Agent 经常使用 FluentSnippets PHP 路由加上 _canai_js fetch,来实现浏览器 → WordPress 中继。有两条规则仅看 schema 不容易发现:

  1. 绝不要在 X-WP-Nonce 中发送自定义 nonce(也不要使用请求参数 _wpnonce)。 WordPress 核心的 rest_cookie_check_errors() 会在站点范围内的每个 REST 请求中拦截这个确切的标头/参数,并根据内置的 'wp_rest' 操作对其进行验证——这发生在你的路由的 permission_callback 运行之前。使用该名称传递自定义操作的 nonce 会始终导致 rest_cookie_invalid_nonce 错误,从而掩盖你自己的权限逻辑。自定义 nonce 操作应使用其他任意标头名称(例如 X-<Your-App>-Nonce)。

  2. 将 HTTP 状态/错误代码显示给客户端(通过控制台日志或不同的页面状态),即使面向用户的文案仍保持通用也应如此。速率限制 429、nonce 403 和真正的 500 是不同的故障;将它们合并为一条消息,会让生产事故无法排查。

代码片段中的访客语言(plugin v1.99.0)

在启用了 CanAI 原生翻译的站点上(wpcanai-i18n-get-settings → enabled: true),凡是写入面向访客文本的代码片段(SEO 元数据、表单标签和消息、邮件、表单提交后的重定向),都必须遵循页面语言。向 CanAI 查询,而不是向 WordPress 查询。 get_locale() / determine_locale() 只有在语言的所有者勾选了以此语言显示 WordPress 和插件文本(plugin v1.100.0)时才会跟随页面语言;否则,在 /ms/ 页面上它们仍会返回站点语言。$_SERVER['REQUEST_URI'] 只有从 parse_request 操作开始才会携带 /ms 前缀(v1.100.0);在此之前以及旧版插件中,CanAI 都会移除该前缀。请向 CanAI 查询:

功能返回值
wpcanai_current_language()语言代号('en'、'ms');若无法确定,则返回 null:管理后台、AJAX、REST(无论通过何种方式访问)、cron、WP-CLI、WordPress 系统路径(如 wp-login.php)以及 index.php 以外的入口点(v1.100.0)、setup_theme 之前,以及只有一种语言的站点。
wpcanai_language_from_url( string $url )此站点上该 URL 所呈现内容的语言(先检查 ?lang=,再检查前缀,否则使用默认语言);如果属于其他站点、系统路径(/wp-admin/…、/wp-json/…)或只有一种语言,则返回 null。
wpcanai_translate( string $source, array $vars = [], ?string $lang = null )CanAI → Translations 中的字符串,按 Twig t() 的方式原样呈现(内联标签、{placeholders},绝不显示过期译文);对于默认语言或未知语言,返回源字符串。$lang 为 null 时,使用 wpcanai_current_language()。
do_action( 'wpcanai_language_detected', $lang, $request_uri )(v1.100.0)这是一个操作钩子,不是函数:每次前端请求触发一次,在 setup_theme 优先级 1 时触发,此时语言刚刚确定。$request_uri 是访问者的 URI,未经处理且带斜杠:使用 wp_unslash() 处理,并在输出前进行转义。
// This request's language: the page's, else the form's hidden field, else the Referer.
if ( ! function_exists( 'mysite_request_lang' ) ) {
    function mysite_request_lang(): string {
        if ( ! function_exists( 'wpcanai_current_language' ) ) {
            return 'en'; // CanAI older than 1.99.0: the default language
        }
        $lang = wpcanai_current_language();
        if ( null === $lang && isset( $_POST['mysite_lang'] ) ) {
            $lang = sanitize_key( wp_unslash( $_POST['mysite_lang'] ) ); // an unknown slug translates to the source
        }
        return $lang ?? wpcanai_language_from_url( (string) wp_get_referer() ) ?? 'en';
    }
}

// In the code that prints the form (a shortcode, a form plugin's markup filter), never at the top level:
printf( '<input type="hidden" name="mysite_lang" value="%s">', esc_attr( mysite_request_lang() ) );

// Wherever visitor-facing text is built, on the page or in the form handler:
$subject = wpcanai_translate( 'Your download is ready', array(), mysite_request_lang() );

规则:

  1. 在钩子内读取语言(template_redirect、wp_head、表单插件的筛选器),不要在代码片段的顶层读取:CanAI 加载后这些函数就已存在,但只有到 setup_theme 才能确定语言;保存时的测试运行没有页面,因此返回 null。若代码必须在 template_redirect 之前执行操作,请使用 wpcanai_language_detected 钩子。
  2. 绝不要自行推导语言。 不要解析 REQUEST_URI(只有路由完成后才会出现前缀,而且 ?lang= 和系统路径遵循 CanAI 的规则),不要使用 get_locale()(只有站点所有者启用相应设置时,它才会跟随语言),不要使用 wpcanai_lang cookie(它记录访问者最近查看的翻译页面,而非当前所在页面),也不要使用 CanAI 内部实现(MenuLocations::language_manager()、new StringStore()、对 LanguageManager 方法调用 remove_action)——这些内容可能在任何版本中变更。
  3. 提交到 admin-ajax.php / REST 的表单没有页面语言(wpcanai_current_language() 返回 null)。在表单渲染时将语言写入表单(使用 wpcanai_current_language() 设置隐藏字段),并优先读取该字段;wpcanai_language_from_url( (string) wp_get_referer() ) 是备用方案,因为浏览器不一定会发送 Referer。如果后续步骤(邮件、排队任务)需要语言,请将其存储在条目中——cron 运行时没有请求可供查询。
  4. 输出结果时进行转义。 纯源字符串会以纯文本形式返回(类似 __());带有内联标签的字符串会以 HTML 形式返回,其中的文本已完成转义。
  5. 仅供 PHP 使用的字符串不会列在 Strings 标签页或 wpcanai-i18n-list-strings 中(两者都只列出模板和菜单中的 t() 源字符串)。使用 wpcanai-i18n-set-translations(位于 canai-mcp 中)存储这些字符串的译文,键必须与代码片段传入的源字符串完全一致,并向用户提供源字符串 → 译文表。
  6. Cookie 重定向。 如果未登录访问者的语言 cookie 指定了某种译文,访问者从无前缀的 CanAI 页面访问时会收到 302 重定向,前往该译文页面。如果站点所有者希望每个 URL 都呈现其自身对应的语言:add_filter( 'wpcanai_redirect_to_cookie_language', '__return_false' );(参数为 $redirect, $lang, $target)。不要对 CanAI 的钩子调用 remove_action。
wpcanai-list-snippets
  • 参数: { "status"?: string, "type"?: string, "group"?: string, "include_hash"?: bool } — 所有筛选条件均为可选项(status:published|draft;type:PHP|php_content|css|js;group:分组名称)。(v1.80.0) 设置 include_hash: true 后,会为每个代码片段添加 sha256、bytes、lines(需要读取每个文件,因此需选择性启用)。仅在 FluentSnippets 已启用时显示。
  • 返回: { "snippets": [{ "file_name", "name", "type", "status", "run_at", "priority", "group", "tags", "description", "created_at", "updated_at", "has_error", "error_message", "writable" }], "groups": string[], "writable_groups": string[] } — writable 是针对单个代码片段的标记(其分组在允许列表中);writable_groups 是当前允许列表,便于你在尝试写入前了解可修改的分组。读取不受限制。
wpcanai-get-snippet
  • 参数: { "file_name": string, "fields"?: ["meta"|"code"|"hash"] } — 代码片段的文件名(其标识符,例如 3-my-snippet.php);fields 默认为全部三项。
  • **返回:**完整元数据 + code 正文 + 错误状态 + writable(与 list-snippets 行包含相同字段,另加 code 和 condition)+ (v1.80.0) sha256、bytes、lines。fields: ["hash"] 仅返回 file_name、status、group、updated_at、sha256、bytes、lines — 可用它确认写入结果,而无需拉取代码。文件未知时返回错误 snippet_not_found。
wpcanai-create-snippet
  • 参数: { "name": string, "type": string, "run_at": string, "group": string, "code": string, "description"?: string, "tags"?: string, "priority"?: int, "status"?: "draft"|"published" } — name/type/run_at/group/code 均为必填项。PHP code 不带 <?php(如果开头包含该标记,则会被移除)。type/run_at 会根据类型表进行验证。group 必须在可写允许列表中。status 默认为 draft;若为 published,则会先对 PHP 代码进行语法检查。
  • 返回: { "success": true, "file_name": string, "status": string, "group": string, "note": string, "sha256", "bytes", "lines", "purged" } — 文件名会自动生成(无法自行指定),后续所有调用都以此为键。会回读 status;稍后可通过 wpcanai-set-snippet-status 让草稿生效。
wpcanai-update-snippet
  • 参数: { "file_name": string, "code"?: string, "name"?: string, "description"?: string, "tags"?: string, "group"?: string, "run_at"?: string, "priority"?: int, "reactivate"?: bool, "expected_sha256"?: string } — file_name 为必填项;仅发送需要更改的字段(稀疏更新 — 服务会与当前元数据合并)。当前分组(以及移动时的目标 group)必须在允许列表中。出错的代码片段需要设置 reactivate: true。(v1.80.0) expected_sha256 可拒绝过期写入,并返回 hash_mismatch(409;消息中包含两个哈希值,不会写入任何内容)。
  • 返回: { "success": true, "file_name": string, "changed_fields": string[], "sha256", "bytes", "lines", "purged" } — 哈希字段会在写入后回读。
  • 已发布的 PHP 代码更新:过去,更新已发布 PHP 代码片段的 code 会因 Cannot redeclare function 而触发致命错误(FluentSnippets 会在已加载在线代码片段的同一请求中进行验证)。CanAI 会透明地处理此问题。如果较旧版本的插件仍返回该错误,请通过 wpcanai-set-snippet-status 先取消发布,再更新,最后重新发布。
wpcanai-replace-in-snippet(插件 v1.80.0)
  • 参数: { "file_name": string, "replacements": [{ "from": string, "to": string }], "require_all"?: bool, "ignore_whitespace"?: bool, "regex"?: bool, "scope"?: { "text": string, "occurrence"?: int }, "dry_run"?: bool, "expected_sha256"?: string } — 将 wpcanai-replace-in-meta 契约(参见 canai-mcp)应用于代码片段的代码:在 replace_no_match 消息中提供相同的 require_all 未匹配诊断信息,并支持相同的 ignore_whitespace / regex / scope / dry_run。写入操作通过 update-snippet 执行(发布的 PHP 会先进行 lint 检查),并使用相同的组允许列表。
  • 返回: { "success", "file_name", "total", "dry_run", "replacements": [{ "from", "to", "count", "suggestion"? }], "sha256", "bytes", "lines", "purged"? } — 执行 dry_run 时,哈希值针对当前代码,且不会返回 purged。
wpcanai-set-snippet-status
  • 参数: { "file_name": string, "status": "published"|"draft" } — 代码片段所属组必须在允许列表中。
  • 返回: { "success": true, "file_name": string, "status": string, "sha256", "bytes", "lines", "purged" }。会触发 FluentSnippets 的两个生命周期操作,以重建索引缓存,并确保已发布的代码片段实际运行。

操作路由(速查)

目标工具
列出 / 读取 FluentSnippets 代码片段wpcanai-list-snippets / wpcanai-get-snippet
创建 / 更新代码片段wpcanai-create-snippet(status 可选)/ wpcanai-update-snippet
编辑代码片段的部分内容(部署循环)wpcanai-replace-in-snippet → 检查 sha256 → 检查 purged
确认已存储的内容,不包含代码wpcanai-get-snippet { "fields": ["hash"] } / wpcanai-list-snippets { "include_hash": true }
发布 / 取消发布代码片段wpcanai-set-snippet-status
代码片段中访客的语言 / 翻译文本(v1.99.0)PHP 中的 wpcanai_current_language()、wpcanai_language_from_url()、wpcanai_translate() — 参见 代码片段中的访客语言;通过 wpcanai-i18n-set-translations(canai-mcp)管理翻译

安装

npx skills add usewp/canai --skill canai-yolo

与 canai-mcp 配合使用,在同一 MCP 端点上处理内容。

评论

支持 Markdown