WordPress blog posts
The blog-post workflow is the simplest thing this worker does, and a good first task to confirm everything is wired correctly. You stay in chat the whole time; the worker writes drafts via WP REST that you publish manually when ready.
Tools you’ll use
Section titled “Tools you’ll use”| Tool | What it does |
|---|---|
create_draft | Make a new draft post |
update_draft | Patch fields on an existing draft |
schedule_draft | Flip a draft to status=future at a chosen ISO8601 time |
unschedule | Roll a scheduled post back to draft |
get_post | Inspect any single post |
list_drafts | List drafts + scheduled posts |
upload_media | Push an image and get the URL back |
list_taxonomies / create_taxonomy | Tags + categories |
All of them are first-party tools — they don’t need the wp-mcp-adapter
plugin. You only need:
- A WordPress site added at
/admin/siteswith platformwordpress. - An Application Password set (the admin form stores it AES-GCM in D1).
- A token (OAuth Custom Connector or bearer) that includes the site.
Hello world — write your first post
Section titled “Hello world — write your first post”In a chat where the SEO Navigator connector is enabled:
Write a 400-word draft on site
blog-acmetitled “How ceramic coating works”, aimed at car owners who’ve never heard the term. Friendly tone, no bullet lists, end with a single CTA to call us.
Claude will (typically):
- Call
list_sitesif it doesn’t already knowblog-acmeexists. - Compose the body.
- Call
create_draftwithsite_id,title,content_mdorcontent_html.
The result includes the WP post ID, the admin edit URL, and the status —
which will be draft, every time. The post is invisible to the public until
you click Publish in wp-admin.
Update a draft
Section titled “Update a draft”Bump the intro of post 1234 on
blog-acmeto mention the 9H hardness rating. Keep everything else.
Claude calls get_post to fetch the current body, edits, then calls
update_draft with post_id: "1234" and a fresh content_md. If you don’t
say “keep everything else”, expect Claude to rewrite the whole post — be
explicit.
update_draft refuses to touch a post that is already published — it returns
E_FORBIDDEN_OP at the adapter layer.
That’s deliberate: this worker only deals with drafts.
Schedule and unschedule
Section titled “Schedule and unschedule”Schedule post 1234 on
blog-acmefor next Friday at 9am Boston time.
Claude resolves the time zone and calls schedule_draft with an ISO8601
scheduled_at. The worker requires the time to be at least 60 seconds in
the future — anything sooner is rejected with E_FORBIDDEN_OP, to block
“schedule now” tricks.
To pull a scheduled post back:
Move post 1234 back to draft.
Claude calls unschedule → status returns to draft. The post is no longer
on the WP scheduler queue.
Tags and categories
Section titled “Tags and categories”WordPress REST /wp/v2/posts rejects tag/category arrays unless they’re
integer term IDs — not names. The worker handles this for you in two
places:
create_draft/update_draftaccept either names or numeric IDs. Names are matched case-insensitively against existing terms; missing ones are created on the fly viacreate_taxonomy.deploy_md_to_site(the markdown-sync path, see Content + Design teams) uses the same auto-resolution under the hood.
Tag post 1234 with “ceramic coating”, “paint protection”, “Boston”.
Claude calls update_draft with tags: ["ceramic coating", "paint protection", "Boston"].
Behind the scenes the worker lists existing tags, finds matches, creates
“Boston” if it doesn’t exist, then patches the post with the resolved
integer IDs.
If you want to see the current term list first:
List tags on
blog-acme.
Claude calls list_taxonomies with type: "tags".
Featured images
Section titled “Featured images”Two ways to attach a hero image:
1. Upload, then use the URL.
Upload the image at
https://images.unsplash.com/photo-1234toblog-acmeas the featured image for post 1234.
Claude calls upload_media with source_url set, then update_draft with
featured_image_url. The worker fetches the URL server-side and posts the
bytes to WP’s /media endpoint — it doesn’t hotlink.
2. Inline (Claude has the bytes already).
upload_media also accepts base64 if Claude generated or screenshot-grabbed
the image. Same shape, just base64 + mime instead of source_url.
Listing drafts
Section titled “Listing drafts”Show me drafts and scheduled posts on
blog-acme.
Claude calls list_drafts (default include_scheduled: true). The worker
asks WP for both status=draft and status=future, merges, and returns
post id + title + status + scheduled date.
If you only want drafts:
Show drafts only (not scheduled) on
blog-acme.
Claude passes include_scheduled: false.
Working across multiple sites
Section titled “Working across multiple sites”list_sites filtered by platform lets you ask Claude things like:
Across all my WordPress sites, show me drafts I haven’t published this week.
Claude calls list_sites { platform: "wordpress" }, iterates list_drafts
per site, and compiles the result. There’s no built-in cross-site index — the
worker iterates serially. With ten sites and three drafts each, expect ~3
seconds end-to-end.
Author attribution
Section titled “Author attribution”WP posts ship with an author ID (the integer of a user account). The worker:
- Defaults to the Application Password user (the one in
wp_username). - Honors a
authorargument oncreate_draftif you pass a numeric WP user ID — useful for ghost-writing flows (“write as user 7”). - Won’t let you set the author to someone the App Password doesn’t have
permission to act as (WP returns 401, surfaced as
E_UPSTREAM).
Common errors
Section titled “Common errors”| Symptom | What’s happening | Fix |
|---|---|---|
E_FORBIDDEN_OP — status=publish refused | Something asked for an immediate publish | This worker only drafts. Use schedule_draft with a future time instead. |
E_FORBIDDEN_OP — scheduled_at too soon | Time is within 60s of now | Pick a time at least a minute out. |
E_UPSTREAM — WP 400 invalid term id | Tag/category passed as wrong type | Pass strings (names) — the worker resolves them. Numeric IDs also accepted. |
E_UPSTREAM — WP 401 | App Password wrong / user lacks capabilities | Re-issue the Application Password in wp-admin. |
E_SCOPE_DENIED | Token doesn’t include this site | /admin/tokens → edit → tick the site. |
| Draft shows up but no featured image | upload_media failed silently | Check /admin/audit for the matching call; common cause is image > 10 MB. |
What’s next
Section titled “What’s next”- WordPress pages (Gutenberg + Elementor) — for landing pages and templated layouts.
- Code-first pages — vibe-code an HTML/CSS/JS bundle and deploy as a Gutenberg page.
- Content + Design teams — split the writing and deploying into two roles using GitHub markdown.