Skip to main content

WordPress operator MCP server — 25 task-scoped tools for Claude and other LLM agents. App-password auth, REST API, local-first, stdio transport.

Project description

wpflow — WordPress Operator MCP Server

v0.1 · Single-site WordPress operator for LLM agents (Claude Desktop, Claude Code, Cursor). Authenticates with a WordPress Application Password — no OAuth, no vendor review. Local-first, stdio transport, official Anthropic mcp SDK.

Pricing intent (launch): Solo $19/mo · Pro $49/mo · Agency $149/mo. Free tier = local install, 1 site.


What it does

Gives an agent 25 task-scoped tools against a live WP site via the REST API:

  • Posts — list_posts, get_post, create_post, update_post, delete_post, search_content
  • Pages — list_pages, get_page, update_page
  • Media — list_media, upload_media
  • Plugins — list_plugins, activate_plugin, deactivate_plugin
  • Themes — list_themes, get_active_theme
  • Users — list_users, get_user
  • Comments — list_comments, moderate_comment
  • Taxonomy — list_categories, list_tags, create_term
  • Health — site_health, verify_connection

List tools default to 10 results / max 100, return summaries (not full bodies) — token-efficient by design.


Install

python -m venv .venv
.venv\Scripts\pip install -r requirements.txt

Configure

Create an Application Password in WordPress: {site_url}/wp-admin/profile.php → Application Passwords → Add New → copy the 24-char space-separated value.

Copy .env.example to .env and fill in:

WPFLOW_SITE_URL=https://your-site.com
WPFLOW_USERNAME=admin
WPFLOW_APP_PASSWORD=xxxx xxxx xxxx xxxx xxxx xxxx

Claude Desktop config

Add to claude_desktop_config.json:

{
  "mcpServers": {
    "wpflow": {
      "command": "python",
      "args": ["C:/path/to/wpflow/server.py"],
      "env": {
        "WPFLOW_SITE_URL": "https://your-site.com",
        "WPFLOW_USERNAME": "admin",
        "WPFLOW_APP_PASSWORD": "xxxx xxxx xxxx xxxx xxxx xxxx"
      }
    }
  }
}

Run tests

.venv\Scripts\python test_server.py

Reads data/test_site.json (gitignored) for live credentials, then exercises every tool.


Troubleshooting

Cloudflare 403 / error 1010 (browser_signature_banned)

Hosts behind Cloudflare Bot Management (TasteWP, InstaWP free tiers, some shared hosts) block requests with Python's default urllib User-Agent. wpflow already sends a real browser-like UA on every request — you shouldn't see this unless you've forked the client and removed the header. If you DO see 403 with cf-mitigated: challenge, check that your edit preserved the BROWSERISH_UA in wp_client.py.

auth_failed on verify_connection

The Application Password is 24 characters with spaces. Copy it exactly as WP displays it — the interior spaces are part of the credential. Don't strip them.

rest_api_disabled

Some managed hosts (or a misbehaving security plugin) turn off the WP REST API entirely. Re-enable at {site}/wp-admin under Settings → Permalinks (just re-save) or disable the plugin causing it.

rest_disabled_for_plugins

A few hosts allow the public REST API but block /wp-json/wp/v2/plugins specifically. Use wp-admin to manage plugins, or migrate to a host that exposes the full REST surface.

Log location

logs/wpflow.log (rotating, 5 MB × 3). Auth tokens and app passwords are scrubbed.


Known quirks

  • WP 6.9 doesn't expose wp_version via /wp-json/ (site info only). verify_connection returns "unknown" for wp_version; site_health likewise. If you need it, read X-Generator on a rendered-page GET or query wp-admin.
  • delete_post(force=false) returns previous_status: "trash" immediately (WP moves it to trash and reports the new status, not the pre-trash status). This matches WP REST behavior.
  • list_comments default shows all statuses (status=any); WP REST requires context=edit for that, which requires auth. Unauthenticated calls will 401.

Security

  • No code execution path — no eval, no shell, no WP-CLI passthrough.
  • upload_media MIME whitelist: jpeg, png, gif, webp, svg, mp4, pdf.
  • upload_media URL source: SSRF-guarded (private/loopback/link-local IPs rejected; https only).
  • upload_media local path: must resolve inside WPFLOW_UPLOAD_ROOT (defaults to ~/Downloads and ~/Pictures).
  • 5 MB response cap per WP call.
  • TLS verification always on; WPFLOW_ALLOW_INSECURE=1 only relaxes the https-required check for dev sites, not certificate verification.

License

MIT.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

wpflow-0.1.0.tar.gz (26.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

wpflow-0.1.0-py3-none-any.whl (31.8 kB view details)

Uploaded Python 3

File details

Details for the file wpflow-0.1.0.tar.gz.

File metadata

  • Download URL: wpflow-0.1.0.tar.gz
  • Upload date:
  • Size: 26.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.0

File hashes

Hashes for wpflow-0.1.0.tar.gz
Algorithm Hash digest
SHA256 1ca435e03fcbc286e697beb13c6e69f983ff96bdd04f7cde3485f8d5b3269424
MD5 b7077a53cf7df1941e4314bd7ccc210c
BLAKE2b-256 932e5f6b4b96414541ac9893ea2f69f4f137281b5f7525be87229ec824218dc2

See more details on using hashes here.

File details

Details for the file wpflow-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: wpflow-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 31.8 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.0

File hashes

Hashes for wpflow-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 0fa13fdcff3872d5c7fdd527b748df536f196a2f742ffdb576be8582599f6ed2
MD5 e30812329a49a7d2f28e63c4c5e9942e
BLAKE2b-256 77b40a9f70fdfae2c010b9cfd1e975114f9e4d2cb2b634ad5793e5e59e4cb470

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page