Skip to main content
Glama

Server Configuration

Describes the environment variables required to run the server.

NameRequiredDescriptionDefault
WPOPS_TIMEOUTNoTimeout in seconds for requests. Divi 5 pages are large; raise it if you see timeouts.45
WPOPS_TRANSPORTNoTransport mode: 'rest' (HTTPS only, recommended) or 'auto'.auto
WPOPS_USER_AGENTNoUser agent string to use. Override per site if a CDN rule blocks or redirects it.WP-Ops-MCP/1.0
WPOPS_CREDENTIALSYesPath to the credentials JSON file. Use an absolute path - the default is relative to the working directory, which your MCP client chooses.

Instructions

Guidance the server publishes about itself, which clients place ahead of the tool catalog so the model reads it before choosing anything.

This server publishes no instructions, or was last inspected before Glama recorded them.

Capabilities

Features and capabilities supported by this server

Protocol revision2025-11-25

CapabilityDetails
tools
{
  "listChanged": false
}
prompts
{
  "listChanged": false
}
resources
{
  "subscribe": false,
  "listChanged": false
}
experimental
{}

Tools

Functions exposed to the LLM to take actions

NameDescription
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_add_menu_item to repoint an item at a different page). 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_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.

Prompts

Interactive templates invoked by user choice

NameDescription

No prompts

Resources

Contextual data attached and managed by the client

NameDescription

No resources

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/ad02/wp-ops-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server