How the /publish-draft Command Integrates with WordPress REST API and Yoast SEO

The /publish-draft command automates WordPress publishing by parsing markdown drafts, creating taxonomies via REST endpoints, and injecting Yoast SEO meta fields through a custom MU-plugin that exposes yoast_seo as a writable REST field.

The publish-draft command in the TheCraigHewitt/seomachine repository streamlines content workflows by bridging Claude's AI capabilities with WordPress's REST API and Yoast SEO optimization. This command orchestrates a complete publishing pipeline that converts markdown drafts into fully optimized WordPress posts with proper taxonomies and SEO metadata.

How the /publish-draft Command Works

When invoked, the /publish-draft command triggers the WordPressPublisher class located in data_sources/modules/wordpress_publisher.py. This class implements a six-step workflow that handles everything from markdown parsing to Yoast meta injection.

The command accepts a file path and optional post type parameter, then executes a chain of methods that validate credentials, parse content, ensure taxonomies exist, create the draft, and finally optimize it for search engines.

Step-by-Step WordPress REST API Integration

Parsing and Converting Markdown Content

The process begins with parse_draft_file(), which reads the markdown file and extracts critical metadata. This method identifies the H1 title from the markdown body and parses front-matter fields including Meta Title, Meta Description, Target Keyword, Category, Tags, and URL Slug【5†L53-L87】.

Once parsed, the markdown_to_html() method converts the remaining markdown body to HTML using the markdown library, producing WordPress-ready content【5†L37-L51】.

Creating Categories and Tags via REST

Before publishing, the system ensures required taxonomies exist. The get_or_create_category() and get_or_create_tag() methods interact with the WordPress REST API at /wp-json/wp/v2/categories and /wp-json/wp/v2/tags respectively【5†L83-L106】【5†L115-L124】.

These methods first query existing terms by name. If a term is missing, they send a POST request to create it, returning the term ID for association with the new post.

Publishing the Draft Post

The create_draft() method constructs a JSON payload containing the parsed title, HTML content, URL slug, status set to draft, and optional category and tag IDs. It POSTs this data to /wp-json/wp/v2/{post_type}, where post_type defaults to posts but can be pages or custom types【5†L65-L88】【5†L90-L108】.

Upon successful creation, WordPress returns the post ID, which the system uses for subsequent Yoast meta updates.

Yoast SEO Integration via MU-Plugin

How the Yoast MU-Plugin Exposes REST Fields

The integration relies on wordpress/seo-machine-yoast-rest.php, a Must-Use plugin that runs on every WordPress request. This plugin registers Yoast SEO meta fields with show_in_rest => true, making them accessible via the REST API【6†L52-L62】.

Specifically, it creates a composite REST field named yoast_seo that bundles three critical SEO fields: focus_keyphrase, seo_title, and meta_description【6†L75-L84】. This abstraction simplifies the API contract, allowing the Python publisher to send a single structured object rather than individual meta fields.

Setting Yoast Meta Data via the REST API

After creating the draft post, set_yoast_meta() sends a second POST request to the same endpoint (/wp-json/wp/v2/{post_type}/{id}) with a payload containing the yoast_seo object. The JSON structure includes the SEO title, meta description, and target keyword extracted from the draft's front matter【5†L12-L18】【5†L33-L42】.

The MU-plugin's update_callback processes this payload, validating user permissions and updating the corresponding post meta entries (_yoast_wpseo_focuskw, _yoast_wpseo_title, _yoast_wpseo_metadesc). This makes the SEO data instantly available in the WordPress admin and visible to Yoast's analysis tools.

Code Examples

Using the /publish-draft Command

Invoke the command from Claude's interface with the draft file path:

/publish-draft drafts/content-marketing-guide-2025-12-10.md

This creates a standard blog post with default SEO metadata.

To create a WordPress page instead:

/publish-draft drafts/pricing-comparison.md --type page

For custom post types:

/publish-draft drafts/product-comparison.md --type compare

Note: The custom post type must be registered in WordPress before publishing.

Direct Python Implementation

For automation workflows, import the WordPressPublisher class directly:

from data_sources.modules.wordpress_publisher import WordPressPublisher

# Initialize with environment variables (WORDPRESS_URL, WORDPRESS_USERNAME, WORDPRESS_APP_PASSWORD)

publisher = WordPressPublisher()

# Publish the draft

result = publisher.publish_draft(
    file_path="drafts/content-marketing-guide-2025-12-10.md",
    post_type="post"  # Options: "post", "page", or custom types

)

# Access the WordPress edit URL

print("Review draft at:", result["edit_url"])

This approach allows integration with CI/CD pipelines or content calendars.

REST API Request Structure for Yoast

The underlying HTTP request for updating Yoast SEO fields follows this pattern:

POST https://example.com/wp-json/wp/v2/posts/123
Authorization: Basic base64(username:app_password)
Content-Type: application/json

{
  "yoast_seo": {
    "seo_title": "Best Content Marketing Guide 2025",
    "meta_description": "A complete guide on content marketing tactics for 2025.",
    "focus_keyphrase": "content marketing guide"
  }
}

The MU-plugin receives this payload, validates the user, and maps the fields to the underlying Yoast meta keys.

Key Files and Implementation Details

File Role Key Components
.claude/commands/publish-draft.md Command definition and usage documentation CLI syntax, parameter descriptions, environment variable requirements
data_sources/modules/wordpress_publisher.py Core Python implementation WordPressPublisher class, parse_draft_file(), create_draft(), set_yoast_meta()
wordpress/seo-machine-yoast-rest.php WordPress MU-plugin REST field registration, yoast_seo composite field, meta update callbacks

The integration requires the MU-plugin to be installed in the WordPress wp-content/mu-plugins/ directory, while the Python publisher relies on environment variables (WORDPRESS_URL, WORDPRESS_USERNAME, WORDPRESS_APP_PASSWORD) for authentication.

Summary

  • The /publish-draft command automates WordPress publishing through the WordPressPublisher class in data_sources/modules/wordpress_publisher.py.
  • It parses markdown drafts, extracts front-matter SEO data, and converts content to HTML for WordPress ingestion.
  • Taxonomy creation (categories and tags) occurs dynamically via the WordPress REST API endpoints /wp-json/wp/v2/categories and /wp-json/wp/v2/tags.
  • The wordpress/seo-machine-yoast-rest.php MU-plugin exposes Yoast SEO fields via a composite yoast_seo REST field, enabling the publisher to set meta titles, descriptions, and focus keywords through standard REST API POST requests.
  • Authentication relies on WordPress Application Passwords and Basic Auth headers for secure API communication.

Frequently Asked Questions

How does the /publish-draft command authenticate with WordPress?

The command uses WordPress Application Passwords for secure REST API authentication. The WordPressPublisher class reads credentials from environment variables (WORDPRESS_URL, WORDPRESS_USERNAME, WORDPRESS_APP_PASSWORD) and transmits them via HTTP Basic Auth headers with every API request. This approach follows WordPress security best practices while enabling automated publishing workflows without storing plaintext passwords.

What Yoast SEO fields can be set through the REST API integration?

The integration supports setting three primary Yoast SEO fields through the composite yoast_seo REST object: seo_title (the optimized browser title), meta_description (the search engine snippet), and focus_keyphrase (the target keyword for content analysis). These fields map to Yoast's internal meta keys (_yoast_wpseo_title, _yoast_wpseo_metadesc, _yoast_wpseo_focuskw) via the MU-plugin's update callbacks, making the data immediately available in the WordPress admin interface.

Can I publish to custom post types using the /publish-draft command?

Yes, the command supports custom post types through the --type parameter. When invoking the command, you can specify any registered WordPress post type (e.g., --type compare or --type product). The WordPressPublisher.create_draft() method dynamically constructs the REST endpoint URL using this parameter, posting to /wp-json/wp/v2/{post_type}. Note that the custom post type must already be registered in WordPress and support the REST API for this functionality to work correctly.

What happens if a category or tag doesn't exist in WordPress?

The WordPressPublisher class automatically creates missing taxonomies before publishing. The get_or_create_category() and get_or_create_tag() methods first query the WordPress REST API to check for existing terms by name. If a term is not found, the system sends a POST request to the appropriate endpoint (/wp-json/wp/v2/categories or /wp-json/wp/v2/tags) to create the taxonomy term dynamically. This ensures that drafts can reference new categories or tags without manual pre-configuration in the WordPress admin.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →