@respira/wordpress-mcp-server 8.3.21 → 8.3.22

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 CHANGED
@@ -5,6 +5,31 @@ 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.22] - 2026-09-11
9
+
10
+ Writes that reach the site, or say plainly why they did not.
11
+
12
+ ### Fixed
13
+
14
+ - **Hosts that turn PUT, PATCH and DELETE into reads.** Some hosts pass those requests to
15
+ WordPress as GET. The write then failed with `rest_no_route`, or, on a route that also
16
+ reads, came back as a successful read while nothing was written. The server now spots
17
+ both and sends the write once more as POST with WordPress's own method override, and a
18
+ DELETE blocked in front of WordPress gets the same fallback PUT already had.
19
+ `diagnose_connection` reports these hosts as `verb_rewritten` instead of `not_blocked`.
20
+ - **A site URL that redirects is named as the problem.** When the configured URL redirects
21
+ to another address, for example the bare domain to `www`, every write failed after a
22
+ fraction of a second with "no response received", which reads like a site that is down.
23
+ The error now says the URL redirects, names the address to use instead, and confirms
24
+ nothing was written. `diagnose_connection` puts the redirect first in its advice.
25
+ - **Site template operations take `type` and `operation` as well as `op`.** The three
26
+ template update tools now describe all three names. The plugin side ships in 8.8.34.
27
+
28
+ ### Changed
29
+
30
+ - The HTML conversion tool describes fidelity mode, the default in plugin 8.8.34 when the
31
+ HTML brings a stylesheet on Divi 5 and Bricks.
32
+
8
33
  ## [8.3.21] - 2026-09-11
9
34
 
10
35
  A share link that lives as long as you need.
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=303ad1de-fse-operation-schema.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"303ad1de-fse-operation-schema.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/303ad1de-fse-operation-schema.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,98 @@
1
+ /**
2
+ * Ticket 303ad1de: the published schema for FSE operations has to name the key
3
+ * the plugin reads, and describe only fields the plugin reads.
4
+ *
5
+ * Until this fix every entry in `operations` was published as a bare
6
+ * `{ type: 'object' }`. An agent editing andel.cl's single-producto template
7
+ * guessed the key `type`, and the plugin answered 422
8
+ * "Operation 1: unsupported operation type".
9
+ *
10
+ * This asks the packaged server for its tool list over the MCP protocol, the
11
+ * same way a client does, so it checks what agents are actually told.
12
+ *
13
+ * The plugin side of the contract is written out below rather than read from
14
+ * PHP. It is Respira_FSE_Templates::apply_operations() in
15
+ * wordpress-plugin/respira-for-wordpress/includes/class-respira-fse-templates.php:
16
+ * the operation names it dispatches on, and every key it reads from an
17
+ * operation. Change them together.
18
+ */
19
+ import assert from 'node:assert/strict';
20
+ import { test } from 'node:test';
21
+ import { resolve } from 'node:path';
22
+ import { createServer } from 'node:http';
23
+ import { Client } from '@modelcontextprotocol/client';
24
+ import { StdioClientTransport } from '@modelcontextprotocol/client/stdio';
25
+ const PLUGIN_OPERATION_NAMES = [
26
+ 'set_attributes',
27
+ 'replace_block',
28
+ 'insert_before',
29
+ 'insert_after',
30
+ 'insert_inside',
31
+ 'remove_block',
32
+ 'move_block',
33
+ ];
34
+ /** Every key apply_operations() reads from one operation. */
35
+ const PLUGIN_OPERATION_KEYS = new Set([
36
+ 'operation', 'op', 'type',
37
+ 'path', 'from_path', 'to_path', 'expected_block_name', 'attributes', 'block', 'position',
38
+ ]);
39
+ /** Block keys serialize_block() and the plugin's normalize_block() read. */
40
+ const PLUGIN_BLOCK_KEYS = new Set(['blockName', 'attrs', 'innerBlocks', 'innerHTML', 'innerContent']);
41
+ const TOOLS = ['respira_update_site_template', 'respira_update_site_pattern', 'respira_update_site_navigation'];
42
+ test('FSE update tools publish an operation schema that names the key the plugin reads', { timeout: 20_000 }, async () => {
43
+ const wordpress = createServer((_request, response) => {
44
+ response.writeHead(200, { 'content-type': 'application/json' });
45
+ response.end(JSON.stringify({ wordpress_version: '6.8', page_builder: { name: 'gutenberg', version: '6.8' }, active_plugins: [] }));
46
+ });
47
+ await new Promise((resolveListen) => wordpress.listen(0, '127.0.0.1', resolveListen));
48
+ const address = wordpress.address();
49
+ assert.ok(address && typeof address === 'object');
50
+ const config = Buffer.from(JSON.stringify({
51
+ sites: [{ id: 'fse-schema', name: 'FSE schema', url: `http://127.0.0.1:${address.port}`, apiKey: 'respira_fse_schema', default: true }],
52
+ preferences: { autoDuplicate: true, securityChecks: true },
53
+ })).toString('base64');
54
+ const client = new Client({ name: 'respira-303ad1de', version: '1.0.0' });
55
+ try {
56
+ await client.connect(new StdioClientTransport({
57
+ command: process.execPath,
58
+ args: [resolve(process.cwd(), 'dist/index.js')],
59
+ cwd: process.cwd(),
60
+ stderr: 'pipe',
61
+ env: { ...process.env, RESPIRA_CONFIG_B64: config, RESPIRA_USAGE_OPT_OUT: '1', NODE_ENV: 'test' },
62
+ }));
63
+ const { tools } = await client.listTools();
64
+ for (const name of TOOLS) {
65
+ const tool = tools.find((candidate) => candidate.name === name);
66
+ assert.ok(tool, `${name} must be published`);
67
+ const operations = tool.inputSchema.properties?.operations;
68
+ assert.equal(operations?.type, 'array', `${name}: operations must be an array`);
69
+ const item = operations.items;
70
+ const properties = item?.properties ?? {};
71
+ // The discriminator: a named key, constrained to the names the plugin dispatches on.
72
+ assert.deepEqual(properties.operation?.enum, PLUGIN_OPERATION_NAMES, `${name}: operations[].operation must enumerate the plugin's operation names`);
73
+ assert.ok(item.required?.includes('operation'), `${name}: operations[].operation must be required`);
74
+ // The fields each operation needs are described, and typed the way the plugin reads them.
75
+ for (const pathKey of ['path', 'from_path', 'to_path']) {
76
+ assert.equal(properties[pathKey]?.type, 'array', `${name}: ${pathKey} must be an array`);
77
+ assert.equal(properties[pathKey]?.items?.type, 'integer', `${name}: ${pathKey} must hold integer indexes`);
78
+ }
79
+ assert.equal(properties.attributes?.type, 'object', `${name}: set_attributes needs attributes`);
80
+ assert.equal(properties.position?.type, 'integer', `${name}: insert_inside takes an integer position`);
81
+ assert.equal(properties.block?.type, 'object', `${name}: block operations need block`);
82
+ assert.ok(properties.block?.required?.includes('blockName'), `${name}: block.blockName must be required, as the plugin requires it`);
83
+ assert.equal(properties.block?.properties?.innerContent?.type, 'array', `${name}: block.innerContent, what WordPress saves, must be described`);
84
+ // Nothing is described that the plugin would ignore.
85
+ for (const key of Object.keys(properties)) {
86
+ assert.ok(PLUGIN_OPERATION_KEYS.has(key), `${name}: operations[].${key} is published but the plugin never reads it`);
87
+ }
88
+ for (const key of Object.keys(properties.block?.properties ?? {})) {
89
+ assert.ok(PLUGIN_BLOCK_KEYS.has(key), `${name}: operations[].block.${key} is published but WordPress never reads it`);
90
+ }
91
+ }
92
+ }
93
+ finally {
94
+ await client.close();
95
+ await new Promise((resolveClose, rejectClose) => wordpress.close((error) => error ? rejectClose(error) : resolveClose()));
96
+ }
97
+ });
98
+ //# sourceMappingURL=303ad1de-fse-operation-schema.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"303ad1de-fse-operation-schema.test.js","sourceRoot":"","sources":["../../src/__tests__/303ad1de-fse-operation-schema.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AACH,OAAO,MAAM,MAAM,oBAAoB,CAAC;AACxC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AACzC,OAAO,EAAE,MAAM,EAAE,MAAM,8BAA8B,CAAC;AACtD,OAAO,EAAE,oBAAoB,EAAE,MAAM,oCAAoC,CAAC;AAE1E,MAAM,sBAAsB,GAAG;IAC7B,gBAAgB;IAChB,eAAe;IACf,eAAe;IACf,cAAc;IACd,eAAe;IACf,cAAc;IACd,YAAY;CACb,CAAC;AACF,6DAA6D;AAC7D,MAAM,qBAAqB,GAAG,IAAI,GAAG,CAAC;IACpC,WAAW,EAAE,IAAI,EAAE,MAAM;IACzB,MAAM,EAAE,WAAW,EAAE,SAAS,EAAE,qBAAqB,EAAE,YAAY,EAAE,OAAO,EAAE,UAAU;CACzF,CAAC,CAAC;AACH,4EAA4E;AAC5E,MAAM,iBAAiB,GAAG,IAAI,GAAG,CAAC,CAAC,WAAW,EAAE,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,cAAc,CAAC,CAAC,CAAC;AAEtG,MAAM,KAAK,GAAG,CAAC,8BAA8B,EAAE,6BAA6B,EAAE,gCAAgC,CAAC,CAAC;AAEhH,IAAI,CAAC,kFAAkF,EAAE,EAAE,OAAO,EAAE,MAAM,EAAE,EAAE,KAAK,IAAI,EAAE;IACvH,MAAM,SAAS,GAAG,YAAY,CAAC,CAAC,QAAQ,EAAE,QAAQ,EAAE,EAAE;QACpD,QAAQ,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE,CAAC,CAAC;QAChE,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,iBAAiB,EAAE,KAAK,EAAE,YAAY,EAAE,EAAE,IAAI,EAAE,WAAW,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE,cAAc,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC;IACtI,CAAC,CAAC,CAAC;IACH,MAAM,IAAI,OAAO,CAAO,CAAC,aAAa,EAAE,EAAE,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,EAAE,WAAW,EAAE,aAAa,CAAC,CAAC,CAAC;IAC5F,MAAM,OAAO,GAAG,SAAS,CAAC,OAAO,EAAE,CAAC;IACpC,MAAM,CAAC,EAAE,CAAC,OAAO,IAAI,OAAO,OAAO,KAAK,QAAQ,CAAC,CAAC;IAClD,MAAM,MAAM,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC;QACxC,KAAK,EAAE,CAAC,EAAE,EAAE,EAAE,YAAY,EAAE,IAAI,EAAE,YAAY,EAAE,GAAG,EAAE,oBAAoB,OAAO,CAAC,IAAI,EAAE,EAAE,MAAM,EAAE,oBAAoB,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;QACvI,WAAW,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,cAAc,EAAE,IAAI,EAAE;KAC3D,CAAC,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;IAEvB,MAAM,MAAM,GAAG,IAAI,MAAM,CAAC,EAAE,IAAI,EAAE,kBAAkB,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC,CAAC;IAC1E,IAAI,CAAC;QACH,MAAM,MAAM,CAAC,OAAO,CAAC,IAAI,oBAAoB,CAAC;YAC5C,OAAO,EAAE,OAAO,CAAC,QAAQ;YACzB,IAAI,EAAE,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,eAAe,CAAC,CAAC;YAC/C,GAAG,EAAE,OAAO,CAAC,GAAG,EAAE;YAClB,MAAM,EAAE,MAAM;YACd,GAAG,EAAE,EAAE,GAAG,OAAO,CAAC,GAAG,EAAE,kBAAkB,EAAE,MAAM,EAAE,qBAAqB,EAAE,GAAG,EAAE,QAAQ,EAAE,MAAM,EAA4B;SAC5H,CAAC,CAAC,CAAC;QACJ,MAAM,EAAE,KAAK,EAAE,GAAG,MAAM,MAAM,CAAC,SAAS,EAAE,CAAC;QAE3C,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YACzB,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,SAAS,CAAC,IAAI,KAAK,IAAI,CAAC,CAAC;YAChE,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE,GAAG,IAAI,oBAAoB,CAAC,CAAC;YAC7C,MAAM,UAAU,GAAI,IAAI,CAAC,WAAmB,CAAC,UAAU,EAAE,UAAU,CAAC;YACpE,MAAM,CAAC,KAAK,CAAC,UAAU,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,IAAI,+BAA+B,CAAC,CAAC;YAChF,MAAM,IAAI,GAAG,UAAU,CAAC,KAAK,CAAC;YAC9B,MAAM,UAAU,GAAG,IAAI,EAAE,UAAU,IAAI,EAAE,CAAC;YAE1C,qFAAqF;YACrF,MAAM,CAAC,SAAS,CAAC,UAAU,CAAC,SAAS,EAAE,IAAI,EAAE,sBAAsB,EAAE,GAAG,IAAI,sEAAsE,CAAC,CAAC;YACpJ,MAAM,CAAC,EAAE,CAAC,IAAI,CAAC,QAAQ,EAAE,QAAQ,CAAC,WAAW,CAAC,EAAE,GAAG,IAAI,2CAA2C,CAAC,CAAC;YAEpG,0FAA0F;YAC1F,KAAK,MAAM,OAAO,IAAI,CAAC,MAAM,EAAE,WAAW,EAAE,SAAS,CAAC,EAAE,CAAC;gBACvD,MAAM,CAAC,KAAK,CAAC,UAAU,CAAC,OAAO,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,IAAI,KAAK,OAAO,mBAAmB,CAAC,CAAC;gBACzF,MAAM,CAAC,KAAK,CAAC,UAAU,CAAC,OAAO,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,SAAS,EAAE,GAAG,IAAI,KAAK,OAAO,4BAA4B,CAAC,CAAC;YAC7G,CAAC;YACD,MAAM,CAAC,KAAK,CAAC,UAAU,CAAC,UAAU,EAAE,IAAI,EAAE,QAAQ,EAAE,GAAG,IAAI,mCAAmC,CAAC,CAAC;YAChG,MAAM,CAAC,KAAK,CAAC,UAAU,CAAC,QAAQ,EAAE,IAAI,EAAE,SAAS,EAAE,GAAG,IAAI,2CAA2C,CAAC,CAAC;YACvG,MAAM,CAAC,KAAK,CAAC,UAAU,CAAC,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,GAAG,IAAI,+BAA+B,CAAC,CAAC;YACvF,MAAM,CAAC,EAAE,CAAC,UAAU,CAAC,KAAK,EAAE,QAAQ,EAAE,QAAQ,CAAC,WAAW,CAAC,EAAE,GAAG,IAAI,+DAA+D,CAAC,CAAC;YACrI,MAAM,CAAC,KAAK,CAAC,UAAU,CAAC,KAAK,EAAE,UAAU,EAAE,YAAY,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,IAAI,+DAA+D,CAAC,CAAC;YAEhJ,qDAAqD;YACrD,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC,EAAE,CAAC;gBAC1C,MAAM,CAAC,EAAE,CAAC,qBAAqB,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,GAAG,IAAI,kBAAkB,GAAG,6CAA6C,CAAC,CAAC;YACvH,CAAC;YACD,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC,KAAK,EAAE,UAAU,IAAI,EAAE,CAAC,EAAE,CAAC;gBAClE,MAAM,CAAC,EAAE,CAAC,iBAAiB,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,GAAG,IAAI,wBAAwB,GAAG,4CAA4C,CAAC,CAAC;YACxH,CAAC;QACH,CAAC;IACH,CAAC;YAAS,CAAC;QACT,MAAM,MAAM,CAAC,KAAK,EAAE,CAAC;QACrB,MAAM,IAAI,OAAO,CAAO,CAAC,YAAY,EAAE,WAAW,EAAE,EAAE,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,YAAY,EAAE,CAAC,CAAC,CAAC;IAClI,CAAC;AACH,CAAC,CAAC,CAAC"}
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Ticket 953dde4e: withmaroua.com, plugin 8.8.32. `respira_update_bricks_global_class`
3
+ * failed with `rest_no_route` (404).
4
+ *
5
+ * The plugin registers the item route correctly (EDITABLE + DELETABLE,
6
+ * class-respira-bricks-tools.php). The host rewrites PUT, PATCH and DELETE
7
+ * into a request WordPress dispatches as GET. Verified live, unauthenticated,
8
+ * on the public READABLE-only /respira/v1/ping route: PUT and DELETE there
9
+ * answer 200 with the ping body on withmaroua.com, and 404 rest_no_route on a
10
+ * healthy 8.8.32 site. A POST carrying `X-HTTP-Method-Override: PUT` reaches
11
+ * the PUT handler on withmaroua.com.
12
+ *
13
+ * Two consequences, both reproduced here against the real WordPressClient:
14
+ *
15
+ * 1. On a route with no GET handler (Bricks global classes) WordPress answers
16
+ * `rest_no_route`. The PUT->POST fallback retried only `_non_wordpress`
17
+ * codes, so nothing retried.
18
+ * 2. On a route that ALSO has a GET handler (pages) the rewritten write is
19
+ * answered by the READ handler with a 200, and the tool reports success
20
+ * with nothing written.
21
+ *
22
+ * WordPress core honours `_method` and `X-HTTP-Method-Override` in
23
+ * WP_REST_Server::serve_request() and dispatches to the overridden method's
24
+ * handler. The emulated host below does the same, and emulates the plugin's
25
+ * reliability layer (class-respira-mcp-reliability.php) closely enough that
26
+ * `executions` counts real handler runs rather than requests.
27
+ */
28
+ export {};
29
+ //# sourceMappingURL=953dde4e-verb-rewriting-host-method-override.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"953dde4e-verb-rewriting-host-method-override.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/953dde4e-verb-rewriting-host-method-override.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG"}
@@ -0,0 +1,305 @@
1
+ /**
2
+ * Ticket 953dde4e: withmaroua.com, plugin 8.8.32. `respira_update_bricks_global_class`
3
+ * failed with `rest_no_route` (404).
4
+ *
5
+ * The plugin registers the item route correctly (EDITABLE + DELETABLE,
6
+ * class-respira-bricks-tools.php). The host rewrites PUT, PATCH and DELETE
7
+ * into a request WordPress dispatches as GET. Verified live, unauthenticated,
8
+ * on the public READABLE-only /respira/v1/ping route: PUT and DELETE there
9
+ * answer 200 with the ping body on withmaroua.com, and 404 rest_no_route on a
10
+ * healthy 8.8.32 site. A POST carrying `X-HTTP-Method-Override: PUT` reaches
11
+ * the PUT handler on withmaroua.com.
12
+ *
13
+ * Two consequences, both reproduced here against the real WordPressClient:
14
+ *
15
+ * 1. On a route with no GET handler (Bricks global classes) WordPress answers
16
+ * `rest_no_route`. The PUT->POST fallback retried only `_non_wordpress`
17
+ * codes, so nothing retried.
18
+ * 2. On a route that ALSO has a GET handler (pages) the rewritten write is
19
+ * answered by the READ handler with a 200, and the tool reports success
20
+ * with nothing written.
21
+ *
22
+ * WordPress core honours `_method` and `X-HTTP-Method-Override` in
23
+ * WP_REST_Server::serve_request() and dispatches to the overridden method's
24
+ * handler. The emulated host below does the same, and emulates the plugin's
25
+ * reliability layer (class-respira-mcp-reliability.php) closely enough that
26
+ * `executions` counts real handler runs rather than requests.
27
+ */
28
+ import { test, after } from 'node:test';
29
+ import assert from 'node:assert/strict';
30
+ import http from 'node:http';
31
+ import { WordPressClient } from '../wordpress-client.js';
32
+ const servers = [];
33
+ after(() => {
34
+ for (const s of servers)
35
+ s.close();
36
+ });
37
+ const WRITE_VERBS = new Set(['POST', 'PUT', 'PATCH', 'DELETE']);
38
+ // Registered routes, as the plugin registers them. Keyed by the method
39
+ // WordPress dispatches on, exactly like register_rest_route's `methods`.
40
+ const ROUTES = [
41
+ { pattern: /^\/$/, methods: { GET: 'rest_index' } },
42
+ { pattern: /^\/respira\/v2$/, methods: { GET: 'namespace_index' } },
43
+ { pattern: /^\/respira\/v2\/status$/, methods: { GET: 'status' } },
44
+ { pattern: /^\/respira\/v1\/ping$/, methods: { GET: 'ping' } },
45
+ { pattern: /^\/respira\/v1\/diagnostic\/report$/, methods: { GET: 'diagnostic_report' } },
46
+ // class-respira-bricks-tools.php:351 (EDITABLE) and :365 (DELETABLE). No GET.
47
+ {
48
+ pattern: /^\/respira\/v2\/bricks\/global-classes\/[^/]+$/,
49
+ methods: { POST: 'update_global_class', PUT: 'update_global_class', PATCH: 'update_global_class', DELETE: 'delete_global_class' },
50
+ },
51
+ {
52
+ pattern: /^\/respira\/v[12]\/pages\/\d+$/,
53
+ methods: { GET: 'read_page', POST: 'update_page', PUT: 'update_page', PATCH: 'update_page', DELETE: 'delete_page' },
54
+ },
55
+ { pattern: /^\/respira\/v2\/theme-files\/[^/]+\/append$/, methods: { PATCH: 'append_theme_file' } },
56
+ ];
57
+ const NO_ROUTE = { code: 'rest_no_route', message: 'No route was found matching the URL and request method.', data: { status: 404 } };
58
+ async function startSite(opts = {}) {
59
+ const hits = [];
60
+ const executions = new Map();
61
+ const replayCache = new Map();
62
+ const idempotencyHeaders = opts.idempotencyHeaders !== false;
63
+ const server = http.createServer((req, res) => {
64
+ req.resume();
65
+ req.on('end', () => {
66
+ const wire = String(req.method || 'GET').toUpperCase();
67
+ const url = new URL(req.url || '/', 'http://placeholder');
68
+ const route = url.searchParams.get('rest_route') || url.pathname.replace(/^\/wp-json/, '') || '/';
69
+ if (opts.edgeBlock && opts.edgeBlock.method === wire) {
70
+ res.writeHead(opts.edgeBlock.status, { 'Content-Type': 'text/html' });
71
+ res.end(`<!DOCTYPE html><html><head><title>${opts.edgeBlock.status}</title></head><body>Blocked</body></html>`);
72
+ return;
73
+ }
74
+ let method = wire;
75
+ if (opts.rewriteVerbs && (wire === 'PUT' || wire === 'PATCH' || wire === 'DELETE'))
76
+ method = 'GET';
77
+ const keepHeader = !opts.stripOverride;
78
+ const keepParam = !opts.stripOverride?.alsoMethodParam;
79
+ const overrideHeader = keepHeader ? String(req.headers['x-http-method-override'] || '') : '';
80
+ const methodParam = keepParam ? String(url.searchParams.get('_method') || '') : '';
81
+ // WP_REST_Server::serve_request(): `_method` first, then the header.
82
+ if (methodParam)
83
+ method = methodParam.toUpperCase();
84
+ else if (overrideHeader)
85
+ method = overrideHeader.toUpperCase();
86
+ const idempotencyKey = String(req.headers['idempotency-key'] || '');
87
+ const originalMethod = String(req.headers['x-respira-original-method'] || '');
88
+ hits.push({ wire, dispatched: method, route, overrideHeader, methodParam, originalMethod, idempotencyKey });
89
+ const headers = { 'Content-Type': 'application/json' };
90
+ const isRespira = route.startsWith('/respira/');
91
+ if (isRespira && idempotencyHeaders)
92
+ headers['X-Respira-Idempotency-Supported'] = '1';
93
+ // Respira_MCP_Reliability::replay_or_lock_write (rest_pre_dispatch, before routing).
94
+ let cacheKey = '';
95
+ if (isRespira && idempotencyHeaders && WRITE_VERBS.has(method) && /^[A-Za-z0-9._:-]{8,128}$/.test(idempotencyKey)) {
96
+ const dedupeMethod = WRITE_VERBS.has(originalMethod.toUpperCase()) ? originalMethod.toUpperCase() : method;
97
+ cacheKey = [dedupeMethod, route, idempotencyKey].join('|');
98
+ const cached = replayCache.get(cacheKey);
99
+ headers['X-Respira-Call-Id'] = idempotencyKey;
100
+ if (cached) {
101
+ res.writeHead(cached.status, { ...headers, 'X-Respira-Idempotent-Replay': 'true' });
102
+ res.end(cached.body);
103
+ return;
104
+ }
105
+ }
106
+ const send = (status, body) => {
107
+ const text = JSON.stringify(body);
108
+ if (cacheKey && status < 500)
109
+ replayCache.set(cacheKey, { status, body: text });
110
+ res.writeHead(status, headers);
111
+ res.end(method === 'HEAD' ? undefined : text);
112
+ };
113
+ const matched = ROUTES.find((r) => r.pattern.test(route));
114
+ if (matched && method === 'OPTIONS')
115
+ return send(200, {});
116
+ const handler = matched
117
+ ? matched.methods[method] || (method === 'HEAD' ? matched.methods.GET : undefined)
118
+ : undefined;
119
+ if (!handler)
120
+ return send(404, NO_ROUTE);
121
+ executions.set(handler, (executions.get(handler) || 0) + 1);
122
+ switch (handler) {
123
+ case 'rest_index':
124
+ return send(200, { namespaces: ['respira/v1', 'respira/v2'] });
125
+ case 'namespace_index':
126
+ return send(200, { namespace: 'respira/v2' });
127
+ case 'status':
128
+ return send(200, { status: 'ok' });
129
+ case 'ping':
130
+ return send(200, { ok: true, plugin: 'respira-for-wordpress' });
131
+ case 'diagnostic_report':
132
+ return send(200, { ok: true });
133
+ case 'read_page':
134
+ return send(200, { id: 7, title: 'The page as it already is', read: true });
135
+ case 'update_page':
136
+ return send(200, { success: true, page: { id: 7, handler, method } });
137
+ case 'delete_global_class':
138
+ case 'delete_page':
139
+ if (opts.denyDelete) {
140
+ return send(403, { code: 'rest_forbidden', message: 'Sorry, you are not allowed to delete this.', data: { status: 403 } });
141
+ }
142
+ return send(200, { success: true, handler, method });
143
+ default:
144
+ return send(200, { success: true, handler, method });
145
+ }
146
+ });
147
+ });
148
+ servers.push(server);
149
+ await new Promise((resolve) => server.listen(0, '127.0.0.1', resolve));
150
+ const { port } = server.address();
151
+ const client = new WordPressClient({
152
+ id: '953dde4e',
153
+ name: '953dde4e',
154
+ url: `http://127.0.0.1:${port}`,
155
+ apiKey: 'respira_test_key',
156
+ });
157
+ return {
158
+ client,
159
+ hits,
160
+ executions: (name) => executions.get(name) || 0,
161
+ hitsFor: (route) => hits.filter((h) => h.route === route),
162
+ };
163
+ }
164
+ const trail = (hits) => hits.map((h) => `${h.wire}->${h.dispatched}`);
165
+ // ---------------------------------------------------------------------------
166
+ // The reported case: a rewritten verb on a route with no GET handler.
167
+ // ---------------------------------------------------------------------------
168
+ test('update_bricks_global_class on a host that rewrites PUT to GET is retried as POST with the method override', async () => {
169
+ const site = await startSite({ rewriteVerbs: true });
170
+ const route = '/respira/v2/bricks/global-classes/zzprobe1';
171
+ const result = await site.client.callRestV2('PUT', '/bricks/global-classes/zzprobe1', { name: 'zzprobe1', settings: {} });
172
+ assert.equal(result.handler, 'update_global_class');
173
+ assert.equal(result.method, 'PUT', 'WordPress dispatched the retry to the PUT handler');
174
+ assert.equal(site.executions('update_global_class'), 1, 'the handler ran exactly once');
175
+ const hits = site.hitsFor(route);
176
+ assert.deepEqual(trail(hits), ['PUT->GET', 'POST->PUT']);
177
+ assert.equal(hits[1].overrideHeader, 'PUT');
178
+ assert.equal(hits[1].methodParam, 'PUT');
179
+ assert.equal(hits[1].originalMethod, 'PUT');
180
+ assert.ok(hits[0].idempotencyKey, 'the write carried an Idempotency-Key');
181
+ assert.equal(hits[1].idempotencyKey, hits[0].idempotencyKey, 'the retry reuses the same key');
182
+ });
183
+ test('delete_bricks_global_class on the same host is retried as POST with the DELETE override', async () => {
184
+ const site = await startSite({ rewriteVerbs: true });
185
+ const route = '/respira/v2/bricks/global-classes/zzprobe1';
186
+ const result = await site.client.callRestV2('DELETE', '/bricks/global-classes/zzprobe1');
187
+ assert.equal(result.handler, 'delete_global_class');
188
+ assert.equal(site.executions('delete_global_class'), 1);
189
+ assert.equal(site.executions('update_global_class'), 0, 'the POST retry must not land on the update handler');
190
+ const hits = site.hitsFor(route);
191
+ assert.deepEqual(trail(hits), ['DELETE->GET', 'POST->DELETE']);
192
+ assert.equal(hits[1].overrideHeader, 'DELETE');
193
+ assert.equal(hits[1].methodParam, 'DELETE');
194
+ });
195
+ test('PATCH (append_theme_file) on the same host is retried as POST with the PATCH override', async () => {
196
+ const site = await startSite({ rewriteVerbs: true });
197
+ const result = await site.client.appendThemeFile('style.css', '/* appended */');
198
+ assert.equal(result.handler, 'append_theme_file');
199
+ assert.equal(result.method, 'PATCH');
200
+ assert.equal(site.executions('append_theme_file'), 1);
201
+ const hits = site.hits.filter((h) => h.route.endsWith('/append'));
202
+ assert.deepEqual(trail(hits), ['PATCH->GET', 'POST->PATCH']);
203
+ });
204
+ test('if the host strips the override header, the _method parameter still keeps a DELETE off the update handler', async () => {
205
+ const site = await startSite({ rewriteVerbs: true, stripOverride: { alsoMethodParam: false } });
206
+ const result = await site.client.callRestV2('DELETE', '/bricks/global-classes/zzprobe1');
207
+ assert.equal(result.handler, 'delete_global_class');
208
+ assert.equal(site.executions('update_global_class'), 0);
209
+ });
210
+ // ---------------------------------------------------------------------------
211
+ // The same host on a route that ALSO has a GET handler: silent read.
212
+ // ---------------------------------------------------------------------------
213
+ test('update_page on a host that rewrites PUT to GET does not report the READ as a successful update', async () => {
214
+ const site = await startSite({ rewriteVerbs: true });
215
+ const page = await site.client.updatePage(7, { title: 'New title' });
216
+ assert.equal(page.handler, 'update_page', `got the ${page.read ? 'read' : 'unknown'} payload instead of the update result`);
217
+ assert.equal(page.method, 'PUT');
218
+ assert.equal(site.executions('update_page'), 1, 'the update ran exactly once');
219
+ assert.deepEqual(trail(site.hitsFor('/respira/v2/pages/7')), ['PUT->GET', 'POST->PUT']);
220
+ });
221
+ test('delete_page on a host that rewrites DELETE to GET really deletes instead of returning the page', async () => {
222
+ const site = await startSite({ rewriteVerbs: true });
223
+ const result = await site.client.deletePage(7);
224
+ assert.equal(result.handler, 'delete_page');
225
+ assert.equal(site.executions('delete_page'), 1);
226
+ assert.deepEqual(trail(site.hitsFor('/respira/v1/pages/7')), ['DELETE->GET', 'POST->DELETE']);
227
+ });
228
+ // ---------------------------------------------------------------------------
229
+ // When the retry does not help, the ORIGINAL error is what surfaces.
230
+ // ---------------------------------------------------------------------------
231
+ test('a genuinely missing route surfaces the original rest_no_route, noting the override retry, after exactly one retry', async () => {
232
+ const site = await startSite();
233
+ const route = '/respira/v2/bricks/not-a-route/1';
234
+ await assert.rejects(() => site.client.callRestV2('PUT', '/bricks/not-a-route/1', { x: 1 }), (err) => {
235
+ assert.equal(err.name, 'rest_no_route', 'the original WordPress code, not the retry\'s');
236
+ assert.match(err.message, /\[attempted: PUT /, 'the original request, not the POST retry');
237
+ assert.match(err.message, /X-HTTP-Method-Override: PUT/, 'says a method-override retry was attempted');
238
+ return true;
239
+ });
240
+ assert.deepEqual(trail(site.hitsFor(route)), ['PUT->PUT', 'POST->PUT'], 'one retry, then stop');
241
+ });
242
+ test('when the host strips both override forms the retry fails and the original error is surfaced', async () => {
243
+ const site = await startSite({ rewriteVerbs: true, stripOverride: { alsoMethodParam: true } });
244
+ await assert.rejects(() => site.client.appendThemeFile('style.css', '/* appended */'), (err) => {
245
+ assert.equal(err.name, 'rest_no_route');
246
+ assert.match(err.message, /\[attempted: PATCH /);
247
+ assert.match(err.message, /X-HTTP-Method-Override: PATCH/);
248
+ return true;
249
+ });
250
+ assert.equal(site.executions('append_theme_file'), 0);
251
+ });
252
+ // ---------------------------------------------------------------------------
253
+ // DELETE blocked in front of WordPress now gets the fallback PUT always had.
254
+ // ---------------------------------------------------------------------------
255
+ test('a DELETE the edge rejects with an HTML 405 is retried as POST with the DELETE override', async () => {
256
+ const site = await startSite({ edgeBlock: { method: 'DELETE', status: 405 } });
257
+ const result = await site.client.deletePage(7);
258
+ assert.equal(result.handler, 'delete_page');
259
+ assert.equal(site.executions('delete_page'), 1);
260
+ const hits = site.hitsFor('/respira/v1/pages/7');
261
+ assert.deepEqual(hits.map((h) => h.wire), ['POST'], 'the blocked DELETE never reached WordPress; the retry did');
262
+ assert.equal(hits[0].dispatched, 'DELETE');
263
+ });
264
+ // ---------------------------------------------------------------------------
265
+ // Things that must NOT change.
266
+ // ---------------------------------------------------------------------------
267
+ test('a real WordPress 403 on a DELETE is a decision and is not retried', async () => {
268
+ const site = await startSite({ denyDelete: true });
269
+ await assert.rejects(() => site.client.callRestV2('DELETE', '/bricks/global-classes/zzprobe1'), /not allowed to delete/);
270
+ assert.equal(site.hitsFor('/respira/v2/bricks/global-classes/zzprobe1').filter((h) => h.wire === 'POST').length, 0);
271
+ });
272
+ test('the existing non_wordpress PUT fallback is unchanged: a plain POST, no method override', async () => {
273
+ const site = await startSite({ edgeBlock: { method: 'PUT', status: 403 } });
274
+ const result = await site.client.callRestV2('PUT', '/bricks/global-classes/zzprobe1', { name: 'zzprobe1' });
275
+ assert.equal(result.handler, 'update_global_class');
276
+ const posts = site.hitsFor('/respira/v2/bricks/global-classes/zzprobe1').filter((h) => h.wire === 'POST');
277
+ assert.equal(posts.length, 1);
278
+ assert.equal(posts[0].dispatched, 'POST');
279
+ assert.equal(posts[0].overrideHeader, '');
280
+ assert.equal(posts[0].methodParam, '');
281
+ assert.equal(posts[0].originalMethod, 'PUT');
282
+ });
283
+ test('a healthy host answers the write once, with no retry', async () => {
284
+ const site = await startSite();
285
+ const page = await site.client.updatePage(7, { title: 'New title' });
286
+ assert.equal(page.handler, 'update_page');
287
+ assert.deepEqual(trail(site.hitsFor('/respira/v2/pages/7')), ['PUT->PUT']);
288
+ });
289
+ test('a plugin without the idempotency headers (pre-8.0) is never second-guessed', async () => {
290
+ const site = await startSite({ idempotencyHeaders: false });
291
+ await site.client.updatePage(7, { title: 'New title' });
292
+ assert.deepEqual(trail(site.hitsFor('/respira/v2/pages/7')), ['PUT->PUT']);
293
+ });
294
+ // ---------------------------------------------------------------------------
295
+ // diagnose_connection used to grade this host as fine.
296
+ // ---------------------------------------------------------------------------
297
+ test('diagnose_connection reports a host that rewrites PUT into GET', async () => {
298
+ const site = await startSite({ rewriteVerbs: true });
299
+ const report = await site.client.diagnoseConnection({ probe_timeout_ms: 5000 });
300
+ assert.equal(report.write_method_verdict, 'verb_rewritten');
301
+ assert.equal(report.write_method_blocked, true);
302
+ assert.equal(report.write_method_probe.put_dispatched_as_read, true);
303
+ assert.ok(report.recommendations.some((r) => /X-HTTP-Method-Override/.test(r) && /GET/.test(r)), 'a recommendation names what happened and what the connector does about it');
304
+ });
305
+ //# sourceMappingURL=953dde4e-verb-rewriting-host-method-override.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"953dde4e-verb-rewriting-host-method-override.test.js","sourceRoot":"","sources":["../../src/__tests__/953dde4e-verb-rewriting-host-method-override.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Tickets dfe2d236, 6e94282d, addc289e, 77aae005, c2dd9d87 (staging3.naa.edu,
3
+ * mcp 8.3.20, plugin 8.8.13).
4
+ *
5
+ * The configured URL was the bare host. The web server 301s every request on
6
+ * it to the www host. Every read worked and every write failed, in 190 to
7
+ * 600ms, with "Could not connect ... no response received" and
8
+ * `respira_write_outcome_unknown`. diagnose_connection called the site fine.
9
+ *
10
+ * Mechanism (reproduced here against the real client, no adapter mocking):
11
+ *
12
+ * 1. guardAgainstMethodDowngrade() (ticket 1b0eda46, mcp 8.3.18) runs as the
13
+ * `beforeRedirect` hook and throws for a write that meets a 301/302/303.
14
+ * 2. follow-redirects catches that throw in `_processResponse`, wraps it as a
15
+ * RedirectionError (code ERR_FR_REDIRECTION_FAILURE, the guard's error as
16
+ * `.cause`) and emits it as a request 'error'.
17
+ * 3. axios turns a request 'error' into an AxiosError with `.request` and NO
18
+ * `.response`.
19
+ * 4. handleError's no-response branch therefore described a clean, immediate
20
+ * 301 answer as a dropped connection and a write of unknown outcome.
21
+ *
22
+ * Reads follow the redirect (the guard ignores them), which is why reads
23
+ * worked. And a 307/308 was followed for writes too, which re-sent the write,
24
+ * API key header included, to whatever host the Location named.
25
+ *
26
+ * The "www" host here is a second local server on a different port. Host and
27
+ * port together are what a URL's `host` compares, so this exercises the same
28
+ * code path as staging3.naa.edu -> www.staging3.naa.edu.
29
+ */
30
+ export {};
31
+ //# sourceMappingURL=dfe2d236-site-url-redirects-on-write.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dfe2d236-site-url-redirects-on-write.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/dfe2d236-site-url-redirects-on-write.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG"}