Install
$ agentstack add skill-busyeugene-content-marketing-skills-publish-wordpress ✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ✓ Network access No
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ✓ Environment & secrets No
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
Verified badge
Passed review? Show it. Paste this badge into your README, it links to the public security report.
Reliability & compatibility
Declared compatibility
Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.
We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.
How agent discovery & health will work →About
Publish → WordPress
Publish a local markdown draft to WordPress using the WordPress REST API v2. Works with self-hosted WordPress and WordPress.com Business or higher.
Setup
Required:
WORDPRESS_BASE_URL— e.g.https://example.com(no trailing slash). The REST endpoint is{BASE}/wp-json/wp/v2.WORDPRESS_USERNAME— WP user withedit_postsandupload_filescapabilities.WORDPRESS_APP_PASSWORD— an Application Password (Users → Profile → Application Passwords in WP admin). Not your login password.
Auth header for every request:
Authorization: Basic
Content-Type: application/json
Optional:
SLACK_WEBHOOK_URL— Slack notification on publish.
Inputs
- Draft path — markdown file under
posts/. - Post status —
draft,pending,private,publish. Defaultdraft. - Update existing? —
skip,update,create-new. Defaultupdateif a post with the same slug exists. - Categories — comma-separated names or IDs. Will be created if they don't exist.
- Tags — comma-separated; same behavior as categories.
- Featured image — from the draft's
hero_imagefrontmatter, or user override path/URL. - Field mapping override — optional JSON for custom post types or ACF fields.
Use AskUserQuestion for post status and existing-item handling.
Process
1. Preflight
GET {BASE}/wp-json/to confirm REST is reachable and auth works. A 401 means wrong username or app password.GET {BASE}/wp-json/wp/v2/users/meto confirm the user has the required capabilities.- Cache the site's categories and tags for the session.
2. Load and transform
- Read the draft.
- Parse frontmatter.
- Convert markdown body to HTML. WordPress accepts HTML directly in the
contentfield. Preserve: headings, paragraphs, lists, code, blockquotes, images, links. Convert fenced code blocks to ``. - Strip HTML comments and any markdown-specific artifacts (e.g. inline frontmatter).
3. Featured image upload
If hero_image is a local path:
POST {BASE}/wp-json/wp/v2/media
Content-Disposition: attachment; filename="{name}.webp"
Content-Type: image/webp (or png/jpeg)
Capture the returned id → use it as featured_media. Set alt_text via:
POST {BASE}/wp-json/wp/v2/media/{id}
{ "alt_text": "{hero_alt}", "caption": "{optional}" }
If hero_image is a URL, download it first, then upload — don't point WP at a remote URL.
4. Resolve taxonomies
For each category:
- Look up by name in the cache. If found, use the ID.
- If not found:
POST /wp/v2/categories { "name": "{name}" }, use the returned ID.
Same for tags via /wp/v2/tags.
5. Check for existing post
GET /wp/v2/posts?slug={slug}&status=any&per_page=1
- Found +
skip→ stop and report. - Found +
update→POST /wp/v2/posts/{id}with updated fields. - Found +
create-new→ append-2,-3, … to the slug until unique. - Not found → create.
6. Create or update
POST /wp/v2/posts
{
"title": "{title}",
"slug": "{slug}",
"status": "{status}",
"content": "{html body}",
"excerpt": "{meta_description}",
"featured_media": {media_id},
"categories": [ids],
"tags": [ids],
"meta": {
"_yoast_wpseo_title": "{meta_title}",
"_yoast_wpseo_metadesc": "{meta_description}",
"_yoast_wpseo_focuskw": "{keyword}"
}
}
Notes:
- Yoast and Rank Math meta keys are NOT writable via the WP REST API by default. The Yoast REST API is read-only, and neither plugin registers its meta fields with
show_in_rest: true. Writing them via the standardmetablock will silently fail or return a 400 unless the site has either (a)register_post_meta()calls infunctions.phpexposing the keys, or (b) a bridge plugin such as "WP REST Yoast Meta" installed. Before posting, attempt a test write and read it back. If the write didn't take, skip the meta block, log a warning, and tell the user which fields couldn't be set so they can update them manually in WP admin. - For Yoast, the meta keys are
_yoast_wpseo_title,_yoast_wpseo_metadesc,_yoast_wpseo_focuskw. For Rank Math, they arerank_math_title,rank_math_description,rank_math_focus_keyword. Ask which plugin is installed once per session and cache the answer. - For ACF fields, post to
/wp/v2/posts/{id}withacf: { field_name: value }after creation (requires the "ACF to REST API" plugin or ACF Pro 5.11+). - Date fields: set
datein ISO 8601 (site-local timezone). If publishing now, omit to use the server's current time.
7. Verify the live URL (if publishing)
- Fetch
GET /wp/v2/posts/{id}to confirm the new values took. - If
status = publish, hit the returnedlinkwith a HEAD request and expect 200. If it's 404 or 5xx, warn the user.
8. Optional Slack notification
Same pattern as publish-webflow.
9. Publish log
Append to publish-log.md:
| {YYYY-MM-DD HH:mm} | wordpress | {status} | {title} | {post_id} | {link or "—"} | {result} |
10. Print summary
One block: post ID, status, live URL (if published), SEO plugin used, Slack notified, publish-log updated.
Fallbacks
- App password wrong or 2FA blocking: fail fast with the exact fix instruction ("Generate an Application Password under Users → Profile → Application Passwords").
- REST endpoint 404 or disabled: the site has REST API disabled by a security plugin; report the exact failing URL and tell the user to re-enable.
- Featured media upload fails: retry once; if it still fails, create the post without the featured image and log a warning.
- Yoast/Rank Math not installed: skip the SEO meta block and note it in the summary.
- Network error mid-upload: do not retry blindly — first check whether the post was created to avoid duplicates.
Safety
- Default status is
draft. Never publish live without explicit user confirmation in the current session. - Never delete posts. On slug collision in
skipmode, stop and report.
Verification
GET /wp/v2/posts/{id}returns 200 with the expected title, slug, and body.- Featured image field is populated if
hero_imagewas set. - Category and tag IDs match the requested names.
- If published, the public URL returns 200.
publish-log.mdhas a new row.
Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: busyeugene
- Source: busyeugene/content-marketing-skills
- License: MIT
- Homepage: https://busyless.space/
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet, be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.