mcpField
W
wordpress

wordpress

github.com/acedatacloudマーケティング・グロース1772 回インストール今日更新
リポジトリ

wordpress について

`curl` と `jq` で WordPress REST API を操作し、コンテンツを管理するスキルです。接続確認、投稿やメディアの操作に加え、認証情報、重複投稿、公開に関する安全対策を扱います。

主な機能

  1. 01認証済みサイトへのアクセスと権限の確認
  2. 02投稿の作成、更新、予約、一覧表示
  3. 03カテゴリー、タグ、メディアの管理
  4. 04公開前の重複投稿チェック
  5. 05HTMLコンテンツとSEO項目の処理

活用シーン

  1. 01下書きを作成して編集リンクを返す
  2. 02既存記事を更新または予約投稿する
  3. 03アイキャッチ画像をアップロードしてSEO項目を設定
  4. 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意味ユーザーへの案内
401incorrect_password/Basic 認証情報が不正Application Password が間違っているか、無効化されています。再生成して WordPress コネクターに再接続してください
401通常の http:// サイトでの rest_not_logged_inHTTPS でないと Application Password は無効になります。HTTPS を有効にしてください
403rest_cannot_create/権限不足ユーザーの権限では公開できません。Author/Editor/Admin 権限が必要です。または、サイトで Application Password が無効になっています
404rest_no_routeREST API が無効になっているか、セキュリティプラグインが /wp-json をブロックしています。再度有効にしてください
400rest_invalid_paramフィールドが不正です(例:存在しないカテゴリ ID)。修正して再試行してください
500rest_upload_sideload_errorWeb サーバーが 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 になります。
excerptSEO プラグインのメタディスクリプションと、一覧ページの要約のソースになります。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」プラグイン、一部のマネージドホストなど)。症状としては、404 rest_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 を参照してください。

コメント