wordpress について
`curl` と `jq` で WordPress REST API を操作し、コンテンツを管理するスキルです。接続確認、投稿やメディアの操作に加え、認証情報、重複投稿、公開に関する安全対策を扱います。
主な機能
- 01認証済みサイトへのアクセスと権限の確認
- 02投稿の作成、更新、予約、一覧表示
- 03カテゴリー、タグ、メディアの管理
- 04公開前の重複投稿チェック
- 05HTMLコンテンツとSEO項目の処理
活用シーン
- 01下書きを作成して編集リンクを返す
- 02既存記事を更新または予約投稿する
- 03アイキャッチ画像をアップロードしてSEO項目を設定
- 04投稿と分類用IDを確認
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ acedatacloud/ skills --skill wordpressインストール後、Claude Code で/wordpressを入力して読み込まれたか確認します。
詳細
curl + jq を使って WordPress REST API(/wp-json/wp/v2)を操作します。
ユーザーのセルフホスト型 WordPress の認証情報は、環境変数として設定されています。
$WORDPRESS_SITE_URL— サイトのルート URL。例:https://blog.example.com$WORDPRESS_USERNAME— WordPress のログインユーザー名$WORDPRESS_APP_PASSWORD— Application Password(WP 5.6 以降のコア機能)です。ログインパスワードではありません。シークレットとして扱い、ログに記録したり、表示したりしないでください。
認証には HTTPS 経由の HTTP Basic(username:app_password)を使います。最初に再利用可能なベースを設定し、以降のすべての呼び出しで再利用します。
# Normalize the site URL (strip a trailing slash) and build the API base.
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.
WP=(curl -sS --fail-with-body -u "$WORDPRESS_USERNAME:$WORDPRESS_APP_PASSWORD")
これらの呼び出しに
-L/--locationを追加しないでください。 リダイレクトが発生する場合、通常はサイト URL が間違っています。curl はホストやスキームをまたぐリダイレクト(例:http→https、ルートドメイン→www)でAuthorizationヘッダーを削除します。ここで起きるのはまさにそのケースで、リダイレクト先へのリクエストは未認証となり、HTTP 200 のサイトの HTML ページが返されることがあります。成功したように見えても、何も書き込まれていません。代わりに URL を修正してください(以下の/wp-jsonに関する注意点を参照)。
エラーは {"code": "...", "message": "...", "data": {"status": 401}} の形式で返されます。message はそのまま表示してください。一般的なコードは次のとおりです。
| HTTP | 意味 | ユーザーへの案内 |
|---|---|---|
| 401 | incorrect_password/Basic 認証情報が不正 | Application Password が間違っているか、無効化されています。再生成して WordPress コネクターに再接続してください |
| 401 | 通常の http:// サイトでの rest_not_logged_in | HTTPS でないと Application Password は無効になります。HTTPS を有効にしてください |
| 403 | rest_cannot_create/権限不足 | ユーザーの権限では公開できません。Author/Editor/Admin 権限が必要です。または、サイトで Application Password が無効になっています |
| 404 | rest_no_route | REST API が無効になっているか、セキュリティプラグインが /wp-json をブロックしています。再度有効にしてください |
| 400 | rest_invalid_param | フィールドが不正です(例:存在しないカテゴリ ID)。修正して再試行してください |
| 500 | rest_upload_sideload_error | Web サーバーが wp-content/uploads に書き込めません。ディレクトリの権限を修正してください |
contentは HTML であり、Markdown ではありません。 Markdown をそのまま渡すと、文字列のまま表示されます。pandocはインストールされていません。サンドボックスにプリインストールされている Python のmarkdownパッケージを使って変換してください。インポートに失敗した場合は、最初にpip install markdownを実行してから、次のようにします。HTML=$(python3 -c " import sys, markdown print(markdown.markdown(sys.stdin.read(), extensions=['fenced_code','tables'])) " <<'MD' ## 标题 正文 **粗体** MD )
ステップ 0 — 最初に接続を確認する
"${WP[@]}" "$API/users/me?context=edit" | jq '{id, name, slug, roles}'
context=edit が必要です。これを指定しないと WP は roles/capabilities をまったく返さないため、そのアカウントで公開できるかどうかを確認できません。roles に administrator、editor、または author が含まれた状態で 200 が返れば、サイト URL、ユーザー名、Application Password、および公開権限が確認できています。失敗した場合は、エラーをユーザーに伝えて処理を停止し、書き込みを試みないでください。
投稿を公開する、または下書きにする
公開すると誰でも閲覧でき、元に戻すのも困難です。status=publish を使う前に、ユーザーに確認してください。 既定では status=draft にして、編集リンクを返してください。
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 は以下のエンドポイントから取得します)。
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}'
- 既存の下書きを公開する:本文に
{"status":"publish"}を指定して、POST $API/posts/<id>を実行します。 - 投稿を更新する:任意のフィールドを指定して
POST $API/posts/<id>を実行します(WP REST では更新に PUT ではなく POST を使います)。 - 投稿を削除(ゴミ箱に移動)する:
"${WP[@]}" -X DELETE "$API/posts/<id>"を実行します。 - 投稿を予約する:
{"status":"future","date_gmt":"2030-01-01T00:00:00"}を指定します(UTC、末尾にZは付けません)。
実際に重要な SEO フィールド
WordPress コアは <link rel="canonical"> を自動で出力しますが、メタディスクリプションタグは一切含まれていません。メタディスクリプションタグが表示されるのは、サイトで SEO プラグイン(Yoast、Rank Math、SEOPress)か、タグを出力するテーマを使用している場合に限られます。これらは投稿の excerpt を参照するため、適切な説明文を設定するには excerpt の指定が必要です。ただし、プラグインやテーマのない標準のコア環境では何も起こりません。SEO 対象の投稿では、必ず次のフィールドを設定してください。
| フィールド | 理由 |
|---|---|
slug | パーマリンクです。短い ASCII キーワードのフレーズを明示的に設定してください。そうしないと、CJK のタイトルがパーセントエンコードされた URL になります。 |
excerpt | SEO プラグインのメタディスクリプションと、一覧ページの要約のソースになります。1 文で記述してください。 |
categories / tags | 内部リンクとトピックのクラスタリングに使われます。 |
featured_media | ソーシャルメディア/OG カードの画像です。 |
メディアの alt_text | 画像 SEO とアクセシビリティに使われます。メディアオブジェクトに設定してください(下記参照)。 |
jq -n --arg s "claude-api-guide" --argjson c 3 --argjson m 9 \
'{title:"如何稳定调用 Claude API:完整对接指南",
slug:$s,
content:"<h2>小标题</h2><p>正文</p>",
excerpt:"一句话摘要,用于 meta description。",
status:"publish", categories:[$c], featured_media:$m}' \
| "${WP[@]}" -X POST "$API/posts" -H "Content-Type: application/json" -d @- \
| jq '{id, status, slug, link}'
公開前:重複を確認する
**WordPress は重複した slug を拒否しません。代わりに、黙って -2 を付加します。**そのため、無人実行やスケジュール実行で同じ記事を再投稿すると、検索結果で互いに競合する、ほぼ同一の URL が延々と作られます。必ず事前に確認してください。
SLUG="claude-api-guide"
# Fail CLOSED at BOTH steps: a failed request, or a 200 that isn't a JSON array
# (HTML from a redirect/permalink issue, or an error object), must abort — never
# fall through and create the duplicate this check exists to prevent.
if ! LOOKUP=$("${WP[@]}" "$API/posts?slug=$SLUG&status=publish,draft,future&_fields=id,link"); then
echo "duplicate check failed (request error) — aborting" >&2; exit 1
fi
if ! EXISTING=$(printf '%s' "$LOOKUP" | jq -er 'if type=="array" then (.[0].id // "") else error("not a JSON array") end'); then
echo "duplicate check failed (unexpected response) — aborting" >&2; exit 1
fi
if [ -n "$EXISTING" ]; then
echo "already exists as post $EXISTING — update it instead of creating a new one"
# update: "${WP[@]}" -X POST "$API/posts/$EXISTING" ...
fi
ほぼ重複する投稿を新規作成するより、既存の投稿を更新することを優先してください。出力を確認する人がいないスケジュールタスクでは、特に重要です。
投稿の一覧表示/読み込み
"${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 - を追加してください)。
カテゴリーとタグ(ID の取得または作成)
# 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}'
# Create one (returns its id)
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 を再利用してください。
メディアのアップロード(アイキャッチ画像/本文内画像)
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"
# Set alt text (image SEO + accessibility) — a separate call on the media object.
jq -n --arg a "Claude API 架构图" '{alt_text:$a}' \
| "${WP[@]}" -X POST "$API/media/$MEDIA_ID" \
-H "Content-Type: application/json" -d @- | jq '{id, alt_text}'
# Attach as the post's featured image:
# add "featured_media": <MEDIA_ID> to the post body.
注意点
-
アプリケーションパスワードには、実質的に HTTPS が必須です。 WP は
wp_is_application_passwords_available()で利用可否を制御しており、通常のhttp://の本番サイトでは false になるため、認証済みリクエストはrest_not_logged_in(401)で失敗します。(この制御はフィルターで変更でき、WP_ENVIRONMENT_TYPEがlocalの場合は回避されるため、開発環境では動作することがあります。ただし、ユーザーが接続する実際のサイトでは HTTPS が必要です。) -
$WORDPRESS_SITE_URLは、サイトに設定されたアドレスと完全に一致させる必要があります。 一致しない場合(wwwの有無やhttpとhttpsの違いなど)、WP は/wp-json/...に対して正規ホストへの 301 を返します。-Lでその場しのぎをするのではなく、コネクターに保存されたサイト URL を修正してください。 -
セキュリティプラグインやホストによって
/wp-jsonがブロックされることがあります(Wordfence、「disable REST API」プラグイン、一部のマネージドホストなど)。症状としては、404rest_no_routeが返されるか、JSON の代わりに HTML のログインページが表示されます。ユーザーが REST API へのアクセスを許可する必要があります。 -
パーマリンクが有効になっていない場合があります。
/wp-json/wp/v2/...に JSON ではなくサイトの HTML が返される場合、サイトでは通常のパーマリンクが使われています。代わりに、常に利用できる次のクエリ形式を使ってください:$SITE/?rest_route=/wp/v2/posts。 -
アプリケーションパスワードにはスペースが含まれます(例:
abcd efgh ijkl mnop)。そのまま使ってください。curl -uはスペースを問題なく処理するため、取り除かないでください。 -
通知なしで公開しないでください。 ユーザーが「投稿して」と言った場合でも、明示的に公開を依頼されていない限り、下書きを作成して
wp-adminの編集リンクを返すことを優先してください。(無人のスケジュールタスクでは、ユーザーが実行を事前承認しているため、直接公開することが想定されます。ただし、その場合も重複チェックを実行してください。)
出力を記録する
公開に成功して公開済みの URL を取得したら、ユーザーが成果物を My Outputs で追跡できるよう、組み込みの publish_artifact ツールを 1 回呼び出してください。
publish_artifact(kind="article", channel="wordpress", title="<title>", url="<the REAL returned URL>", status="delivered")
実際に返された URL を使ってください。URL を捏造しないでください。公開済みの項目ごとに 1 回だけ、成果物の提供が確認された後に呼び出してください。公開に失敗した場合は呼び出しを省略するか、status="failed" を使ってください。_shared/artifacts.md を参照してください。