Install
$ agentstack add skill-arturseo-geo-claude-code-skills-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 Used
- ✓ 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.
About
WordPress Skill
Authentication Setup
WordPress REST API requires authentication. Ask the user for:
- Site URL (e.g.,
https://example.com) - Auth method — prefer Application Passwords (WP 5.6+):
- Users → Profile → Application Passwords → Add New
- Returns
username:app-password— base64 encode for Basic Auth header
- API base:
{site_url}/wp-json/wp/v2/
const headers = {
'Authorization': 'Basic ' + btoa('username:app-password'),
'Content-Type': 'application/json'
};
MCP Server Authentication (Recommended)
When a WordPress MCP server is configured, use its tools directly instead of raw HTTP. MCP tools handle auth automatically — no need to manage headers or tokens.
// .mcp.json — example WordPress MCP config
{
"mcpServers": {
"wordpress": {
"command": "npx",
"args": ["-y", "@anthropic/wordpress-mcp"],
"env": {
"WP_URL": "https://example.com",
"WP_USERNAME": "admin",
"WP_APP_PASSWORD": "xxxx xxxx xxxx xxxx"
}
}
}
}
Security Best Practices
- Never store credentials in code — use environment variables or
.envfiles - Application Passwords are scoped to the user's role — create a dedicated API user with minimal permissions
- Rotate Application Passwords quarterly
- Use HTTPS exclusively — never send credentials over HTTP
- Add IP allowlisting in
.htaccessor nginx if the API is only called from known servers - Disable XML-RPC if not needed:
add_filter('xmlrpc_enabled', '__return_false');
Core Operations
Create / Update a Post
POST /wp-json/wp/v2/posts
{
"title": "Post Title",
"content": "HTML or Gutenberg blocks",
"status": "draft" | "publish" | "private" | "future",
"date": "2026-03-22T10:00:00",
"slug": "post-slug",
"excerpt": "Short summary",
"categories": [id1, id2],
"tags": [id1, id2],
"featured_media": media_id,
"meta": { "custom_field": "value" },
"template": "template-name.php"
}
# Update: PATCH /wp-json/wp/v2/posts/{id}
# Delete: DELETE /wp-json/wp/v2/posts/{id}?force=true
Pages
POST /wp-json/wp/v2/pages
{
"title": "Page Title",
"content": "...",
"status": "publish",
"parent": 0,
"template": "page-template.php",
"menu_order": 1
}
Get Posts (with filtering)
GET /wp-json/wp/v2/posts?status=publish&per_page=100&page=1
GET /wp-json/wp/v2/posts?search=keyword&categories=5
GET /wp-json/wp/v2/posts?orderby=modified&order=desc
GET /wp-json/wp/v2/posts?after=2026-01-01T00:00:00&before=2026-03-01T00:00:00
GET /wp-json/wp/v2/posts?slug=my-post-slug
GET /wp-json/wp/v2/posts?_embed&_fields=id,title,link,excerpt
Pagination: Response headers include X-WP-Total and X-WP-TotalPages.
Upload Media
POST /wp-json/wp/v2/media
Content-Disposition: attachment; filename="image.jpg"
Content-Type: image/jpeg
[binary data]
# After upload — update alt text and caption:
PATCH /wp-json/wp/v2/media/{id}
{
"alt_text": "Descriptive alt text",
"caption": "Photo credit: Source",
"title": { "raw": "Image Title" }
}
Manage Taxonomies
GET /wp-json/wp/v2/categories
POST /wp-json/wp/v2/categories { "name": "New Cat", "slug": "new-cat", "parent": 0, "description": "Category description" }
GET /wp-json/wp/v2/tags
POST /wp-json/wp/v2/tags { "name": "New Tag", "slug": "new-tag" }
# Custom taxonomies (must have show_in_rest: true)
GET /wp-json/wp/v2/{taxonomy_slug}
POST /wp-json/wp/v2/{taxonomy_slug} { "name": "Term Name" }
Custom Post Types
# CPTs must have show_in_rest: true in registration
GET /wp-json/wp/v2/{cpt_slug}
POST /wp-json/wp/v2/{cpt_slug}
GET /wp-json/wp/v2/types # List all registered post types
GET /wp-json/wp/v2/types/{slug} # Get single type details
Custom Fields (ACF and Meta)
# Native meta fields (must be registered with show_in_rest)
PATCH /wp-json/wp/v2/posts/{id}
{ "meta": { "custom_key": "value" } }
# ACF fields (requires ACF to REST API plugin or ACF PRO 5.11+)
# ACF exposes fields under the "acf" key:
GET /wp-json/wp/v2/posts/{id}?_fields=acf
PATCH /wp-json/wp/v2/posts/{id}
{ "acf": { "field_name": "value", "repeater_field": [{"sub_field": "val"}] } }
# Register meta for REST API visibility:
register_post_meta('post', 'my_field', [
'show_in_rest' => true,
'single' => true,
'type' => 'string',
]);
Gutenberg Block Patterns
When writing content as Gutenberg blocks (use when site uses block editor):
Text here
Heading
Item one
Item two
First
Second
Quote textAttribution
code here
HeaderCell
Click Here
Grouped content
Column 1
Column 2
Reusable Blocks and Block Patterns
# Reusable blocks are stored as wp_block post type
GET /wp-json/wp/v2/blocks
POST /wp-json/wp/v2/blocks
{
"title": "CTA Block",
"content": "Call to action",
"status": "publish"
}
# Use a reusable block in content:
# Block patterns are registered via PHP — query available patterns:
GET /wp-json/wp/v2/block-patterns/patterns
GET /wp-json/wp/v2/block-patterns/categories
WooCommerce Products
# Requires WooCommerce REST API (separate auth with consumer key/secret)
# Auth: query string ?consumer_key=ck_xxx&consumer_secret=cs_xxx
# Or: Authorization: Basic base64(ck_xxx:cs_xxx)
POST /wp-json/wc/v3/products
{
"name": "Product Name",
"type": "simple" | "variable" | "grouped" | "external",
"regular_price": "29.99",
"sale_price": "19.99",
"description": "Full description with HTML",
"short_description": "Brief summary",
"sku": "PROD-001",
"categories": [{"id": 9}],
"tags": [{"id": 5}],
"images": [{"src": "url", "alt": "alt text"}],
"manage_stock": true,
"stock_quantity": 100,
"weight": "0.5",
"dimensions": {"length": "10", "width": "5", "height": "2"},
"attributes": [{"name": "Color", "options": ["Red", "Blue"], "visible": true, "variation": true}],
"meta_data": [{"key": "_custom_field", "value": "data"}]
}
# Orders, coupons, customers
GET /wp-json/wc/v3/orders?status=processing&per_page=50
POST /wp-json/wc/v3/coupons
GET /wp-json/wc/v3/customers
GET /wp-json/wc/v3/reports/sales?date_min=2026-01-01&date_max=2026-03-22
SEO Plugin Integration
RankMath (meta fields via REST API)
{
"meta": {
"rank_math_title": "SEO Title — %sep% %sitename%",
"rank_math_description": "Meta description under 160 chars",
"rank_math_focus_keyword": "primary keyword",
"rank_math_robots": ["index", "follow"],
"rank_math_canonical_url": "https://example.com/canonical",
"rank_math_schema_Article": "{\"@type\":\"Article\"}"
}
}
Yoast SEO (meta fields via REST API)
{
"meta": {
"_yoast_wpseo_title": "SEO Title",
"_yoast_wpseo_metadesc": "Meta description",
"_yoast_wpseo_focuskw": "focus keyword",
"_yoast_wpseo_canonical": "https://example.com/canonical",
"_yoast_wpseo_opengraph-title": "OG Title",
"_yoast_wpseo_opengraph-description": "OG Description",
"_yoast_wpseo_opengraph-image": "https://example.com/og-image.jpg"
}
}
Schema Markup
Prefer RankMath or Yoast built-in schema. For custom schema, inject via a block:
{
"@context": "https://schema.org",
"@type": "Article",
"headline": "Title",
"author": {"@type": "Person", "name": "Author"},
"datePublished": "2026-03-22"
}
User and Role Management
# List users
GET /wp-json/wp/v2/users?per_page=100&roles=editor
# Create user
POST /wp-json/wp/v2/users
{
"username": "newuser",
"email": "user@example.com",
"password": "secure-password",
"roles": ["editor"],
"first_name": "First",
"last_name": "Last"
}
# Update user role
PATCH /wp-json/wp/v2/users/{id}
{ "roles": ["author"] }
# Application Passwords (for API access)
POST /wp-json/wp/v2/users/{id}/application-passwords
{ "name": "CI Pipeline" }
# Returns: { "password": "xxxx xxxx xxxx xxxx" } — store securely, shown only once
Role hierarchy: Administrator > Editor > Author > Contributor > Subscriber
Site Health and Performance
# Site health (WP 5.2+)
GET /wp-json/wp-site-health/v1/tests/background-updates
GET /wp-json/wp-site-health/v1/tests/dotorg-communication
# Check WP version
GET /wp-json/ # Root endpoint — returns site name, description, WP version, routes
# Plugin/theme status (requires admin)
GET /wp-json/wp/v2/plugins
PATCH /wp-json/wp/v2/plugins/{plugin_slug}
{ "status": "active" | "inactive" }
Performance Checklist
- Enable object caching (Redis or Memcached)
- Use a page cache plugin (WP Super Cache, W3 Total Cache, LiteSpeed Cache)
- Serve images as WebP with lazy loading (
loading="lazy") - Minify CSS/JS — use Autoptimize or plugin-native minification
- Set up a CDN (Cloudflare, BunnyCDN)
- Schedule database cleanup (post revisions, transients, spam comments)
- Monitor with Query Monitor plugin during development
Multisite Management
# Multisite endpoints (requires network admin)
GET /wp-json/wp/v2/sites # List sites in network
POST /wp-json/wp/v2/sites # Create new site
GET /wp-json/wp/v2/sites/{id}
# Switch site context in WP-CLI:
wp --url=subsite.example.com post list
wp site list --format=table
Multisite REST API requires the wp-multi-network plugin or custom endpoints on older installs. WP 6.x+ provides native multisite REST support.
Batch Operations
For bulk tasks (update 50+ posts, migrate content), generate a Python or Node.js script:
- Authenticate once, store token
- Paginate with
per_page=100&page=N - Process with rate limiting (sleep 0.5s between writes)
- Log successes and failures to CSV
- Use
_fieldsparameter to reduce payload size
Batch Endpoint (WP 6.x+)
POST /wp-json/batch/v1
{
"requests": [
{"method": "POST", "path": "/wp/v2/posts", "body": {"title": "Post 1", "status": "draft"}},
{"method": "POST", "path": "/wp/v2/posts", "body": {"title": "Post 2", "status": "draft"}},
{"method": "PATCH", "path": "/wp/v2/posts/123", "body": {"status": "publish"}}
]
}
Limit: 25 requests per batch by default (rest_get_max_batch_size filter).
Migration Workflows
Import from external source
- Map source fields to WP fields (title, content, slug, date, author, categories)
- Create categories/tags first, store ID mappings
- Upload media, store old URL → new URL map
- Search-replace old URLs in content before creating posts
- Set
dateandslugto preserve original URLs/dates - Validate: check for broken internal links, missing images, orphaned media
Export for migration
GET /wp-json/wp/v2/posts?per_page=100&page=1&_embed
# _embed includes author, featured image, and taxonomy details inline
# Paginate through all pages, save to JSON
Common Workflows
Publish a new blog post end-to-end:
- Check/create categories → get IDs
- Upload featured image → get media ID
- Write/format content as HTML or Gutenberg blocks
- POST to /posts with all fields
- Set SEO meta (RankMath or Yoast fields)
- Return the published URL
Audit existing content:
- GET all posts (paginate)
- Check: missing excerpts, missing featured images, draft posts older than 90 days, duplicate slugs
- Check SEO: missing meta descriptions, title length, focus keyword
- Check media: missing alt text, oversized images
- Return prioritized fix list
Scheduled content:
{
"status": "future",
"date": "2026-04-01T09:00:00",
"date_gmt": "2026-04-01T13:00:00"
}
Error Handling
| Code | Meaning | Fix | |---|---|---| | 401 | Bad credentials | Check app password, re-encode base64 | | 403 | Insufficient permissions | User needs Editor/Admin role | | 404 | Post/endpoint not found | Check CPT has show_in_rest: true | | 409 | Conflict (duplicate slug) | Change the slug | | 422 | Validation error | Check required fields and data types | | 429 | Rate limited | Back off, increase sleep between requests | | 500 | Server error | Check PHP error log, memory limit, plugin conflicts |
Always return the post ID and permalink on success.
WP-CLI Reference (SSH Access)
When the user has SSH access, WP-CLI is faster than REST API for many operations:
wp post create --post_title="Title" --post_status=publish --post_content="Content"
wp post list --post_type=post --post_status=publish --format=csv
wp post update 123 --post_status=draft
wp media import https://example.com/image.jpg --title="Image"
wp media regenerate --yes
wp user create newuser user@example.com --role=editor
wp plugin list --status=active --format=table
wp plugin update --all
wp search-replace 'old-domain.com' 'new-domain.com' --dry-run
wp cache flush
wp rewrite flush
wp cron event run --due-now
wp db optimize
Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: arturseo-geo
- Source: arturseo-geo/claude-code-skills
- License: MIT
- Homepage: https://thegeolab.net
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.