@respira/wordpress-mcp-server 8.3.3 → 8.3.5
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.
- package/CHANGELOG.md +26 -0
- package/README.md +8 -6
- package/TOOL_CATALOG.md +3 -1
- package/dist/__tests__/active-site-state-namespace.test.d.ts +12 -0
- package/dist/__tests__/active-site-state-namespace.test.d.ts.map +1 -0
- package/dist/__tests__/active-site-state-namespace.test.js +104 -0
- package/dist/__tests__/active-site-state-namespace.test.js.map +1 -0
- package/dist/__tests__/default-site-routing-notice.test.js +42 -2
- package/dist/__tests__/default-site-routing-notice.test.js.map +1 -1
- package/dist/__tests__/e2d884cb-put-dropped-post-fallback.test.d.ts +35 -0
- package/dist/__tests__/e2d884cb-put-dropped-post-fallback.test.d.ts.map +1 -0
- package/dist/__tests__/e2d884cb-put-dropped-post-fallback.test.js +362 -0
- package/dist/__tests__/e2d884cb-put-dropped-post-fallback.test.js.map +1 -0
- package/dist/config.d.ts +19 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +40 -0
- package/dist/config.js.map +1 -1
- package/dist/server.d.ts +43 -2
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +209 -24
- package/dist/server.js.map +1 -1
- package/dist/types/index.d.ts +19 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/wordpress-client.d.ts +78 -9
- package/dist/wordpress-client.d.ts.map +1 -1
- package/dist/wordpress-client.js +247 -108
- package/dist/wordpress-client.js.map +1 -1
- package/package.json +2 -2
- package/tool-capabilities.json +28 -4
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,32 @@ All notable changes to Respira WordPress MCP Server will be documented in this f
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [8.3.5] - 2026-08-19
|
|
9
|
+
|
|
10
|
+
### Fixed
|
|
11
|
+
|
|
12
|
+
- **An update to a host that silently drops PUT now completes instead of hanging forever.** 8.1.5 taught the client to retry a write as `POST` when a host *rejects* `PUT`, which fires on a status code. This host does not reject it, it drops it: the connection stays open and expires with no response and no status at all. `isRetryableAsPost()` only ever looked at the response status, and a timeout has no response, so the fallback that would have fixed it never ran. The error handler was also discarding the transport code on that branch, so there was nothing left to test even if it had looked. Both are fixed, the seven `PUT` write paths now go through one helper rather than seven hand-rolled attempts, which picked up two that had no fallback at all, and exactly one `POST` follows, never a second `PUT`. Reported by O.Q., who also reported and verified the original 8.1.5 fix on the same site.
|
|
13
|
+
- **The fallback cannot execute a write twice.** The plugin's idempotency cache keys on the HTTP method, so a `PUT` that reached PHP and executed, and whose response was then dropped, would not have deduplicated against the retry. The fallback now sends `X-Respira-Original-Method: PUT` and the plugin keys on that, so both attempts land in the same bucket and the second is served from the first one's result. Requires plugin 8.6.31. A `DELETE` and a `PUT` sharing one key still get separate buckets, which is unchanged.
|
|
14
|
+
- **`diagnose_connection` no longer reports a dropped request as "not blocked".** It probed with `OPTIONS` and scored the result by status code, and a probe that times out has no status, so the comparison came out false and the verdict came back clear. That is why this customer's own diagnostic reported every check passing while nothing could be written. It now probes `PUT` directly and reports three states rather than two, so a dropped probe reads as unknown instead of fine, and the recommendation names the dropped-verb case and the way around it.
|
|
15
|
+
|
|
16
|
+
### Added
|
|
17
|
+
|
|
18
|
+
- **`forceWriteMethod: "post"` per site**, alongside `forceRestRoute`, so a host that blocks `PUT` can be worked around in one line of config without waiting for a release.
|
|
19
|
+
- **`purge_cache`.** Purges one page or the whole site and reports which caching layers it found and exactly what it called, which it looked for and did not find, and what it skipped and why. Requires plugin 8.6.31.
|
|
20
|
+
- **WooCommerce order reads carry `customer_ip_address`, `customer_user_agent` and `meta_data`**, with credential-looking meta keys withheld, counted and named. Requires the WooCommerce add-on 4.0.4.
|
|
21
|
+
|
|
22
|
+
## [8.3.4] - 2026-08-15
|
|
23
|
+
|
|
24
|
+
### Added
|
|
25
|
+
|
|
26
|
+
- **`wordpress_deactivate_design_direction` reaches npm.** The tool was written and dispatching, but was never documented in the public tool list, and the parity check that guards every release refused to publish anything until it was. So it sat in the source, unpublished, while the plugin release that carried it failed silently. It is documented now, and the Design Direction section count matches its own rows again. Stopping work against the active direction no longer means activating a different one or trashing the one you have: the pointer clears, the document keeps every token and its whole history, and reactivating is a single call. Tool count moves to 213 core, 318 with the WooCommerce add-on.
|
|
27
|
+
|
|
28
|
+
### Fixed
|
|
29
|
+
|
|
30
|
+
- **A tool call with no `site_id` answered from the default site and said nothing about it.** On a connection carrying more than one site, `get_site_context`, `list_pages`, `list_posts` and `get_active_site` now report which site answered and why. This mattered because the name on an MCP connection selects nothing: routing is by `site_id`, and with none supplied the call falls through to the active site, which is whichever site the account connected first, chosen by activation order rather than by anyone. An agency reporting that a connection they had named for their staging site returned a client's production site was told it was a configuration problem on their side. It was not, and this is the signal that would have shown it in one call instead of a two-week thread. Single-site connections, which are most of them, see no change. Reported by D.
|
|
31
|
+
- **Two MCP connections on the same machine shared one active-site pointer.** The active site was persisted to `~/.respira/state.json` under a single key with nothing identifying which connection wrote it, so switching sites in one connection re-pointed every other Respira connection on that machine at the next start. The pointer is now stored per configuration. An existing file is honoured on upgrade rather than reset, and a pointer written by a different connection is ignored instead of followed.
|
|
32
|
+
- **`add_image` advertised `src` as the only way to name an image.** Passing a media library `image_id` was refused with a missing-parameter error. The schema now says either works, and `add_testimonial` gained the same for its author photo (both need Respira for WordPress 8.6.28+). Reported by P.F.
|
|
33
|
+
|
|
8
34
|
## [8.3.2] - 2026-08-13
|
|
9
35
|
|
|
10
36
|
### Added
|
package/README.md
CHANGED
|
@@ -7,14 +7,14 @@
|
|
|
7
7
|
<h1 align="center">Respira WordPress MCP Server</h1>
|
|
8
8
|
|
|
9
9
|
<p align="center">
|
|
10
|
-
<strong>
|
|
10
|
+
<strong>213 MCP tools, 318 with the WooCommerce add-on. 17 page builders and the native Site Editor. MCP 2026-07-28 with legacy-client support.</strong><br>
|
|
11
11
|
Element-level editing, full page creation, HTML-to-builder conversion with rendering fidelity validation, per-site tool governance, storefront design intelligence, stock images, bulk operations — all through one MCP server.
|
|
12
12
|
</p>
|
|
13
13
|
|
|
14
14
|
<p align="center">
|
|
15
15
|
<a href="https://www.npmjs.com/package/@respira/wordpress-mcp-server"><img src="https://img.shields.io/npm/v/@respira/wordpress-mcp-server.svg?style=flat-square&color=10b981" alt="npm version"></a>
|
|
16
16
|
<a href="https://www.npmjs.com/package/@respira/wordpress-mcp-server"><img src="https://img.shields.io/npm/dm/@respira/wordpress-mcp-server.svg?style=flat-square" alt="npm downloads"></a>
|
|
17
|
-
<img src="https://img.shields.io/badge/tools-
|
|
17
|
+
<img src="https://img.shields.io/badge/tools-318-10b981?style=flat-square" alt="318 Tools with WooCommerce">
|
|
18
18
|
<img src="https://img.shields.io/badge/builders-17-10b981?style=flat-square" alt="17 Page Builders">
|
|
19
19
|
<img src="https://img.shields.io/badge/TypeScript-100%25-blue?style=flat-square&logo=typescript&logoColor=white" alt="TypeScript">
|
|
20
20
|
</p>
|
|
@@ -247,7 +247,7 @@ Or run the interactive setup wizard: `npx @respira/wordpress-mcp-server --setup`
|
|
|
247
247
|
|
|
248
248
|
### Tool Limit? Use `enabledTools`
|
|
249
249
|
|
|
250
|
-
Some MCP clients (Antigravity, etc.) have a hard limit on active tools (often 100). Respira can expose up to
|
|
250
|
+
Some MCP clients (Antigravity, etc.) have a hard limit on active tools (often 100). Respira can expose up to 318 tools when the WooCommerce add-on is active. To stay under the limit, add `enabledTools` to your config — only those tools will appear in the listing:
|
|
251
251
|
|
|
252
252
|
```json
|
|
253
253
|
{
|
|
@@ -398,7 +398,7 @@ These are Respira-native WordPress operations, not a copied generic tool catalog
|
|
|
398
398
|
| `wordpress_update_site_navigation` | Target one link/submenu while preserving unknown extension blocks |
|
|
399
399
|
| `wordpress_delete_site_navigation` | Delete through approval; referenced navigation is refused by default |
|
|
400
400
|
|
|
401
|
-
### Design Direction (
|
|
401
|
+
### Design Direction (12 tools)
|
|
402
402
|
|
|
403
403
|
One saved design direction per site — identity, DTCG-subset tokens, dials, guidance, waivers — that agents read before building anything. Readiness is computed on every response: `ready` needs the bg + ink + accent color roles plus a heading/body font pair, and activation refuses a direction that is not ready. The direction document is site data, not instructions.
|
|
404
404
|
|
|
@@ -412,6 +412,7 @@ The design check closes the loop: `wordpress_check_design` runs deterministic sl
|
|
|
412
412
|
| `wordpress_list_design_directions` | List saved directions with readiness and the active flag |
|
|
413
413
|
| `wordpress_save_design_direction` | Save or update a schema-validated DRAFT; response reports readiness |
|
|
414
414
|
| `wordpress_activate_design_direction` | Activate through approval; refuses a not-ready direction |
|
|
415
|
+
| `wordpress_deactivate_design_direction` | Stop resolving builds against the active direction through approval; clears the site pointer only, so the document keeps its tokens, guidance and history and reactivating is one call. Returns 409 `respira_no_active_direction` when nothing is active |
|
|
415
416
|
| `wordpress_delete_design_direction` | Trash through approval; clears the active pointer if needed |
|
|
416
417
|
| `wordpress_import_design_tokens` | Import DTCG / Tokens Studio / Tailwind / CSS tokens into a draft; dry-run by default, returns the readiness delta, never activates |
|
|
417
418
|
| `wordpress_apply_design_direction` | Push the direction's tokens into the resolved builder's native store through approval; snapshot first, per-builder report persisted |
|
|
@@ -547,13 +548,14 @@ Add any widget to a page in one call:
|
|
|
547
548
|
| `respira_list_terms` / `respira_get_term` / `respira_create_term` / `respira_update_term` / `respira_delete_term` | Term CRUD |
|
|
548
549
|
| `respira_list_post_types` / `respira_get_post_type` | Post type info |
|
|
549
550
|
|
|
550
|
-
### Site & Plugins (
|
|
551
|
+
### Site & Plugins (15 tools)
|
|
551
552
|
| Tool | Description |
|
|
552
553
|
|---|---|
|
|
553
554
|
| `respira_get_site_context` | WordPress version, theme, plugins, URL |
|
|
554
555
|
| `respira_get_theme_docs` | Theme documentation and structure |
|
|
555
556
|
| `respira_list_plugins` / `respira_install_plugin` / `respira_activate_plugin` / `respira_deactivate_plugin` / `respira_update_plugin` / `respira_delete_plugin` | Plugin management |
|
|
556
557
|
| `respira_list_options` / `respira_get_option` / `respira_update_option` / `respira_delete_option` | WordPress options |
|
|
558
|
+
| `respira_purge_cache` | Purge page and object caches and report which caching layers were touched. Pass `post_id` for one page, omit it for the whole site. Every Respira write already purges what it changed; this is the manual lever for changes Respira did not make |
|
|
557
559
|
| `respira_validate_security` | Security audit |
|
|
558
560
|
| `respira_get_server_compatibility` | Plugin/MCP version check |
|
|
559
561
|
|
|
@@ -854,6 +856,6 @@ MIT © [Respira](https://respira.press)
|
|
|
854
856
|
---
|
|
855
857
|
|
|
856
858
|
<p align="center">
|
|
857
|
-
<strong>
|
|
859
|
+
<strong>213 core tools, 318 with WooCommerce. 17 builders. The AI infrastructure layer for WordPress.</strong><br>
|
|
858
860
|
<a href="https://respira.press">respira.press</a>
|
|
859
861
|
</p>
|
package/TOOL_CATALOG.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
<!-- Generated by scripts/check-tool-registry-parity.mjs. Do not edit by hand. -->
|
|
4
4
|
|
|
5
|
-
Generated catalog: **
|
|
5
|
+
Generated catalog: **187 core tools**, **105 WooCommerce tools**, **292 total**.
|
|
6
6
|
|
|
7
7
|
The runtime applies site context and per-site governance, so an individual connection may expose a smaller relevant subset.
|
|
8
8
|
|
|
@@ -51,6 +51,7 @@ The runtime applies site context and per-site governance, so an individual conne
|
|
|
51
51
|
- `wordpress_create_theme_builder_template` — write; native + hosted
|
|
52
52
|
- `wordpress_create_translation` — write; native + hosted
|
|
53
53
|
- `wordpress_create_user` — write; native + hosted
|
|
54
|
+
- `wordpress_deactivate_design_direction` — write; native + hosted
|
|
54
55
|
- `wordpress_deactivate_plugin` — write; native + hosted
|
|
55
56
|
- `wordpress_delete_acf_field_group` — write; hosted/stdio
|
|
56
57
|
- `wordpress_delete_comment` — write; native + hosted
|
|
@@ -145,6 +146,7 @@ The runtime applies site context and per-site governance, so an individual conne
|
|
|
145
146
|
- `wordpress_make_responsive` — read; hosted/stdio
|
|
146
147
|
- `wordpress_mint_design_preview` — read; native + hosted
|
|
147
148
|
- `wordpress_move_element` — write; native + hosted
|
|
149
|
+
- `wordpress_purge_cache` — write; native + hosted
|
|
148
150
|
- `wordpress_read_page` — read; native + hosted
|
|
149
151
|
- `wordpress_read_post` — read; native + hosted
|
|
150
152
|
- `wordpress_read_theme_file` — read; hosted/stdio
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ticket 5004a473: ~/.respira/state.json is machine-global. Before this,
|
|
3
|
+
* `last_active_site_id` was one top-level key, so a switch_site made in one
|
|
4
|
+
* Respira MCP connection re-pointed EVERY other Respira connection on the same
|
|
5
|
+
* machine at its next start, across accounts and clients.
|
|
6
|
+
*
|
|
7
|
+
* The active site is now stored per connection under configOriginKey(). These
|
|
8
|
+
* tests cover the pure halves of that (no real state file is read or written)
|
|
9
|
+
* plus the identity function itself.
|
|
10
|
+
*/
|
|
11
|
+
export {};
|
|
12
|
+
//# sourceMappingURL=active-site-state-namespace.test.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"active-site-state-namespace.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/active-site-state-namespace.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG"}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ticket 5004a473: ~/.respira/state.json is machine-global. Before this,
|
|
3
|
+
* `last_active_site_id` was one top-level key, so a switch_site made in one
|
|
4
|
+
* Respira MCP connection re-pointed EVERY other Respira connection on the same
|
|
5
|
+
* machine at its next start, across accounts and clients.
|
|
6
|
+
*
|
|
7
|
+
* The active site is now stored per connection under configOriginKey(). These
|
|
8
|
+
* tests cover the pure halves of that (no real state file is read or written)
|
|
9
|
+
* plus the identity function itself.
|
|
10
|
+
*/
|
|
11
|
+
import { test } from 'node:test';
|
|
12
|
+
import assert from 'node:assert/strict';
|
|
13
|
+
import { pickActiveSiteIdForConfig, applyActiveSiteIdForConfig } from '../server.js';
|
|
14
|
+
import { configOriginKey } from '../config.js';
|
|
15
|
+
const NS_A = 'aaaaaaaaaaaaaaaa';
|
|
16
|
+
const NS_B = 'bbbbbbbbbbbbbbbb';
|
|
17
|
+
// ── configOriginKey ────────────────────────────────────────────────────────
|
|
18
|
+
test('two connections with different RESPIRA_CONFIG_B64 get different keys', () => {
|
|
19
|
+
const a = configOriginKey({ RESPIRA_CONFIG_B64: 'eyJzaXRlcyI6W11=' });
|
|
20
|
+
const b = configOriginKey({ RESPIRA_CONFIG_B64: 'eyJzaXRlcyI6WzFdfQ==' });
|
|
21
|
+
assert.notEqual(a, b);
|
|
22
|
+
});
|
|
23
|
+
test('the key is stable for the same inputs and never leaks the config blob', () => {
|
|
24
|
+
const env = { RESPIRA_CONFIG_B64: 'secret-token-bearing-blob' };
|
|
25
|
+
const first = configOriginKey(env);
|
|
26
|
+
assert.equal(first, configOriginKey(env));
|
|
27
|
+
assert.match(first, /^[0-9a-f]{16}$/);
|
|
28
|
+
assert.doesNotMatch(first, /secret/);
|
|
29
|
+
});
|
|
30
|
+
test('RESPIRA_CONFIG_B64 outranks RESPIRA_CONFIG_FILE, matching loadConfig precedence', () => {
|
|
31
|
+
const withBoth = configOriginKey({
|
|
32
|
+
RESPIRA_CONFIG_B64: 'blob',
|
|
33
|
+
RESPIRA_CONFIG_FILE: '/tmp/one.json',
|
|
34
|
+
});
|
|
35
|
+
const b64Only = configOriginKey({ RESPIRA_CONFIG_B64: 'blob' });
|
|
36
|
+
assert.equal(withBoth, b64Only);
|
|
37
|
+
});
|
|
38
|
+
test('the RESPIRA_SITES scope filter is part of the connection identity', () => {
|
|
39
|
+
const unscoped = configOriginKey({ RESPIRA_CONFIG_FILE: '/tmp/one.json' });
|
|
40
|
+
const scoped = configOriginKey({
|
|
41
|
+
RESPIRA_CONFIG_FILE: '/tmp/one.json',
|
|
42
|
+
RESPIRA_SITES: 'db-services.be',
|
|
43
|
+
});
|
|
44
|
+
assert.notEqual(unscoped, scoped);
|
|
45
|
+
// Order and whitespace in the filter must not change the identity.
|
|
46
|
+
assert.equal(configOriginKey({ RESPIRA_CONFIG_FILE: '/tmp/one.json', RESPIRA_SITES: 'a.com,b.com' }), configOriginKey({ RESPIRA_CONFIG_FILE: '/tmp/one.json', RESPIRA_SITES: ' b.com , a.com ' }));
|
|
47
|
+
});
|
|
48
|
+
// ── read path ──────────────────────────────────────────────────────────────
|
|
49
|
+
test('empty state restores nothing', () => {
|
|
50
|
+
assert.equal(pickActiveSiteIdForConfig({}, NS_A), null);
|
|
51
|
+
});
|
|
52
|
+
test("a connection reads its own entry, not another connection's", () => {
|
|
53
|
+
const state = { active_site_by_config: { [NS_A]: 'jdc-bv', [NS_B]: 'db-services-staging' } };
|
|
54
|
+
assert.equal(pickActiveSiteIdForConfig(state, NS_A), 'jdc-bv');
|
|
55
|
+
assert.equal(pickActiveSiteIdForConfig(state, NS_B), 'db-services-staging');
|
|
56
|
+
});
|
|
57
|
+
test('migration: a pre-upgrade file (no owner recorded) is still honoured', () => {
|
|
58
|
+
// This is the single-connection customer upgrading. Losing his active site
|
|
59
|
+
// here would be a silent regression, so the legacy key still counts.
|
|
60
|
+
const legacyState = { last_active_site_id: 'shivamandir-org', schema_version: 1 };
|
|
61
|
+
assert.equal(pickActiveSiteIdForConfig(legacyState, NS_A), 'shivamandir-org');
|
|
62
|
+
assert.equal(pickActiveSiteIdForConfig(legacyState, NS_B), 'shivamandir-org');
|
|
63
|
+
});
|
|
64
|
+
test("a legacy key claimed by another connection is ignored — the actual bug", () => {
|
|
65
|
+
const state = { last_active_site_id: 'jdc-bv', last_active_config: NS_A };
|
|
66
|
+
assert.equal(pickActiveSiteIdForConfig(state, NS_A), 'jdc-bv');
|
|
67
|
+
assert.equal(pickActiveSiteIdForConfig(state, NS_B), null);
|
|
68
|
+
});
|
|
69
|
+
test('the namespaced entry wins over a legacy key the connection also owns', () => {
|
|
70
|
+
const state = {
|
|
71
|
+
active_site_by_config: { [NS_A]: 'db-services-staging' },
|
|
72
|
+
last_active_site_id: 'jdc-bv',
|
|
73
|
+
last_active_config: NS_A,
|
|
74
|
+
};
|
|
75
|
+
assert.equal(pickActiveSiteIdForConfig(state, NS_A), 'db-services-staging');
|
|
76
|
+
});
|
|
77
|
+
test('a corrupt state file never throws and never restores', () => {
|
|
78
|
+
assert.equal(pickActiveSiteIdForConfig({ active_site_by_config: 'nope' }, NS_A), null);
|
|
79
|
+
assert.equal(pickActiveSiteIdForConfig({ active_site_by_config: [] }, NS_A), null);
|
|
80
|
+
assert.equal(pickActiveSiteIdForConfig({ last_active_site_id: '' }, NS_A), null);
|
|
81
|
+
assert.equal(pickActiveSiteIdForConfig({ last_active_site_id: 42 }, NS_A), null);
|
|
82
|
+
assert.equal(pickActiveSiteIdForConfig(null, NS_A), null);
|
|
83
|
+
});
|
|
84
|
+
// ── write path ─────────────────────────────────────────────────────────────
|
|
85
|
+
test('writing an active site keeps other connections untouched', () => {
|
|
86
|
+
const before = { active_site_by_config: { [NS_A]: 'jdc-bv' } };
|
|
87
|
+
const patch = applyActiveSiteIdForConfig(before, NS_B, 'db-services-staging');
|
|
88
|
+
assert.deepEqual(patch.active_site_by_config, {
|
|
89
|
+
[NS_A]: 'jdc-bv',
|
|
90
|
+
[NS_B]: 'db-services-staging',
|
|
91
|
+
});
|
|
92
|
+
assert.equal(before.active_site_by_config[NS_A], 'jdc-bv', 'input state must not be mutated');
|
|
93
|
+
});
|
|
94
|
+
test('the legacy pair is kept in sync for older server versions on the same machine', () => {
|
|
95
|
+
const patch = applyActiveSiteIdForConfig({}, NS_A, 'db-services-staging');
|
|
96
|
+
assert.equal(patch.last_active_site_id, 'db-services-staging');
|
|
97
|
+
assert.equal(patch.last_active_config, NS_A);
|
|
98
|
+
});
|
|
99
|
+
test('round trip: what one connection writes, only that connection reads back', () => {
|
|
100
|
+
const patch = applyActiveSiteIdForConfig({ last_active_site_id: 'jdc-bv' }, NS_A, 'db-services-staging');
|
|
101
|
+
assert.equal(pickActiveSiteIdForConfig(patch, NS_A), 'db-services-staging');
|
|
102
|
+
assert.equal(pickActiveSiteIdForConfig(patch, NS_B), null);
|
|
103
|
+
});
|
|
104
|
+
//# sourceMappingURL=active-site-state-namespace.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"active-site-state-namespace.test.js","sourceRoot":"","sources":["../../src/__tests__/active-site-state-namespace.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -48,11 +48,51 @@ test('explicit site_id suppresses the notice (the call is already targeted)', ()
|
|
|
48
48
|
const result = notice(server, 'respira_update_element', { site_id: 'shivamandir-org', element_id: 'abc' });
|
|
49
49
|
assert.equal(result, null);
|
|
50
50
|
});
|
|
51
|
-
test('read tools
|
|
51
|
+
test('ordinary read tools get no notice, even on a multi-site account', () => {
|
|
52
52
|
const server = new RespiraWordPressServer([SITE_A, SITE_B]);
|
|
53
|
-
assert.equal(notice(server, 'respira_list_pages', {}), null);
|
|
54
53
|
assert.equal(notice(server, 'respira_read_page', { page_id: 5 }), null);
|
|
54
|
+
assert.equal(notice(server, 'respira_list_menus', {}), null);
|
|
55
|
+
assert.equal(notice(server, 'respira_get_builder_info', {}), null);
|
|
56
|
+
});
|
|
57
|
+
// ── ticket 5004a473: identity/content reads DO get a notice ────────────────
|
|
58
|
+
// D.B.'s connection, which he had named "DB-Services staging", answered
|
|
59
|
+
// get_site_context and list_pages for another site entirely. Both are reads,
|
|
60
|
+
// and reads used to be silent, so he had no way to see the routing.
|
|
61
|
+
test('identity + content reads on a multi-site account name the site that answered', () => {
|
|
62
|
+
const server = new RespiraWordPressServer([SITE_A, SITE_B]);
|
|
63
|
+
for (const tool of [
|
|
64
|
+
'respira_get_site_context',
|
|
65
|
+
'respira_get_active_site',
|
|
66
|
+
'respira_list_pages',
|
|
67
|
+
'respira_list_posts',
|
|
68
|
+
]) {
|
|
69
|
+
const result = notice(server, tool, {});
|
|
70
|
+
assert.ok(result, `expected a routing notice for ${tool}`);
|
|
71
|
+
// Names the site that actually answered: name AND host.
|
|
72
|
+
assert.match(result, /The Golden Rhodes/);
|
|
73
|
+
assert.match(result, /thegoldenrhodes\.com/);
|
|
74
|
+
// Says why it was picked, and that the connection name means nothing.
|
|
75
|
+
assert.match(result, /no site_id/);
|
|
76
|
+
assert.match(result, /name of your MCP connection does not select/);
|
|
77
|
+
assert.match(result, /respira_switch_site/);
|
|
78
|
+
// A read must never claim something was written.
|
|
79
|
+
assert.doesNotMatch(result, /Wrote to/);
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
test('an allowlisted read with an explicit site_id stays silent', () => {
|
|
83
|
+
const server = new RespiraWordPressServer([SITE_A, SITE_B]);
|
|
84
|
+
assert.equal(notice(server, 'respira_list_pages', { site_id: 'shivamandir-org' }), null);
|
|
85
|
+
assert.equal(notice(server, 'respira_get_site_context', { site_id: 'shivamandir-org' }), null);
|
|
86
|
+
});
|
|
87
|
+
test('single-site accounts get no read notice either (this is the majority case)', () => {
|
|
88
|
+
const server = new RespiraWordPressServer([SITE_A]);
|
|
55
89
|
assert.equal(notice(server, 'respira_get_site_context', {}), null);
|
|
90
|
+
assert.equal(notice(server, 'respira_list_pages', {}), null);
|
|
91
|
+
assert.equal(notice(server, 'respira_get_active_site', {}), null);
|
|
92
|
+
});
|
|
93
|
+
test('the wordpress_* alias of an allowlisted read is treated the same', () => {
|
|
94
|
+
const server = new RespiraWordPressServer([SITE_A, SITE_B]);
|
|
95
|
+
assert.ok(notice(server, 'wordpress_list_pages', {}), 'wordpress_* read alias should also notice');
|
|
56
96
|
});
|
|
57
97
|
test('single-site accounts never get a notice (no wrong-site risk)', () => {
|
|
58
98
|
const server = new RespiraWordPressServer([SITE_A]);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"default-site-routing-notice.test.js","sourceRoot":"","sources":["../../src/__tests__/default-site-routing-notice.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,MAAM,MAAM,oBAAoB,CAAC;AACxC,OAAO,EAAE,sBAAsB,EAAE,MAAM,cAAc,CAAC;AAGtD,MAAM,MAAM,GAAwB;IAClC,EAAE,EAAE,qBAAqB;IACzB,IAAI,EAAE,mBAAmB;IACzB,GAAG,EAAE,6BAA6B;IAClC,MAAM,EAAE,iBAAiB;IACzB,OAAO,EAAE,IAAI;CACd,CAAC;AAEF,MAAM,MAAM,GAAwB;IAClC,EAAE,EAAE,iBAAiB;IACrB,IAAI,EAAE,aAAa;IACnB,GAAG,EAAE,yBAAyB;IAC9B,MAAM,EAAE,iBAAiB;CAC1B,CAAC;AAEF,SAAS,MAAM,CAAC,MAA8B,EAAE,IAAY,EAAE,IAAU;IACtE,OAAQ,MAAc,CAAC,6BAA6B,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;AACnE,CAAC;AAED,IAAI,CAAC,uEAAuE,EAAE,GAAG,EAAE;IACjF,MAAM,MAAM,GAAG,IAAI,sBAAsB,CAAC,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IAC5D,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,wBAAwB,EAAE,EAAE,UAAU,EAAE,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC;IAE9F,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,2BAA2B,CAAC,CAAC;IAC/C,MAAM,CAAC,KAAK,CAAC,MAAO,EAAE,mBAAmB,CAAC,CAAC;IAC3C,MAAM,CAAC,KAAK,CAAC,MAAO,EAAE,sBAAsB,CAAC,CAAC;IAC9C,MAAM,CAAC,KAAK,CAAC,MAAO,EAAE,cAAc,CAAC,CAAC;IACtC,qDAAqD;IACrD,MAAM,CAAC,KAAK,CAAC,MAAO,EAAE,SAAS,CAAC,CAAC;IACjC,MAAM,CAAC,KAAK,CAAC,MAAO,EAAE,qBAAqB,CAAC,CAAC;AAC/C,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,uEAAuE,EAAE,GAAG,EAAE;IACjF,MAAM,MAAM,GAAG,IAAI,sBAAsB,CAAC,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IAC5D,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,wBAAwB,EAAE,EAAE,OAAO,EAAE,iBAAiB,EAAE,UAAU,EAAE,KAAK,EAAE,CAAC,CAAC;IAC3G,MAAM,CAAC,KAAK,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;AAC7B,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,
|
|
1
|
+
{"version":3,"file":"default-site-routing-notice.test.js","sourceRoot":"","sources":["../../src/__tests__/default-site-routing-notice.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ticket e2d884cb — `respira_update_post` hung forever on
|
|
3
|
+
* linvestisseurantifragile.com (Apache) while `respira_read_post` (GET) and
|
|
4
|
+
* `respira_create_post_duplicate` (POST) worked. Reproducible 3/3 on two
|
|
5
|
+
* different posts, no write ever applied.
|
|
6
|
+
*
|
|
7
|
+
* The verb was the only difference. The host DROPS PUT rather than rejecting
|
|
8
|
+
* it: the connection is held and never answered, so the client's own timeout
|
|
9
|
+
* is the only thing that ends the call. The PUT->POST fallback that would have
|
|
10
|
+
* saved the write existed already (ticket 2b3a706b, same customer, same host,
|
|
11
|
+
* one behaviour earlier) but fired on status codes only — and a dropped
|
|
12
|
+
* request has no status. `handleError` rebuilt the failure as
|
|
13
|
+
* `respira_write_outcome_unknown` with no `response`, so
|
|
14
|
+
* `isRetryableAsPost()` returned false and the PUT error was rethrown.
|
|
15
|
+
*
|
|
16
|
+
* The dangerous half of the fix is the retry itself. A PUT that DID reach PHP,
|
|
17
|
+
* executed, and only lost its response on the way back must not run a second
|
|
18
|
+
* time as a POST: the plugin's idempotency cache key and fingerprint both
|
|
19
|
+
* include the HTTP method (class-respira-mcp-reliability.php), and update_post
|
|
20
|
+
* is not idempotent in every branch — the duplicate-first path mints a draft
|
|
21
|
+
* duplicate of the customer's page. So the fallback POST carries the PUT's
|
|
22
|
+
* Idempotency-Key AND declares `X-Respira-Original-Method: PUT`, and the plugin
|
|
23
|
+
* keys on the declared method. The companion PHP proof that the plugin honours
|
|
24
|
+
* the header lives at
|
|
25
|
+
* wordpress-plugin/respira-for-wordpress/tests/standalone/e2d884cb-idempotency-dedupe-across-verb.php
|
|
26
|
+
*
|
|
27
|
+
* These tests run a real HTTP server so the whole interceptor chain runs,
|
|
28
|
+
* including the `.response`-stripping codedError() step that made the first
|
|
29
|
+
* version of this fallback a no-op. The drop is simulated by destroying the
|
|
30
|
+
* socket mid-request, which is what a WAF that discards the verb looks like to
|
|
31
|
+
* Node (`socket hang up`, code ECONNRESET) and is the same no-response,
|
|
32
|
+
* no-status shape as the customer's timeout without costing a test 120s.
|
|
33
|
+
*/
|
|
34
|
+
export {};
|
|
35
|
+
//# sourceMappingURL=e2d884cb-put-dropped-post-fallback.test.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"e2d884cb-put-dropped-post-fallback.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/e2d884cb-put-dropped-post-fallback.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG"}
|