@respira/wordpress-mcp-server 8.1.0 → 8.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/CHANGELOG.md +1232 -0
  2. package/README.md +12 -3
  3. package/package.json +3 -2
package/CHANGELOG.md ADDED
@@ -0,0 +1,1232 @@
1
+ # Changelog
2
+
3
+ All notable changes to Respira WordPress MCP Server will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [8.1.1] - 2026-07-30
9
+
10
+ ### Documentation
11
+
12
+ - Updated the npm package description to name MCP 2026-07-28 and legacy-client compatibility directly.
13
+ - Added the 8.1 release summary to the top of the npm README and corrected the stale “Next release” heading.
14
+ - Included `CHANGELOG.md` in the npm package so the complete version history ships with every installation.
15
+
16
+ ## [8.1.0] - 2026-07-30
17
+
18
+ ### Added
19
+
20
+ - **MCP 2026-07-28 protocol support through the official TypeScript SDK 2 packages.** New clients can negotiate the modern stateless protocol with `server/discover`, per-request metadata, typed results, server identity, and cache semantics.
21
+ - **Dual-era stdio serving.** Existing MCP clients keep using the established `initialize` handshake, while compatible clients can pin or negotiate `2026-07-28`. Existing Respira configuration, site keys, and connection commands do not change.
22
+ - **Release coverage for both protocol generations.** The packaged-server smoke test now proves both an exact `2026-07-28` negotiation and the legacy initialization path against the complete tool catalog.
23
+
24
+ ### Security
25
+
26
+ - Updated Axios, FormData, Sentry, and their OpenTelemetry runtime dependencies to patched releases. The production npm dependency audit reports zero known vulnerabilities.
27
+
28
+ ## [8.0.6] - 2026-07-29
29
+
30
+ ### Fixed
31
+
32
+ - **`approval_token` is now declared in the input schema of every tool that requires it.** 11 destructive tools (`delete_post`, `delete_comment`, `delete_term`, `delete_option`, `bulk_pages_operation`, and the site-pattern/site-navigation create/update/delete tools) implement the standard two-step approval flow, but didn't list `approval_token` as a parameter, so a client had no way to discover it exists until a call failed once. Four of those (`delete_post`, `delete_comment`, `delete_term`, `delete_option`) had a deeper gap: their underlying client methods never accepted or forwarded the token at all, so even a client that guessed the parameter name correctly would have had it silently dropped before reaching the site. Both are fixed now. Reported by B.
33
+ - **`respira_acf_*` tools now honor a per-call `site_id` override.** Same root cause as the Bricks/Elementor fix shipped in 8.0.3, just never applied to the ACF branch: the dynamic-dispatch path used the globally active site instead of the already-resolved, site_id-aware client. In a multi-site config this meant an ACF write pinned to one site could silently land on a different, active site while the response still claimed the pinned one. Reported by J.
34
+
35
+ ## [8.0.5] - 2026-07-29
36
+
37
+ ### Fixed
38
+
39
+ - **Bundled skills now match the public catalog.** `content-portability`, `site-onboarding`, `woo-agent-storefront`, and `woo-catalog-perfection` were live on the respira.press marketplace and installable straight from GitHub, but missing from the skills bundled with the MCP server itself, so `respira-install-skills` and any offline install couldn't find them. Pulled all four SKILL.md files in from the catalog repo and re-bundled (43 skills, up from 39).
40
+
41
+ ## [8.0.4] - 2026-07-29
42
+
43
+ ### Fixed
44
+
45
+ - **`respira_redeem_token` now merges sites instead of replacing them.** Redeeming a per-site-scoped token (the dashboard's "Set up assistant" link for one specific site) legitimately carries only that one site. The write to `~/.respira/config.json` previously used that token's site list verbatim, silently dropping every other site an earlier redeem had already connected. A customer connecting three sites one at a time this way ended up with only the last one, with no error saying so. Redeem now reads whatever is already on disk first and merges the new token's sites into it (a fresh key for an already-connected site replaces the old one; everything else is kept), and the response reports both how many sites this token added and how many are now configured in total. Reported by Des.
46
+
47
+ ## [8.0.3] - 2026-07-29
48
+
49
+ ### Fixed
50
+
51
+ - **`site_id` is now honoured on every Bricks and Elementor tool.** Every tool handled in the main dispatch switch already resolved a per-call `site_id` override correctly. The Bricks and Elementor tools that fall through to the dynamic-dispatch default case (most of `bricks-tools.ts` and `elementor-tools.ts`, including `wordpress_get_bricks_typography` / `wordpress_update_bricks_typography`) instead used the globally active site regardless of `site_id`, while still naming the requested site back in the response — so a call aimed at one site with another site active silently read or wrote the active site instead, with nothing in the response revealing it. Verified live: the same call now lands exactly on the site named in `site_id`, confirmed by reading the actual stored data on both sites independently rather than trusting the response. Reported by E.M.
52
+
53
+ ## [8.0.2] - 2026-07-27
54
+
55
+ ### Fixed
56
+
57
+ - **`respira_redeem_token` now writes the config where the server will read it.** The write target was hardcoded to `homedir()/.respira/config.json` and ignored `RESPIRA_CONFIG_FILE`. On a plain machine the two agree, which is why this looked correct for a long time. They diverge exactly where it hurts: a host that points `RESPIRA_CONFIG_FILE` somewhere else, or a server running in a sandbox whose home is not the home the user is looking at. Redeem then reported success, wrote a real file, and the next boot read a different path and found nothing. To the customer that looks like "the installer could not write the config itself", followed by placing the file by hand. The honest description is that it was written to the wrong place. Both the success and failure messages now name the absolute path, so "could not write ~/.respira/config.json" can no longer send someone hunting in the wrong home. Reported in substance by I.B.
58
+
59
+ ## [8.0.1] - 2026-07-27
60
+
61
+ ### Fixed
62
+
63
+ - **`RESPIRA_CONFIG_FILE` now resolves `${HOME}`, `~` and `%USERPROFILE%` itself.** The Cowork MCP config ships `"RESPIRA_CONFIG_FILE": "\${HOME}/.respira/config.json"`, and whether that token gets expanded depends entirely on who launches the server. On Windows it frequently is not, because `HOME` is often unset there. The literal string then arrived as the path, the file was not found, the server fell back to bootstrap mode with zero sites, and every tool answered "No WordPress site configured" while otherwise looking healthy. Starting the same server by hand worked, because the shell expanded the token, which made this look like a Cowork problem rather than a path problem. The server now expands the token itself, so it no longer depends on the launcher. When a path still carries a variable Respira cannot resolve, the warning says so and names the value, instead of reporting a missing file that is actually present. Reported by K.L. on Windows.
64
+
65
+ ## [8.0.0] - 2026-07-26
66
+
67
+ Requires Respira for WordPress 8.0.0+. The WooCommerce tools require Respira
68
+ WooCommerce Add-on 4.0.0+: the storefront, reporting, store-configuration and
69
+ webhook routes they call exist only in that release. On an older add-on those
70
+ tools return `rest_no_route`.
71
+
72
+ ### Fixed
73
+
74
+ - **`wordpress_update_theme_builder_template` now advertises and requires `confirm_live_edit`.** Rewriting a Divi global header, footer or body changes every page that uses it and goes live with no approval step, so the plugin refuses the first call. The tool schema advertises the parameter and the description states the blast radius, so a model can tell the user what is about to change before it calls. Requires plugin 8.0.0+; against an older plugin the parameter is simply ignored and behaviour is unchanged.
75
+ - **`upload_media` accepts Windows paths and `file://` URIs again.** A `C:\Users\...\photo.jpg` path, or any `file://` URI, was not recognised as a local file and fell through to the base64 branch, where it decoded to garbage and uploaded a corrupt file rather than failing. Restores the 7.5.x behaviour, which 8.0 had lost by carrying a narrower inline check than the one that shipped.
76
+ - **Two WordPress installs on the same hostname stay two sites.** A site id was derived from the hostname alone, so `example.com` and `example.com/blog` both became `example-com` and the second silently replaced the first: you configured two sites and saw one, with no error. The id now includes the port and the URL path. A config written by an older server is healed in place when the paths disambiguate it, and a genuine duplicate is refused rather than booted with ambiguous routing.
77
+ - The handshake that could not shake hands: `respira_move_element`, `respira_reorder_elements`, `respira_remove_element`, `respira_duplicate_element` and `respira_batch_update` now expose `edit_target` (and its `editTarget` alias) plus `confirm_live_edit` in their schemas. Previously the live-edit confirmation flow told agents to re-call with parameters the schemas did not advertise, so strict MCP clients rejected the retry and live writes through these five tools could never complete. `respira_update_element` already had the params; the other element ops now match it, including the same camelCase normalization toward the plugin. Reported by C.Y. (Elementor, move + reorder).
78
+
79
+ ### Added
80
+
81
+ - First-class FSE template/template-part and Gutenberg design-token tools.
82
+ - Ten Respira-native Site Editor tools for user patterns and block navigation: source-aware pattern discovery, synced/unsynced pattern creation, compact nested navigation traversal, exact block-path updates, stale/wrong-target protection, reference-aware deletion, structural proposals, snapshots, read-back checks, and explicit server-render verification.
83
+ - Bounded WooCommerce revenue, time-series, top-product, order, and customer reporting; safe store-configuration discovery; and approval-gated native webhook management.
84
+ - Agent-facing activity history with compact diffs and snapshot/proposal/rollback links.
85
+ - A generated public tool catalog and machine-readable capability manifest used by release parity checks.
86
+ - `wordpress_search_abilities` classifies discovery results as Respira-native, inhaled, unavailable, or replaced by a safer Respira workflow.
87
+ - `woocommerce_repair_feed_scheduler` separates conservative stale-lock recovery from the read-only feed-status tool and verifies that scheduler history was preserved.
88
+
89
+ ### Changed
90
+
91
+ - Storefront mutations now return staged or verified mutation contracts and reject unsupported/no-op FSE writes instead of reporting false success.
92
+ - Plugin activation runs compatibility preflight and a signed fresh-process boot probe with narrow automatic rollback.
93
+ - Reliability telemetry adds privacy-safe WordPress/Woo/plugin/theme/target/template/verification dimensions.
94
+ - The structural FSE and Global Styles writer is beta-gated per site; template inspection remains available outside the cohort.
95
+
96
+ ## [7.6.2] - 2026-07-23
97
+
98
+ ### Fixed
99
+
100
+ - **PageSpeed errors keep their specific WordPress/upstream code.** Structured 429 and 5xx responses, proxy HTML responses, and generic HTTP failures now pass through one coded-error path, preserving error envelopes for the agent and the machine-readable reason for quality telemetry.
101
+ - **Invalid PageSpeed calls stop at schema validation.** `wordpress_run_pagespeed_audit` and `wordpress_analyze_pagespeed` now require at least one of `page_id` or `url`.
102
+ - **Transport retries no longer overwrite attempt evidence.** npm events now emit an explicit `attempt_id`; central ingestion deduplicates by attempt rather than logical call id.
103
+
104
+ ### Changed
105
+
106
+ - **MCP quality reporting now distinguishes task friction from Respira reliability.** Expected safety blocks, caller mistakes, customer environment failures, upstream failures, product errors, unknown failures, and incomplete attempts are separate outcomes. Operational alerts exclude expected/caller/setup outcomes, prefer matched-site cohorts, and require multi-site evidence.
107
+ - **The npm package no longer publishes private repository metadata.** The public package continues to link to the Respira website and support page.
108
+
109
+ ## [7.6.1] - 2026-07-23
110
+
111
+ ### Added
112
+
113
+ - **`wordpress_build_mega_menu`.** Builds a complete native Divi 4 or Divi 5 mega-menu hierarchy in one call, optionally assigning it to a theme location.
114
+
115
+ ## [7.6.0] - 2026-07-23
116
+
117
+ ### Added
118
+
119
+ - **Divi Theme Builder tools.** List, create, and update native header, footer, and body templates.
120
+ - **WPML translation tools.** List a post's translations, create a linked translation, and update an existing translation while preserving its translation group.
121
+
122
+ ## [7.5.6] - 2026-07-22
123
+
124
+ ### Fixed
125
+
126
+ - **A successful `?rest_route=` diagnostic fallback is now sticky.** Once `respira_diagnose_connection` proves the fallback works, subsequent calls in the session use it automatically instead of returning to the shadowed pretty `/wp-json/` path.
127
+
128
+ ## [7.5.5] - 2026-07-21
129
+
130
+ ### Fixed
131
+
132
+ - **Two WordPress installs on the same hostname but different paths no longer collapse into one site.** Derived identities now include the path and non-default port; unresolved duplicate ids stop startup with a clear configuration error.
133
+
134
+ ### Improved
135
+
136
+ - **Custom Elementor widget guidance uses targeted native edits.** Agents are told to read the live third-party widget control schema, find the stable element id, update only changed settings, and verify the result instead of replacing `_elementor_data`.
137
+
138
+ ## [7.5.4] - 2026-07-19
139
+
140
+ ### Fixed
141
+
142
+ - **`tools/list` no longer performs three sequential live site checks.** WooCommerce detection, ACF detection, and builder filtering share one cached, two-second-bounded context fetch, avoiding first-handshake disconnects on slow WordPress sites.
143
+
144
+ ## [7.5.3] - 2026-07-18
145
+
146
+ ### Fixed
147
+
148
+ - **Windows absolute paths and `file://` URIs work in `upload_media`.**
149
+ - **`delete_media` now forwards `approval_token` and `force`, allowing the approval flow to complete.**
150
+
151
+ ## [7.5.2] - 2026-07-18
152
+
153
+ ### Fixed
154
+
155
+ - **Interrupted writes are no longer retried as if they were reads.** Every connector mutation now carries a stable `Idempotency-Key`. Reads still retry transient 5xx and connection failures, but writes retry only 502/503/504 responses that explicitly advertise the plugin's replay contract; a reset or timeout with no response is surfaced as `respira_write_outcome_unknown` with instructions to verify the target and preserve the call id. This removes the duplicate-mutation risk behind "second or third attempt" recovery loops.
156
+ - **Telemetry delivery survives short network and dashboard outages.** Failed batches return to a bounded queue with exponential backoff instead of being discarded. A rejected scoped token is cleared and reminted on the next attempt.
157
+
158
+ ### Changed
159
+
160
+ - **Local WordPress API keys no longer authenticate central telemetry.** With plugin 7.5.46+, the connector exchanges its local credential inside WordPress for a revocable `rp_mcp_*` token bound to that licensed site. Local `respira_*` keys never leave WordPress.
161
+ - **Connection-quality events can distinguish retry from sweep.** Events now include a per-process-secret HMAC of canonical arguments, stable session/call metadata, transport mode, connector/plugin versions, separate tool and total duration, and privacy-safe write-stage timings when the plugin returns them. Arguments, results, prompts, and WordPress content are never sent.
162
+
163
+ ## [7.5.1] - 2026-07-18
164
+
165
+ ### Fixed
166
+
167
+ - **Every `respira_acf_*` tool was unreachable.** `normalizeToolName()` rewrites `respira_*` names to `wordpress_*` before dispatch, but the ACF dispatcher only matched the original names. Normalized ACF names now map back to their real handlers.
168
+ - **Dashboard site-token 401s lost their specific reason.** Invalid/stale token and inactive-license WP error codes now survive as the thrown error name instead of collapsing into a generic `Error` telemetry bucket.
169
+
170
+ ## [7.5.0] - 2026-07-17
171
+
172
+ ### Added
173
+
174
+ - **Eight STAGGS product-configurator tools, bringing the WooCommerce surface to 87 tools.** Agents can inspect configurator status, enable or disable it per product, and read/write allow-listed attribute and plugin state with dry-run support. Requires Respira WooCommerce Add-on 3.2.0+.
175
+
176
+ ## [7.4.1] - 2026-07-16
177
+
178
+ ### Fixed
179
+
180
+ - **Sites added on the dashboard after connector startup self-heal into the live session.** `list_sites` and `switch_site` refresh the account inventory, merge missing sites by id/normalized URL, and work under both file config and frozen `RESPIRA_CONFIG_B64` without reinstalling the connector.
181
+
182
+ ## [7.4.0] - 2026-07-16
183
+
184
+ ### Added
185
+
186
+ - **23 new WooCommerce tools — Agent-Ready Commerce (79 total, up from 56).** Requires add-on v3.1.0. Product feeds in six formats with a store llms.txt (`configure_feed`, `generate_feed`, `get_feed_status`, `validate_feed`, `set_feed_category_mapping`), the readiness fix loop (`readiness_fixlist`, `set_image_alt`, plus `update_product` gaining `global_unique_id` GTIN and `gallery_image_ids`), assisted checkout with attribution (`create_cart_link`, `agent_orders_report`), and 14 write tools for WooCommerce Subscriptions, Bookings and Memberships (status transitions validated against each extension, booking reschedules with conflict scan, dry-run by default). Verified 71/71 against a live store with the real extensions installed.
187
+
188
+ ## [7.3.0] - 2026-07-16
189
+
190
+ ### Added
191
+
192
+ - **35 new WooCommerce tools — the add-on's full commerce surface (56 total, up from 21).** Completes the commerce rollout that began in June; thanks to D.S. for flagging that his MCP client was not seeing the full surface. Newly exposed: the 15 catalog/pricing/inventory/storefront tools that existed as REST routes since add-on v2.0 but were never MCP tools (`list_products_advanced`, `find_product`, `catalog_health`, `bulk_update_products`, `bulk_update_prices`, `schedule_sale`, `revert_pricing`, `bulk_update_stock`, `find_low_stock`, `analyze_shop_page`, `analyze_product_page`, `update_product_card_layout`, `add_low_stock_badge`, `add_sale_badge`, `update_checkout_layout`), plus 20 new tools shipped in add-on v3.0: brands CRUD (5), variations and attributes incl. WooCommerce 10.9 `wc-visual` color/image swatches (5), coupons and customers (5), order notes and refunds with dry-run-by-default (3), and product/catalog AI-readiness scoring (2). All 56 verified end to end against a live WooCommerce 10.9.4 site.
193
+
194
+ ### Improved
195
+
196
+ - **A missing `/woocommerce/` route now explains itself.** When a Woo tool hits a site running an add-on older than the tool, the `rest_no_route` error is enriched with "update the Respira WooCommerce Add-on (v3.0+)" instead of the raw WordPress boilerplate.
197
+
198
+ ## [7.2.7] - 2026-06-21
199
+
200
+ ### Improved
201
+
202
+ - **Capability negotiation, Phase 2: the server now hides a route-skewed tool based on what the connected plugin actually exposes, not just its version number.** Building on 7.2.6: from plugin 7.4.20 the compatibility endpoint declares per-capability flags (for example `pagespeed`, `abilities`, `theme_files`) computed from the REST routes registered on that exact install. The server now reads those flags and hides the matching tools when the route is genuinely absent, which is more precise than the version map and self-maintains as routes change. Plugins older than 7.4.20 do not declare these flags, so they continue to use the verified version map from 7.2.6 with no change in behavior. Still conservative: a tool is hidden only on a positive signal that its route is absent, and an unknown plugin version sees every tool.
203
+
204
+ ## [7.2.6] - 2026-06-21
205
+
206
+ ### Improved
207
+
208
+ - **Capability negotiation: tools whose plugin route the connected site is too old to have are no longer advertised.** The server now hides version-skewed tools when the connected plugin predates the route they call, so the agent is never offered a tool that returns `respira_rest_no_route` (the largest non-element failure class in the quality telemetry, spread across many tools because only about half of connected sites run a current plugin). Gated, with minimums verified against the plugin source: the PageSpeed tools (`run_pagespeed_audit`, `analyze_pagespeed`) and the Abilities tools (`abilities_gap_report`, `invoke_ability`) require plugin 7.1.0+; the theme-file tools (`read_theme_file`, `write_theme_file`, `append_theme_file`) require 7.0.42+. Conservative by design: a tool is only hidden when the plugin version is known and below its minimum, so an unknown or very old plugin still sees every tool exactly as before. Reuses the existing per-site context filter and the memoized compatibility handshake, so there is no extra round-trip.
209
+
210
+ ## [7.2.5] - 2026-06-20
211
+
212
+ ### Fixed
213
+
214
+ - **`update_module` and `apply_builder_patch` can now complete the live-edit handshake.** Both tools exposed `edit_target` but not `confirm_live_edit`, so a write to a published original returned `confirmation_required` with `confirm_live_edit: true` in the next-call example, yet there was no top-level param to actually send it, so every retry looped at the gate. Added `confirm_live_edit` (boolean, top-level) to both tool schemas and forwarded it to the plugin, matching the shape already on `update_element`. Reported on per-site connectors driving weekly menu rebuilds (e402506e).
215
+
216
+ ## [7.2.4] - 2026-06-19
217
+
218
+ ### Fixed
219
+
220
+ - **`inject_builder_content` no longer hangs on a slow large-page write.** It inherited the bare 30s request timeout while being the heaviest write the server makes, so a large page behind a host or edge request-time limit (for example Cloudflare in front of WP Engine) stalled to the MCP client's multi-minute ceiling with no response. It now allows a 90s window (matching the snapshot-restore path) and, on timeout, returns an actionable error: the write may have completed server-side (verify with `extract_builder_content`), and to stay under the limit, build the page incrementally with `mode:"append"` / `add_section` rather than one giant inject. Reported on a WP Engine + Cloudflare site (9a8fc2fd).
221
+ - **`respira_search_docs` fails fast instead of hanging.** Its docs fetch to respira.press had no timeout, so on a slow or unreachable link (a QUIC-hostile network or a proxy) it could hang the tool to the client ceiling. It now aborts after 20s and returns a clear error so triage continues. This is a respira.press-reachability bound and is independent of the site being edited.
222
+
223
+ ## [7.2.3] - 2026-06-16
224
+
225
+ ### Added
226
+
227
+ - **Oxygen 6 (Jenga) handshake guidance.** The instructions now distinguish Oxygen 6 from Oxygen Classic and hand the agent the native Oxygen 6 element vocabulary, the `oxygen_header` / `oxygen_footer` / `oxygen_template` post types, the rule to use a Template Content Area element (never a Post Content element, which errors on Oxygen 6), and the rule never to dump a whole page into a single HTML block. So the agent builds native, editable Oxygen 6 pages instead of rediscovering the structure and falling back to an HTML block. Pairs with the plugin's `get_builder_info` Oxygen 6 element schemas (plugin 7.4.10) and a new "Build an Oxygen 6 Page" skill (catalog now 36 workflows).
228
+
229
+ ## [7.2.2] - 2026-06-12
230
+
231
+ ### Fixed
232
+
233
+ - **`respira_redeem_token` returned an empty response in non-Cowork agents** (e.g. Antigravity). The redeem path bypasses the normal tool-call wrapper and returned a plain object; non-Claude clients that expect the standard `{content:[{type:"text",...}]}` envelope received nothing and rendered an empty result. The result is now wrapped in the MCP content envelope before returning.
234
+ - **Cowork token redemption now surfaces a RESPIRA_CONFIG_B64 update hint.** When the MCP server is configured via the `RESPIRA_CONFIG_B64` environment variable (common in managed hosting setups), redeeming a new Cowork token writes to `~/.respira/config.json` but the env var takes precedence, so the new site never activates. The success response now includes an explicit instruction explaining how to base64-encode and update the env var.
235
+
236
+ ### Added
237
+
238
+ - **`post_parent` schema exposed on `update_page`, `create_post`, `update_post`, and `build_page` tools.** The plugin has always forwarded `post_parent` to WordPress, but the MCP schema didn't declare the parameter, so agents couldn't set page hierarchy. All four tool schemas now document `post_parent` (with a note that `0` detaches from any parent to make a root page).
239
+
240
+ ## [7.2.1] - 2026-06-10
241
+
242
+ ### Changed
243
+
244
+ - **Divi 5 build guidance points the agent at the rich native modules.** The server's Divi 5 blurb now says heading, text, image, button, and blurb are advertised by default and safe to build with, and to prefer them over `divi/code` raw HTML for real content. New build templates in `divi-prompts.ts` (hero, features row of three blurbs, centered CTA, and a basic landing composition) use the native modules in proper section/row/column nesting, replacing the single trivial hero. A new `build-divi5-page` skill in the marketplace catalog teaches the same workflow.
245
+ - **No more per-inject Divi 4 vs Divi 5 round-trip.** The editing contract now tells the agent to auto-detect the version from the page (`respira_get_builder_info` / `respira_read_page`, with the plugin already flipping into Divi 5 block mode on its own) and only ask the human on a genuinely ambiguous page with no builder set.
246
+
247
+ ### Added
248
+
249
+ - **Dropped-styling warnings are hoisted to the top of build results.** When a Divi 5 (or any builder) write drops style settings the builder does not recognise, `wordpress_inject_builder_content`, `wordpress_build_page`, and `wordpress_update_module` now surface a plain-English hint at the top of the result (`dropped_styling_hint` + a `STYLING DROPPED` message) listing which settings did not apply, telling the agent to pick a supported attribute or use a `divi/code` block, and not to retry the same write. Mirrors the existing `write_was_noop` hoist on `update_element`. Handles both a `warnings` array (`reason: 'unknown_setting'`) and a top-level `dropped_settings` list.
250
+
251
+ ## [7.2.0] - 2026-06-07
252
+
253
+ ### Added
254
+
255
+ - **Cascade rollback tools.** `wordpress_restore_session` undoes a whole session of changes in one call, restoring every distinct object a destructive multi-step task touched (pages, ACF field groups, post types, taxonomies, options) to its pre-session state. `wordpress_begin_session` / `wordpress_end_session` bracket a cascade explicitly; without a bracket, changes auto-group by a time window. Pairs with the 7.3.0 plugin, which captures recovery snapshots for destructive structure operations and returns `recovery_session_id` on each.
256
+
257
+ ### Changed
258
+
259
+ - **ACF field-group tool guidance.** `wordpress_create_acf_field_group` now steers the agent to pick the right field type (taxonomy / relationship / post_object / url / image) rather than defaulting to text, to set `return_format` on media/url/date fields, and to vary `wrapper.width`. The response surfaces non-blocking `field_hints`. The tool description also states that groups created this way are editable in the ACF admin UI.
260
+ - **Reversibility wording matched to behavior** in the server instructions and snapshot tool descriptions: rollback covers every page/post edit and every destructive structure change, with whole-session cascade rollback.
261
+
262
+ ## [7.1.4] - 2026-06-06
263
+
264
+ ### Fixed
265
+
266
+ - **Cowork token redemption could hang instead of failing with a clear error.** The `respira_redeem_token` fetch to respira.press was the only network call in the package without a timeout. On a slow network, a corporate proxy, or a firewall in the way (all common on Windows), the request never resolved, so the redeem tool call hung until the MCP host killed the session with a generic "server timed out" — making it look like the connector itself was broken. The call now aborts after 15s and returns an actionable `redeem_timeout` error explaining it is a network/proxy issue, not the token. Reported by a Windows user 2026-06-06.
267
+
268
+ ## [7.1.3] - 2026-06-06
269
+
270
+ ### Fixed
271
+
272
+ - **`--doctor` no longer hangs.** The health-check command is implemented, shown in `--help`, and recommended in error messages, but it was missing from the CLI dispatch in `index.ts` — so running `npx @respira/wordpress-mcp-server --doctor` fell through to starting the stdio MCP server and hung ("Respira WordPress MCP Server running on stdio"). Added `--doctor` / `-d` to the command gate so it runs the checks and exits. Reported by a Discord user 2026-06-06.
273
+
274
+ ## [7.1.2] - 2026-06-06
275
+
276
+ ### Fixed
277
+
278
+ - **Tool calls no longer get stuck on `https://example.com`.** If the connector was ever started without a configuration, it wrote a placeholder file to `~/.respira/config.json` pointing at example.com, and that placeholder then won over your real `WORDPRESS_URL` on every later run, so every tool call hit example.com no matter what you configured. The connector now recognises that placeholder and ignores it, so your real site is used. Reported by a Windows user 2026-06-06.
279
+ - **The connector now accepts the credential names people actually reach for.** Alongside `WORDPRESS_URL` / `WORDPRESS_API_KEY`, it now also reads `RESPIRA_URL` / `RESPIRA_API_KEY` and the `--wordpress-url` / `--api-key` command-line flags. Previously only the `WORDPRESS_*` pair worked and anything else was silently ignored.
280
+
281
+ ## [Bundle 7.1.2] - 2026-06-05
282
+
283
+ ### Fixed
284
+
285
+ - **Claude Desktop connector no longer crashes on connect.** The 7.1.0 bundle added a TLS bootstrap that relaunched the connector in a second Node process (to pass `--use-system-ca`). That re-exec works in a plain shell but breaks under Claude Desktop's built-in Node: the relaunched process never answers the connect handshake, and Claude kills it in a restart loop showing "Server disconnected". Reported by three customers on 2026-06-05 and reproduced on a 10-site account; the same machines ran fine for months on the pre-relaunch build. The connector already merges your operating system's certificate roots at runtime, so the re-exec was redundant. The bundle now runs in-process by default and connects cleanly. This is a `.mcpb` (Claude Desktop) bundle fix only; the npm server and other clients were never affected.
286
+
287
+ ## [7.1.1] - 2026-06-05
288
+
289
+ ### Fixed
290
+
291
+ - **The connector now fails loud instead of stalling silently on startup.** An older installed build handed a newer dashboard setup code used to start, accept the connect handshake, then hang until Claude Desktop killed it about seven seconds later with no error shown. It now does two things. First, the setup code carries a minimum connector version, and if the installed build is older it stops immediately with a plain message: reinstall the current connector from respira.press/dashboard/mcp. Second, a startup watchdog turns any other stall into a written diagnostic (`~/.respira/last-startup-error.txt`) and a clear exit, rather than a quiet death. Both paths name the reinstall step so the fix is obvious. Reported by a customer who could not connect after adding a site to an old bundled build.
292
+
293
+ ## [7.1.0] - 2026-06-01
294
+
295
+ Version alignment release for the Respira WordPress plugin 7.1 launch. The MCP server and Claude Desktop `.mcpb` bundle now publish as 7.1.0 so customers can see they are running the same release generation as the plugin.
296
+
297
+ ### Fixed
298
+
299
+ - **Claude Desktop connector now trusts your system certificate store.** The `.mcpb` wrapper relaunches with Node's `--use-system-ca` flag when the bundled Node supports it, and the MCP server also merges OS roots into its per-site HTTPS agents at runtime. This fixes valid public certificates that the bundled Node rejected with `UNABLE_TO_VERIFY_LEAF_SIGNATURE` or `SELF_SIGNED_CERT_IN_CHAIN` while the user's system Node and browser already trusted the site.
300
+ - **TLS trust loading is additive only.** Respira keeps Node's built-in roots, adds OS roots, then adds a bundled Mozilla fallback CA file when no custom CA is configured. Certificate verification remains on. Working setups should behave identically.
301
+ - **Better certificate failure guidance.** If certificate verification still fails, the error now explains that the connector already trusts the system store and points to the explicit per-site `"insecureTLS": true` override in `~/.respira/config.json`, plus the Claude Desktop extension setting and `word@respira.press`.
302
+
303
+ ### Added
304
+
305
+ - **`.mcpb` TLS settings.** Claude Desktop extension settings now expose `Skip TLS verification` as a last-resort opt-out and `Custom CA bundle path` for private CA or corporate proxy roots.
306
+
307
+ ## [6.21.2] - 2026-05-28
308
+
309
+ QA hotfix following Mihai's run-4 sweep against the v7.1.0-beta.3 plugin. Three of the seven PARTIALs in that run were one bug: WP_Error envelopes from the plugin carried the structured contract every error-handling skill is built against (`missing_args`, `required_path_keys`, `hint`, `instructions`, `registered_abilities_sample`, `destructive_tools`, ...), but the MCP-server wire path was dropping `data.data` and shipping only the human message + the HTTP status.
310
+
311
+ ### Fixed
312
+
313
+ - **Preserve the structured WP_Error envelope across the wire.** `wordpress-client.ts` constructed an `Error(API error (status): message)` and mapped `data.code` to `error.name`, but every other field from the plugin's WP_Error `data` block was lost. v6.21.2 attaches the structured fields (everything except the redundant `status`) to the Error as a non-enumerable `respiraErrorEnvelope` property. `server.ts` reads it in the error-result builder and spreads the fields at top level of the tool result JSON, so an agent sees `{ error: "...", code: "respira_playbook_step_arg_missing", missing_args: [...], required_path_keys: ["type"], hint: "...", site: {...}, file_bug_hint: "..." }` instead of just `{ error, site, file_bug_hint }`. Skills built against the WP_Error contract (e.g. the "did-you-mean rename" auto-fix flow on `missing_args[].guess_from`) now get the data they expect.
314
+
315
+ ## [6.21.0] - 2026-05-27
316
+
317
+ Sprint 2 of the v7.1 Playbooks ship: JSON workflow compositions that register themselves as WordPress Abilities. Five new MCP tools land alongside the corresponding plugin-side surface (v7.1.0-beta.x — same merge cycle). Once a Playbook is created, it becomes callable via `respira_invoke_ability` under the ability id `respira-playbooks/<id>` — to the agent it looks like any other tool in the catalog.
318
+
319
+ ### Added
320
+
321
+ - **`wordpress_create_playbook`.** Authors a JSON workflow composition that registers itself as a WordPress Ability and shows up as a callable tool on the next MCP handshake. The agent describes the workflow once, Respira stores it, and future invocations are one tool call instead of an instruction-by-instruction agent run. Template steps with `{{input.x}}` (caller inputs) and `{{<capture_name>.field}}` (results from prior steps). Destructive tools are refused at create-time with `respira_playbook_destructive_tool_refused` (delete_page, delete_user, restore_snapshot, apply_builder_patch, etc.) — split the flow and have the agent call destructive ops directly with their existing approval round-trip.
322
+ - **`wordpress_list_playbooks`.** Read-only enumeration of every Playbook on the site, with id / ability_id / label / step count / invocation_count / last_invoked_at. Use for "what playbooks are available" discovery before authoring a new one.
323
+ - **`wordpress_get_playbook`.** Read one Playbook's full definition (input_schema, steps, returns template, audit metadata).
324
+ - **`wordpress_update_playbook`.** Modify an existing Playbook in place. Id is immutable. The destructive-tool check runs again on every update.
325
+ - **`wordpress_delete_playbook`.** Drops a Playbook + its registered Ability. The Ability disappears from the MCP catalog on next handshake. Existing posts / data created by past invocations are not touched.
326
+
327
+ ### Internal
328
+
329
+ - All five route through the generic `callRestV1` wrapper added in v6.20.0 — no new client wrappers needed.
330
+
331
+ ### Notes
332
+
333
+ - Sprint 3 (v71 admin Playbooks screen + a new "Build a Playbook" skill) and Sprint 4 (public /playbooks landing page on respira.press) ship as separate plugin-side + product-website PRs without bumping the MCP version.
334
+ - Playbook execution runs through the plugin's internal REST handler dispatch (`rest_do_request`), so the existing per-tool auth + audit-log + snapshot belt applies to every step. Destructive tools that escape the validator (defense-in-depth) would still refuse at the dispatch layer.
335
+
336
+ ## [6.20.0] - 2026-05-27
337
+
338
+ Sprint 1 of the v7.1 Playbooks ship: agent-creatable Custom Post Types, taxonomies, and ACF field groups. Closes the gap surfaced by the Novamira comparison. Twelve new tools, all routed through the new /respira/v1/custom-structures/* surface on the plugin side. Companion plugin v7.1.0-beta.x (next bump) ships the WP-side Respira_Custom_Structures class that registers the structures against core on init from a wp_option store.
339
+
340
+ ### Added
341
+
342
+ - **`wordpress_create_post_type` / `update` / `delete` / `list_custom_post_types`.** Agent provisions a new CPT in one call. Slug allowlist enforced server-side (lowercase, letters/digits/underscore, 1-20 chars, no reserved slugs like `post` / `page` / `product`). Deletes are non-destructive of existing posts — orphans are surfaced via `orphaned_post_count` in the response so the agent can clean up via `wordpress_delete_post` per post (each gets its own approval round-trip).
343
+ - **`wordpress_create_taxonomy` / `update` / `delete` / `list_custom_taxonomies`.** Same shape as the CPT surface. Attaches to one or more post types via the `post_types` array. Hierarchical (category-like) or flat (tag-like) via the `hierarchical` flag.
344
+ - **`wordpress_create_acf_field_group` / `update` / `delete` / `list_acf_field_groups`.** ACF (free or Pro) must be active — all four refuse with `respira_acf_not_active` otherwise. Fields and location rules pass through ACF's canonical schema unchanged.
345
+
346
+ ### Internal
347
+
348
+ - **New `callRestV1` helper on `wordpress-client.ts`.** Mirror of the existing `callRestV2` for endpoints under `/wp-json/respira/v1/`. The 12 new tools route through this so no per-tool wrapper method was needed.
349
+
350
+ ### Notes
351
+
352
+ - All twelve write tools are `manage_options`-gated on the WP side. Lite version stays read-only — Pro-only for the v7.1 ship.
353
+ - Structure definitions are stored in wp_options (`respira_custom_post_types`, `respira_custom_taxonomies`, `respira_custom_acf_field_groups`). Site export/import via wp-cli covers them automatically; no migration script required.
354
+ - Sprint 2 (Playbooks core — JSON workflow compositions that register themselves as Abilities) lands in v6.21.0.
355
+
356
+ ## [6.19.7] - 2026-05-28
357
+
358
+ Follow-up to Mihai's live QA retest on `mihai.love` (running v7.1.0-beta.1). Two client-side surfaces that didn't fit in v6.19.3 / v6.19.4. Originally queued as 6.19.5 on the v7.1 branch before main shipped a different 6.19.5; renumbered on merge.
359
+
360
+ ### Added
361
+
362
+ - **`delete_media` schema exposes `approval_token` + `force`.** The destructive gate landed on the WP side in v7.1.0-beta.1 but the MCP schema didn't expose the params, so agents couldn't complete the two-step approval round-trip. Now documented in the tool description and accepted in the input schema, matching the `delete_page` / `delete_user` / `delete_plugin` pattern.
363
+
364
+ ### Fixed
365
+
366
+ - **B3 / N20: `inject_builder_content` Divi detector misclassified wrapped extracts.** `detectDiviContentFormat` now unwraps the `{content: [...]}` round-trip shape via the shared helper before walking. Pre-fix, wrapped extracts returned `unknown` from the detector and slipped through on the v6.18.7 B-7 fallback; genuine mixed payloads nested inside a wrapper would have shipped unflagged. Now they hit the same prefix-detection path as bare arrays.
367
+ ## [6.19.11] - 2026-05-31
368
+
369
+ A customer on a LiteSpeed/cPanel site with a perfectly valid certificate (SSL Labs A, full chain) kept getting an SSL/TLS error and spent days checking a cert that was never the problem. The cause was local TLS interception on their Windows machine (antivirus "HTTPS scanning" or a proxy presenting a root Node does not trust), and the error message pointed them at the site instead of the machine. The documented `tlsRejectUnauthorized` config they had set was also silently ignored.
370
+
371
+ ### Fixed
372
+
373
+ - The `UNABLE_TO_VERIFY_LEAF_SIGNATURE` / `SELF_SIGNED_CERT_IN_CHAIN` connection hint now says the certificate could not be verified on this machine (and the site cert is probably fine), and points at local antivirus HTTPS scanning or a corporate proxy, with `NODE_EXTRA_CA_CERTS` as the secure fix. `CERT_HAS_EXPIRED` and `ERR_TLS_CERT_ALTNAME_INVALID` now get their own site-side hints instead of one generic message.
374
+
375
+ ### Added
376
+
377
+ - A real TLS escape hatch that takes effect: per-site `insecureTLS: true` in `~/.respira/config.json`, or `RESPIRA_TLS_INSECURE=1` per process. Sets `rejectUnauthorized: false` on the client's own HTTPS agent (so it works even when the global `NODE_TLS_REJECT_UNAUTHORIZED` does not propagate through the npx launcher on Windows) and logs a loud warning on boot. `NODE_EXTRA_CA_CERTS` remains the preferred, verification-on fix.
378
+
379
+ ## [6.19.10] - 2026-05-30
380
+
381
+ Setup wizard points to the one-command AI-tool auto-config. `--setup` writes `~/.respira/config.json` (which sites the server talks to) but only printed the Cursor/Claude config for manual paste, so a new user reasonably thought their AI tool was wired up when it was not ("Cursor was not setup, had to do it manually anyway").
382
+
383
+ ### Changed
384
+
385
+ - `printNextSteps` now leads with `npx add-mcp "npx -y @respira/wordpress-mcp-server"`, which auto-detects the AI tool (Cursor, Claude Code, Windsurf, Codex, and more) and writes its config while preserving other MCP servers. The manual JSON is kept as a clearly-labelled fallback, and the copy now explains that setup saved the site config while the AI tool still needs its own entry pointing at the server.
386
+
387
+ ## [6.19.9] - 2026-05-30
388
+
389
+ Clearer first-run setup wizard. A new user who ran `npx @respira/wordpress-mcp-server --setup` before installing the plugin was told the prerequisite was "Respira plugin installed (respira.press/releases)" — but the plugin is not downloadable from /releases, it lives in the dashboard behind a free account. The wizard now states the real prerequisites in order (free account first, then plugin, then API key) and points to the dashboard for the download + license key.
390
+
391
+ ### Changed
392
+
393
+ - `--setup` prerequisites now lead with "a free respira.press account" and explain the plugin download and license key both live in the dashboard.
394
+ - The "not ready" exit message and the in-prompt API-key hint point to signing in at respira.press and downloading from the dashboard, instead of /releases.
395
+ - `--help` Links: plugin location updated to the dashboard download.
396
+
397
+ ## [6.19.8] - 2026-05-29
398
+
399
+ Multi-site write safety. Two layers for the "edit landed on the wrong site" class of problem in multi-site configs, where Claude Desktop omits site_id and doesn't reliably call respira_switch_site, so a write meant for site B silently routes to the default (first) site.
400
+
401
+ ### Added
402
+
403
+ - **`routing_notice` on write responses (default on).** When a write tool runs against the default site because the caller omitted site_id AND more than one site is connected, the response envelope now carries a one-line `routing_notice` naming the site that was actually written to ("Wrote to <name> (<host>) — your default site, because no site_id was given. N sites are connected..."). Makes the wrong-site window visible the moment the write happens instead of after the user notices the wrong page changed. Read tools, single-site accounts, and calls that already pass site_id get no notice. C.J. + T.S. (studioscaler) both reported this symptom.
404
+ - **`RESPIRA_REQUIRE_SITE_ID` strict-scoping flag (opt-in, default off).** When set (`1`/`true`/`yes`/`on`) on a multi-site configuration, a write tool called without an explicit site_id is refused with `respira_site_id_required` instead of silently routing to the default site. The error lists the connected site_id values so the agent retries with the right one in one round. This is the prevention counterpart to the notice above: a notice tells you the write hit the wrong site after it landed; this stops it landing at all. Single-site users and switch_site-based multi-site users are unaffected when the flag is off. Built for T.S.'s multi-tenant Cowork setup where switch_site mutates global state across concurrent sessions and breaks per-session isolation.
405
+
406
+ ### Notes
407
+
408
+ - This is NOT a fix for a broken site_id wiring. Per-call site_id has been honoured by every write tool (including `inject_builder_content`) since v6.12.0 — they all resolve through the same `resolveClient(args)` path, and an unknown site_id throws rather than falling back. The wrong-site case only occurs when site_id is absent from the call (model omission). These two features make that absence visible (notice) and, optionally, fatal (flag).
409
+
410
+ ## [6.19.7] - 2026-05-28
411
+
412
+ Ride-along with plugin v7.0.65. One schema fix in `respira_duplicate_element`.
413
+
414
+ ### Fixed
415
+
416
+ - **`respira_duplicate_element` schema now matches the plugin route contract.** Pre-fix, the tool exposed a single `element_id` arg, but the plugin route `/builder/elements/duplicate/{id}` has always required `identifier_type` + `identifier_value` — same shape as `find_element` / `update_element` / `remove_element`. The mismatch made every duplicate call fail with the WP REST validator error "Missing parameter(s): identifier_type, identifier_value". On builders that assign stable element IDs (Elementor, Bricks, Beaver, Divi 5, Gutenberg) the symptom was a single 400 error per call. On WPBakery / Uncode / Flatsome / Visual Composer pages (no stable element IDs anywhere) the tool was completely unusable. Lucas Young (actorsgym.co.uk) filed a Sentry user-feedback report — his agent fell back to the raw `wp/v2/pages/{id}` content edit route to work around it. Schema now exposes `identifier_type` (id | css_class | text | widget_type | global_id | path) + `identifier_value` directly. Legacy `element_id` arg is preserved as a back-compat alias that auto-translates to `identifier_type:"id"` in the dispatcher.
417
+
418
+ ## [6.19.6] - 2026-05-28
419
+
420
+ Ride-along with plugin v7.0.64. One MCP-side fix in the per-page builder auto-detect.
421
+
422
+ ### Fixed
423
+
424
+ - **Per-page builder auto-detect now reads `active_builder.name`.** The fallback resolver in `extractBuilderContent`, `getPageOutline`, and `getBuilderInlineSchemas` read `info?.name` when `builder` was omitted, but `/context/builder-info` actually returns `{active_builder: {name, version}, builders: {...}, detected_builders: [...]}` — `info.name` was always undefined. On sites with one unambiguous builder, callers were still forced to pass `builder` explicitly. Now reads `info.active_builder?.name`. cspmarketingsolutions report 312f21c9 (Elementor 4.0.3 site where `get_builder_info` worked but `get_page_outline` returned `respira_builder_not_detected`).
425
+
426
+ ### Notes
427
+
428
+ - The companion plugin v7.0.64 bundles five plugin-side fixes; the most important is making `write_was_noop` builder-aware so Elementor / Oxygen / Bricks / Beaver writes stop tripping the noop signal on every successful call. If you're seeing `write_was_noop:true` warnings + retry loops on a meta-based builder, update the plugin to v7.0.64.
429
+
430
+ ## [6.19.5] - 2026-05-27
431
+
432
+ GitHub-issue triage pass on the public `respira-press/respira-wordpress-mcp` repo. Three reports were open; one needed an MCP-side schema fix, two were already resolved by prior releases and just needed an upgrade-path acknowledgment.
433
+
434
+ ### Added
435
+
436
+ - **`respira_update_element` exposes `edit_target` + `confirm_live_edit` at the top level.** [#3](https://github.com/respira-press/respira-wordpress-mcp/issues/3) (madeat3am, 2026-05-21). On a Bricks site with `respira_allow_direct_edit=1`, the first call against a published original returned `status: confirmation_required` with a `next_call_examples.live` payload of `{editTarget, confirm_live_edit: true}`, but the MCP tool schema only exposed `editTarget` — `confirm_live_edit` had nowhere to live, so autonomous flows couldn't complete the handshake through MCP. Schema now exposes both `edit_target` (snake_case, preferred) and `confirm_live_edit` (boolean) as top-level params; the dispatcher translates `edit_target` to the plugin's existing `editTarget` arg. `editTarget` stays in the schema as a deprecated alias for pre-v6.19.5 back-compat. A pollution side-effect was also documented: passing the flags inside `updates` (the only writable slot pre-v6.19.5) caused them to be persisted as element settings; the new `updates` description warns against this explicitly.
437
+
438
+ ### Notes
439
+
440
+ - **[#1](https://github.com/respira-press/respira-wordpress-mcp/issues/1) (`remove_element` parameter shape) was already resolved.** Filed 2026-04-07 against MCP v5.5.0 and plugin v5.5.5. The MCP schema sent `element_id` while the plugin REST endpoint expected `identifier_type` + `identifier_value`. Both ends were realigned in the 6.x line: the MCP schema sends `identifier_type` + `identifier_value` today, and the plugin's `/builder/elements/remove/<post_id>` endpoint expects exactly those. Upgrading to current MCP + plugin resolves.
441
+ - **[#2](https://github.com/respira-press/respira-wordpress-mcp/issues/2) (`move_element` parameter shape) was already resolved.** Filed 2026-04-07 against the same setup. End-to-end fix landed in plugin v7.0.60 (which accepts both `identifier_type/identifier_value` and the legacy `element_id_type/element_id_value`) paired with MCP v6.19.3 (which aligns to the `identifier_*` shape used by find / update / remove). Upgrading to MCP 6.19.3+ and plugin 7.0.60+ resolves.
442
+
443
+ ## [6.19.4] - 2026-05-27
444
+
445
+ Companion release to plugin v7.0.61. Three changes, all driven directly by the `/dashboard/admin/mcp-quality` 7-day signal (12,033 tool calls across the customer base).
446
+
447
+ ### Added
448
+
449
+ - **`wordpress_update_element` surfaces the plugin's new `write_was_noop` warning at the tool-result level.** Plugin v7.0.61 adds `write_diag` + `warning: "write_was_noop"` to the update_element response when `post_content` did not change despite a successful API return. The MCP wrapper now front-loads this signal: `⚠️ NO-OP DETECTED` prefix on the `message` string, plus a new `agent_must_escalate: true` top-level field so MCP clients branching on the response payload see the silent-fail loudly. The update_element tool had an 81% in-session retry rate (2,572 calls/week — top volume), which is the silent-fail signature behind the M.P. Gutenberg blog-post bug class. Agents now have the unambiguous signal to break the loop and escalate to the user instead of retrying update_element.
450
+ - **`wordpress_upload_media` pre-flight size check.** Dashboard showed 50.7% success / p95 = 120 SECONDS / 34.8% timeout >60s. Almost the entire long tail is the agent passing a base64 payload of a large image — the per-call watchdog fires at 120s and rejects without the user knowing. New behavior: hard refusal with a clear `file_too_large` error when the decoded payload would exceed 25MB (saves the 2-minute wait for the inevitable timeout); soft warning embedded in the response when the payload is 5-25MB so the agent can set expectations with the user or retry on a smaller crop instead of looping. Preflight is done client-side from the base64 payload length — no extra network call.
451
+
452
+ ### Changed
453
+
454
+ - **Node floor raised from 18 → 20.** Node 18 went end-of-life 2025-04-30, npm 11 requires Node 20.17+, and `@supabase/supabase-js` already prints a deprecation warning on Node 18. Telemetry shows the median customer Node version is 20.x; keeping 18 supported only kept the deprecation noise alive in agent chats. `engines.node` is `>=20.0.0` and `preflightNodeVersion()` now exits with a clear "upgrade Node, recommended v22" error if invoked on 18 or older.
455
+
456
+ ## [6.19.3] - 2026-05-27
457
+
458
+ Companion release to plugin v7.0.59. Three changes — one of them is the long-running session-wide deadlock M.P. reported on Fischers Fritze.
459
+
460
+ ### Fixed
461
+
462
+ - **Per-client HTTP agents + destroy-on-watchdog-trip.** Pre-v6.19.3 the `WordPressClient` axios instances used Node's shared `http.globalAgent` / `https.globalAgent`. When a write tool hung past `RESPIRA_MAX_TOOL_TIMEOUT_MS` the watchdog rejected the outer `Promise.race` but the orphan axios call kept its socket checked out against the shared pool — subsequent calls that needed a socket to the same host blocked at the agent layer until the orphan finally died. M.P. reported the symptom: one deadlocked `build_page` on Oxygen → every subsequent call including `diagnose_connection` also hung 4 min; only Claude Desktop restart recovered. Two changes: per-client `http.Agent` + `https.Agent` with `keepAlive:false` and `maxSockets:16` so the pool is bounded and owned; new `abortInFlightRequests()` destroys agents on watchdog trip, instantly freeing any orphan socket and rebuilding the pool so the next tool call gets a fresh pool instead of queuing. Doesn't fix WP-side deadlocks (PHP-FPM workers stuck, MySQL row locks) but the MCP no longer amplifies them into session-wide wedging.
463
+ - **`restore_snapshot` polls for completion when the request times out.** Heavy Divi / Oxygen pages can take 1-3 min to restore; pre-v6.19.3 the synchronous POST exceeded axios's 30s timeout even though the server-side restore completed, and the user got a `tool_timeout` envelope with no signal that the restore had landed. Now: bumps the request timeout to 90s, and on timeout polls for the `after_restore` kind snapshot the server writes when restore completes. Up to 6 attempts at 5s intervals (30s budget on top of the 90s request) before giving up and surfacing the timeout. When the marker is found returns a success envelope flagged `slow:true / completed_via_polling:true` so the caller knows restore landed via the slower path. No plugin changes — the marker is the existing `after_restore` snapshot `Respira_Snapshots::restore_snapshot()` already writes.
464
+
465
+ ### Removed
466
+
467
+ - **`wordpress_search_abilities` MCP tool.** Hit `/wp-json/respira/v1/abilities/search`, a route the plugin never registered. Every call 404'd. `wordpress_get_builder_info` + `wordpress_search_docs` cover the discoverable surface; `wordpress_invoke_ability` still works for the inhaled subset.
468
+
469
+ ### Schema
470
+
471
+ - **`wordpress_move_element` + `wordpress_reorder_elements` aligned with the plugin contract.** The MCP schemas shipped param names the plugin handlers never read; the plugin handlers had to be patched in 7.0.59 to accept the legacy keys as back-compat too. New canonical shape: `move_element` takes `identifier_type` + `identifier_value` + `target_container_path` (a dot-path, not a container id) + `position`; `reorder_elements` takes `container_path` + `new_order`.
472
+
473
+ ## [6.19.2] - 2026-05-24
474
+
475
+ Diagnostics + schema polish bundle on top of v6.19.1. All three changes were surfaced by the same `/dashboard/admin/mcp-quality` pass.
476
+
477
+ ### Fixed
478
+
479
+ - **`rest_no_route` errors now carry the attempted path.** Previously, when WordPress returned `rest_no_route` (54 hits across two sites the week of 2026-05-24), the MCP recorded the generic WP string "No route was found matching the URL and request method" without the URL it tried. The admin dashboard had no way to tell whether the miss was a builder-specific subroute, a method mismatch (POST on a GET-only route), or a plugin install gap. `wordpress-client.handleError` now appends `[attempted: <METHOD> <path>]` to the message when `data.code === 'rest_no_route'`, pulled from the axios request config.
480
+
481
+ ### Changed
482
+
483
+ - **`inject_builder_content` description now lists Divi render-critical attrs.** When any of the 10 Divi module types in the validator is missing its required attr (e.g. `divi/heading` without `title`, `divi/button` without `button_text`), the plugin returns `respira_divi_required_attrs_missing` (9 hits the week of 2026-05-24). The tool description now spells out the full map up front so agents pass the right `settings` keys on the first try instead of round-tripping through a 500.
484
+ - **`read_theme_file` description hardened against non-stylesheet attempts.** The schema already enforced `.css / .scss / .less / .json`, but agents kept trying `.php` / `.html` (5 hits the week of 2026-05-24, returning `respira_theme_file_extension_not_allowed`). The description now states up front that PHP and template files are out of scope on purpose and that the human should edit them via SFTP or a code editor.
485
+
486
+ ## [6.19.1] - 2026-05-24
487
+
488
+ Telemetry classification fix surfaced by the `/dashboard/admin/mcp-quality` board after the v6.19.0 release.
489
+
490
+ ### Fixed
491
+
492
+ - **Generic `Error` bucket in tool telemetry.** The dispatch wrapper at `server.ts` recorded `error_code` from `Error.name`, but ~6 tool wrappers (`find_element`, `inject_builder_content`, `upload_media`, `batch_update`, `update_custom_post`, and a handful of others) wrap and rethrow with plain `new Error(...)`, which leaves the JS default name of literal `'Error'`. The admin/mcp-quality dashboard then grouped 37.4% of all failures from the week of 2026-05-24 (293 of 784 errors) into one opaque `Error` bucket. Added a two-stage fallback: (1) if `err.name` is generic, scan `err.message` for an inline `respira_*` / `rest_*` / `http_<status>` / `network_*` code and lift it; (2) otherwise stamp `<tool>_unclassified` so the dashboard groups by tool. Backstop only; existing classification via `wordpress-client.handleError` is preserved unchanged.
493
+
494
+ ## [6.19.0] - 2026-05-24
495
+
496
+ 37-bug QA pass against `mihai.love` running v7.1.0-beta.1 surfaced six MCP-side ship-blockers and a cluster of polish items. This release closes the structured fixes; remaining v7.1 plugin-side work ships from the WP plugin repo.
497
+
498
+ ### Added
499
+
500
+ - **`respira_run_pagespeed_audit` + `respira_analyze_pagespeed`** (Phase E Tier 2). Real PageSpeed Insights v5 audit replaces the heuristic CWV path. `respira_run_pagespeed_audit` returns Lighthouse lab data (scores for performance/accessibility/best-practices/seo, lab metrics FCP/LCP/TBT/CLS/SI/TTI, opportunities ranked by savings_ms, diagnostics) plus CrUX field data (real-user p75 over ~28 days, when available). `respira_analyze_pagespeed` is the analyzer-envelope wrapper that feeds the Reports → Health tab composite via the new `pagespeed` slot. 1h transient cache per (url, strategy); `fresh=true` bypasses. Set `respira_pagespeed_api_key` WP option to lift the 1 req/sec free-tier rate limit to 200 req/min.
501
+ - **`get_accessibility_scan` schema accepts UUID strings.** N15 — pre-fix the input schema declared `scan_id: number` but `list_accessibility_scans` returns UUID strings, so the two tools couldn't be paired.
502
+ - **`list_accessibility_scans` pagination + summary mode.** N16 — added `limit`, `offset`, `summary_only` (default true) params. Summary mode strips `violations[].nodes` and per-violation AI fix prompts; full payload remains via `get_accessibility_scan`. Cuts list response size ~30x.
503
+ - **`get_snapshot include` opt-in.** N26 — defaults to metadata-only (~5KB); pass `include=content` for the full builder payload (~78KB).
504
+
505
+ ### Fixed
506
+
507
+ - **N6: site_id envelope leak.** Every tool response carried `site: <default site>` even when a per-call `site_id` override resolved correctly. The MCP correctly serviced the call against the override but reported the default in the envelope, breaking Cowork multi-tab parallel sessions that branched on the response field. `getActiveSiteSummary(args)` now checks `args.site_id` first and resolves via the site map; falls back to `currentSite` only when no override is present.
508
+ - **N9: schema gaps on 5 destructive tools.** `create_user`, `update_user`, `delete_user`, `delete_custom_post`, `delete_menu` all enforce server-side approval/force gates but the MCP schemas didn't expose the params. Added `approval_token` to all 5; added `force` + `confirm_live_edit` to `delete_custom_post`.
509
+ - **N17: apply_builder_patch vs batch_update schema mismatch.** `batch_update.operations.items` now declares the full per-op shape so agents don't conflate it with `apply_builder_patch`.
510
+ - **N37: delete_page surreptitious confirm_live_edit.** Now documented in the schema.
511
+ - **`get_core_web_vitals` deprecation notice.** Tool description flags the heuristic data source and points at `run_pagespeed_audit` for real measurement.
512
+
513
+ ### Notes
514
+
515
+ - Phase E Tier 2 plugin-side files (the `Respira_PageSpeed_Client` class, REST endpoints, Health tab `pagespeed` slot) ship in the WP plugin v7.1 — needed by the new MCP tools.
516
+ - N11 (analyzer duplicate side effect) flagged for follow-up — needs response shapes from a live analyzer call to root-cause.
517
+
518
+ ## [6.18.7] - 2026-05-23
519
+
520
+ ### Fixed
521
+
522
+ - **B-7: `inject_builder_content` rejects simplified types.** Pre-fix the detector returned `unknown` for payloads using simplified types (`section`, `row`, `column`, `heading`, etc. without `divi/` or `et_pb_` prefix) and hard-failed with "Divi content format could not be detected." When the caller supplies an explicit `divi_version`, we now trust them and let the server-side complexifier handle the mapping. Only mixed payloads (both `et_pb_*` AND `divi/*` in the same tree) and explicit version mismatch remain hard errors.
523
+ - **`{content: [...]}` wrapper unwrap.** Round-tripped extracts come back as `{content: [...]}`. The new `unwrapBuilderContent` helper normalises before forwarding so every downstream caller sees the same shape.
524
+ - **Camrin@cspmarketingsolutions friendly→canonical param translation.** Three structural tools (`move_element`, `duplicate_element`, `reorder_elements`) advertised friendly param names but the REST endpoints required typed pairs. Dispatcher now translates before forwarding. Back-compat: translation only fires when the friendly param is set AND the canonical equivalent is undefined.
525
+
526
+ ## [6.18.6] - 2026-05-21
527
+
528
+ ### Fixed
529
+
530
+ - **Republished `respira-wordpress-latest.mcpb` bundle at 6.18.5.** The .mcpb bundle in `/downloads` had been pinned at MCP 6.11.11 since first publish — ten minor versions stale. Bumped to 6.18.5 and rebuilt the 7.5MB bundle.
531
+ - **Node version preflight on the `--setup` CLI.** Adds a clean "Node ≥20 required; you have v18.x" error instead of letting the MCP boot half-loaded on stale Windows Node installs.
532
+
533
+ ## [6.18.3] - 2026-05-21
534
+
535
+ User on Breakdance 2.7.2 + plugin v7.0.46 + MCP 6.18.2 reported on 2026-05-21 that `respira_scan_page_accessibility` returns `400 Missing parameter(s): url` on every call. Sibling tools (analyze_seo, analyze_readability) work on the same page_id. Diagnosis: the MCP wrapper has always sent `{ page_id }` to the WP endpoint, but the WP endpoint at `/respira/v1/accessibility/scan-page` requires `url` (it runs an axe / WCAG sweep against a public URL, so it needs the permalink, not the post ID). The mismatch was latent since the tool was first added; the report just surfaced it.
536
+
537
+ ### Fixed
538
+
539
+ - **`respira_scan_page_accessibility` wrapper resolves `page_id` to permalink before calling the endpoint.** Calls `getPage(pageId)` first, reads the `url` field off the returned page object, sends both `{ url, page_id, standard }` to `/accessibility/scan-page`. Tool schema also gains an optional `url` override input so callers can pass a staging or preview URL that doesn't map to a Respira `page_id` directly. If both `page_id` is given AND the override is absent AND the page resolution fails (page not found, draft, or no permalink), the wrapper throws a clear error naming the page_id instead of letting the WP endpoint return the cryptic 400.
540
+
541
+ ### Notes
542
+
543
+ - Wrapper-side fix only — no plugin change required. The WP endpoint at `class-respira-accessibility.php` line 138 has always required `url`; that contract is unchanged.
544
+ - Verified by inspection of the type definition (`Page.url` exists on the `getPage()` response).
545
+ - Customer who reported can pull the new MCP server via `npx -y @respira/wordpress-mcp-server@6.18.3` or wait for their AI host to pick up the latest npm on next session.
546
+
547
+ ## [6.18.5] - 2026-05-21
548
+
549
+ The `get_site_context` trace from this morning's mcp-quality investigation pulled out three distinct causes inside the 25%-failure cohort. Two are now properly classified.
550
+
551
+ ### Fixed
552
+
553
+ - **`resolveClient` no-site throw now sets `error.name = respira_no_site_configured`.** Previously the throw name was the literal `Error`, so the admin/mcp-quality dashboard couldn't tell a pre-setup state (user hasn't redeemed a token yet) from a real WP API failure. Same fix on the two site-id branches: `respira_site_id_not_found` and `respira_site_id_filtered`.
554
+ - **`.local` / `localhost` / `.test` URL detection in `WordPressClient.handleError`.** When axios fails on a connection-level error AND the configured site URL is a local dev address, the error name flips to `respira_local_url_unreachable` and the hint explains the MCP server must run on the same machine as the WP install (or behind a tunnel like ngrok / Tailscale / Cloudflare Tunnel). Two unrelated users in the 24h trace hit this; both saw a generic `Error` envelope.
555
+ - **Network errors in general now name themselves `network_<code>`** (`network_econnrefused`, `network_enotfound`, `network_etimedout`, `network_cert_has_expired`, etc.) so the admin dashboard groups them distinctly from WP-level errors. The 5-second consistent latency on `.local` hits — a `network_enotfound` — is now visually distinguishable from a WP timeout that fired at 5s for a different reason.
556
+
557
+ ### Notes
558
+
559
+ - The third cause in the trace was a real auth bug for one customer (token mismatch on `ksmannequin.com`). That's a config issue handled out-of-band, not a code fix.
560
+
561
+ ## [6.18.4] - 2026-05-21
562
+
563
+ R.E. (trial signup at 2026-05-21 00:09 UTC, InstaWP free-tier staging) hit `respira_upload_media` 14 times in 39 minutes. 12 of 14 attempts timed out at the watchdog ceiling (120s); 2 succeeded. He saw the generic `tool_timeout` envelope every time, which named the upload tool and pointed at `RESPIRA_MAX_TOOL_TIMEOUT_MS` but never explained why the upload was actually slow. He stopped using the trial after that session.
564
+
565
+ ### Fixed
566
+
567
+ - **Upload axios timeout default drops from 120s to 90s** so the helpful "Upload timeout: <filename> (<size>KB)..." message fires before the watchdog wins the race. The watchdog envelope is generic; the axios envelope names the file, the size, the PHP-side knobs to check (`upload_max_filesize`, `post_max_size`, `max_execution_time`), and the env var to raise. Existing customers who set `RESPIRA_UPLOAD_TIMEOUT_MS` explicitly are unaffected.
568
+ - **Host-context detection on upload timeout**. The error message now appends a specific recovery hint when the site URL matches a known slow substrate:
569
+ - `instawp.site` → "InstaWP free-tier; try a smaller file, upload via wp-admin > Media, or migrate to a non-free host."
570
+ - `.local` / `localhost` / `.test` → "Local dev address; the MCP server must run on the same machine."
571
+ - `mysites.io` / `wpsandbox.net` / `tastewp.com` → "Free WP sandbox host; CPU + bandwidth caps cause timeouts."
572
+ - These three substrates collectively account for a non-trivial slice of trial-user setups, where the upload-timeout failure mode is bandwidth-cap not WordPress-config.
573
+
574
+ ### Notes
575
+
576
+ - This doesn't fix the underlying free-host bandwidth problem (i can't). It tells the agent (and through it, the customer) exactly what to do next. Worth it.
577
+
578
+ ## [6.18.3] - 2026-05-21
579
+
580
+ (prior release notes from interim ships)
581
+
582
+ ## [6.18.2] - 2026-05-21
583
+
584
+ C.F. (lilox.io) reported on 2026-05-21 that "the MCP stops working after 3-4 tool calls and i need to restart." Studio reproduction plus a sweep of `mcp_tool_events` across the customer base confirmed it: 14 sessions in the trailing 24 hours died in the 1-4 call zone. The dying calls completed successfully with normal duration (700ms-3s, no spike) and the last tool varied across delete_page, get_site_context, list_pages, diagnose_connection, build_page. So the WP plugin and the MCP server's tool processing are not the problem — the issue sits between the MCP server's stdout and the host's stdin.
585
+
586
+ Two contributing causes identified:
587
+
588
+ 1. **Host supervisor reaping the subprocess.** The v6.17.2 in-code comment already documents this failure mode: "The Claude Code / Conductor MCP supervisor sometimes spawns a new server without reaping the previous one, leaving 5+ parallel pairs alive that flap the tool catalog because each one ships a different feature set." Fix lives in the upstream host (Claude Desktop, Cursor); a separate upstream issue is filed.
589
+
590
+ 2. **Tool results exceeding STDIO buffer size.** Studio reproduction caught two tools returning huge payloads in a single result: `respira_list_options` (623 KB) and `respira_get_builder_info` (76 KB). macOS pipe buffer is 64 KB. A single multi-hundred-KB write fragments across many pipe segments. If the host's drain loop falls behind, the MCP write blocks, and most hosts kill the subprocess after a stall.
591
+
592
+ ### Fixed
593
+
594
+ - **Response-size cap.** Every tool result is now serialized + length-checked before returning to the host. Default cap is 100 KiB. Anything above returns a structured truncation envelope with the original size, a per-tool pagination hint (per_page / search / specific accessor), a 2 KB JSON preview, and a `_why:` line explaining the cap. The agent can recover by retrying the tool with the suggested pagination args. Set `RESPIRA_MAX_TOOL_RESULT_BYTES=0` to disable, or to a larger byte count for hosts known to tolerate big payloads.
595
+ - **Per-tool pagination hints** baked in for the worst offenders: list_options, get_builder_info, list_pages, list_posts, list_users, list_media, extract_builder_content, list_plugins. Each carries the exact arg shape to retry with.
596
+ - **Envelope preserves `result.site`** when present so callers that branch on the site shape don't crash on truncation.
597
+
598
+ ### Notes
599
+
600
+ - The truncation envelope shape is additive. Tools that already returned `{ ... }` objects keep their shape under the cap. Above the cap, callers see `{ _truncated: true, _hint, _original_size_bytes, _cap_bytes, ... }`.
601
+ - No upstream-host change shipped here. The supervisor-reaping bug is filed separately with Anthropic.
602
+
603
+ ## [6.18.1] - 2026-05-21
604
+
605
+ The `/dashboard/admin/mcp-quality` rollout on 2026-05-20 surfaced a data-quality gap I had been blind to: every error in `mcp_tool_events.error_code` was the literal string `"Error"`. The dashboard could not tell `respira_no_builder` from `respira_element_not_found` from a connection timeout because they all collapsed to the same name on the wire. Studio reproduction of `wordpress_find_element`'s 50% failure rate, on the back of a 78% in-session re-call rate, confirmed the issue was blocking real diagnosis.
606
+
607
+ ### Fixed
608
+
609
+ - **Error classification reaches telemetry.** `WordPressClient.handleError()` and `formatErrorWithInstructions()` now stamp the WP_Error `code` field (e.g. `respira_no_builder`, `respira_element_not_found`, `respira_post_not_found`) onto `error.name`. The usage emitter reads `error.name` for the `error_code` column, so the dashboard now classifies failures distinctly. Falls back to `http_<status>` when no WP code is present.
610
+ - **Companion to plugin v7.0.43.** The Pro plugin patches `respira_no_builder` and `respira_element_not_found` with `data.instructions` arrays. The MCP server's existing `formatErrorWithInstructions()` already renders that bullet list back to the agent. Combined effect: agents that previously got a one-liner 400 and retried blindly now receive a structured recovery path pointing at `respira_find_builder_targets`, `respira_get_page_outline`, and a `stop_retrying_blind` instruction.
611
+
612
+ ### Notes
613
+
614
+ - Existing customers running 6.18.0 keep working. `error.name = "Error"` was never a contract, just an unobserved default. The change is backward-compatible for any consumer that doesn't rely on the literal string `"Error"`.
615
+
616
+ ## [6.18.0] - 2026-05-20
617
+
618
+ C.L. (Voice Media Group) filed a feature request on 2026-05-20 from the MCP: no Respira tool writes to a theme file on disk. The Customizer Additional CSS workaround (theme_mods + custom_css CPT) works for single-site CSS but doesn't satisfy an agency that source-controls a shared child theme stylesheet across many sites — the file the team's human devs commit lives at `/wp-content/themes/vds/css/custom.css`, and the agent has to be able to write the same file.
619
+
620
+ ### Added
621
+
622
+ - **`respira_read_theme_file` / `respira_write_theme_file` / `respira_append_theme_file`.** Three new tools that talk to the corresponding REST endpoints under `/respira/v2/theme-files/*` shipped in plugin v7.0.42. Each takes a `relative_path` (relative to `wp-content/themes/`, e.g. `vds/css/custom.css`) and the write / append variants take a `content` body. Read returns `content`, `content_md5`, `byte_size`, `mtime`, `theme_role` (active_stylesheet | active_template | other), `is_writable`. Write returns `bytes_written`, `content_md5`, `mtime`. Append returns `appended_bytes`, `total_bytes`, `content_md5`, `mtime`.
623
+
624
+ ### Notes
625
+
626
+ - **CSS-family only, scope-bounded by design.** Allowlist is `css`, `scss`, `less`, `json`. PHP and JS theme writes are intentionally out of scope (separate RCE-class security review queued for a later release). On a write to a `.php` / `.js` path the plugin returns `respira_theme_file_extension_not_allowed`.
627
+ - **Path encoding.** The MCP client base64-url-encodes the relative path before hitting the REST endpoint so slashes don't fight the route pattern. Callers pass a normal relative path string; the encoding is invisible.
628
+ - **Capability + size guards.** Each call goes through the plugin's standard API-key check plus an `edit_themes` capability check on the user behind the API key (Subscriber / Author / Contributor / Editor are blocked even with a valid API key). Hard cap: 1 MiB per file. Post-append final size is also bounded by 1 MiB so a 999 KB existing file + 50 KB append doesn't slip through.
629
+ - **Filesystem-not-writable cases** (managed hosts that mount `wp-content/themes` read-only) return a clean `respira_theme_file_not_writable` 403 with the file path in the message, so the agent can suggest the appropriate next step (Customizer route, or ask the host to make the file writable).
630
+ - **Audit log.** Every successful write or append calls `Respira_Auth::log_action` plugin-side, so the Activity page in WP admin shows the agent's filesystem touches alongside its page edits.
631
+
632
+ ## [6.17.2] - 2026-05-20
633
+
634
+ Visibility patch for two field reports that landed today. No new tools, no new transport behavior — every change is detection + warning so silent failure modes become loud failure modes.
635
+
636
+ ### Added
637
+
638
+ - **RESPIRA_SITES filter visibility.** When the `RESPIRA_SITES` env var is set, the bootstrap stderr now logs the active allow-list and the names of any configured sites it hides. Previously, a stale value baked into `claude_desktop_config.json` by the Cowork connector deeplink would silently filter out sites that were added to the dashboard after install, and the only way to discover this was to read the server source. Origin: aspiresix.com / baymcp.com report 2026-05-19.
639
+ - **`respira_list_sites` returns `hidden_by_filter`.** When the filter hides one or more sites, the tool result now includes a `hidden_by_filter` array (full site summaries) plus a `filter_note` string explaining where to fix it. The AI can now answer "why don't I see baymcp.com?" with the actual reason instead of pretending the site doesn't exist.
640
+ - **Orphan-process detection at boot.** If other `wordpress-mcp-server` processes are running when this one starts, the bootstrap stderr lists their PIDs and recommends `pkill -f wordpress-mcp-server`. The supervisor (Claude Code / Conductor) sometimes spawns a new server on reconnect without reaping the previous one, leaving 5+ parallel pairs alive — each one answering tool calls non-deterministically with a different feature set, which presents as the user-visible "MCP keeps disconnecting" and "tool catalog flapping" symptoms. We can't kill the siblings (they belong to the supervisor), but listing them makes the failure mode obvious. Origin: cekt-ro bug report 2026-05-20.
641
+ - **Global-install warning.** If the running binary path is under `/opt/homebrew/` or `/usr/local/` (a global Homebrew or `npm i -g` install), the bootstrap stderr warns that `npm exec` is ignoring any `@latest` pin in `claude_desktop_config.json` and serving this older binary instead. Suggests `npm uninstall -g @respira/wordpress-mcp-server` (or `brew uninstall`) to unblock. Same cekt-ro report — a customer on `@latest` in config was silently being served `6.14.2` from a homebrew install while the rest of the npm registry chain was at `6.17.1`.
642
+ - **`respira_diagnose_connection` runtime + filter blocks.** Adds two new top-level keys to the diagnose response:
643
+ - `runtime`: `version`, `pid`, `binary_path`, `is_global_install`, `sibling_pids`, `uptime_seconds`, `node_version`. Lets the AI verify it's talking to the version it thinks it is and surface orphan processes from a single tool call instead of requiring shell access.
644
+ - `filter`: `RESPIRA_SITES_active`, `allowed_hostnames`, `hidden_site_count`. Same data exposed by `respira_list_sites` but available to any agent already in the diagnose flow.
645
+ - **Bootstrap stderr includes PID.** `respira-mcp vX.Y.Z ready · pid 12345 · N sites: ...` (was: `respira-mcp vX.Y.Z ready · N sites: ...`). Lets the customer cross-reference the printed PID against `ps -ef` to confirm which exact process is answering tool calls when multiple are running.
646
+
647
+ ### Notes
648
+
649
+ - No client-side fix in scope for v6.17.2. Orphan-process accumulation and the `npm exec` @latest-pin-stripping are both supervisor / package-manager bugs that the server cannot fix directly. The server can only make them visible. Resolving the underlying behavior requires changes in Claude Code / Conductor (process lifecycle) and the `--setup` wizard (global-install detection at install time), both deferred to later patches.
650
+
651
+ ## [6.17.1] - 2026-05-20
652
+
653
+ Documentation-only patch. No code changes.
654
+
655
+ ### Changed
656
+
657
+ - **npm package `description` rewritten** to lead with v6.17.0 (SOUL.md, respira_search_docs, respira_report_issue, 30s diagnose probe default, bootstrap stderr log), then v6.16.0 (telemetry-by-default), then a tighter run through v6.14.0 → v6.0.0. The previous description still led with v6.14.0 in the middle of v6.13.0 / v6.12.0 detail; new release notes were stacking behind a stale opening.
658
+ - **README "What's New" section refreshed.** Old block led with v6.0 Storefront context-aware filtering; new block leads with v6.17.0 (SOUL.md + the three new tools + triage rule), then v6.16.0, then v6.3, then v6.0 in that order. Comparison table and the rest of the README untouched.
659
+
660
+ ## [6.17.0] - 2026-05-19
661
+
662
+ Customer-driven release responding to a stream of bug reports from D.F. about diagnose probes timing out at 15s on sites that curl handles in under 4s, and "I have no idea if respira-mcp came back after I restarted Claude." Three friction fixes plus the long-awaited self-service bug-reporting surface AND a docs-first triage path so most "bugs" never need to be filed.
663
+
664
+ ### Added
665
+
666
+ - **SOUL.md — agent persona + rules of engagement loaded at every handshake.** Every AI agent that connects to the Respira MCP now reads SOUL.md before its first tool call. Defines identity (single-builder product, no team, no support tier), voice (lowercase "i" first person, no "we", no em dashes, no exclamations, no phone-call offers), values (privacy hard line, builder-native edits only, dogfood-first, honest math, respect the customer's time and tokens), rules of engagement (always run get_builder_info before structural edits, always snapshot before destructive ops, never write to wp_postmeta or theme PHP, beautiful + accurate + fast in that order), and how to handle uncertainty + creative work. The file is mirrored at https://www.respira.press/soul.md for outside-MCP integrations (Cursor system prompts, Claude Projects, GPT custom instructions).
667
+
668
+ - **`respira_search_docs` tool.** Full-text search over the Respira documentation corpus (mirrored from webmyc/Respira.press-Documentation-and-Community). The agent calls this BEFORE offering to file a bug — most "bugs" are documented known issues with workarounds (Cloudflare allowlists, builder-specific gotchas, the rest_route fallback, Divi 5 preset 404s). Hits the new public endpoint at https://www.respira.press/docs-search backed by Postgres FTS via a generated tsvector. Returns top N results with title, excerpt, and URL. Public, unauthenticated, free to call. Pairs with a nightly cron at /api/cron/docs-index that refreshes the corpus from the GitHub repo.
669
+
670
+ - **`respira_report_issue` tool.** File a structured bug report from inside the AI chat. The agent calls this with title/brief/severity/steps_to_reproduce/expected/actual/error_messages/last_tool; the MCP server auto-attaches site URL, builder name + version, MCP server version, OS. Optional `diagnostics` arg lets the agent pass a prior `respira_diagnose_connection` result for the maintainer to inspect. Posts to https://www.respira.press/mcp/report-issue and lands as a bug report at `/dashboard/bug-reports`. Auth reuses the same 3-tier bearer chain as the usage emitter (RESPIRA_USAGE_TOKEN env → active site api_token → first site api_token). Privacy hard line: prompts, tool args, tool results, WP content never enter the payload — only the structured fields the agent puts in.
671
+
672
+ ### Changed
673
+
674
+ - **`respira_diagnose_connection` probe timeout: default 30000ms (was 15000ms).** New `probe_timeout_ms` arg, clamped to [5000, 60000]. Sites behind Cloudflare custom rules routinely cross 15s on first-hit HEADs while curl from the same machine returns in under 4s. The old 15s default produced spurious "MCP timed out but the site is fine" reports.
675
+
676
+ - **`tool_timeout` error hint expanded.** The structured `tool_timeout` response now also points to `respira_diagnose_connection` (to triangulate which network layer is slow) and `respira_report_issue` (to file a structured bug report) so the agent doesn't have to ask the user what to do. Previously the only hint was "raise RESPIRA_MAX_TOOL_TIMEOUT_MS."
677
+
678
+ - **Bootstrap stderr log includes site list.** The line printed on `await server.connect(transport)` now reads `respira-mcp vX.Y.Z ready · N sites: hostname1, hostname2, ...` instead of the generic `running on stdio`. Tells the customer at a glance whether the MCP came back after a Claude restart and which sites it loaded.
679
+
680
+ ### Operational impact
681
+
682
+ The bug-reports surface lives at https://www.respira.press/dashboard/bug-reports — paying customers can vote on bugs (signals severity), file new ones with attachments (markdown logs, screenshots), and see their own pending reports. Reports filed via `respira_report_issue` from inside an AI chat carry a "from agent" badge in the feed and the structured diagnose snapshot if the agent ran one.
683
+
684
+ Updated agent triage rule, baked into the MCP server's handshake instructions: tool error → apply structured hint + retry once → respira_search_docs (NEW) → respira_diagnose_connection → ask user to file bug → respira_report_issue. The docs step routes most "bugs" to a one-read answer before they ever reach the maintainer.
685
+
686
+ ## [6.16.0] - 2026-05-19
687
+
688
+ Usage telemetry now ships by default for every customer on v6.16.0+, not only those who set up an OTEL bearer at /dashboard/settings/earn. Audit of the live data on 2026-05-19 showed only 1 distinct user shipping MCP events despite ~41 sites running v6.14.0+ — the entire pipeline was gated on a setup step almost nobody had done. This commit closes that gap.
689
+
690
+ ### Changed
691
+
692
+ - **`usage-emitter.ts` — 3-tier auth (OTEL bearer → per-site license api_token → off).** The emitter now accepts per-site api_token registrations via `registerSiteToken(siteUrl, apiToken)`. The MCP server iterates each configured site at boot and registers its `siteConfig.apiKey` automatically — no customer action. At flush time the emitter groups queued events by their resolved bearer and posts each group with the right Authorization header (typically just one group in the single-site or single-OTEL case). Events without a site_url ride along on the first registered token. `RESPIRA_USAGE_OPT_OUT=1` still turns the emitter into a hard no-op regardless of available tokens.
693
+
694
+ - **`/api/mcp-spend/track` (server-side) — accepts api_token bearers.** The endpoint now recognises two token shapes: `rp_otel_*` (resolved against `ai_spend_tokens` as before) and anything else (resolved against `sites.api_token`, with user_id + license_id coming from the licenses table). Events authenticated via api_token land with `token_id=null`; the column dropped its NOT NULL constraint in migration `20260519_mcp_tool_events_token_optional.sql`.
695
+
696
+ ### Privacy posture (unchanged)
697
+
698
+ Same hard line as v6.14.0: only metadata leaves the customer machine — tool name, read/write classification, site URL, duration, success/error class, occurred_at, mcp_session_id. Tool args, tool results, and any WP content are never even part of the contract. `RESPIRA_USAGE_OPT_OUT=1` disables the entire emitter.
699
+
700
+ ### Operational impact
701
+
702
+ For the next ~weeks as customers `npx -y` their way onto v6.16.0+, the MCP quality dashboard at `/dashboard/admin/mcp-quality` will start showing real cross-customer data instead of just the founder's own activity. Regressions surfaced there auto-fire Slack alerts via the cron at `/api/cron/mcp-quality-alerts`.
703
+
704
+ ## [6.15.0] - 2026-05-18
705
+
706
+ Reliability hardening pass. Driven by a structured bug report from a ~6h cekt.ro session that observed the MCP stdio channel flapping every 10–30 minutes — disconnect, then 30s–2min before tools reappeared. Three independent fixes that together close the most common flap sources, plus a defensive timeout on the v6.14.0 usage emitter.
707
+
708
+ ### Changed
709
+
710
+ - **`WordPressClient` — exponential-backoff retry on transient failures.** The axios response interceptor now retries up to 3 times (200ms / 600ms / 1800ms) on either an HTTP 5xx response or a transient connection error (`ECONNRESET`, `ETIMEDOUT`, `ECONNABORTED`, `ENETUNREACH`, `EHOSTUNREACH`, `EAI_AGAIN`, `EPIPE`, `ECONNREFUSED`). Pre-v6.15.0 a single 500 from the customer's WP host bubbled through to the tool handler, took down the in-progress call, and frequently correlated with the MCP stdio session disconnecting moments later (cekt.ro hypothesis #2). Retries are tagged via `_respiraRetryAttempt` on the axios request config so a 4th re-entry into the interceptor falls through to the existing `handleError` path; no infinite loops. Idempotent reads benefit most; mutating writes against a 500-returning endpoint will also retry, which is safe because the WP endpoints are themselves transactional — either the operation completed and the next call returns the persisted state, or it didn't and the retry is harmless. Cache-revalidation, no behavioural change for any call that succeeded on the first attempt.
711
+
712
+ - **`index.ts` — top-level panic boundary.** Pre-v6.15.0 the `uncaughtException` handler logged the error and then `process.exit(1)`, taking the entire MCP stdio session down whenever any unexpected throw escaped a handler. The unhandled-rejection handler re-threw, which routed back to the same exit path. From this version onwards both handlers log loudly to stderr (with full stack) and the process continues. EPIPE (client disconnected) and npx cache corruption (broken install) still exit because those have no recovery; everything else is absorbed. Per-tool-call state in the MCP server is independent (each call resolves its own client, its own retry counter, its own emitter record), so absorbing a thrown exception in one handler doesn't corrupt the dispatch of the next call. Closes cekt.ro hypothesis #1.
713
+
714
+ - **`usage-emitter.ts` — explicit 3s `AbortSignal.timeout` on the dashboard POST.** v6.14.0 shipped the per-tool-call usage telemetry with an unbounded `fetch()` — if the customer's machine couldn't reach `www.respira.press/mcp-spend/track` for any reason (DNS, captive portal, corporate proxy), the fetch promise hung indefinitely. The cekt.ro bug report flagged this as a suspect surface (hypothesis #5). A 3s abort caps the worst case; the next flush retries with the next batch.
715
+
716
+ ### Notes
717
+
718
+ - The 30-call parallel `respira_delete_comment` batch dropping 28/30 calls (cekt.ro report) is NOT addressed by this release. That's likely either an MCP SDK request-multiplexer limit or a WordPress 6.4-side batch endpoint bug — needs targeted repro on Studio. Queued for v6.15.1 or v6.16.0 depending on root cause.
719
+ - Stdio heartbeat / `$/disconnect` notification on intentional exit are also queued for a later release. They require MCP protocol-level work; this release is purely server-side reliability.
720
+ - Pairs with no plugin release. Safe to upgrade independently; existing clients keep working without a restart, though they need a restart to actually pick up the new build.
721
+
722
+ ## [6.14.2] - 2026-05-17
723
+
724
+ Pairs with plugin v7.0.33. One real schema change plus the matching client wiring — `wordpress_delete_page` is finally agent-completable.
725
+
726
+ ### Added
727
+
728
+ - **`wordpress_delete_page` — `approval_token` param.** Pre-v6.14.2 the schema only exposed `id` and `force` even though the plugin returned `respira_approval_required` with a fresh `approval_token` on every blocked call. Agents could see the token in the response body but had no way to pass it back, so every agent-driven cleanup of its own duplicates got stuck waiting for a human to delete via wp-admin. The schema now accepts an optional `approval_token` string; `deletePage()` threads it through to `DELETE /pages/{id}?approval_token=<value>`. Mirrors the two-step approval flow `respira_update_plugin` has had since v6.11.10. Reported by Pablo Licheri / Sándor Oláh on cekt.ro 2026-05-17.
729
+
730
+ ## [6.14.1] - 2026-05-17
731
+
732
+ Pairs with plugin v7.0.32. Description-only updates on two existing tools — no schema changes, no new tools. Same MCP transport contract; safe to upgrade without restarting clients.
733
+
734
+ ### Changed
735
+
736
+ - **`wordpress_build_page` description + `structure` schema description** — now documents the canonical nested `children: [...]` shape with a full Divi 5 example (section → row → column → heading + text), names the accepted Divi-vocabulary aliases (`rows / cols / columns / modules / elements / innerBlocks`), and mentions the plugin-side `respira_children_dropped` hard error that catches silent drops.
737
+ - **`wordpress_inject_builder_content` description** — same shape documentation appended (canonical example + alias list + drop guard). Pre-v6.14.1 the schema gave only a flat single-section example and a generic "Use extract_builder_content to see the format" pointer, which left agents guessing on nested payloads.
738
+
739
+ ## [6.14.0] - 2026-05-17
740
+
741
+ Opt-in per-tool-call usage telemetry to power the new per-site / per-tool cost breakdown on the customer dashboard at `/dashboard/settings/cost-and-rates`. Pairs with the dashboard-side `mcp_tool_events` table + `/mcp-spend/track` ingest endpoint shipped the same day.
742
+
743
+ ### Added
744
+
745
+ - **`usage-emitter.ts` — per-tool-call telemetry batcher.** Times every `handleToolCall` invocation, classifies the tool as `read` / `write` from its name prefix, and batches up to 50 events (or every 5 seconds, whichever first) to `https://www.respira.press/mcp-spend/track`. Payload is metadata only: `tool_name`, `tool_kind`, `site_url`, `duration_ms`, `success`, `error_code`, `occurred_at`, `mcp_session_id`. Tool args, tool results, and WP content are NEVER part of the contract and not even available to the module. Auth reuses the same `rp_otel_*` bearer the customer already pasted into their Claude Code OTEL snippet, auto-discovered from the `OTEL_EXPORTER_OTLP_HEADERS` env var so most customers need zero new config; the explicit `RESPIRA_USAGE_TOKEN` env var overrides if the customer's OTEL setup doesn't fit the standard shape. The session id is a `crypto.randomUUID()` regenerated per MCP process lifetime. Failure modes are silent — a single stderr warn on first 401, dropped batches on network errors, hard queue cap of 500 events so a long-running session with an unreachable endpoint can't grow unbounded. Dispatch correctness is never blocked on telemetry: the wrapping `try/finally` records both successes and failures without rethrowing into the dispatch path.
746
+
747
+ - **`RESPIRA_USAGE_OPT_OUT=1` env var.** Disables the emitter entirely. The MCP process still works in every other respect; only the dashboard cost-attribution feed stops.
748
+
749
+ ### Why
750
+
751
+ The customer-facing Cost & rates dashboard already shows aggregate Claude Code AI spend via OpenTelemetry (`ai_spend_events`). What it can't show is *which WP site* the spend went to or *which tool category* drove it, because Claude Code's OTLP exporter only attaches `model` + `session.id` — it has no idea respira_update_element ran against site A vs respira_find_element ran against site B. This release closes that gap by phoning home one metadata record per tool call, cross-joined to OTEL turns server-side by `(user_id, time bracket)` and weighted by `tool_kind` to attribute dollars per site / per tool.
752
+
753
+ ### Privacy posture
754
+
755
+ Hard line: no tool args, no tool results, no WP content of any shape ever leave the customer's machine via this channel. The emitter receives only the canonical tool name and the wrapper's own timing data — it has no read path to the args object or the result object. The site URL is the only customer-identifiable string in the payload; opt out with `RESPIRA_USAGE_OPT_OUT=1` if even that's too much.
756
+
757
+ ## [6.13.0] - 2026-05-17
758
+
759
+ Pairs with plugin v7.0.31. Two new MCP tools + a pagination upgrade to `wordpress_find_builder_targets`, all driven by the cekt.ro feature request (Uncode 2.9.4.7 + WPBakery 8.7.1.2).
760
+
761
+ ### Added
762
+
763
+ - **`wordpress_get_page_outline`** — row-level outline of a builder page. Returns one entry per top-level row with `{ index, type, kind, primary_heading, child_count, child_types }`. Lighter than `extract_builder_content`; designed for the common "what is the structure of this page" read that previously forced agents to call `find_builder_targets` (truncated at 200) + save to file + `jq` through it client-side. Works on every supported builder via a generic walker; adapters with a dedicated outline implementation (WPBakery + Uncode today) return a richer child-type histogram. `builder` param optional — auto-detects from the active site builder when omitted.
764
+ - **`wordpress_get_builder_inline_schemas`** — per-shortcode / per-block attribute schemas for the requested builder. Currently populated for WPBakery + Uncode + TagDiv via `vc_map()` at runtime (cached as a transient for 1h plugin-side). Optional `types` filter to fetch only specific shortcode schemas. Other builders return `schemas: {}` + `supported: false` + a structured hint.
765
+
766
+ ### Changed
767
+
768
+ - **`wordpress_find_builder_targets` — pagination + populated labels.** Three changes: (1) `total_matches` is now the true full-tree match count, independent of `limit` — pre-v6.13.0 the walker bailed out as soon as `results.length` hit `limit`, so `total_matches` always equalled `limit` and callers had no way to detect truncation; (2) new `offset` parameter + `has_more` boolean + `next_offset` integer in the response so callers can paginate without re-walking; (3) the makePreview walker now reads `node.text`, `node.attributes` (alongside `node.settings`), and a wider set of attribute keys (`btn_title`, `alt`, `caption`) so WPBakery + Uncode targets surface populated previews + `admin_label` + `text` without forcing a custom client per builder. The Divi 5 `_nodeId` fallback also lands here — targets without a top-level `id` now get the `_nodeId` from `attributes._nodeId` / `attrs._nodeId` in their `id` slot so agents can round-trip the id back into `update_element`.
769
+
770
+ ## [6.12.0] - 2026-05-16
771
+
772
+ T.S. (studioscaler) reported on 2026-05-16 that switching the active site in one Cowork chat contaminated every other Cowork chat connected to the same MCP server instance. Root cause: `respira_switch_site` mutated the shared in-memory `currentSite` on the MCP server process, and Cowork shares one MCP server process across all of its chats. Last switch globally won, so a developer juggling three sites across three chats kept landing tool calls on whichever site they'd most recently switched to anywhere. v6.12.0 closes the contamination by making every tool call self-describing: a new optional `site_id` parameter on every non-agnostic tool schema lets each call pin its own target site without touching the global `currentSite`. Non-breaking: existing callers that don't pass `site_id` keep working identically.
773
+
774
+ ### Added
775
+
776
+ - **Optional `site_id` parameter on every tool schema.** Auto-injected by `getTools()` into all 100+ tools before `generateDualTools()` so both `wordpress_*` and `respira_*` aliases get it. When present, the call uses that site for this single tool invocation only. When absent, falls back to `this.currentSite` as before. Excluded by design from `list_sites`, `get_active_site`, `switch_site`, and `redeem_token` because those operate on global state (the `SITE_AGNOSTIC_TOOLS` allowlist).
777
+ - **`resolveClient(args)` helper on RespiraMCPServer.** Returns the `args.site_id`-resolved `WordPressClient` when supplied (and in this MCP configuration group), else falls back to `currentSite`, else throws a clean structured error listing the available site IDs. Used by `dispatchToolCall` to hoist client resolution once per call at the top of the switch instead of reading `this.currentSite` at every dispatch site.
778
+
779
+ ### Changed
780
+
781
+ - **`dispatchToolCall` rewritten to use the per-call resolved client.** 131 references to `this.currentSite.X(args)` inside the dispatcher rewritten to `client.X(args)`. `client` is computed once at the top of the function via `SITE_AGNOSTIC_TOOLS.has(name) ? this.currentSite : this.resolveClient(args)`. The result: every non-agnostic tool call routes to whichever site the caller named on this call, and no shared `currentSite` mutation ever happens for `site_id`-passing callers.
782
+ - **`handleToolCall` no longer requires a pre-configured `currentSite`.** The pre-v6.12.0 eager `if (!this.currentSite) throw new Error('No WordPress site configured')` guard at the top of `handleToolCall` is removed. Site resolution now happens per-call inside `dispatchToolCall`, so a server that has multiple sites loaded but no `default: true` site can still accept tool calls when each call carries its own `site_id`. Existing single-site / default-site setups behave identically because `resolveClient` falls back to `currentSite` when no `site_id` is passed.
783
+
784
+ ### Notes
785
+
786
+ - This is the Cowork multi-chat fix. Each Cowork chat can now instruct its agent: "always pass `site_id: <X>` on every tool call" (system prompt, or per-message). The agent's tool calls then route exclusively to site X regardless of which other site a parallel chat happens to be working on at the same moment. The shared `currentSite` mutation pattern (one chat calls `switch_site`, all chats see the switch) is fully avoided.
787
+ - Backwards compatible by construction. Every existing caller that relies on the implicit-`currentSite` pattern keeps working without any change. The `site_id` parameter is optional and additive; older agents that haven't learned about it will simply not pass it.
788
+ - Recommended for any customer running Cowork against 2+ WordPress sites concurrently. Solo-site setups don't need the upgrade for correctness, but the parameter is available for future flexibility.
789
+
790
+ ## [6.11.13] - 2026-05-13
791
+
792
+ Companion release to plugin **v7.0.16**. Four customer reports drive both releases. (1) Mihai on the customer site: Cursor's agent hit "API error (404): No route was found matching the URL and request method" on `wordpress_read_page` / `wordpress_find_element` for Divi 4 custom-post-type pages, plus a separate "Builder undefined not supported" error on `wordpress_extract_builder_content` when the agent skipped the `builder` arg. (2) N.M. on the customer site: `wordpress_update_element` reported success but writes never surfaced on the live page, while the workaround using `wordpress_update_module` with `editTarget: "live"` worked correctly. (3) P.S. on the customer site: `build_page` produced empty Beaver Builder Row shells (typed-node normalizer bug, fixed plugin-side in 7.0.16) and `inject_builder_content` silently overwrote existing content because `mode` defaults to `replace` with no confirmation gate (fixed plugin-side with a 409 `respira_replace_confirmation_required` and a `confirm_replace` flag, which this release threads through the MCP tool surface). (4) K.B. on the customer site: after upgrading the MCP server, every version-introspection call kept reporting v6.11.4 even though npm had pulled the latest. The constant was hand-bumped at the 6.11.4 release and never tracked subsequent publishes. Plus a separate Bricks `update_element` silent-success bug — same plugin-side fix as the broader settings-patch normalisation in 7.0.16. Plugin-side CPT, editTarget, normalizer, confirm_replace, and update_element settings-patch fixes live in 7.0.16. This release covers the five MCP-side surfaces that pair with them.
793
+
794
+ ### Changed
795
+
796
+ - **`wordpress_extract_builder_content` auto-detects the active builder when `builder` is omitted.** Pre-6.11.13 the schema marked `builder` as required (`required: ['builder', 'page_id']`) and the WP-side dispatcher then rejected `wordpress_extract_builder_content({ page_id })` with `WordPress server error (500): Builder "undefined" not supported`. The agent had to know the builder name in advance, which broke the documented "Use get_builder_info to discover the active builder first" workflow because the discovery step wasn't actually wired in. Now `builder` is optional: when missing, `WordPressClient.extractBuilderContent` calls `/context/builder-info` first and uses the returned `name` (lowercased) as the route segment. If detection fails (no builder reported, /context/builder-info request errors), the call returns a structured `success: false` envelope with `errorCode: 'respira_builder_not_detected'` and a hint to pass `builder` explicitly, rather than hitting the WP route with an `undefined` segment and getting the cryptic 500. Tool description now also calls out that it works on any post type (pages, posts, custom post types) — pairs with the plugin v7.0.16 CPT generalisation. Reported on the customer site (Divi 4 financial calendar pages, page IDs 82599 + 81974, both CPT not standard `page` post type).
797
+ - **`wordpress_inject_builder_content` schema exposes `confirm_replace`.** Matches the plugin v7.0.16 confirmation gate. When the page already has content and the caller asks for `mode="replace"` (or doesn't pass `mode`, since replace is the default), the plugin returns 409 `respira_replace_confirmation_required`; the agent must either re-send with `mode="append"` (preserve existing) or with `mode="replace"` AND `confirm_replace=true` (overwrite). The wordpress-client forwards `confirm_replace` on every inject call; the tool description spells out the gate so agents understand the new failure mode instead of treating the 409 as an unrelated server error. Reported by P.S. on the customer site 2026-05-12 — a `build_page` of two rows followed by an `inject_builder_content` to add a third row deleted the first two without warning.
798
+ - **`wordpress_update_element` schema exposes `editTarget`.** Matches the plugin v7.0.16 element-ops change. `editTarget=duplicate` (the default) routes the write to the Respira duplicate of the post (auto-creates one if needed); `editTarget=live` writes straight to the published original when direct-edit is enabled in Respira settings or the post is a draft / existing duplicate. The WP-side response now always carries `target_id`, `original_id`, `edit_target`, `is_duplicate`, `duplicate_created`, `post_status`, and `post_type`; the MCP tool description calls this out so the caller can never mistake a duplicate-routed write for a live-page change. Reported by N.M. on the customer site 2026-05-05.
799
+
800
+ ### Fixed
801
+
802
+ - **Server self-identifies as the actual installed version instead of a stale literal.** Pre-6.11.13 the MCP handshake, the `instructions` block, and the version-checker all read from `RespiraWordPressServer.MCP_SERVER_VERSION = '6.11.4'`, a hand-bumped constant that hadn't been touched since the 6.11.4 release. After K.B. upgraded the MCP via `npx add-mcp` to v6.11.12, every "what version are you running" call from the agent still returned `6.11.4`, which made diagnostic triage misleading (the agent thought it was eight releases behind and recommended an upgrade that had already happened). The constant now reads from `package.json` at module load via the same helper pattern already used by `MCP_CLIENT_VERSION` in wordpress-client.ts. Falls back to `'unknown'` only when `package.json` is unreadable or doesn't expose a `version` string — no behaviour change on a healthy install. Reported by K.B. on the customer site 2026-05-13.
803
+
804
+ ### Added
805
+
806
+ - **`wordpress_diagnose_connection` now detects edge-layer write blocks.** New probe issues `OPTIONS /wp-json/respira/v1/ping` alongside the existing GET probes; when GET returns 2xx but OPTIONS returns 4xx/5xx, the diagnostic surfaces a `write_method_blocked: true` flag and a layer-specific recommendation. If Cloudflare is in front of the site (cf-ray header present), the recommendation includes the literal CF expression `and not (http.request.uri.path contains "/wp-json/respira/")` to append to the Custom Rule that matches non-GET methods. For Wordfence sites, the recommendation points at Wordfence > Firewall > Whitelisted URLs. For other edge layers, the recommendation lists the common origin-firewall culprits (Apache `<LimitExcept GET POST>`, ModSecurity, custom WAF rules). Caught the customer's exact failure mode in retrospective testing: a Cloudflare custom rule named "Bad Bot - Action Block" had an OR condition blocking PUT, PATCH, DELETE, OPTIONS, and PURGE for every URI path, including `/wp-json/respira/`, so every Respira write failed at the edge with no plugin-side trace. N.M. spent five days on the report before identifying the CF rule — the diagnostic now flags this on the first run.
807
+
808
+ ## [6.11.12] - 2026-05-12
809
+
810
+ Companion release to plugin **v7.0.15**. The plugin-side fixes (Connect Automatically, install_plugin fatal, hidden-page deprecations) ship inside the WordPress plugin and reach customers on plugin update. This release covers the one fix that lives on the MCP side.
811
+
812
+ ### Fixed
813
+
814
+ - **`wordpress_delete_page` returns the WordPress response body and auto-sends `confirm_live_edit=true`.** `WordPressClient.deletePage` was typed `Promise<void>` and `await this.client.delete(...)` discarded the response. So when the WP handler returned the `respira_live_edit_confirmation_required` error (because the MCP only sent `force=true`, never the second `confirm_live_edit=true` flag the handler requires for direct deletion of original/non-duplicate posts), the caller saw no error — just the MCP harness's `withSiteContext(undefined)` wrap (`{site:…}`). To the caller it looked like a successful no-op delete: 200, site metadata, page still there. D.D. on the customer site reported this 2026-05-12 — three `respira_delete_page` calls (force=true on each) all returned `{site:…}` with no error, while subsequent `respira_list_pages` confirmed every page still present. Fix changes the return type to `Promise<any>` and returns `res.data`, and adds `confirm_live_edit=true` to the DELETE params whenever `force=true` is passed. Now the caller sees the real WP response — `success: true` on completion, the WP_Error code+message on failure, or `respira_approval_required` (202) when the page hasn't been pre-approved through the standard Respira approval flow.
815
+
816
+ ## [6.11.11] - 2026-05-11
817
+
818
+ ### Added
819
+
820
+ - **`extractServerErrorDetails` now surfaces validator-rule errors and warnings.** Pre-6.11.11 the extractor read `data.exception_type`, `data.exception_message`, `data.hint`, and `data.diagnostics`, but ignored `data.errors` and `data.warnings`. The companion Respira for WordPress 7.0.9 changes the Oxygen inject `WP_Error` envelope to ship the actual validator rules in `data.errors` — this extractor reads them as `validator_errors=<rule1>; <rule2>;...` (and `validator_warnings=...` for the non-blocking ones) in the `Debug details` line of every 500 response. Reported by M.P. across 5 inject variants against the customer site on 2026-05-11. Detail line cap raised from 520 to 800 chars to fit the validator list.
821
+ - **`rule_count` surfaced when present** so callers see `rule_count=3` alongside the rule list — useful for quick "did this fail one check or twenty?" triage.
822
+
823
+ ### Fixed
824
+
825
+ - **UTF-8 BOM no longer breaks config load on Windows.** PowerShell's `Set-Content -Encoding UTF8` (the obvious incantation for writing JSON on Windows) silently writes a BOM, and `JSON.parse` rejects the resulting file with `Unexpected token ''`. The server logged this to `~/.respira/last-startup-error.txt` but the boot still failed with no useful tool-call surface (every call returned `MCP error -32603: No WordPress site configured`). v6.11.11 strips the BOM in `readConfigFromPath` and `readConfigFromBase64` before parse — idempotent no-op when the BOM isn't present. Reported by M.P. as Cowork bug B2.
826
+
827
+ ## [6.11.10] - 2026-05-11
828
+
829
+ ### Fixed
830
+
831
+ - **Plugin tool schemas now expose `approval_token`** so MCP clients can complete the second-step approval handshake without leaving the tool layer. Pre-this release the schemas for `wordpress_install_plugin` / `activate_plugin` / `deactivate_plugin` / `update_plugin` / `delete_plugin` only declared `slug` (or `slug_or_url`), so when the first call returned `respira_approval_required` with a token, the client had no way to echo that token back through the same tool. The result was an unfinishable handshake — users had to fall back to direct REST calls. Reported by Mihai on 2026-05-11 against urbankid.ro.
832
+
833
+ Each plugin tool now accepts an optional `approval_token`. The dispatch in `handleToolCall` forwards it through to the WordPress client; the client appends it to the POST body so the plugin's approval guard can validate against the same merged payload it used to mint the token. Tool descriptions updated to document the two-step handshake explicitly.
834
+
835
+ Companion fix on the WordPress side (Respira for WordPress 7.0.4, shipping today) makes the approval guard merge `$request->get_url_params()` with body params before signing the token. Together the two releases close the loop.
836
+
837
+ ## [6.11.9] - 2026-05-11
838
+
839
+ ### Changed
840
+
841
+ - **`detectClientFromHeuristics` now distinguishes Claude Desktop from Claude Code.** Previously every `claude*` parent process collapsed to `claude-code` in the leaderboard. The two run the same npx binary under STDIO, but the env they spawn it under is different: Claude Code CLI sets `CLAUDECODE=1` and `CLAUDE_CODE_ENTRYPOINT`, while Claude Desktop spawns from inside `Claude.app` (macOS) / `Claude.exe` (Windows) and exports neither. New heuristic order: cowork → cursor → CLAUDECODE-or-CLAUDE_CODE_ENTRYPOINT env (→ claude-code) → parent process is Claude.app/Claude.exe (→ claude-desktop) → fallback `claude` substring (→ claude-code). `detectVersion('claude-desktop')` reads `CLAUDE_VERSION`.
842
+
843
+ After this release, the AI-agents leaderboard splits the existing Claude Code bucket into two distinct cards. Pre-release Desktop traffic stays attributed to `claude-code` since the underlying `agent_client` column in `usage_stats` keeps its historical values.
844
+
845
+ ## [6.11.8] - 2026-05-11
846
+
847
+ ### Fixed
848
+
849
+ - **`respira_redeem_token` was getting routed to the WordPress site as `wordpress_redeem_token`** instead of being handled at the MCP-server level where the config write happens. Surface: `Unknown tool: wordpress_redeem_token` returned by the WordPress plugin, no config written, no sites connected. Reported by Mihai (founder) on 2026-05-11 from a live Cowork session against mihai.love.
850
+
851
+ Root cause: `normalizeToolName` rewrites every `respira_*` call to `wordpress_*` before dispatch (so the existing `wordpress_*` switch cases catch both naming styles). The new `respira_redeem_token` tool was declared with name `respira_redeem_token` directly, breaking that convention — `generateDualTools` only auto-aliases tools whose declared name starts with `wordpress_`, so no canonical `wordpress_redeem_token` switch case existed. The early-route guard in `setRequestHandler` also compared against `respira_redeem_token` after the name had already been normalized to `wordpress_redeem_token`, so it never fired. The call fell through to `currentSite.callTool` and got forwarded as a normal WordPress REST request.
852
+
853
+ Fix:
854
+ - Renamed the tool declaration to `wordpress_redeem_token`. `generateDualTools` now creates the user-facing `respira_redeem_token` alias automatically (same shape every other dual-named tool uses).
855
+ - Early-route guard now compares against the canonical name `wordpress_redeem_token` so it fires correctly when no site is configured (bootstrap mode).
856
+ - Switch case in `handleToolCall` also uses the canonical name so re-redemption works after a site is already connected.
857
+
858
+ ## [6.11.7] - 2026-05-11
859
+
860
+ ### Fixed
861
+
862
+ - **TypeScript build failure on `mcp-v6.11.6` blocked the npm publish.** The bootstrap-mode `RespiraConfig` literal omitted the required `preferences.autoDuplicate` and `preferences.securityChecks` fields; `tsc` failed and the release-mcp-server workflow exited 2 before reaching the `npm publish` step. As a result, every Cowork user who installed plugin v1.1.1 ended up running the cached v6.11.4 npx server, which has no `respira_redeem_token` tool — so the Open-in-Cowork prompt produced "i don't have a respira_redeem_token tool in my toolset" instead of writing the config.
863
+
864
+ This release re-ships the bootstrap-mode + redeem-tool work from 6.11.6 with the typed config literal corrected. The `npm publish` succeeds and the Cowork plugin's `npx -y @respira/wordpress-mcp-server@latest` now resolves to a build that has the redeem tool.
865
+
866
+ ## [6.11.6] - 2026-05-11
867
+
868
+ ### Added
869
+
870
+ - **Zero-touch Cowork onboarding (Family 3b of the 2026-05-10 zero-touch plan).** New `respira_redeem_token` MCP tool exchanges a one-time token from respira.press for the canonical config file, writes it to `~/.respira/config.json`, and reports back the number of sites connected. Companion flow on respira.press: clicking **Open in Cowork** on `/dashboard/mcp` mints a 5-minute token and opens a `claude://cowork/new` deeplink with the token embedded in a pre-filled prompt. The user hits send, Claude calls `respira_redeem_token`, and the user is connected. No file download, no folder hunting, no manual move.
871
+
872
+ - **`RESPIRA_BOOTSTRAP_OK=1` env var.** When set, the server boots even with no `~/.respira/config.json` present (or with an empty `sites: []` array). Only `respira_redeem_token` works in this mode; every other tool returns `No WordPress site configured`. The Cowork plugin's `.mcp.json` ships this flag set so first-time users with no config don't hit a forever-spinner. After redemption succeeds, the user restarts the chat and every tool comes online.
873
+
874
+ ### Notes
875
+
876
+ - `respira_redeem_token` writes a timestamped backup of any existing `config.json` before overwriting (`config.json.bak.<unix-ms>`), so users who run it accidentally don't lose their previous setup.
877
+ - Reaches `https://www.respira.press/api/cowork/redeem` over HTTPS. Override the base URL with `RESPIRA_API_BASE` for self-hosted setups or staging.
878
+
879
+ ## [6.11.5] - 2026-05-11
880
+
881
+ ### Fixed
882
+
883
+ - **Cowork plugin silent startup crash.** The skill-documented config shape was `{url, apiKey}` (two fields) but `normalizeConfig` required four (`id, name, url, apiKey`) and threw on startup when the slim shape was used. Every Cowork user who followed the documented `/respira:connect-site` flow ended up with a config that crashed the server on launch with the stderr line `Site 1 is missing required fields (id, name, url, apiKey)` — which the MCP host swallowed and never surfaced. The symptom in Cowork was "MCP server still connecting…" forever, no tools registered, no error visible. Reported by Mihai (founder) on 2026-05-10 after running through Cowork onboarding on mihai.love.
884
+
885
+ Fix: only `url` and `apiKey` are now required. `id` and `name` auto-derive from the hostname when missing (`mihai.love` → id `mihai-love`, name `Mihai`). This makes the skill's two-field shape correct retroactively, and the four-field shape from the `.mcpb` continues to work unchanged.
886
+
887
+ ### Added
888
+
889
+ - **`~/.respira/last-startup-error.txt` diagnostic file.** On fatal startup errors (bad config, missing file, unreachable site), the server now writes a single-line diagnostic with a timestamp + hint URL to `~/.respira/last-startup-error.txt` before exiting. Cowork slash commands (or a future `/respira:doctor`) can read this file to surface the actual error to the user instead of leaving them stuck on a forever-spinner.
890
+
891
+ ## [6.11.2] - 2026-05-07
892
+
893
+ ### Fixed
894
+
895
+ - **Auto-fallback to `?rest_route=` when WordPress rewrite rules shadow `/wp-json/[anything]`.** A class of WordPress sites have plugin or theme rewrite rules that match `/wp-json/[anything]` and rewrite the path to `index.php` *without* the `?rest_route=` query var. WP's rewrite engine then tries to handle the path as a regular WP request, fails to find a matching post, and triggers `redirect_canonical()` — which 301-redirects to the homepage. The response chain ends with `HTTP/1.1 301` and header `x-redirect-by: WordPress`, and any tool call that flows through the pretty-permalink REST path (`/wp-json/respira/v2/...`) ends up with the homepage HTML in `response.data`.
896
+
897
+ The fingerprint:
898
+ - `/wp-json/` GET still returns the route listing JSON (REST router works).
899
+ - `/wp-json/respira/v1/pages` returns the homepage HTML, with `x-redirect-by: WordPress` on the redirect chain.
900
+ - `?rest_route=/respira/v1/pages` against the site root returns the expected JSON (rewrite rules don't apply to query-param URLs).
901
+
902
+ The MCP server's `wordpress_diagnose_connection` tool (shipped in v6.11.0) already detected HTML-instead-of-JSON responses. v6.11.2 adds the missing piece: a transparent retry that auto-flips to `?rest_route=` when an HTML response is observed. On first hit:
903
+
904
+ 1. Re-issue the same call as `?rest_route=...&<original-params>` against the site root, preserving method, body, headers, and all query params.
905
+ 2. If it returns JSON, set a per-session sticky flag `useRestRouteFallback: true` so subsequent requests skip the pretty-permalink probe and go directly to `?rest_route=`. One stderr warning is emitted on first activation per site: `[respira-mcp] Site <name> has REST rewrite shadowing; falling back to ?rest_route= for this session. Run wordpress_diagnose_connection for triangulation.`
906
+ 3. If the fallback also returns HTML, surface the original error with both URLs in the message.
907
+
908
+ New site-config flag `forceRestRoute: true` skips the pretty-permalink probe entirely from the start — useful for sites where the rewrite shadowing is known and stable.
909
+
910
+ `wordpress_diagnose_connection` now also probes `/?rest_route=/respira/v1/ping` and reports `rest_route_fallback_worked: boolean`, `rest_route_fallback_active: boolean`, and `force_rest_route_configured: boolean` for triangulation.
911
+
912
+ Reported by Dominic Shales (RESET Media Group) on the climatesolutions.news site; reproduced earlier in the local Beaver test env. Generally applicable to any site with a `/wp-json/[catchall]` rewrite rule from a security plugin, a custom router, or a misconfigured theme.
913
+
914
+ ### Added
915
+
916
+ - **Unit tests for the rewrite-shadowing fallback path** in `mcp-server/src/__tests__/rest-route-fallback.test.ts`, covering: HTML-to-pretty + JSON-to-rest_route → transparent JSON return; HTML-to-both → error with both URLs; sticky-flag behavior across multiple calls; `wordpress_diagnose_connection` reporting `rest_route_fallback_worked: true` when applicable; `forceRestRoute: true` skipping the pretty-permalink probe.
917
+
918
+ ## [6.11.1] - 2026-05-07
919
+
920
+ ### Changed
921
+
922
+ - **Snake_case sweep across the tool catalog.** mcp-v6.11.0 renamed `siteId` → `site_id` to close github #20 (divixgame-ui multi-site blocker). The same audit flagged 14 more user-facing camelCase parameter names; v6.11.1 lands the rest of the sweep in one release so the catalog speaks one casing. Every tool's `inputSchema.properties` now advertises snake_case only.
923
+
924
+ Renames: `pageId` → `page_id`, `postId` → `post_id`, `perPage` → `per_page`, `editTarget` → `edit_target`, `originalId` → `original_id`, `customCss` → `custom_css`, `skipSecurityCheck` → `skip_security_check`, `featuredMedia` → `featured_media`, `createMissingTerms` → `create_missing_terms`, `appendTerms` → `append_terms`, `mimeType` → `mime_type`, `moduleIdentifier` → `module_identifier`, `diviVersion` → `divi_version`, `slugOrUrl` → `slug_or_url`.
925
+
926
+ Back-compat policy: handlers accept BOTH camelCase and snake_case for one release. Calls that send the legacy camelCase form trigger a one-line stderr warning — `[respira-mcp] deprecated: 'postId' is renamed to 'post_id', will be removed in mcp-v6.13 (tool: <tool_name>)` — and continue working unchanged. Schemas advertise snake_case only, so LLMs writing fresh code through tool discovery get the new shape automatically. Removal target: mcp-v6.13.
927
+
928
+ Plugin-side: every renamed parameter is purely server-side (URL path interpolation or remapped to snake_case before hitting the REST endpoint). The plugin REST endpoints already accept snake_case for everything except `editTarget`, where the resolver explicitly accepts both `editTarget` and `edit_target` since v4.2.0 (`get_edit_target_mode()`). No plugin-side back-compat work was required.
929
+
930
+ Implementation: a single `normalizeArgsForBackCompat()` pass runs at the dispatcher entry point. It mirrors camelCase → snake_case (with deprecation warning) when only camelCase is present, and snake_case → camelCase (silent) when only snake_case is present. The silent reverse-mirror keeps the internal `wordpress-client.ts` layer working unchanged this release — that surface still reads camelCase fields like `data.customCss` / `data.featuredMedia` / `args.editTarget` before mapping them to plugin-side snake_case. Its rename can ride along in a follow-up release without breaking any callers.
931
+
932
+ - **MCP_SERVER_VERSION bumped from `6.6.4` to `6.11.1`** so the version reported in the MCP handshake / tool-discovery response matches `package.json`. The previous drift was a bookkeeping miss — every plugin-side release was bumping the surface version without updating the server-side constant.
933
+
934
+ ### Notes
935
+
936
+ - 14 distinct camelCase keys were renamed across 46 schema occurrences and 21 dispatcher reads in `src/server.ts`. `src/bricks-tools.ts`, `src/elementor-tools.ts`, and `src/acf-tools.ts` were already snake_case and required no changes. TypeScript build is clean (`npm run build`).
937
+ - In-repo callers checked: the `product-website` Astro site contains agent-prompt strings (e.g. `'pageId: PAGE_ID'` in `src/pages/api/accessibility/scans.ts`) that instruct LLMs how to call MCP tools. Those continue to work via the back-compat layer; they will be updated to snake_case in a follow-up sweep so the public-facing prompts encourage the new form.
938
+
939
+ ## [6.3.3] - 2026-04-18
940
+
941
+ ### Fixed
942
+ - **Sentry noise from legacy npx caches**: older cached installs (pre-6.3.1) were still loading `@sentry/profiling-node` → `@sentry-internal/node-cpu-profiler`, which then tried to `require()` a platform-specific native `.node` binary that doesn't ship with those caches on Apple Silicon. The resulting `Cannot find module './sentry_cpu_profiler-darwin-arm64-XX.node'` errors were hitting our telemetry from users who haven't run `npx clear-npx-cache` yet. Added SDK-level `ignoreErrors` patterns (`sentry_cpu_profiler` / `node-cpu-profiler` / `@sentry-internal` / ENOENT inside `_npx`/`node_modules`) in addition to the existing `beforeSend` filter — `ignoreErrors` runs earlier in the SDK pipeline and catches sync crash paths the `beforeSend` hook can miss. The existing profiler issue in Sentry has also been archived on our side. No functional change for users who aren't hitting this — the SDK filter only drops non-actionable noise.
943
+
944
+ ## [6.3.2] - 2026-04-17
945
+
946
+ ### Fixed
947
+ - **Server crash on startup under Node.js 25**: `@sentry/node-core` ships a 0-byte prebuilt `.node` native binary for Node 25's ABI. When `require()` mmap-loads a 0-byte native module, the dynamic linker aborts the process before any JS-level `try/catch` (including the existing one wrapping `Sentry.init`) gets a chance to run. The MCP server now skips Sentry init entirely on Node ≥ 25 and boots normally without telemetry. Stable Node versions (≤ 24) are unaffected — telemetry continues as before. Will be reverted once the Sentry SDK ships a real Node 25 binary. Reported by a user running Homebrew Node 25.
948
+
949
+ ## [6.3.1] - 2026-04-16
950
+
951
+ ### Fixed
952
+ - **Friendlier npx cache corruption errors**: When Node hits an `ENOENT` inside the `_npx` or `node_modules` cache (interrupted install, external drive disconnect, antivirus quarantine), the server now exits with a clear recovery message — `npx clear-npx-cache`, `npm cache clean --force`, or switch to global install — instead of dumping a stacktrace. Both `unhandledRejection` from async ESM imports and `uncaughtException` paths are covered.
953
+ - **Sentry noise reduction**: `beforeSend` filter now also drops `ENOENT` errors originating in `_npx` / `node_modules` paths. These are user-side cache issues, not Respira bugs, and they were burying real errors in telemetry.
954
+
955
+ ### Documentation
956
+ - README **Installation Options** now leads with global install (`npm install -g`) as the most stable path for daily use, with an explicit "if you hit any npx errors, switch to this" callout.
957
+ - New **Troubleshooting** entry for ENOENT/`_npx` errors with three labeled recovery commands.
958
+
959
+ ## [6.3.0] - 2026-04-15
960
+
961
+ ### Added
962
+ - **`structuredContent` in all tool results**: Every successful tool response now includes `structuredContent` — the raw JSON object alongside the existing text content. Follows the MCP 2025-06-18 spec. Clients that understand `structuredContent` get direct programmatic access; older clients continue using the text field unchanged. Zero breaking change.
963
+ - **Improved error taxonomy**: Unknown tool names now return a proper `CallToolResult` with `isError: true` and a self-correction hint instead of throwing a protocol-level JSON-RPC error. Helps LLMs recover gracefully ("that tool doesn't exist, let me discover available tools") instead of hitting a hard protocol failure.
964
+
965
+ ## [6.2.0] - 2026-04-14
966
+
967
+ ### Added
968
+ - **Elementor Conversion Validator**: New `validate_elementor_json` tool — validates Elementor JSON against 7 rendering fidelity rules (explicit margins, typography custom flag, text-editor padding, row children width, fullwidth sections, native widget preference, mobile breakpoints). Auto-fixes error-severity issues. Runs automatically on every `convert_html_to_builder` call.
969
+ - **Per-site tool allowlist**: Sites can now restrict which MCP tools are available via whitelist/blacklist modes. Three presets: Full Access, Read Only, Support Agent Safe. Enforced server-side before any tool logic runs.
970
+ - **Approval-required on destructive tools**: 17 destructive tools (`delete_page`, `delete_post`, `delete_media`, etc.) now require explicit human approval via a token-based flow. First call returns 202 + approval token; second call with token executes. 5-minute single-use tokens.
971
+ - **Per-site approval overrides**: Dashboard can configure which tools require approval on a per-site basis.
972
+
973
+ ## [6.0.2] - 2026-04-10
974
+
975
+ ### Security
976
+ - **axios** upgraded from ^1.6.0 to ^1.15.0 — fixes SSRF via NO_PROXY hostname normalization bypass (GHSA-3p68-rc4w-qgx5, critical) and DoS via proto key in mergeConfig (GHSA-43fc-jf86-j433, high).
977
+ - **@modelcontextprotocol/sdk** upgraded from ^1.24.0 to ^1.29.0 — fixes ReDoS (GHSA-8r9q-7v3j-jr4g, high) and cross-client data leak via shared transport reuse (GHSA-345p-7cg4-v4c7, high).
978
+ - **path-to-regexp** pinned to >=8.4.0 via overrides — fixes DoS via sequential optional groups (GHSA-j3q9-mxjg-w52f, high).
979
+ - **ajv** pinned to >=8.18.0 via overrides — fixes ReDoS with $data option (GHSA-2g4f-4pwh-qvx6, moderate).
980
+ - **qs** pinned to >=6.14.2 via overrides — fixes DoS / memory exhaustion (GHSA-6rw7-vpxm-498p, moderate).
981
+
982
+ ### Changed
983
+ - Production dependency audit now reports **0 vulnerabilities** (`npm audit --omit=dev`).
984
+
985
+ ## [6.0.1] - 2026-04-09
986
+
987
+ ### Fixed
988
+ - **Context-aware filtering**: Fixed MCP server version reporting in protocol handshake for v6.0.
989
+
990
+ ## [6.0.0] - 2026-04-09
991
+
992
+ ### Added
993
+ - **Context-aware tool filtering**: MCP server automatically filters the tool list based on your site's detected builder and active plugins. ~170 down to ~130 tools. Fail-open design.
994
+ - **Flatsome UX Builder**: Builder #12 with full round-trip shortcode editing, element-level precision, and 55-element intelligence.
995
+ - **15 new WooCommerce Commerce tools**: Storefront design intelligence, bulk pricing, catalog health, advanced filtering, natural language search. Total WooCommerce tools: 36.
996
+ - **WooCommerce add-on bundled** with Studio and Founder plans at no extra cost.
997
+
998
+ ## [5.5.1] - 2026-04-08
999
+
1000
+ ### Fixed
1001
+ - **Version reporting**: MCP protocol handshake was reporting version 5.4.0 instead of the actual installed version. Clients (Claude Code, Cursor, etc.) now see the correct version number.
1002
+
1003
+ ## [5.4.2] - 2026-04-03
1004
+
1005
+ ### Added
1006
+ - **HTTP Basic Auth support**: Sites behind server-level password protection (nginx/Apache) can now connect by adding `httpAuth: { username, password }` to the MCP config. Env vars `WORDPRESS_HTTP_USER` / `WORDPRESS_HTTP_PASS` also supported.
1007
+ - **Interactive httpAuth setup**: `npx @respira/wordpress-mcp-server setup` now detects HTTP 401/403 responses and prompts for server credentials.
1008
+
1009
+ ### Fixed
1010
+ - **Actionable error messages**: Connection failures now explain the real problem instead of generic "Authentication failed". Detects HTML responses from nginx/Apache (not WordPress), `WWW-Authenticate: Basic` headers, firewall blocks, SSL errors, DNS failures, and connection refused — each with a specific fix suggestion.
1011
+
1012
+ ## [5.4.1] - 2026-04-01
1013
+
1014
+ ### Fixed
1015
+ - Fixed `extractBuilderContent` using raw `axios.get()` instead of `this.client.get()`, causing builder content reads to route to the wrong site after switching. This was the root cause of cross-site content overwrites in multi-site agency setups.
1016
+ - Fixed Divi 4 responsive content (`tablet_text`, `phone_text`, visibility settings, custom CSS) being rendered as raw JSON in `complexify_structure`. Content now displays correctly across all breakpoints.
1017
+
1018
+ ## [5.4.0] - 2026-03-30
1019
+
1020
+ ### Added
1021
+ - **Bricks Deep Intelligence**: 7 new Bricks-specific tools taking the total from 13 to 20 dedicated Bricks tools:
1022
+ - `search_bricks_elements` — cross-site element search by type, class, or setting value
1023
+ - `bricks_health_check` — page diagnostic (orphaned elements, duplicate IDs, broken refs, heading hierarchy)
1024
+ - `bricks_detect_acss` — Automatic.css detection with design token extraction
1025
+ - `bricks_import_acss` — ACSS utility class import into Bricks global class registry
1026
+ - `bricks_query_loops` — query loop element discovery across the site
1027
+ - `bricks_style_profile` — page design pattern analysis (colors, spacing, typography)
1028
+ - `bricks_design_system` — single-call export of complete Bricks design system for AI context
1029
+ - **Optimistic locking**: `extract_builder_content` now returns `contentHash`; pass it to `inject_builder_content` to detect concurrent edits (409 Conflict on mismatch)
1030
+ - Tool count updated: 172 tools (131 core + 20 Bricks + 21 WooCommerce), up from 165
1031
+
1032
+ ## [4.0.9] - 2026-03-22
1033
+
1034
+ ### Fixed
1035
+ - Builder tool descriptions now list exact accepted builder names (`gutenberg`, `divi`, `elementor`, `bricks`, `beaver`, `oxygen`, `breakdance`, `brizy`, `thrive`, `visual-composer`, `wpbakery`) instead of vague examples. Prevents AI agents from sending hyphenated variants like `beaver-builder` that fail route matching on older plugin versions.
1036
+
1037
+ ## [4.0.6] - 2026-03-14
1038
+
1039
+ ### Fixed
1040
+ - Fixed `find_builder_targets` not traversing into child widgets: child walk used `node.children` but Elementor's simplified structure uses `node.elements`, causing only top-level containers to be returned.
1041
+ - Fixed `find_builder_targets` query matching: preview text was built from top-level node fields but Elementor stores widget content in `node.settings.*` (title, editor, text, html, etc.), causing the `query` parameter to match nothing.
1042
+
1043
+ ## [4.0.5] - 2026-03-14
1044
+
1045
+ ### Fixed
1046
+ - Fixed `wordpress_upload_media` Content-Type header conflict: the axios instance's default `Content-Type: application/json` was not being properly overridden by the multipart form-data content type, causing PHP to never populate `$_FILES` and WordPress to reject uploads. Now explicitly sets the uppercase `Content-Type` header to ensure the multipart boundary is sent correctly.
1047
+ - Added `maxContentLength` and `maxBodyLength: Infinity` to prevent axios from truncating large file uploads.
1048
+ - (4.0.3) Added local file path support for `wordpress_upload_media` — detects absolute/relative/~ paths, reads from disk, and auto-detects MIME type from extension.
1049
+
1050
+ ## [4.0.0] - 2026-03-12
1051
+
1052
+ ### Added
1053
+ - Added account-wide agency config install flow support with `RESPIRA_CONFIG_B64` and `RESPIRA_CONFIG_FILE` precedence ahead of single-site env configuration.
1054
+ - Added installer CLI mode: `--install-config --client <cursor|codex|claude-code> --install-token <token>`.
1055
+ - Added site discovery tools: `wordpress_list_sites`, `wordpress_get_active_site`, and `wordpress_find_builder_targets`.
1056
+ - Added compact `site` metadata to standalone MCP tool responses and errors so clients can always see the active site context.
1057
+
1058
+ ### Changed
1059
+ - Bumped the standalone MCP server to `4.0.0` for the agency multi-site setup contract and explicit live-edit targeting support.
1060
+ - Default read behavior now prefers compact site context and summary-first mutation responses.
1061
+ - Setup wizard now accepts dashboard-issued site tokens (`respira_site_...`) in addition to local plugin API keys.
1062
+
1063
+ ### Compatibility
1064
+ - Requires Respira WordPress Plugin `4.2.0+` for dashboard site-token auth, explicit `editTarget` live-edit preflight, and the latest v2 write-routing behavior.
1065
+
1066
+ ## [3.3.7] - 2026-03-11
1067
+
1068
+ ### Added
1069
+ - Added a shared registry parity check script so the standalone MCP server and WordPress ability registry can be validated together before release.
1070
+
1071
+ ### Changed
1072
+ - Updated abilities discovery messaging to describe the current coexistence model: standalone MCP, adapter default/public server, and dedicated Respira adapter server.
1073
+ - Release alignment with Respira plugin `4.1.3` ability-metadata and docs updates.
1074
+
1075
+ ## [3.3.6] - 2026-03-10
1076
+
1077
+ ### Changed
1078
+ - Release alignment with Respira plugin `4.1.0` approval-flow changes (ID-preserving Woo/CPT approvals, direct-edit confirmation contract, bulk approval setting).
1079
+ - Refreshed release metadata for current compatibility messaging; no runtime tool-behavior changes from `3.3.5`.
1080
+
1081
+ ## [3.3.5] - 2026-03-07
1082
+
1083
+ ### Added
1084
+ - Added normalized MCP agent telemetry headers on WordPress requests: `X-Respira-Agent-Client`, `X-Respira-Agent-Version`, `X-Respira-Agent-Transport`, `X-Respira-Agent-Source`.
1085
+ - Added shared agent signature detector for env override + heuristic fallback.
1086
+
1087
+ ### Changed
1088
+ - Setup/discovery flows now send the same agent telemetry headers as runtime tool calls for consistent attribution.
1089
+
1090
+ ## [3.3.4] - 2026-03-06
1091
+
1092
+ ### Added
1093
+ - WooCommerce taxonomy toolset: `woocommerce_list/get/create/update/delete_category` and `woocommerce_list/get/create/update/delete_tag`.
1094
+ - Category/tag assignment fields for product writes in MCP schemas (`category_ids`/`categories`, `tag_ids`/`tags`).
1095
+
1096
+ ### Changed
1097
+ - Updated published inventory contract to `82` core WordPress tools + `21` conditional WooCommerce tools (`103` total when add-on is active).
1098
+
1099
+ ### Compatibility
1100
+ - Requires Woo add-on `1.0.2+` for taxonomy endpoints and tool execution.
1101
+
1102
+ ## [3.3.3] - 2026-03-05
1103
+
1104
+ ### Added
1105
+ - Daily npm version check for `@respira/wordpress-mcp-server` while the MCP server is in active use.
1106
+ - Persistent per-user cache at `~/.respira/wordpress-mcp-version-check.json` to avoid repeated network checks.
1107
+ - Update prompt payload (`respira_mcp_update`) injected into tool responses when a newer version is available, with upgrade commands.
1108
+
1109
+ ### Notes
1110
+ - Set `RESPIRA_MCP_DISABLE_UPDATE_CHECK=1` to disable automatic update checks.
1111
+
1112
+ ## [3.3.2] - 2026-03-05
1113
+
1114
+ ### Fixed
1115
+ - Improved WordPress 5xx handling to surface structured plugin diagnostics (`code`, exception type/message, and hints) instead of only generic internal-server-error text.
1116
+ - Better sanitization/compaction of HTML error bodies returned by WordPress critical errors for clearer MCP debugging output.
1117
+
1118
+ ## [3.3.1] - 2026-03-05
1119
+
1120
+ ### Fixed
1121
+ - Improved compatibility with the latest official WordPress MCP Adapter behavior and error envelopes.
1122
+ - Tightened capability negotiation messaging and release documentation so desktop clients and browser/WebMCP paths describe the same v2-first, v1-safe fallback model.
1123
+
1124
+ ### Changed
1125
+ - Release alignment update for Respira plugin `3.3.1` patch train.
1126
+
1127
+ ## [3.3.0] - 2026-03-04
1128
+
1129
+ ### Added
1130
+ - Automatic API capability negotiation via `GET /wp-json/respira/v2/status` with per-site caching.
1131
+ - Snapshot tools: `wordpress_list_snapshots`, `wordpress_get_snapshot`, `wordpress_diff_snapshots`, `wordpress_restore_snapshot`.
1132
+ - Builder patch tool: `wordpress_apply_builder_patch` with v2 route wiring.
1133
+ - `include` support on read tools so clients can request fidelity data (`content.rendered`, `builder.payload`, `snapshot.current`, etc.).
1134
+
1135
+ ### Changed
1136
+ - Read and update flows now prefer `respira/v2` contracts when available and fall back cleanly to `respira/v1`.
1137
+ - Custom post tooling now follows v2 duplicate-first safety and safe-update behavior.
1138
+ - Updated setup messaging to explicitly communicate automatic v2/v1 negotiation.
1139
+
1140
+ ## [3.0.0] - 2026-02-25
1141
+
1142
+ ### Added
1143
+ - Published complete tool inventory contract in npm package docs: 77 core `wordpress_*` tools + 11 conditional `woocommerce_*` tools (88 total).
1144
+ - Added BrowserMCP/WebMCP positioning in MCP package docs to match Respira plugin 3.0 bundled bridge behavior.
1145
+ - Added WordPress Abilities + MCP Adapter integration details with current categories and dynamic 77/88 exposure model.
1146
+
1147
+ ### Changed
1148
+ - Bumped npm package version to **3.0.0**.
1149
+ - Updated npm package description to reflect full tool inventory, multisite support, and Abilities/WebMCP integration context.
1150
+ - Updated README badges and capability summary from legacy "60+ tools" messaging to exact v3.0 counts.
1151
+
1152
+ ## [2.2.2] - 2026-02-25
1153
+
1154
+ ### Fixed
1155
+ - Added stricter Divi inject payload validation to reject `unknown` and `mixed` content formats before save.
1156
+ - Updated Divi context guidance to prevent blind base64 transforms for code modules.
1157
+
1158
+ ## [2.2.1] - 2026-02-23
1159
+
1160
+ ### Added
1161
+ - Added `respira_approvals_url` in MCP mutation responses so assistants can direct users to verify and approve changes.
1162
+
1163
+ ### Changed
1164
+ - `wordpress_inject_builder_content` now requires explicit Divi format confirmation via `diviVersion` (`"4"` or `"5"`) on every Divi inject call.
1165
+ - Improved Divi guidance context/prompts for strict Divi 4 vs Divi 5 routing and migration-safe messaging.
1166
+
1167
+ ## [2.0.11] - 2026-02-20
1168
+
1169
+ ### Fixed
1170
+ - **EPIPE handling** - Gracefully handle client disconnects; prevent crash when Cursor/Claude closes before server finishes writing to stdout
1171
+
1172
+ ## [2.0.9] - 2025-01-XX
1173
+
1174
+ ### Added
1175
+ - **Meta and Custom CSS Support for Custom Post Types** - Added `meta` and `customCss` parameters to `wordpress_update_custom_post` function
1176
+ - `meta` parameter allows updating any post meta fields directly
1177
+ - `customCss` parameter automatically detects page builder (Divi/Elementor) and saves to appropriate meta field
1178
+ - Supports automatic builder detection for custom CSS injection
1179
+ - Elementor cache clearing when CSS is updated
1180
+ - Enables full automation of custom post type updates including CSS styling
1181
+
1182
+ ### Dependencies
1183
+ - Requires Respira WordPress Plugin v1.9.1+ for meta and custom CSS support on custom post types
1184
+
1185
+ ## [1.6.4] - 2025-01-XX
1186
+
1187
+ ### Added
1188
+ - **Custom CSS Support** - Added `customCss` parameter to `wordpress_update_page` and `wordpress_update_post` functions
1189
+ - Supports Divi Builder Custom CSS via `_et_pb_custom_css` meta field
1190
+ - Proper CSS sanitization and validation
1191
+ - Enables full automation of Divi Builder page updates without manual CSS copy-paste
1192
+
1193
+ ### Security
1194
+ - **Dependency Update** - Updated `@modelcontextprotocol/sdk` from ^0.5.0 to ^1.24.0
1195
+ - Fixes high severity vulnerability (GHSA-w48q-cv73-mx4w)
1196
+ - DNS rebinding protection now enabled by default
1197
+
1198
+ ### Dependencies
1199
+ - Requires Respira WordPress Plugin v1.8.38+ for custom CSS support on posts
1200
+
1201
+ ## [1.6.3] - 2025-01-XX
1202
+
1203
+ ### Fixed
1204
+ - **Critical Bug Fix** - Fixed "Async operation was not properly awaited" error in `wordpress_update_module`
1205
+ - Fixed axios interceptor to properly await async `handleError` method
1206
+ - The interceptor was rejecting with a Promise object instead of the Error, causing the MCP server to detect it as an unhandled Promise
1207
+ - This was causing `wordpress_update_module` to fail with async error messages
1208
+
1209
+ ## [1.6.2] - 2025-01-XX
1210
+
1211
+ ### Improved
1212
+ - **Error Handling** - Enhanced error message extraction to handle both nested and unnested error formats
1213
+ - Improved error message extraction from various error object structures
1214
+ - Better handling of string errors, error objects, and nested error properties
1215
+ - More robust error serialization to prevent circular reference issues
1216
+ - Inspired by improvements in WordPress MCP adapter v0.4.1
1217
+
1218
+ ## [1.6.1] - 2025-12-09
1219
+
1220
+ ### Fixed
1221
+ - **Error Handling** - Improved error handling to detect and properly report Promise objects that weren't awaited
1222
+ - Now provides clear error messages instead of `Error: [object Promise]`
1223
+ - Better error detection for async operations that fail
1224
+ - More informative error messages for debugging
1225
+
1226
+ ### Dependencies
1227
+ - Requires Respira WordPress Plugin v1.8.32+ for full Elementor module update support
1228
+
1229
+ ## [1.6.0] - 2025-12-XX
1230
+
1231
+ ### Added
1232
+ - Initial release with full WordPress MCP support