wordpress

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese
Drive the WordPress REST API (
/wp-json/wp/v2
) with
curl + jq
.
The user's self-hosted WordPress credentials are injected as env vars:
  • $WORDPRESS_SITE_URL
    — site root, e.g.
    https://blog.example.com
  • $WORDPRESS_USERNAME
    — the WordPress login username
  • $WORDPRESS_APP_PASSWORD
    — an Application Password (WP 5.6+ core), NOT the login password. Treat it like a secret — never log or echo it.
Auth is HTTP Basic (
username:app_password
) over HTTPS. Set up a reusable base once, then every call reuses it:
bash
undefined
使用
curl + jq
调用WordPress REST API
/wp-json/wp/v2
)。
用户的自托管WordPress凭证会作为环境变量注入:
  • $WORDPRESS_SITE_URL
    — 站点根地址,例如
    https://blog.example.com
  • $WORDPRESS_USERNAME
    — WordPress登录用户名
  • $WORDPRESS_APP_PASSWORD
    应用密码(WordPress 5.6+核心功能),并非登录密码。请将其视为机密信息——切勿记录或输出
认证方式为HTTPS下的HTTP Basic认证(
username:app_password
)。先设置可复用的基础配置,之后每次调用均可复用:
bash
undefined

Normalize the site URL (strip a trailing slash) and build the API base.

标准化站点URL(去除末尾斜杠)并构建API基础地址。

SITE="${WORDPRESS_SITE_URL%/}" API="$SITE/wp-json/wp/v2"
SITE="${WORDPRESS_SITE_URL%/}" API="$SITE/wp-json/wp/v2"

-u sends HTTP Basic auth; --fail-with-body surfaces the JSON error body on 4xx/5xx.

-u 发送HTTP Basic认证;--fail-with-body 在4xx/5xx错误时显示JSON错误体。

WP=(curl -sS --fail-with-body -u "$WORDPRESS_USERNAME:$WORDPRESS_APP_PASSWORD")

Errors come back as `{"code": "...", "message": "...", "data": {"status": 401}}` —
show `message` verbatim. Common codes:

| HTTP | Meaning | What to tell the user |
|------|---------|-----------------------|
| 401 | `incorrect_password` / bad Basic auth | Application Password wrong or revoked → regenerate it and reconnect the WordPress connector |
| 403 | `rest_cannot_create` / insufficient role | The user's role can't publish; needs Author/Editor/Admin, or Application Passwords are disabled on the site |
| 404 | `rest_no_route` | REST API disabled or a security plugin blocks `/wp-json` → the user must re-enable it |
| 400 | `rest_invalid_param` | Bad field (e.g. unknown category id) → fix and retry |

> **`content` is HTML, not Markdown.** Convert Markdown to HTML first
> (`pandoc -f markdown -t html`, or a simple converter). Raw Markdown renders literally.
WP=(curl -sS --fail-with-body -u "$WORDPRESS_USERNAME:$WORDPRESS_APP_PASSWORD")

错误返回格式为`{"code": "...", "message": "...", "data": {"status": 401}}`——直接显示`message`内容。常见错误码:

| HTTP | 含义 | 需告知用户的内容 |
|------|---------|-----------------------|
| 401 | `incorrect_password` / 无效Basic认证 | 应用密码错误或已失效 → 重新生成密码并重新连接WordPress连接器 |
| 403 | `rest_cannot_create` / 角色权限不足 | 用户角色无发布权限;需要作者/编辑/管理员权限,或站点已禁用应用密码功能 |
| 404 | `rest_no_route` | REST API已禁用或安全插件拦截了`/wp-json` → 用户需重新启用REST API |
| 400 | `rest_invalid_param` | 参数无效(如未知分类ID) → 修正后重试 |

> **`content`字段为HTML格式,而非Markdown。** 需先将Markdown转换为HTML(可使用`pandoc -f markdown -t html`或简易转换器)。原始Markdown会被原样渲染。

Step 0 — verify the connection first

步骤0 — 先验证连接

bash
"${WP[@]}" "$API/users/me" | jq '{id, name, slug, roles: (.roles // [])}'
A 200 with your user object confirms the site URL, username, and Application Password all work. If this fails, stop and surface the error — don't attempt writes.
bash
"${WP[@]}" "$API/users/me" | jq '{id, name, slug, roles: (.roles // [])}'
返回200状态码及用户对象,说明站点URL、用户名和应用密码均有效。如果此步骤失败,请停止操作并显示错误信息——不要尝试写入操作。

Publish or draft a post

发布或保存草稿文章

Publishing is public and hard to undo — confirm with the user before using
status=publish
.
Default to
status=draft
and hand back the edit link.
bash
jq -n --arg t "国内如何稳定调用 Claude API" \
      --arg c "<p>正文 HTML……</p>" \
      --arg s "draft" \
  '{title:$t, content:$c, status:$s}' \
| "${WP[@]}" -X POST "$API/posts" \
    -H "Content-Type: application/json" -d @- \
| jq '{id, status, link, edit: "\(env.WORDPRESS_SITE_URL)/wp-admin/post.php?action=edit&post=\(.id)"}'
With categories / tags / excerpt (ids come from the endpoints below):
bash
jq -n --arg t "标题" --arg c "<p>正文</p>" --arg e "一句话摘要" \
  '{title:$t, content:$c, excerpt:$e, status:"draft",
    categories:[5], tags:[12,34]}' \
| "${WP[@]}" -X POST "$API/posts" -H "Content-Type: application/json" -d @- \
| jq '{id, status, link}'
  • Publish an existing draft:
    POST $API/posts/<id>
    body
    {"status":"publish"}
    .
  • Update a post:
    POST $API/posts/<id>
    with any subset of fields (WP REST uses POST, not PUT, for updates).
  • Delete (trash) a post:
    "${WP[@]}" -X DELETE "$API/posts/<id>"
    .
发布操作是公开且难以撤销的——使用
status=publish
前请与用户确认。
默认使用
status=draft
并返回编辑链接。
bash
jq -n --arg t "国内如何稳定调用 Claude API" \
      --arg c "<p>正文 HTML……</p>" \
      --arg s "draft" \
  '{title:$t, content:$c, status:$s}' \
| "${WP[@]}" -X POST "$API/posts" \
    -H "Content-Type: application/json" -d @- \
| jq '{id, status, link, edit: "\(env.WORDPRESS_SITE_URL)/wp-admin/post.php?action=edit&post=\(.id)"}'
包含分类/标签/摘要的示例(ID来自下方端点):
bash
jq -n --arg t "标题" --arg c "<p>正文</p>" --arg e "一句话摘要" \
  '{title:$t, content:$c, excerpt:$e, status:"draft",
    categories:[5], tags:[12,34]}' \
| "${WP[@]}" -X POST "$API/posts" -H "Content-Type: application/json" -d @- \
| jq '{id, status, link}'
  • 发布现有草稿:向
    POST $API/posts/<id>
    提交请求体
    {"status":"publish"}
  • 更新文章:向
    POST $API/posts/<id>
    提交任意字段子集(WordPress REST API使用POST而非PUT进行更新)。
  • 删除(移入回收站)文章:
    "${WP[@]}" -X DELETE "$API/posts/<id>"

List / read posts

列出/读取文章

bash
"${WP[@]}" "$API/posts?per_page=10&status=publish,draft&_fields=id,title,status,link,date" \
  | jq '.[] | {id, title: .title.rendered, status, link, date}'
Paginate with
&page=2
; the total page count is in the
X-WP-TotalPages
response header (add
-D -
to see headers).
bash
"${WP[@]}" "$API/posts?per_page=10&status=publish,draft&_fields=id,title,status,link,date" \
  | jq '.[] | {id, title: .title.rendered, status, link, date}'
使用
&page=2
进行分页;总页数在
X-WP-TotalPages
响应头中(添加
-D -
可查看响应头)。

Categories & tags (get or create ids)

分类与标签(获取或创建ID)

bash
undefined
bash
undefined

List existing

列出已有分类/标签

"${WP[@]}" "$API/categories?per_page=100&_fields=id,name,slug" | jq '.[] | {id, name}' "${WP[@]}" "$API/tags?per_page=100&_fields=id,name,slug" | jq '.[] | {id, name}'
"${WP[@]}" "$API/categories?per_page=100&_fields=id,name,slug" | jq '.[] | {id, name}' "${WP[@]}" "$API/tags?per_page=100&_fields=id,name,slug" | jq '.[] | {id, name}'

Create one (returns its id)

创建分类/标签(返回其ID)

jq -n --arg n "AI 教程" '{name:$n}'
| "${WP[@]}" -X POST "$API/categories" -H "Content-Type: application/json" -d @-
| jq '{id, name}'

Creating a term that already exists returns
`{"code":"term_exists", ... "data":{"status":400,"term_id":<id>}}` — reuse
`.data.term_id` instead of failing.
jq -n --arg n "AI 教程" '{name:$n}'
| "${WP[@]}" -X POST "$API/categories" -H "Content-Type: application/json" -d @-
| jq '{id, name}'

创建已存在的分类/标签会返回`{"code":"term_exists", ... "data":{"status":400,"term_id":<id>}}`——请复用`.data.term_id`而非报错。

Upload media (featured image / in-body image)

上传媒体(特色图片/正文中的图片)

bash
FILE="./cover.png"
NAME="$(basename "$FILE")"
MEDIA_ID=$("${WP[@]}" -X POST "$API/media" \
  -H "Content-Disposition: attachment; filename=\"$NAME\"" \
  -H "Content-Type: image/png" \
  --data-binary @"$FILE" | jq -r '.id')
echo "media id=$MEDIA_ID"
bash
FILE="./cover.png"
NAME="$(basename "$FILE")"
MEDIA_ID=$("${WP[@]}" -X POST "$API/media" \
  -H "Content-Disposition: attachment; filename=\"$NAME\"" \
  -H "Content-Type: image/png" \
  --data-binary @"$FILE" | jq -r '.id')
echo "media id=$MEDIA_ID"

Attach as the post's featured image:

将其设置为文章的特色图片:

add "featured_media": <MEDIA_ID> to the post body.

在文章请求体中添加 "featured_media": <MEDIA_ID>。

undefined
undefined

Gotchas

注意事项

  • HTTPS + Application Passwords are required. On plain
    http://
    , WordPress disables Application Passwords → every call 401s. Tell the user to enable HTTPS.
  • A security plugin / host may block
    /wp-json
    (Wordfence, "disable REST API" plugins, some managed hosts). Symptom: 404
    rest_no_route
    or an HTML login page instead of JSON. The user must allow REST API access.
  • The Application Password contains spaces (e.g.
    abcd efgh ijkl mnop
    ). Keep them —
    curl -u
    handles the spaces fine; don't strip them.
  • Never publish silently. Even if the user says "post it", prefer creating a draft and returning the
    wp-admin
    edit link unless they explicitly asked to go live.
  • 必须使用HTTPS + 应用密码。在纯
    http://
    协议下,WordPress会禁用应用密码功能→所有调用均返回401错误。请告知用户启用HTTPS。
  • 安全插件/主机可能拦截
    /wp-json
    (如Wordfence、“禁用REST API”插件、部分托管主机)。症状:返回404错误
    rest_no_route
    或返回HTML登录页面而非JSON。用户必须允许REST API访问。
  • 应用密码包含空格(例如
    abcd efgh ijkl mnop
    )。请保留空格——
    curl -u
    可正常处理空格;不要去除。
  • 切勿静默发布。即使用户说“发布”,除非用户明确要求上线,否则优先创建草稿并返回
    wp-admin
    编辑链接。

Record the output

记录输出结果

After you successfully publish and obtain the live result URL, call the built-in
publish_artifact
tool ONCE so the user can track this deliverable in My Outputs:
publish_artifact(kind="article", channel="wordpress", title="<title>", url="<the REAL returned URL>", status="delivered")
Use the real returned URL — never fabricate one. Call it once per published item, only after delivery is confirmed; skip it (or use
status="failed"
) if publishing failed. See
_shared/artifacts.md
.
成功发布并获取到实际结果URL后,调用内置的
publish_artifact
工具一次,以便用户在我的输出中跟踪此交付物:
publish_artifact(kind="article", channel="wordpress", title="<title>", url="<the REAL returned URL>", status="delivered")
请使用实际返回的URL——切勿伪造。每个已发布的内容仅调用一次,且仅在确认交付成功后调用;若发布失败,请跳过(或使用
status="failed"
)。详情请查看
_shared/artifacts.md