| wp_list_sitesA | List fleet sites (install, account, environment, domain). Filter by account (hostacct1-6), environment (prod|staging), or query
(case-insensitive substring of install name or domain). Returns ids + count only. |
| wp_site_healthB | Check one install's reachability: WP REST (if a domain is known) + SSH/WP-CLI. install is the WP Engine install name (e.g. 'daytonplas1stg').
|
| wp_discover_siteA | Fingerprint an install (active theme, builder + Divi major version, WP/PHP
versions, plugin count, multisite) and persist the profile. Transport-aware: an
install with REST credentials is fingerprinted over the plugin /info endpoint
(no SSH); otherwise over one SSH/WP-CLI session. This is the learning entrypoint: content/settings tools expect a profile to exist.
install is the WP Engine install name. |
| wp_get_contentA | List posts/pages on an install (lean: id, title, slug, status, type). post_type defaults to 'page'. query is an optional simple search term.
|
| wp_create_contentA | Bulk-create posts/pages, rendered into the site's builder (Divi 4 / Gutenberg). Each item: {title, blocks:[...], slug?, status?}. Blocks are builder-agnostic:
{kind:'heading',level,text} | {kind:'paragraph',text} | {kind:'button',text,url} |
{kind:'image',url,alt?} (use the url returned by wp_upload_media).
Optional seo (an object: title/description/canonical/noindex) is applied to every
created item's meta (REST transport only). DEFAULTS TO dry_run=true (returns a
per-item plan + a seo_preview; writes nothing). Pass dry_run=false to apply.
Idempotent: existing slugs are skipped. Requires a profile (run wp_discover_site
first). Prod writes require allow_prod=true. |
| wp_find_pageA | Find pages AND posts matching query, merged (id, title, slug, status, type). Read-only. Use this to locate the post_id to feed wp_extract_page / wp_edit_page.
install is the WP Engine install name; query is a simple search term. |
| wp_extract_pageA | Parse one page into an editable element tree (read-only, no writes). Returns {builder, roundtrip_ok, tree} where tree is a recursive summary of
element nodes: address (index path for edit targeting), tag, admin_label,
text_preview (first 80 chars), children. Requires a profile (wp_discover_site). |
| wp_edit_pageA | Edit an existing page via a draft duplicate (the live post is never written). Ops (list of dicts): update_element, set_attr, insert_section, insert_module,
remove_element, duplicate_element. DEFAULTS TO dry_run=true (returns a preview,
writes nothing). Pass dry_run=false to stage the edit onto a draft duplicate;
promote it later with wp_publish_swap. Requires a profile (wp_discover_site) and
an editable builder (Divi 4/5 or Gutenberg). Prod writes require allow_prod=true. |
| wp_publish_swapA | Promote a staged draft onto the live post (WP keeps a revision), then verify. Copies the draft's content onto live_id, purges cache (Divi), deletes the
draft, and probes the public URL. Prod writes require allow_prod=true. |
| wp_discard_draftA | Delete a staged wpops draft without publishing. Prod writes require allow_prod=true. |
| wp_get_seoA | Read a page/post's SEO meta: title, description, canonical, noindex. Maps the active SEO plugin's meta (SEOPress / Rank Math / Yoast) to generic fields.
Read-only. Requires the REST transport (the wpcli/SSH gateway has no SEO support).
install is the WP Engine install name; get post_id from wp_find_page / wp_get_content. |
| wp_set_seoA | Set a page/post's SEO meta (title/description/canonical/noindex). Pass only the fields to change; omitted (None) fields are left untouched, and
supplying none at all is refused. Writes to the active SEO plugin's meta (SEOPress
/ Rank Math / Yoast) and is verified by readback. DEFAULTS TO dry_run=true (returns
a preview; writes nothing). Pass dry_run=false to apply. Requires the REST transport.
Prod writes require allow_prod=true. |
| wp_upload_mediaA | Upload a LOCAL image file into a site's media library, with optional alt text. file_path is a path on the machine running this server (not a URL). Images only:
.png/.jpg/.jpeg/.gif/.webp, max 10 MB; .svg is refused (script vector). The
filename is sanitized and the directory dropped. DEFAULTS TO dry_run=true (returns
a preview: filename/mime/bytes; uploads nothing). Pass dry_run=false to upload -
the returned url is what you feed to an {kind:'image'} block in wp_create_content
or wp_edit_page. Requires the REST transport. Prod writes require allow_prod=true.
|
| wp_list_menusA | List a site's navigation menus (id, name, slug, theme locations). Read-only. Start here: the id or exact name/slug it returns is what you pass as
menu to wp_add_menu_item. Menu items are not fetched. Requires the REST
transport (the wpcli/SSH gateway has no menu support). |
| wp_add_menu_itemA | Add one item to a navigation menu (a site page, or a custom link). menu is a menu id, or its EXACT name or slug (from wp_list_menus) - a reference
matching 0 or 2+ menus is refused and lists the available menus. Pass exactly one
target: page_id (from wp_find_page / wp_get_content) for a page on the site, or
url for a custom link. parent is the id of the menu item to nest under (0 =
top level); position is the item's menu_order (omit to append at the end).
DEFAULTS TO dry_run=true (returns a preview; writes nothing). Pass dry_run=false
to apply - the add is verified by reading the menu back. Requires the REST
transport. Prod writes require allow_prod=true.
|
| wp_update_menu_itemA | Change one existing menu item: rename it, move it, re-nest it, or retarget it. item_id is the menu ITEM id (from wp_list_menus -> the menu's items), not a page
id. Only the arguments you pass are changed - omit the rest and they are left
alone; passing none at all is refused rather than reported as a no-op success.
position is the item's menu_order (0 = first) and parent the id of the item to
nest under (0 = top level); url retargets a custom link (use wp_remove_menu_item
|
| wp_remove_menu_itemA | Remove one item from a navigation menu (the page it links to is NOT deleted). item_id is the menu ITEM id - not a page id. DEFAULTS TO dry_run=true (returns
a preview; deletes nothing). Pass dry_run=false to apply. Requires the REST
transport. Prod writes require allow_prod=true.
|
| wp_change_slugA | Rename one page/post's URL slug (the old URL usually starts 404ing - read on). CHECK old_url_redirects IN THE RESULT. WordPress only 301s the old URL for
published POSTS; for PAGES (hierarchical, and what most site URLs are) it does not,
so the old URL 404s until a redirect is added elsewhere. Both the preview and the
result say which case this is, with a warning when the old URL will break. post_id comes from wp_find_page / wp_get_content. new_slug is sanitized to
WP-safe form (lowercase, a-z 0-9 and hyphens; anything else becomes a hyphen) - a
value with nothing usable left is refused rather than sent. If the slug is already
taken, WP stores a uniquified one (about -> about-2): that still succeeds and comes
back with uniquified: true and the ACTUAL slug, so check it. DEFAULTS TO
dry_run=true (returns a preview with the current slug; writes nothing). Pass
dry_run=false to apply - the rename is verified by reading the post back. Requires
the REST transport. Prod writes require allow_prod=true.
|
| wp_get_settingsA | Read a site's editable settings: title, description, timezone, posts_per_page,
show_on_front, page_on_front, page_for_posts. Read-only, and deliberately narrow: only those seven fields are returned (the site
URL, admin email and language are never exposed or editable through this server).
Requires the REST transport, and an ADMINISTRATOR app password - WP's settings
endpoint refuses lesser roles. |
| wp_set_settingA | Change ONE site setting (see wp_get_settings for the current values). key must be one of: title, description, timezone (e.g. 'America/Denver'),
posts_per_page (int), show_on_front ('posts' or 'page'), page_on_front (page id),
page_for_posts (page id). Any other key is refused - the site URL and admin email
are not editable here by design. page_on_front is NOT checked against the site's
pages: a wrong id blanks the homepage, so read it back with wp_get_settings and look
at the site. DEFAULTS TO dry_run=true (returns a preview; writes nothing). Pass
dry_run=false to apply - the write is verified against what the site reports back,
and the result carries the site's ACTUAL stored value next to what you
requested (WordPress escapes text as it stores it, so a title with '&' or an
apostrophe comes back encoded - that is the site behaving normally). Requires the
REST transport and an administrator app password. Prod writes require
allow_prod=true.
|
| wp_get_acfA | Read a page/post's ACF (Advanced Custom Fields) values as a JSON object. Returns every ACF field value on the post, keyed by field name - strings, numbers,
booleans, and lists (repeaters/galleries) pass through untouched. Read-only.
Requires the REST transport AND ACF active on the site (ACF free or Pro); a site
without ACF comes back as an error, not a crash. install is the WP Engine install
name; get post_id from wp_find_page / wp_get_content. |
| wp_set_acfA | Set a page/post's ACF field VALUES (not the field-group schema). fields is an object of {field_name: value}; values are arbitrary JSON (string,
number, bool, or a list for a repeater/gallery) and are written through ACF's own
update_field so the field-key linkage is correct. Leading-underscore selectors are
rejected by the site and reported under skipped (the rest still apply, and the
result comes back as 'partial' rather than 'applied'). DEFAULTS TO dry_run=true
(returns a preview; writes nothing). Pass dry_run=false to apply - the write is
verified by reading each value back (ACF's storage normalization, e.g. a true/false
stored as 1, is tolerated). Only fields on an ACF field group already registered for
the post take effect. Requires the REST transport and ACF active. Prod writes require
allow_prod=true.
|
| wp_list_usersA | List a site's WordPress users (id, username, name, email, roles). Read-only. search is an optional substring WordPress matches against the login,
name and email. Start here: the id it returns is what you pass to wp_get_user /
wp_update_user / wp_delete_user. Requires the REST transport AND an ADMINISTRATOR
app password - WP's user endpoints refuse lesser roles (that comes back as an error,
not a crash). |
| wp_get_userA | Read one WordPress user by id (username, name, email, roles, url). Read-only. Get user_id from wp_list_users. Requires the REST transport and an
administrator app password. |
| wp_create_userA | Create a WordPress user with a role and a password. username cannot be changed later (WordPress forbids it) and email must not
already be in use. roles is a role slug or a list of them ('subscriber',
'editor', 'administrator', ...); omit it and WP applies the site's default role.
THE PASSWORD IS NEVER ECHOED BACK - it is sent to the site and replaced by a marker
in every preview and result, so it does not end up in this transcript. Choose a
strong one and record it in your password manager BEFORE calling: this server
cannot show it to you afterwards. DEFAULTS TO dry_run=true (returns a preview;
creates nothing). Pass dry_run=false to create. Requires the REST transport and an
administrator app password. Prod writes require allow_prod=true.
|
| wp_update_userA | Change an existing user's fields. fields is an object; the editable keys are name, email, roles, password, url,
description - ANY OTHER KEY IS REFUSED (WordPress silently ignores unknown fields,
which would report a change that never happened). username is not editable.
Setting roles REPLACES the user's roles, so passing ['subscriber'] on an
administrator demotes them. A password here is a real password reset: the user's
old password stops working immediately, and the new one is masked in the preview
exactly as in wp_create_user. An empty fields is refused rather than reported as
a successful no-op. DEFAULTS TO dry_run=true (returns a preview; writes nothing).
Pass dry_run=false to apply. Requires the REST transport and an administrator app
password. Prod writes require allow_prod=true.
|
| wp_delete_userA | PERMANENTLY delete a user; their content goes to reassign (0 = deleted too). There is no trash and no undo for a user: WordPress's REST API only deletes with
force, so this cannot be reversed from here. reassign decides what happens to
everything they authored - posts, pages, media: the DEFAULT of 0 DELETES THEIR
CONTENT WITH THEM, while any other user id transfers authorship to that user (get
one from wp_list_users). If the account wrote anything you want to keep, pass a
reassign target. DEFAULTS TO dry_run=true (returns a preview that spells out which
of the two outcomes you are about to get; deletes nothing). Pass dry_run=false to
delete. Requires the REST transport and an administrator app password. Prod writes
require allow_prod=true. |
| wp_list_optionsA | List the site's AUTOLOADED option names (names only, no values). Read-only. LIMITATION, and it matters: this lists autoloaded options only (WP's
wp_load_alloptions). An option that is not autoloaded is ABSENT FROM THIS LIST but
still readable with wp_get_option - so a missing name here does NOT prove the
option does not exist. Requires the REST transport, wp-ops-connect 1.3.0+ and an
administrator app password (the endpoint gates on manage_options). |
| wp_get_optionA | Read one wp_options row by exact name (works for non-autoloaded options too). Read-only. CHECK exists, not the value: an option may legitimately hold '', 0 or
false, so a falsy value on its own does not mean "not set". Values come back as
the site stores them (WordPress serializes everything to a string, so a number can
read back quoted). Requires the REST transport, wp-ops-connect 1.3.0+ and an
administrator app password. |
| wp_set_optionA | Write ANY wp_options row - including siteurl/home, where a wrong value can take
the site offline. There is NO allowlist here: this reaches every option a plugin or theme reads,
including the ones that decide where WordPress thinks it lives. Setting siteurl
or home to a wrong value takes the site offline and locks you out of wp-admin
(recovery needs database or wp-config access, not this server). Treat unfamiliar
options the same way - many are serialized plugin settings where a partial write
corrupts the whole structure. READ IT FIRST with wp_get_option and keep the old
value. The dry-run preview shows the option's CURRENT value next to the new one so
an overwrite is visible before it happens. DEFAULTS TO dry_run=true (writes
nothing). Pass dry_run=false to write - the write is verified by re-reading the
option, tolerating WordPress's storage casts (25 vs '25', true vs 1, '&' escaped to
'&'), and the result carries the site's ACTUAL stored value next to what you
requested. Requires the REST transport, wp-ops-connect 1.3.0+ and an administrator
app password. Prod writes require allow_prod=true. |
| wp_get_theme_fileA | Read one theme file (PHP/CSS/JS) from the ACTIVE theme. file is a path RELATIVE to the theme directory, e.g. "functions.php" or
"css/custom.css". Absolute paths and ".." are refused. Defaults to the active
(child) theme; pass theme for a specific stylesheet directory. Requires the REST
transport, wp-ops-connect 1.4.0+, and an app password whose user has edit_themes.
Sites with DISALLOW_FILE_EDIT set refuse this by design.
|
| wp_set_theme_fileA | Write one theme file. THIS EXECUTES CODE ON THE SITE - the highest-risk tool here. A theme file is PHP. A syntax error does not break a layout, it takes the SITE down.
Mitigations, in order: the write goes through WordPress core's own theme editor, which makes a loopback
request after writing and AUTOMATICALLY REVERTS the file if the site fatals; the result carries previous, the exact prior contents, so you can revert with a
second call - KEEP IT; the write is verified by re-reading the file, and a mismatch is an error, never
a success; path traversal, absolute paths, and non-code extensions are refused, and a
non-active theme needs allow_other_theme=true (editing a parent theme is how
changes get lost on the next update - prefer the child theme).
DEFAULTS TO dry_run=true, which writes nothing and returns the file's CURRENT
contents next to what you propose. Read it and diff before setting dry_run=false.
Prod writes require allow_prod=true. CREATING files is not supported - core's
self-reverting editor only edits files the theme already has; add new files by
deploy/SFTP first. allow_create is accepted but cannot make creation work.
|
| wp_delete_optionA | Delete a wp_options row entirely (not "set it to empty"). Whatever reads that option falls back to its default - which for a plugin's settings
row means every setting it holds is gone at once, and for a core option can change
how the site behaves. Read it with wp_get_option and keep the value first: nothing
here can restore it. Deleting an option that was never set is reported as
action: "noop" (nothing to delete), not an error. DEFAULTS TO dry_run=true
(returns a preview with the current value; deletes nothing). Pass dry_run=false to
delete. Requires the REST transport, wp-ops-connect 1.3.0+ and an administrator app
password. Prod writes require allow_prod=true. |
| wp_list_commentsA | List comments (id, post, author, content, status, date). Read-only. Filter by post_id (from wp_find_page / wp_get_content) and/or status one of approved, hold (awaiting moderation), spam, trash. Omit both to see the
most recent comments across the site. Requires the REST transport and an app
password with moderate_comments.
|
| wp_moderate_commentA | Set one comment's moderation status: approved, hold, spam or trash. Those four strings are WordPress's own vocabulary and anything else is refused
('approve' and 'APPROVED' are NOT accepted). 'approved' publishes the comment,
'hold' returns it to the moderation queue, 'spam' teaches the spam filters, 'trash'
removes it recoverably. comment_id comes from wp_list_comments. DEFAULTS TO
dry_run=true (returns a preview; writes nothing). Pass dry_run=false to apply - the
result is checked against the status the site reports back. Requires the REST
transport. Prod writes require allow_prod=true. |
| wp_delete_commentA | Delete a comment: force=true is PERMANENT; the default trashes it (recoverable). By default the comment goes to the trash and can be restored from wp-admin -
that comes back as action: "trashed", which is a success. With force=true it is
erased from the database and nothing here can bring it back. Prefer
wp_moderate_comment with 'spam' for spam: it trains the filters, where a delete
teaches them nothing. comment_id comes from wp_list_comments. DEFAULTS TO
dry_run=true (returns a preview stating which of the two outcomes you would get;
deletes nothing). Pass dry_run=false to apply. Requires the REST transport. Prod
writes require allow_prod=true. |
| wp_list_taxonomiesA | List a site's taxonomies (slug, name, rest_base, hierarchical, post types). Read-only. Start here: the slug it returns ('category', 'post_tag', or any
custom taxonomy a plugin/theme registers) is what every other term tool takes as
taxonomy. Only taxonomies registered with show_in_rest appear. Requires the REST
transport (the wpcli/SSH gateway has no taxonomy support). |
| wp_list_termsA | List the terms in one taxonomy (id, name, slug, parent, post count). Read-only. taxonomy is a slug from wp_list_taxonomies. search is an optional
simple filter on the term name. parent comes back as None on a flat taxonomy
(tags) - that means "no hierarchy", not "top level", which WordPress reports as 0.
Requires the REST transport. |
| wp_create_termA | Create one term (a category, tag, or any custom taxonomy term). taxonomy is a slug from wp_list_taxonomies. slug is derived from the name when
omitted. parent (a term id from wp_list_terms, 0 = top level) only applies to a
HIERARCHICAL taxonomy - WordPress ignores it on tags. Creating a term does not
assign it to anything: use wp_set_post_terms for that. DEFAULTS TO dry_run=true
(returns a preview; writes nothing). Pass dry_run=false to apply - the create is
verified by reading the term back. Requires the REST transport. Prod writes require
allow_prod=true.
|
| wp_update_termA | Rename or re-parent one existing term. fields is an object of field -> value; only name, slug, parent and description
can be changed and anything else is refused. CHANGING A SLUG CHANGES THAT TERM'S
ARCHIVE URL (/category/old/ starts 404ing), so treat it like wp_change_slug.
Passing no fields at all is refused rather than reported as a no-op success.
term_id comes from wp_list_terms. DEFAULTS TO dry_run=true (returns a preview of
the exact fields; writes nothing). Pass dry_run=false to apply. Requires the REST
transport. Prod writes require allow_prod=true.
|
| wp_delete_termA | Delete a term: this PERMANENTLY deletes it and unassigns it from every post. There is NO TRASH for terms - WordPress requires force on this call and nothing
here can restore it. The posts keep their content but quietly lose that filing, and
the term's archive URL (/category/news/) starts 404ing, which is usually noticed
long after the fact. Check wp_list_terms for the term's post count first, and
prefer re-filing those posts with wp_set_post_terms before deleting. term_id
comes from wp_list_terms. DEFAULTS TO dry_run=true (returns a preview stating that
outcome; deletes nothing). Pass dry_run=false to apply. Requires the REST
transport. Prod writes require allow_prod=true. |
| wp_set_post_termsA | Set a post's terms for one taxonomy - REPLACES them, never appends. term_ids is the COMPLETE list the post should end up with (ids from
wp_list_terms); anything not in it is unassigned, and an empty list clears the
taxonomy on that post. Read the post's current terms first if you mean to add one.
post_id comes from wp_find_page / wp_get_content. The write is verified against
what the site reports it stored: WordPress silently drops ids the taxonomy does not
own, and assigning a taxonomy that is not registered for that post's type stores
NOTHING while still answering 200 - both come back as a TermAssignmentMismatch
error rather than a false success. DEFAULTS TO dry_run=true (returns a preview;
writes nothing). Pass dry_run=false to apply. Requires the REST transport. Prod
writes require allow_prod=true.
|
| wp_list_pluginsA | List every installed plugin (id, name, status, version, requirements). Read-only. Start here: the plugin id it returns ('akismet/akismet') is what the
two activation tools take, and status tells you whether a toggle is even needed.
Counts come back as active / inactive. Installing, updating and deleting
plugins are deliberately NOT offered by this server - use the fleet's plugin-audit
and MainWP rollout process for those. Requires the REST transport (the wpcli/SSH
gateway has no plugin support) and WordPress 5.5+. |
| wp_activate_pluginA | Activate one already-installed plugin. ACTIVATING AN UNTESTED PLUGIN CAN BREAK A LIVE SITE: the activation hook runs
immediately and can fatal, and the plugin starts filtering the site's output the
moment it loads. Activate on staging first. plugin may be a slug ('akismet'), a
plugin file ('akismet/akismet') or its .php path - all three resolve to the same
plugin, and a slug matching two installed plugins is refused with the candidates
rather than guessed. A plugin that is already active comes back as a no-op, not an
error. DEFAULTS TO dry_run=true (returns a preview naming the plugin and its current
status; changes nothing). Pass dry_run=false to apply - the change is verified by
re-reading the plugin's status. Requires the REST transport. Prod writes require
allow_prod=true. |
| wp_deactivate_pluginA | Deactivate one plugin: this can take site functionality OFFLINE immediately. DEACTIVATING A LIVE PLUGIN TAKES WHATEVER IT PROVIDES OFFLINE the instant it lands -
forms stop submitting, caching stops serving, security rules stop applying, and
page builders stop rendering their layouts (a Divi/Elementor page can come back as
raw shortcodes). Check what the plugin does before calling this, and prefer staging.
Deactivating wp-ops-connect is REFUSED outright: it is the control-plane plugin this
tool reaches the site through, so deactivating it would sever this tool's own
connection to the site and nothing here could re-activate it. plugin may be a slug
('akismet'), a plugin file ('akismet/akismet') or its .php path; a slug matching two
installed plugins is refused with the candidates rather than guessed. A plugin that
is already inactive comes back as a no-op, not an error. DEFAULTS TO dry_run=true
(returns a preview naming the plugin, its current status and what would change;
changes nothing). Pass dry_run=false to apply - the change is verified by re-reading
the plugin's status. Requires the REST transport. Prod writes require allow_prod=true. |