Skip to main content
Glama

Search and replace across the site

wp_search_replace
Destructive

Replace text across posts, page content, and site options instantly. Safely update serialized data and JSON fields during large-scale WordPress content modification.

Instructions

Search and replace across the site's stored data, safe for serialized and JSON values; finding text without changing it is wp_search, one post is changed with wp_update_post. post_ids limits the call to the posts it names (posts, postmeta and page-builder pages); expected_count makes apply=true write only when the call finds exactly that many matching rows (totals.matched of the dry run), so nothing is written where the site changed since the preview. tables: posts (title, content, excerpt), postmeta, options, terms (names, descriptions, termmeta); default posts and postmeta. apply=false (default) is a dry run that returns a per-table preview; apply=true writes. Matching: serialized and JSON values are rewritten losslessly, and the CSS and caches Elementor builds from elementor_data (element cache with the rendered HTML, Markdown cache) are reset. Plain (non-regex) mode also finds the encoded forms WordPress and page builders store (JSON-escaped such as https://…, block attributes such as &, HTML entities such as &, and HTML inside block attributes), so "A & B" is found where a page stores "A & B", and writes the replacement in the form it found, as JSON inside block attributes. A serialized value holding a PHP object, a Serializable or enum record or a reference is left untouched (skipped_object_values), so no class is restored from stored data. Scope "text" (default): a page-builder page that a writer here writes (write_via in wp_get_builder_layout) changes only through that writer, in the texts, links and images it may change, under the field rules of wp_update_builder_element; its post_content copy is left out (copy_only). Matches in locked places (code, HTML, CSS, shortcodes, forms, placeholders, global parts, page code), and on pages the writer refuses as a whole, stay and are counted in locked_matches; scope "raw" reaches them. Fields a writer leaves are named in notes. A page whose page builder has no writer here, and a text a writer finds in none of its fields where the site shows post_content (SeedProd), are replaced as text (as_text, builder_pages_without_writer); a builder that keeps its own copy writes it over that at its next save. For an account without unfiltered_html, a post that WordPress's kses filter would store differently (an SVG, a style or a script dropped, a block attribute changed, in any field it filters) is not written and is named in notes. Scope "raw": the stored data as text everywhere, page-builder data and page code included (page-builder data needs unfiltered_html); a copy in post_content_filtered (SeedProd) is not read, so only post_content changes there until the page builder writes its copy back at its next save, and warnings names such pages. Undo: page-builder pages get undo points under one undo_token (undo_pages lists them). For pages a writer changes it holds their page-builder data and its copies; for pages changed outside a writer and in scope "raw" also post_content and the meta keys the call rewrites on them, except the code fields a page builder locks (custom CSS, for example): an undo leaves those as changed, and warnings names them. Option rows the call changes get an undo point of their own (options_undo_token), recorded before they are written; posts without a page builder, titles, excerpts and terms are not in any undo point, and the answer says so. Per call, at most 25 pages change through writers (next.builders continues) and at most 25 in scope "raw"; of the pages scope "text" changes outside a writer, up to 25 get an undo point, and the others, or any whose undo point cannot be recorded, are written without one and named in warnings (stop_at_undo_cap, allow_without_undo). Never touched: trash, auto-drafts, revisions (unless exclude_revisions=false), protected options and option names the option guard refuses, transients, the guid column, credential-like meta keys, widget rows, internal post types and taxonomies; rows the account may not read or edit are passed over silently. Protected "" meta keys are rewritten only for an account with manage_options and only when allow-listed (filter ab_mcp_pro_protected_meta_allowlist), never _wp_attached_file, _wp_attachment_metadata, _thumbnail_id or _wp_page_template; page-builder data (_elementor_data, panels_data, dslc_code, pagelayer-data, brizy, vcv-pageContent, mfn-page-items, tve_updated_post and more; filter ab_mcp_pro_markup_meta_keys) only for an account with unfiltered_html, on multisite a super admin. Writes go through the WordPress APIs (revisions created, serialization regenerated, caches invalidated), term names and descriptions through wp_update_term(). No slug is asked to change: a term shared by several taxonomies or with an empty stored slug is passed over, and slug_changes lists any slug a filter changed along with a name (term, old and new slug; up to 200, the rest in slug_changes_omitted). Paging: at most 5 000 000 ids per scan (10 000 per query, a tenth in regex mode; the terms table has three scans, the page-builder pages one, builders); next records where each scan stopped, and sent back as after it lets the next call continue without reading anything twice. Answer: per-table counts and previews; notes (up to 10 per table, notes_omitted the rest); warnings; skipped_object_values and skipped_unchecked_values (too large or too deep to walk: more than 64 levels or 12 nested serialized or JSON strings; or JSON stored in a form the encoder does not write, spaced for example, when the replacement holds a quote, a backslash or a control character (outside regex mode) or the result would no longer be a JSON object or array) count values, not rows, and the rest of such a row is still rewritten.

Input Schema

NameRequiredDescriptionDefault
typeNoPost type filter for the posts table and the page-builder pages (postmeta is scanned for every post whose meta the account may read and edit); a call without the posts table ignores it (flags.type_ignored). Default any.
afterNoThe next object of a previous call (keys posts, postmeta, options, term_names, term_descriptions, termmeta, builders; each the id to continue behind). Default: start at the beginning of every table.
applyNotrue writes the changes; false (default) returns the dry-run preview only.
limitNoMax matched rows processed per table (1–2000). Default 200. When truncated=true, next, sent back as after, continues there; notes say why a scan stopped, and only where the row limit stopped it does a higher limit take more rows (page-builder pages stay at 25 per call: table builders, scope raw, stop_at_undo_cap). Rows the policy passes over do not count.
regexNotrue treats search as a PCRE regex (no delimiters; slower: every row of the id budget is read and tested in PHP, without a SQL prefilter). Matches the stored bytes only, without the encoded forms. In scope text, a page-builder page whose writer does not apply a pattern to its fields is left as it is and named in notes, with the way: scope "raw". Default false.
scopeNotext (default): page-builder pages that a writer of this version writes are changed only in the texts, links and images their writer may change; matches in locked places are counted in locked_matches; the page-builder pages it changes get an undo point (those changed outside a writer up to 25 per call; warnings names the pages beyond). raw: the stored data as text everywhere, page code included (page-builder data needs unfiltered_html; a copy a page builder keeps in post_content_filtered, as SeedProd does, is not read), with an undo point for every page-builder page.
searchYesText to find (or PCRE pattern without delimiters when regex=true).
tablesNoTables to process: posts, postmeta, options, terms. Default ["posts","postmeta"].
replaceNoReplacement text. Default "" (removes the match).
post_idsNoPost ids (integers, at most 500): only these posts, their postmeta and, among page-builder pages, only these are scanned and written; a revision counts by its own id. Works with the tables posts and postmeta only. Default: every post the account may edit.
expected_countNoWith apply=true: the number of matching rows the call must find (totals.matched of the dry run with the same arguments); with another number nothing is written and the answer says what it found. Default: no check.
case_insensitiveNoCase-insensitive matching. Default false.
stop_at_undo_capNoscope text: one call keeps undo points for up to 25 page-builder pages it changes outside a writer. Default false: the pages beyond them are written without one, as before, and warnings names them. true: the posts and postmeta scans stop before the first such page instead (truncated, with a note); next, sent back as after, continues there, so every page gets an undo point.
exclude_revisionsNotrue (default) skips post revisions; false includes them.
allow_without_undoNotrue writes a page-builder page whose undo point would be over the size limit, without a way back. Default false: such a page is not written. Any other failure to record the undo point still refuses the page. Applies to scope raw and to the pages a writer changes; in scope text a page changed outside a writer is written as before when its undo point cannot be recorded, and warnings names it.

Schema Changelog

Changes observed during successful MCP inspections.

  1. Addedv4.10.0

TDQS

A4.6/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare destructive/openWorld writes, but the description goes far beyond them: dry-run default, undo points and their limits, per-call caps of 25 pages, protected options and meta keys, unfiltered_html/manage_options requirements, locked places, skipped object values, and paging continuation. It is unusually explicit about what gets written, what is refused, and what cannot be undone.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness2/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is a dense wall of run-on sentences with nested clauses and no headings or grouping, making it hard to scan for the few facts an agent needs at call time. Length is partly justified by 15 parameters and complex policy, but the material is not front-loaded or structured for retrieval.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a destructive, 15-parameter tool with no output schema, the description covers return shape (per-table counts, previews, notes, warnings, skipped value counters), scope behavior, permission gates, undo semantics, and paging. An agent has everything needed to invoke it correctly and interpret the result.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so the schema already documents each parameter, but the description adds cross-parameter meaning: expected_count must equal totals.matched from an identical dry run, post_ids restricts posts, postmeta and page-builder pages only, and tables interacts with type. Some parameters (e.g., case_insensitive, exclude_revisions) get no added context, which keeps it below a 5.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The opening sentence names a specific verb and resource (search and replace across stored site data) and immediately distinguishes the read-only and single-post siblings (wp_search, wp_update_post). An agent can identify the tool's role without opening the schema.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It explicitly routes finding text without changes to wp_search and single-post edits to wp_update_post, and it explains when apply=true vs. the default dry run, scope "text" vs. "raw", and expected_count guarding are appropriate. Alternatives and exclusions are named rather than implied.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Deploy Server

Other Tools