WordPress 深度系列
把 mcp-adapter 装上之后,AI 真的可以直接读写你的站点——但同时你刚把 /wp-json/mcp/mcp-adapter-default-server 暴露到了公网。这条 REST 端点接受任何带 Application Password 的请求,包括来自 bot、扫描器、以及 prompt injection 攻击者的请求。
如果你不打算给整个站点加一道 Cloudflare Zero Trust,mcp-adapter 就是一个被精心包装的、可被批量枚举的 REST API。
这篇接 6/17 的「Cloudflare Tunnel 零端口方案」和 6/30 的「WordPress 7.0 MCP 集成实战」,是「AI 集成层」的安全收尾篇。我会讲清楚 6 个真实陷阱,以及如何用 Cloudflare Tunnel + Zero Trust Service Auth + mcp-adapter 自带的能力白名单,把 MCP 端点压缩到「只有我本机的 Claude Code 才调得动」。
架构预览:从 mcp-adapter 默认暴露到 Zero Trust 后的零信任通道
默认部署下,mcp-adapter 走 WordPress 内置 REST API 路由:
/wp-json/mcp/mcp-adapter-default-server
这个端点的鉴权靠 WordPress Application Password。任何拿到用户名 + Application Password 的人(包括 bot)都可以调用你注册的所有 Abilities,比如 site-audit/find-broken-links、content/bulk-update-meta。
接入 Cloudflare Tunnel 之后,流量走向变成:
Claude Code (本机) → Cloudflare Edge (Zero Trust 自助登录) → cloudflared (内网回环) → 127.0.0.1/wp-json/mcp
关键的改变是:
1. **公网没有任何 TCP 端口在监听**——cloudflared 是 outbound-only 连接
2. HTTP 层先过 Zero Trust——没带 Service Token / 邮箱 OTP 的请求在 Cloudflare 边缘就被 403 掉
3. REST 层再有 Application Password 鉴权——双层认证
4. mcp-adapter 0.5.0 的能力白名单——只把必要的 Abilities 暴露给 default server
🛠️ 前置准备
在开始之前确认你的环境:
- WordPress 7.0+(2026-05-20 发布,mcp-adapter 已并入核心)
- `WordPress/mcp-adapter` plugin v0.5.0(GitHub releases,2026-06 引入 observability 子系统)
- WordPress Abilities API(6.9+ 已合并到核心,`WordPress/abilities-api` 仓库 2026-02 归档)
- Cloudflare 账户(Free 套餐就够,Zero Trust 不要求付费)
- 一个域名已托管到 Cloudflare(mcp.example.com 这种子域名)
- 已在 6/17 配置过 Cloudflare Tunnel(如果没配过,参考 6/17 那篇的 cloudflared 安装步骤)
验证命令:
# 验证 mcp-adapter 安装
wp plugin list --allow-root | grep mcp-adapter
# 输出:mcp-adapter 0.5.0 active ...
# 验证 Abilities API 注册
wp eval 'if ( function_exists( "wp_register_ability" ) ) { echo "OK"; } else { echo "NEED 6.9+"; }' --allow-root
# 验证 cloudflared 已在运行
ps aux | grep -E "[c]loudflared"
# 输出至少一行包含 tunnel
🚀 核心部署步骤
Step 1:把 mcp-adapter 默认 server 限定到只读 Abilities
mcp-adapter 0.5.0 引入的 mcp_adapter_server_config filter 可以裁剪 default server 暴露的能力集(PR #217 2026-06-17 合并)。
在自定义 plugin 或 mu-plugin 里加:
激活这个 mu-plugin,然后验证:
curl -s -u "your_user:your_app_password" \
"https://your-domain.com/wp-json/mcp/mcp-adapter-default-server" \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}' | jq '.result.tools[].name'
# 预期输出应该只包含 site-audit/* 与 content/read-* 命名的 tool
如果出现 content/bulk-update-meta 这类写操作,filter 没生效——99% 是 mu-plugin 加载顺序问题,把它移到 /wp-content/mu-plugins/ 而不是 theme 的 functions.php。
Step 2:把 mcp.example.com 子域名接入现有 Tunnel
如果你已经在 6/17 的方案里跑着 cloudflared,不要为 MCP 单独开新 tunnel,加一条路由就行:
# ~/.cloudflared/config.yml(追加,不要覆盖)
tunnel:
credentials-file: /root/.cloudflared/.json
ingress:
# 已有规则保持原样...
- hostname: mcp.example.com
service: http_status:404
originRequest:
noTLSVerify: true
- service: http_status:404
然后在 Cloudflare Zero Trust 控制台加 public hostname:
1. Zero Trust → Networks → Tunnels → 选你的 tunnel → Configure → Public Hostname
2. Subdomain: mcp
3. Domain: example.com
4. Service Type: HTTP
5. URL: localhost
注意我先写的是 http_status:404 占位——下一步要加 Zero Trust policy,没加之前不能直接转发到 wp-json。
重启 cloudflared:
sudo systemctl restart cloudflared
sudo systemctl status cloudflared
Step 3:给 mcp.example.com 加 Zero Trust Self-Hosted Application
在 Zero Trust 控制台:
1. Access → Applications → Add an application → Self-hosted
2. Name: WordPress MCP (Personal)
3. Session duration: 24 hours(不要选 Always,Service Token 会失效)
4. Application domain: mcp.example.com
5. Identity providers: 选 One-time PIN(邮箱验证码)
接下来加两条 Policy:
Policy 1 - 我的 Claude Code 走 Service Auth
Name: Local Claude Code
Action: Allow
Session duration: 24 hours
Apply to: Service Auth - <你创建的 Service Token 名字>
Policy 2 - 浏览器手动调试走 OTP
Name: Browser Debug
Action: Allow
Session duration: 1 hour
Apply to: Emails - your-email@example.com
Order 必须是 Service Auth 在前(更具体)。这两条之外的请求全部 Bypass 拒绝。
Step 4:把 Service Token 嵌入 Claude Code MCP 配置
Cloudflare Service Token 是用 Client ID + Client Secret 鉴权的长期凭证(不是 JWT,可以吊销)。
生成:Access → Service Auth → Create Service Token → 名字叫 local-claude-code → 拿到 Client ID 和 Client Secret。
在 Claude Code 的 MCP 配置里:
{
"mcpServers": {
"wordpress-mcp": {
"url": "https://mcp.example.com/wp-json/mcp/mcp-adapter-default-server",
"headers": {
"CF-Access-Client-Id": "",
"CF-Access-Client-Secret": "",
"Authorization": "Basic " + base64("your_wp_user:your_app_password")
}
}
}
}
或者用 claude mcp add-json:
claude mcp add-json wordpress-mcp '{
"url": "https://mcp.example.com/wp-json/mcp/mcp-adapter-default-server",
"headers": {
"CF-Access-Client-Id": "xxx.service-token",
"CF-Access-Client-Secret": "xxx",
"Authorization": "Basic '"$(echo -n 'your_user:your_app_password' | base64)""'"
}
}'
验证:
claude mcp list
# 预期输出 wordpress-mcp: https://mcp.example.com/wp-json/mcp/mcp-adapter-default-server (14 tools)
Step 5:开启 mcp-adapter observability 写审计日志
v0.5.0 的 observability handler 会在 tools/call 之后回调 record_event(),把它接进 WordPress 自带的 debug log:
add_action( 'mcp_adapter_observability_event', function( $handler, $event, $payload ) {
if ( 'mcp.request' !== $event ) {
return;
}
if ( 'success' !== ( $payload['status'] ?? 'unknown' ) ) {
error_log( sprintf(
'[MCP] %s user=%s tool=%s',
$payload['status'],
$payload['user'] ?? 'anon',
$payload['method'] ?? 'unknown'
) );
}
}, 10, 3 );
写入 /var/log/nginx/wordpress-access.log 或 wp-content/debug.log(logrotate 7 天保留就行)。
💣 踩坑录:6 个真实生产陷阱
陷阱 1:mcp-adapter 默认监听 wp-admin 下所有 REST 路由,能力全暴露
**症状**:刚装完 mcp-adapter,tools/list 返回所有已注册的 Abilities,包括 plugin/install、user/create 这种 destructive 操作的。
**根因**:default server 默认 include 所有已注册的 Abilities,0.5.0 才引入 mcp_adapter_server_config filter。
**修复**:必须装 Step 1 那个 mu-plugin。如果你跳过了这一步,mcp-adapter 就是一个被精心包装的、可被批量枚举的 REST API——任何能拿到 Application Password 的人(包括 prompt injection 攻击者)都能调 plugin/install 给你的站装后门。
陷阱 2:Application Password 配错导致 /wp-json/mcp 401 风暴
**症状**:nginx access log 大量 401,Claude Code 反复重试,mcp-adapter observability 里全是 auth_failed。
**根因**:WordPress 6.9+ 引入了 application_password_is_api_request filter,要求 Application Password 请求必须命中特定 REST 路径,并且 HTTP Basic Auth 的用户名必须用 user_login 而不是 user_email。
修复:
// 不要用 email,用 login
$auth = 'Basic ' . base64_encode( 'my_wp_login:abcd EFGH ijkl MNOP qrst UVWX' );
// 注意 Application Password 里的空格要保留
在 WordPress 6.9 以下用 email 没事,但 7.0+ 会 401。验证命令:
curl -s -o /dev/null -w "%{http_code}" -u "your_login:your_app_pwd" \
"https://your-domain.com/wp-json/mcp/mcp-adapter-default-server" \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'
# 必须返回 200,返回 401 就是用户名格式问题
陷阱 3:Cloudflare Tunnel 反代到 127.0.0.1,但 wp-json 走 https:// 导致 cloudflared 526 错误
**症状**:第一次部署完,https://mcp.example.com/wp-json/mcp/... 报 526 Invalid SSL Certificate。
**根因**:cloudflared 转发到 http://localhost 是对的,但你的 nginx 监听着 443 而且只接受 example.com 的 SNI,mcp.example.com 的请求到了 nginx 那里就 526。
修复:两条路任选一条:
方案 A(推荐):用 Cloudflare Origin Certificate
1. Cloudflare 控制台 → SSL/TLS → Origin Server → Create Certificate
2. 覆盖 *.example.com 和 example.com
3. 把证书和私钥装到 nginx,对 mcp.example.com 也启用 HTTPS
4. cloudflared 改转发 https://localhost:443(保留 noTLSVerify: true)
方案 B:cloudflared 配 noTLSVerify + 强制 HTTP
ingress:
- hostname: mcp.example.com
service: http://127.0.0.1:80
originRequest:
noTLSVerify: true
httpHostHeader: mcp.example.com
并在 nginx 给 mcp.example.com 配 80 端口的 vhost,不要走 443。
陷阱 4:Zero Trust policy 顺序错,Service Auth 被 OTP 抢走
**症状**:Service Token 的请求被 Cloudflare 弹 OTP 登录页(/cdn-cgi/access/login),Claude Code 收到 HTML 而不是 JSON,解析失败。
根因:Access policy 按顺序匹配,你的 OTP policy 在 Service Auth policy 之前。Cloudflare 默认「first match wins」,邮箱地址比 Service Auth 名字更宽泛。
修复:把 Service Auth policy 拖到最上面。验证顺序:
# 拿 Service Token 直接打
curl -s -H "CF-Access-Client-Id: xxx" -H "CF-Access-Client-Secret: yyy" \
"https://mcp.example.com/wp-json/mcp/mcp-adapter-default-server" \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'
# 必须直接拿到 JSON,不能出现 302 重定向到 login 页
如果仍然 302,policy 顺序错了。或者你的 Service Token 名字是 default——Cloudflare 不允许用 default 这种保留字作 service token 名字。
陷阱 5:Abilities API 检测不到,mcp-adapter 启动静默失败
**症状**:装完 mcp-adapter 后 tools/list 返回空数组,但 mcp-adapter-default-server 已注册。WordPress 6.9+ 站点,且 wp_register_ability 函数存在,但 wp_get_abilities() 返回 []。
**根因**:Abilities 注册 hook 时机问题。多数 plugin 在 init hook 里注册,但 mcp-adapter 在 plugins_loaded 就开始抓了——注册的时机晚于 mcp-adapter 抓取时机。
修复:把 Abilities 注册 hook 提前:
// 不要用 init
add_action( 'plugins_loaded', function() {
wp_register_ability( 'site-audit/find-broken-links', [...] );
}, 5 ); // 优先级 5,比默认 10 早
mcp-adapter 0.5.0 的 PR #196 已经在 default server 里加了 mcp_adapter_default_server_abilities filter,紧急时可以直接在 mu-plugin 里手动喂 Abilities 列表:
add_filter( 'mcp_adapter_default_server_abilities', function( $ids ) {
return [
'site-audit/find-broken-links',
'content/read-meta',
'content/read-taxonomies',
];
} );
陷阱 6:cloudflared 旧版(2024.x)不支持 streamable HTTP,mcp-adapter 0.5.0 的 streamable transport 用不了
**症状**:本地 Claude Code 走 streamable HTTP 调 mcp-adapter,cloudflared 日志大量 400 Bad Request: invalid HTTP version,但 SSE 模式能工作。
根因:cloudflared 2024.x 还在用 HTTP/1.1 中转,2025 年初的版本才支持 HTTP/2 stream。mcp-adapter 0.5.0 的 streamable transport 默认走 HTTP/2。
修复:
cloudflared --version
# 2025.4.0 或更新
如果你的发行版仓库里 cloudflared 太旧(Ubuntu 22.04 默认 2024.8.0),用 Cloudflare 官方 apt 源:
curl -fsSL https://pkg.cloudflare.com/cloudflare-main.gpg \
| sudo tee /usr/share/keyrings/cloudflare-main.gpg >/dev/null
echo "deb [signed-by=/usr/share/keyrings/cloudflare-main.gpg] https://pkg.cloudflare.com/cloudflared $(lsb_release -cs) main" \
| sudo tee /etc/apt/sources.list.d/cloudflared.list
sudo apt update && sudo apt install cloudflared
🛡️ 进阶配置:让 mcp-adapter 真的只属于你
完成 Step 1-5 之后,剩下还有三件事可以加固:
1. 给 default server 加 IP 白名单(双重保险)
Cloudflare Tunnel 的回环 IP 是 127.0.0.1,但你可能其他服务也跑在同台机器。给 mcp.example.com 的 nginx vhost 加个内网白名单:
location /wp-json/mcp/ {
allow 127.0.0.1;
allow ::1;
deny all;
try_files $uri $uri/ /index.php?$args;
}
这样即使有人绕过 Cloudflare 直接打 nginx,公网也调不到。
2. rate-limit mcp-adapter 端点
mcp-adapter 走 WordPress REST API,但你可以在 nginx 给 /wp-json/mcp/ 加独立 limit_req:
limit_req_zone $binary_remote_addr zone=mcp_limit:10m rate=10r/m;
location /wp-json/mcp/ {
limit_req zone=mcp_limit burst=20 nodelay;
try_files $uri $uri/ /index.php?$args;
}
10 r/m 足够 Claude Code 日常调用,burst=20 防止误触发。
3. mcp-adapter 0.5.0 的 transport permissions 锁死方法集
0.5.0 的 mcp_adapter_server_config filter 支持 allowed_methods:
$config['allowed_methods'] = [ 'tools/list', 'tools/call' ];
// 不要给 'resources/read','prompts/get' 这些 mcp-adapter 当前不需要的 method
减少 attack surface。
总结
6/17 写了 Cloudflare Tunnel 让你的站点公网零端口,6/30 写了 mcp-adapter 让 AI 直接读写站点,这篇是两者之间的安全桥梁:mcp-adapter 默认端点必须经过 Zero Trust 鉴权,Abilities 必须白名单,cloudflared 必须新版,observability 必须开日志。
完整 checklist:
- [ ] Step 1:`mcp_adapter_server_config` filter 限定 read-only Abilities
- [ ] Step 2:cloudflared ingress 加 `mcp.example.com`
- [ ] Step 3:Zero Trust Self-hosted App + Service Auth policy
- [ ] Step 4:Service Token 嵌入 Claude Code MCP headers
- [ ] Step 5:observability handler 写错误日志到 debug.log
- [ ] 进阶:nginx IP 白名单 + rate-limit + transport method 锁定
mcp-adapter 0.5.0(2026-06 release)的 observability 子系统是这套架构的关键,之前版本没有 mcp_adapter_observability_event hook,所有 MCP 调用都是黑盒。
下一篇方向:WordPress 7.0 协作编辑实战(HTTP polling sync provider 与多人同时编辑冲突解决),或者 mcp-adapter 0.5.0 完整 benchmark(不同 Abilities 的延迟对比)。
👉 Join MiniMax Token Plan: AI coding acceleration for businesses
👉 Join Zhipu Coding Plan: GLM-4.6/GLM-5 coding packages, China-stable, pay-per-token unlimited
👉 Join Aliyun AI: Top AI products with exclusive coupons for business innovation
📌 This article was AI-assisted generated and human-reviewed | TechPassive — An AI-driven content testing site focused on real tool reviews
🔗 Recommended Tools
These are carefully selected tools. Using our affiliate links supports us to keep producing quality content: