MCP Server
@loopress/mcp is an MCP server that exposes Loopress CLI operations as tool calls, so an AI agent (Claude Code, Claude Desktop, or any MCP client) can pull and push snippets, API routes, hooks, ACF objects, forms, nav menus, custom post types, taxonomies, post meta, roles, block patterns, options, Global Styles, plugins, themes and Composer dependencies on a WordPress site (plus push, list and diff static pages and block templates and parts, which don’t support pull, and deploy single-page apps), one resource at a time or all at once, and check project status. It ships as the lps-mcp binary.
It doesn’t reimplement any sync logic: every tool runs the CLI installed alongside it (both installed globally with npm install -g), falling back to the lps binary on your PATH.
Requirements
Section titled “Requirements”- The Loopress CLI installed and on
PATH - A
loopress.jsonalready set up in the directory your MCP client will launch the server from (lps init), since tools resolve paths the same waylpsdoes when run by hand - WordPress authentication already done via the CLI (
lps project config). The MCP server never handles auth itself, it only callslps, which reads the stored Application Password.
Installation
Section titled “Installation”npm install -g @loopress/mcpThen point your MCP client at the lps-mcp binary. In Claude Code:
claude mcp add loopress -- lps-mcpIn a JSON-based client config (Claude Desktop and similar):
{ "mcpServers": { "loopress": { "command": "lps-mcp" } }}On Windows, npm installs lps-mcp as a .cmd shim, so launch it through cmd: claude mcp add loopress -- cmd /c lps-mcp, or "command": "cmd", "args": ["/c", "lps-mcp"] in a JSON config.
The server communicates over stdio from the directory your client launches it in, and takes no arguments of its own.
When an agent connects, the server tells it what a Loopress project is and links the documentation page of each file format (hooks, API routes, ACF and the others). An agent asked for a feature then writes a Loopress hook, not plain WordPress code that push would refuse.
| Tool | Mutating | Description |
|---|---|---|
snippet_push |
Yes | Push local snippet files to WordPress |
snippet_pull |
No | Pull snippets from WordPress into local files |
snippet_list |
No | List snippets currently on WordPress |
snippet_rollback |
Yes | Restore snippets to the snapshot saved automatically before an earlier snippet_push (list shows what’s available) |
api_push |
Yes | Push local custom API route files to WordPress (prune also removes server-side files absent locally) |
api_pull |
No | Pull custom API route files from WordPress |
api_list |
No | List custom API route files currently on WordPress (flags each route public when it needs no authentication) |
api_rm |
Yes | Remove one custom API route file from WordPress |
api_rollback |
Yes | Restore API route files to the snapshot saved automatically before an earlier api_push (list shows what’s available) |
hook_push |
Yes | Push local hook files to WordPress (prune also removes server-side files absent locally) |
hook_pull |
No | Pull hook files from WordPress |
hook_list |
No | List hook files (WordPress actions, filters, and cron jobs) currently on WordPress |
hook_rm |
Yes | Remove one hook file (action, filter, cron) from WordPress |
hook_rollback |
Yes | Restore hook files to the snapshot saved automatically before an earlier hook_push (list shows what’s available) |
page_push |
Yes | Push local static HTML pages to WordPress, or only one (slug) |
page_list |
No | List the static pages managed by Loopress on WordPress |
page_diff |
No | Show what differs between the local pages and WordPress |
template_push |
Yes | Push local block templates, parts and theme CSS as the files of the Loopress child theme |
template_list |
No | Show the Loopress child theme: active or not, its templates and parts, and Site Editor edits |
template_diff |
No | Show what differs between the local templates and parts and WordPress, Site Editor edits included |
app_push |
Yes | Build and push single-page app bundles to WordPress |
app_pull |
No | Pull single-page app bundles from WordPress into local files |
app_list |
No | List single-page apps currently deployed to WordPress |
app_remove |
Yes | Remove a single-page app from WordPress |
acf_push |
Yes | Push local ACF field groups, post types, taxonomies and options pages to WordPress |
acf_pull |
No | Pull ACF objects from WordPress into local files |
acf_list |
No | List ACF objects currently on WordPress |
acf_rollback |
Yes | Restore ACF objects to the snapshot saved automatically before an earlier acf_push (list shows what’s available) |
menu_push |
Yes | Push local nav menus and the active theme menu locations to WordPress |
menu_pull |
No | Pull nav menus and the active theme menu locations from WordPress into local files |
menu_list |
No | List nav menus and the active theme menu locations currently on WordPress |
menu_rollback |
Yes | Restore nav menus and menu locations to the snapshot saved automatically before an earlier menu_push (list shows what’s available) |
cpt_push |
Yes | Push local cpt/<slug>.json custom post types to WordPress (create or update only) |
cpt_pull |
No | Pull the custom post types Loopress manages into cpt/<slug>.json files |
cpt_list |
No | List the post types registered on WordPress and where each comes from |
cpt_rm |
Yes | Stop registering a Loopress custom post type (its posts stay in the database, hidden) |
cpt_rollback |
Yes | Restore custom post types to the snapshot saved automatically before an earlier cpt_push (list shows what’s available) |
taxonomy_push |
Yes | Push local taxonomies/<slug>.json taxonomies to WordPress (create or update only, never their terms) |
taxonomy_pull |
No | Pull the taxonomies Loopress manages into taxonomies/<slug>.json files |
taxonomy_list |
No | List the taxonomies registered on WordPress, where each comes from and the post types it attaches to |
taxonomy_rm |
Yes | Stop registering a Loopress taxonomy (its terms stay in the database, hidden) |
taxonomy_rollback |
Yes | Restore taxonomies to the snapshot saved automatically before an earlier taxonomy_push (list shows what’s available) |
meta_push |
Yes | Push local meta/<post-type>.json meta keys to WordPress (create or update only, never their values) |
meta_pull |
No | Pull the post meta keys Loopress manages into meta/<post-type>.json files |
meta_list |
No | List the meta keys registered for each post type on WordPress, and whether Loopress declares them |
meta_rm |
Yes | Stop registering the Loopress meta keys of one post type (their values stay in the database) |
meta_rollback |
Yes | Restore post meta keys to the snapshot saved automatically before an earlier meta_push (list shows what’s available) |
role_push |
Yes | Push local roles/<slug>.json roles to WordPress (create or update only, never changes which users have them) |
role_pull |
No | Pull the roles Loopress manages into roles/<slug>.json files |
role_list |
No | List the user roles on WordPress, where each comes from and how many users have it |
role_rm |
Yes | Stop registering a Loopress role (its users keep it, with no capability) |
role_rollback |
Yes | Restore roles to the snapshot saved automatically before an earlier role_push (list shows what’s available) |
pattern_push |
Yes | Push local patterns/<slug>.html block patterns to WordPress, registered as loopress/<slug> (create or update only) |
pattern_pull |
No | Pull the block patterns Loopress manages into patterns/<slug>.html files |
pattern_list |
No | List the block patterns registered by Loopress, the theme and plugins |
pattern_rm |
Yes | Stop registering a Loopress block pattern (references to it render nothing) |
pattern_rollback |
Yes | Restore block patterns to the snapshot saved automatically before an earlier pattern_push (list shows what’s available) |
option_push |
Yes | Push locally tracked, non-readonly options to WordPress |
option_pull |
No | Refresh locally tracked options from WordPress |
option_list |
No | List WordPress option names and autoload flags currently on the site (names only, never values) |
option_add |
No | Fetch a WordPress option by name and start tracking it locally |
option_remove |
Yes | Stop tracking an option locally and delete it from WordPress |
option_rollback |
Yes | Restore tracked options to the snapshot saved automatically before an earlier option_push (list shows what’s available) |
form_push |
Yes | Push local form files to WordPress |
form_pull |
No | Pull forms from WordPress into local files |
form_list |
No | List forms currently on WordPress |
form_rollback |
Yes | Restore forms to the snapshot saved automatically before an earlier form_push (list shows what’s available) |
plugin_add |
No | Add a WordPress.org plugin to loopress.json, or change its pinned version (local file only) |
plugin_push |
Yes | Install WordPress.org plugins to match loopress.json (active state included), or push the whole composer.json when there is one, via Composer + WPackagist |
plugin_pull |
No | Refresh the tracked plugins from WordPress into loopress.json (or composer.json), pinned to their live versions; other installed plugins are reported as untracked unless named in plugins |
plugin_status |
No | Compare the plugins on WordPress against loopress.json and report drift |
plugin_audit |
No | Check loopress.json plugins for known vulnerabilities and health issues |
theme_add |
No | Add a WordPress.org theme to loopress.json, or change its pinned version (local file only) |
theme_version_push |
Yes | Install WordPress.org themes to match loopress.json (never switches the active theme) |
theme_push |
Yes | Push everything theme related: versions, then templates and parts, then Global Styles |
theme_version_pull |
No | Pull installed themes from WordPress into loopress.json, pinned to their live versions |
theme_pull |
No | Pull everything theme related: versions, then Global Styles |
theme_status |
No | Compare the themes on WordPress against loopress.json and report version drift |
theme_styles_push |
Yes | Push the local Global Styles file to the active block theme’s Site Editor > Styles on WordPress |
theme_styles_pull |
No | Pull the active block theme’s Global Styles customizations from WordPress into a local file |
theme_styles_rollback |
Yes | Restore Global Styles to the snapshot saved automatically before an earlier theme_styles_push (list shows what’s available) |
composer_push |
Yes | Push composer.json and run Composer on WordPress to resolve and install dependencies |
composer_pull |
No | Pull composer.json/composer.lock from WordPress |
push_all |
Yes | Push every local resource to WordPress in one run, like lps push |
pull_all |
No | Pull every resource from WordPress into local files in one run, like lps pull |
project_promote |
Yes | Copy every tracked resource from one environment (from) to another (to), pulling then pushing, like lps promote |
content_copy |
Yes | Replace the local site’s posts, pages and terms with those of from, then push the project to local, like lps content copy |
project_status |
No | Show which project and environment the other tools will target |
project_diff |
No | Show what differs between local tracked files and a WordPress environment, or between two environments (against) |
project_doctor |
No | Diagnose connectivity, plugin, credential and hook problems for the targeted environment |
validate_local |
No | Check local tracked files are well formed and push-ready, without contacting WordPress |
Every tool accepts an optional env to target a specific environment instead of the project’s default one (local, or its only environment). The _push, _pull, _list and _rollback tools that sync files also accept an optional path to override the directory configured in loopress.json. The ACF tools take an optional type array to scope the operation, mirroring the CLI’s --type flag. project_diff also takes optional only/skip resource-name arrays (the same names as lps diff --only, including template, part and page) and an against environment name to compare two environments instead of an environment against local files. Push tools that check for changes made outside Loopress (push_all, page_push and every resource with a _rollback tool) take an optional overwrite boolean, the CLI’s --overwrite: without it, a confirmed push over such a change fails and names what it would overwrite. Each _rollback tool restores the snapshot its resource’s _push tool saved automatically right before the last real push; pass list: true to see what’s available instead (id, timestamp, environment) or to to pick an older one. The preview result’s drift field reports whether the environment changed since that push, and if it changes again between the preview and the confirmed call, the confirmed call is refused rather than applied, the same staleness check every other mutating tool’s confirmation handshake now does.
Confirming changes
Section titled “Confirming changes”Every mutating tool requires two calls before it touches WordPress:
- Call without
confirmToken: takes a private copy of the working directory, runs a dry-run preview against it, and returns a single-useconfirmToken(expires after 5 minutes) instead of making any change. Most push previews list each item they write underchanges, as created, changed or unchanged against the site (see dry runs), so the agent can tell you what will change. The plugin, theme version, composer and app pushes don’t: their preview has its own shape. - Call again with that
confirmToken: re-runs that same dry-run once more, from the same private copy, right before applying. If it now reports anything different, meaning the environment changed since the preview, the call is refused instead of applying against stale data; call again without aconfirmTokenfor a fresh preview. Otherwise it applies from that same copy, so a file edited on disk between the two calls cannot change what gets pushed either.
This handshake can’t be skipped, so an agent can never push a real change without a human-reviewable preview first. Mutating tools also refuse an environment named production outright, run those changes with lps, where they are confirmed interactively. When env is omitted, the preview call resolves the default environment and pins it, so this refusal covers the default environment too, and the confirmed call applies to the environment that was previewed even if the configuration changes in between. Two known gaps: an lps --path that points outside the working directory, or a loopress.json that maps a resource directory to an absolute path elsewhere, is still read live when the change is applied; and the revalidation dry-run and the real apply are still two separate requests rather than one atomic check-and-write, so a WordPress change landing in that (much narrower) window can still be overwritten, closing that fully needs a conditional write enforced by WordPress itself, tracked separately.
Project config resource
Section titled “Project config resource”The server also exposes loopress://project/config as a resource: the raw contents of loopress.json in the current directory, for an agent to inspect without shelling out itself.