# Search and replace in content

By the end of this page you know how an agent finds text or a CSS class in saved content, how it replaces it in two steps, and how to undo a replacement.

These tools are in the **Search and replace in content** group. It starts off, and it needs the `edit_others_posts` and `edit_theme_options` capabilities. Every post is also checked on its own: a post the account cannot edit is skipped.

## What is searched

By default: posts, pages and every other public post type, plus the three types the Site Editor saves: templates, template parts and patterns. Published, scheduled, draft, pending and private content is included. Content in the trash is never touched.

Only content saved in the database can be reached. A template that still comes straight from a theme file has no saved copy. Search those with `btm.theme-search` instead. See [Edit theme files](https://w4dev.com/docs/block-theme-mcp/theme-files-change-sets-backups-undo/).

A post larger than 1 MB is skipped and counted.

## Two modes

- `text` matches the text anywhere in the saved markup. That includes block settings and URLs. Use it for a phrase, a URL or a piece of markup.
- `class` matches a whole CSS class name, in HTML `class` attributes and in a block’s `className`. Use it to rename or remove a class.

Text mode can match more than you mean. A search for `hero` also finds it inside a sentence or an image address. Class mode matches `hero` only where it is a complete class name, never `hero-title` and never the word in a paragraph. It also changes the HTML and the block’s own setting together, so the block stays valid in the editor.

Class mode is case-sensitive. A class name has no spaces and none of `"`, `'`, `<`, `>` and `&`.

## Search

`btm.content-search` returns the posts that match, each with a count and a short excerpt around the first match. Results come in pages. The agent passes `nextCursor` back as `cursor` until it comes back as `null`.

```
{
  "pattern": "hero-title",
  "mode": "class"
}
```

## Replace is always two steps

### 1. Dry run

`btm.content-replace` runs as a dry run unless told otherwise. A dry run writes nothing.

```
{
  "replacements": [
    { "from": "hero-title", "to": "page-title" }
  ],
  "mode": "class"
}
```

The result lists each post that would change, with up to three before and after previews, and a `token`. A post that would be skipped is listed with the reason:

- `locked`: someone else is editing it.
- `no-revisions`: its post type keeps no revisions, or fewer than two. It is never written, because there would be no way back.
- `too-large`: over the size limit.

### 2. Real run

The agent calls the tool again with `dryRun` set to `false`, the token, and the same mode and replacements:

```
{
  "replacements": [
    { "from": "hero-title", "to": "page-title" }
  ],
  "mode": "class",
  "dryRun": false,
  "token": "<token from the dry run>"
}
```

The real run writes exactly the posts the dry run listed, and no others. The token works once, for 15 minutes, and only for the account that ran the dry run. With anything else the call is refused:

```
This token is unknown, expired, already used, belongs to another user, or was issued for a different mode or replacements. Run a new dry run with exactly the mode and replacements you want, then pass its token.
```

Each post gets an outcome:

- `changed`: saved exactly as previewed.
- `filtered`: saved, but WordPress or another plugin altered the content on save. Read the post to check it.
- `conflict`: the post was edited after the dry run. It was not written. Run a new dry run.
- `locked`, `forbidden`, `no-revisions`: not written, for the reason named.
- `failed`: not written. The row carries the error.

## Undo

Before a post is written, a revision of its current content is saved. The result gives its id as `revisionBefore`.

- For a post or page: open it in wp-admin and restore that revision under **Revisions**.
- For a template or template part: use `btm.template-restore-revision`, or the Site Editor’s revisions.

## Good to know

- A real run writes posts one at a time. If it stops part-way, the posts already written stay written, and the token is spent. Run a new dry run for the rest.
- Up to 20 replacements can go in one call. They are applied in order, each on the result of the one before.
- Some plugins store structured data as a post’s content. Text mode can damage it when a replacement changes the length. Leave such post types out, or use class mode.

For a full example that covers theme files and content together, see [Rename a CSS class across the theme and saved content](https://w4dev.com/docs/block-theme-mcp/rename-a-css-class/).

Source: https://w4dev.com/docs/block-theme-mcp/search-and-replace-in-content/
