@respira/wordpress-mcp-server 8.0.1 → 8.0.3
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/README.md +2 -0
- package/TOOL_CATALOG.md +3 -1
- package/dist/__tests__/config-write-target.test.d.ts +2 -0
- package/dist/__tests__/config-write-target.test.d.ts.map +1 -0
- package/dist/__tests__/config-write-target.test.js +39 -0
- package/dist/__tests__/config-write-target.test.js.map +1 -0
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +102 -9
- package/dist/server.js.map +1 -1
- package/dist/usage-emitter.d.ts +1 -0
- package/dist/usage-emitter.d.ts.map +1 -1
- package/dist/usage-emitter.js +7 -0
- package/dist/usage-emitter.js.map +1 -1
- package/dist/wordpress-client.d.ts +14 -0
- package/dist/wordpress-client.d.ts.map +1 -1
- package/dist/wordpress-client.js +25 -0
- package/dist/wordpress-client.js.map +1 -1
- package/package.json +1 -1
- package/tool-capabilities.json +28 -4
package/README.md
CHANGED
|
@@ -768,6 +768,8 @@ npm cache clean --force
|
|
|
768
768
|
|
|
769
769
|
API key validation happens server-side in the WordPress plugin. The MCP server passes credentials but does not store or validate them.
|
|
770
770
|
|
|
771
|
+
`wordpress_run_security_audit` performs bounded read-only evidence collection for reviewed advisories. `wordpress_update_core_security` installs only an exact, catalog-approved security patch after backup confirmation and explicit two-step approval, and returns post-update verification evidence.
|
|
772
|
+
|
|
771
773
|
Report vulnerabilities to security@respira.press.
|
|
772
774
|
|
|
773
775
|
---
|
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: **171 core tools**, **105 WooCommerce tools**, **276 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
|
|
|
@@ -144,6 +144,7 @@ The runtime applies site context and per-site governance, so an individual conne
|
|
|
144
144
|
- `wordpress_restore_session` — write; hosted/stdio
|
|
145
145
|
- `wordpress_restore_snapshot` — write; native + hosted
|
|
146
146
|
- `wordpress_run_pagespeed_audit` — read; hosted/stdio
|
|
147
|
+
- `wordpress_run_security_audit` — read; native + hosted
|
|
147
148
|
- `wordpress_scan_page_accessibility` — read; hosted/stdio
|
|
148
149
|
- `wordpress_search_abilities` — read; native + hosted
|
|
149
150
|
- `wordpress_search_docs` — read; hosted/stdio
|
|
@@ -152,6 +153,7 @@ The runtime applies site context and per-site governance, so an individual conne
|
|
|
152
153
|
- `wordpress_switch_site` — write; native + hosted
|
|
153
154
|
- `wordpress_update_acf_field_group` — write; hosted/stdio
|
|
154
155
|
- `wordpress_update_comment` — write; native + hosted
|
|
156
|
+
- `wordpress_update_core_security` — write; native + hosted
|
|
155
157
|
- `wordpress_update_custom_post` — write; native + hosted
|
|
156
158
|
- `wordpress_update_design_token` — write; native + hosted
|
|
157
159
|
- `wordpress_update_element` — write; native + hosted
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config-write-target.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/config-write-target.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* redeem must write the config where the server will READ it.
|
|
3
|
+
*
|
|
4
|
+
* The write target used to be hardcoded to homedir()/.respira/config.json,
|
|
5
|
+
* ignoring RESPIRA_CONFIG_FILE. On a plain machine those agree, which is why it
|
|
6
|
+
* looked correct. They diverge exactly where it hurts: a host that sets a
|
|
7
|
+
* different path, or a sandbox whose home is not the user's home. Redeem then
|
|
8
|
+
* reported success, wrote a real file, and the next boot read elsewhere and
|
|
9
|
+
* found nothing, which surfaced to the customer as "the installer could not
|
|
10
|
+
* write it itself".
|
|
11
|
+
*/
|
|
12
|
+
import { test } from 'node:test';
|
|
13
|
+
import assert from 'node:assert/strict';
|
|
14
|
+
import { homedir } from 'node:os';
|
|
15
|
+
import { join } from 'node:path';
|
|
16
|
+
import { expandHomePath } from '../config.js';
|
|
17
|
+
// The resolution rule redeem and loadConfig must agree on, asserted directly.
|
|
18
|
+
function writeTarget(env) {
|
|
19
|
+
const explicit = env?.trim();
|
|
20
|
+
return explicit ? expandHomePath(explicit) : join(homedir(), '.respira', 'config.json');
|
|
21
|
+
}
|
|
22
|
+
test('with no env set, writes the default location', () => {
|
|
23
|
+
assert.equal(writeTarget(undefined), join(homedir(), '.respira', 'config.json'));
|
|
24
|
+
});
|
|
25
|
+
test('RESPIRA_CONFIG_FILE wins over the default', () => {
|
|
26
|
+
assert.equal(writeTarget('/opt/respira/config.json'), '/opt/respira/config.json');
|
|
27
|
+
});
|
|
28
|
+
test('write target matches read target for the token Cowork ships', () => {
|
|
29
|
+
// The exact value in the Cowork MCP config. If these two ever disagree the
|
|
30
|
+
// config is written somewhere the server never looks.
|
|
31
|
+
const shipped = '${HOME}/.respira/config.json';
|
|
32
|
+
assert.equal(writeTarget(shipped), expandHomePath(shipped));
|
|
33
|
+
assert.ok(!writeTarget(shipped).includes('${'), 'target must be fully resolved');
|
|
34
|
+
});
|
|
35
|
+
test('a Windows path with a space survives', () => {
|
|
36
|
+
const win = 'C:\\Users\\Lebowski Firma\\.respira\\config.json';
|
|
37
|
+
assert.equal(writeTarget(win), win);
|
|
38
|
+
});
|
|
39
|
+
//# sourceMappingURL=config-write-target.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config-write-target.test.js","sourceRoot":"","sources":["../../src/__tests__/config-write-target.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,MAAM,MAAM,oBAAoB,CAAC;AACxC,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAE9C,8EAA8E;AAC9E,SAAS,WAAW,CAAC,GAAuB;IAC1C,MAAM,QAAQ,GAAG,GAAG,EAAE,IAAI,EAAE,CAAC;IAC7B,OAAO,QAAQ,CAAC,CAAC,CAAC,cAAc,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,EAAE,EAAE,UAAU,EAAE,aAAa,CAAC,CAAC;AAC1F,CAAC;AAED,IAAI,CAAC,8CAA8C,EAAE,GAAG,EAAE;IACxD,MAAM,CAAC,KAAK,CAAC,WAAW,CAAC,SAAS,CAAC,EAAE,IAAI,CAAC,OAAO,EAAE,EAAE,UAAU,EAAE,aAAa,CAAC,CAAC,CAAC;AACnF,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,2CAA2C,EAAE,GAAG,EAAE;IACrD,MAAM,CAAC,KAAK,CAAC,WAAW,CAAC,0BAA0B,CAAC,EAAE,0BAA0B,CAAC,CAAC;AACpF,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,6DAA6D,EAAE,GAAG,EAAE;IACvE,2EAA2E;IAC3E,sDAAsD;IACtD,MAAM,OAAO,GAAG,8BAA8B,CAAC;IAC/C,MAAM,CAAC,KAAK,CAAC,WAAW,CAAC,OAAO,CAAC,EAAE,cAAc,CAAC,OAAO,CAAC,CAAC,CAAC;IAC5D,MAAM,CAAC,EAAE,CAAC,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,+BAA+B,CAAC,CAAC;AACnF,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,sCAAsC,EAAE,GAAG,EAAE;IAChD,MAAM,GAAG,GAAG,kDAAkD,CAAC;IAC/D,MAAM,CAAC,KAAK,CAAC,WAAW,CAAC,GAAG,CAAC,EAAE,GAAG,CAAC,CAAC;AACtC,CAAC,CAAC,CAAC"}
|
package/dist/server.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAqBH,OAAO,KAAK,EAAE,mBAAmB,EAAe,MAAM,kBAAkB,CAAC;AAsQzE,qBAAa,sBAAsB;IACjC,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,WAAW,CAAgC;IACnD,OAAO,CAAC,KAAK,CAA2C;IACxD,OAAO,CAAC,aAAa,CAAuB;IAC5C,OAAO,CAAC,cAAc,CAAwB;IAC9C,OAAO,CAAC,YAAY,CAA4B;IAChD,8EAA8E;IAC9E,OAAO,CAAC,YAAY,CAA4B;IAChD,8EAA8E;IAC9E,OAAO,CAAC,mBAAmB,CAAS;IACpC,iFAAiF;IACjF,OAAO,CAAC,iBAAiB,CAAK;IAC9B,iFAAiF;IACjF,OAAO,CAAC,mBAAmB,CAAgC;IAE3D,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,kBAAkB,CAAsB;IAEhE;;;OAGG;IACH,OAAO,CAAC,iBAAiB;IAWzB;;;OAGG;IACH,OAAO,CAAC,iBAAiB;gBA4Bb,WAAW,EAAE,mBAAmB,EAAE,EAAE,YAAY,CAAC,EAAE,MAAM,EAAE;IAiVvE,OAAO,CAAC,cAAc;IAItB;;;;;;;;;OASG;IACH,OAAO,CAAC,oBAAoB;IAkB5B;;;;;;;;;;;OAWG;IACH,OAAO,CAAC,aAAa;IA4BrB,gEAAgE;IAChE,OAAO,CAAC,aAAa;IAUrB;;;;;;OAMG;YACW,UAAU;IA2CxB;;;;;;;;;;;;;;OAcG;YACW,WAAW;IAiJzB;;;;;;;;;OASG;YACW,kBAAkB;
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAqBH,OAAO,KAAK,EAAE,mBAAmB,EAAe,MAAM,kBAAkB,CAAC;AAsQzE,qBAAa,sBAAsB;IACjC,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,WAAW,CAAgC;IACnD,OAAO,CAAC,KAAK,CAA2C;IACxD,OAAO,CAAC,aAAa,CAAuB;IAC5C,OAAO,CAAC,cAAc,CAAwB;IAC9C,OAAO,CAAC,YAAY,CAA4B;IAChD,8EAA8E;IAC9E,OAAO,CAAC,YAAY,CAA4B;IAChD,8EAA8E;IAC9E,OAAO,CAAC,mBAAmB,CAAS;IACpC,iFAAiF;IACjF,OAAO,CAAC,iBAAiB,CAAK;IAC9B,iFAAiF;IACjF,OAAO,CAAC,mBAAmB,CAAgC;IAE3D,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,kBAAkB,CAAsB;IAEhE;;;OAGG;IACH,OAAO,CAAC,iBAAiB;IAWzB;;;OAGG;IACH,OAAO,CAAC,iBAAiB;gBA4Bb,WAAW,EAAE,mBAAmB,EAAE,EAAE,YAAY,CAAC,EAAE,MAAM,EAAE;IAiVvE,OAAO,CAAC,cAAc;IAItB;;;;;;;;;OASG;IACH,OAAO,CAAC,oBAAoB;IAkB5B;;;;;;;;;;;OAWG;IACH,OAAO,CAAC,aAAa;IA4BrB,gEAAgE;IAChE,OAAO,CAAC,aAAa;IAUrB;;;;;;OAMG;YACW,UAAU;IA2CxB;;;;;;;;;;;;;;OAcG;YACW,WAAW;IAiJzB;;;;;;;;;OASG;YACW,kBAAkB;IAyIhC;;;;;;;;;;;;;;;;;;;;;;;;;;OA0BG;YACW,qBAAqB;YAiBrB,kBAAkB;IA6FhC,yFAAyF;IACzF,OAAO,CAAC,gBAAgB;IASxB;;;;;;;;;;;;;OAaG;IACH,OAAO,CAAC,6BAA6B;IA0BrC;;;;;;;;;;;;OAYG;IACH,OAAO,CAAC,yBAAyB;IA+BjC,OAAO,CAAC,eAAe;IAmBvB,OAAO,CAAC,aAAa;YAmQP,kBAAkB;YA6BlB,yBAAyB;IASvC;;;OAGG;IACH,OAAO,CAAC,oBAAoB;YAyBd,QAAQ;IAuvGtB;;;;;;OAMG;IACH;;;;;;;;;;;;;oDAagD;IAChD,OAAO,CAAC,mBAAmB,CAAoD;IAC/E,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,gBAAgB,CAAU;IAClD,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,wBAAwB,CAAU;IAC1D,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,yBAAyB,CAAS;IAC1D;;mDAE+C;IAC/C,OAAO,CAAC,0BAA0B,CAAoC;IAEtE;;;;;;;;OAQG;YACW,0BAA0B;YAyC1B,oBAAoB;YA6CpB,2BAA2B;IAQzC;;;;OAIG;YACW,cAAc;IAQ5B,OAAO,CAAC,mBAAmB;IAo7C3B;;;;;;;;;;;;OAYG;IACH,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,qBAAqB,CAe3C;IAEF;;;;OAIG;IACH,OAAO,CAAC,0BAA0B;YAmCpB,cAAc;IA4G5B,oEAAoE;IACpE,OAAO,CAAC,iBAAiB;IAoBzB;;;;;;;;;;;;;;;;;;;OAmBG;IACH,OAAO,CAAC,eAAe;YAuCT,gBAAgB;IAo0C9B;;OAEG;IACH,OAAO,CAAC,iBAAiB;IA8YzB;;OAEG;IACH,OAAO,CAAC,sBAAsB;IA6UxB,GAAG;CAyCV"}
|
package/dist/server.js
CHANGED
|
@@ -1009,17 +1009,36 @@ Allowlist: css, scss, less, json. PHP / JS theme writes are intentionally out of
|
|
|
1009
1009
|
// Write ~/.respira/config.json with the canonical shape. Make the
|
|
1010
1010
|
// directory if absent. If a file already exists, back it up first
|
|
1011
1011
|
// so the user can recover the previous config if they want.
|
|
1012
|
+
// Write to the path this server will actually READ on next boot.
|
|
1013
|
+
//
|
|
1014
|
+
// This used to hardcode homedir()/.respira/config.json and ignore
|
|
1015
|
+
// RESPIRA_CONFIG_FILE entirely. On a plain machine the two agree, so it
|
|
1016
|
+
// looked fine. They diverge exactly where it hurts: when the host sets
|
|
1017
|
+
// RESPIRA_CONFIG_FILE to somewhere else, or when the server runs in a
|
|
1018
|
+
// sandbox whose home is not the home the user is looking at. In that case
|
|
1019
|
+
// redeem reported success, wrote a real file, and the next boot read a
|
|
1020
|
+
// different path and found nothing. The user is then told to place the
|
|
1021
|
+
// file by hand, which is the "the installer could not write it itself"
|
|
1022
|
+
// complaint, and the honest description of it is that we wrote to the
|
|
1023
|
+
// wrong place and blamed the environment.
|
|
1024
|
+
//
|
|
1025
|
+
// RESPIRA_CONFIG_FILE wins, expanded the same way loadConfig expands it so
|
|
1026
|
+
// the two can never disagree about what ${HOME} means.
|
|
1012
1027
|
let configPath;
|
|
1013
1028
|
try {
|
|
1014
1029
|
const { writeFileSync, mkdirSync, existsSync, copyFileSync } = await import('node:fs');
|
|
1015
|
-
const { join } = await import('node:path');
|
|
1030
|
+
const { join, dirname } = await import('node:path');
|
|
1016
1031
|
const { homedir } = await import('node:os');
|
|
1017
|
-
const
|
|
1032
|
+
const { expandHomePath } = await import('./config.js');
|
|
1033
|
+
const explicit = process.env.RESPIRA_CONFIG_FILE?.trim();
|
|
1034
|
+
configPath = explicit
|
|
1035
|
+
? expandHomePath(explicit)
|
|
1036
|
+
: join(homedir(), '.respira', 'config.json');
|
|
1037
|
+
const dir = dirname(configPath);
|
|
1018
1038
|
if (!existsSync(dir))
|
|
1019
1039
|
mkdirSync(dir, { recursive: true });
|
|
1020
|
-
configPath = join(dir, 'config.json');
|
|
1021
1040
|
if (existsSync(configPath)) {
|
|
1022
|
-
const backupPath =
|
|
1041
|
+
const backupPath = `${configPath}.bak.${Date.now()}`;
|
|
1023
1042
|
try {
|
|
1024
1043
|
copyFileSync(configPath, backupPath);
|
|
1025
1044
|
}
|
|
@@ -1028,10 +1047,13 @@ Allowlist: css, scss, less, json. PHP / JS theme writes are intentionally out of
|
|
|
1028
1047
|
writeFileSync(configPath, JSON.stringify(config, null, 2), 'utf8');
|
|
1029
1048
|
}
|
|
1030
1049
|
catch (err) {
|
|
1050
|
+
const attempted = process.env.RESPIRA_CONFIG_FILE?.trim() || '~/.respira/config.json';
|
|
1031
1051
|
return {
|
|
1032
1052
|
success: false,
|
|
1033
1053
|
error: 'write_failed',
|
|
1034
|
-
|
|
1054
|
+
// Name the ABSOLUTE path that failed. "could not write
|
|
1055
|
+
// ~/.respira/config.json" sends someone looking in the wrong home.
|
|
1056
|
+
message: `Token redeemed but could not write the config to ${attempted} (${err?.message || 'unknown error'}). Download it from respira.press/dashboard/mcp and place it at that exact path.`,
|
|
1035
1057
|
};
|
|
1036
1058
|
}
|
|
1037
1059
|
const sites = config.sites.map((s) => ({
|
|
@@ -1039,7 +1061,7 @@ Allowlist: css, scss, less, json. PHP / JS theme writes are intentionally out of
|
|
|
1039
1061
|
url: s.url,
|
|
1040
1062
|
}));
|
|
1041
1063
|
const hasConfigB64 = Boolean(process.env.RESPIRA_CONFIG_B64);
|
|
1042
|
-
const baseMessage = `Connected ${sites.length} site${sites.length === 1 ? '' : 's'} via the Respira Cowork token. The config was written to
|
|
1064
|
+
const baseMessage = `Connected ${sites.length} site${sites.length === 1 ? '' : 's'} via the Respira Cowork token. The config was written to ${configPath}. Restart this Cowork chat (or open a new one) so the MCP server picks up the new sites.`;
|
|
1043
1065
|
const message = hasConfigB64
|
|
1044
1066
|
? `${baseMessage} IMPORTANT: your MCP server is currently configured via the RESPIRA_CONFIG_B64 environment variable, which takes precedence over the file on disk. To activate the new site, update RESPIRA_CONFIG_B64 in your agent's MCP server config with the base64-encoded contents of the new ~/.respira/config.json file, then restart the agent.`
|
|
1045
1067
|
: baseMessage;
|
|
@@ -3280,6 +3302,54 @@ Allowlist: css, scss, less, json. PHP / JS theme writes are intentionally out of
|
|
|
3280
3302
|
},
|
|
3281
3303
|
readOnlyHint: true,
|
|
3282
3304
|
},
|
|
3305
|
+
// Safety & Security center
|
|
3306
|
+
{
|
|
3307
|
+
name: 'wordpress_run_security_audit',
|
|
3308
|
+
description: 'Run a bounded, read-only WordPress security evidence pass. Returns authoritative WordPress/WooCommerce versions, official core checksum mismatches, administrators, standard and must-use plugins, WordPress cron hooks, known advisory indicators, scan coverage and explicit host-level limitations. Set deep_scan=true to inspect uploads and must-use plugin paths for executable files. This tool never deletes or rewrites anything.',
|
|
3309
|
+
inputSchema: {
|
|
3310
|
+
type: 'object',
|
|
3311
|
+
properties: {
|
|
3312
|
+
advisory_ids: {
|
|
3313
|
+
type: 'array',
|
|
3314
|
+
items: { type: 'string' },
|
|
3315
|
+
description: 'Optional reviewed advisory IDs to record as the audit scope, for example wordpress-wp2shell-2026.',
|
|
3316
|
+
},
|
|
3317
|
+
deep_scan: {
|
|
3318
|
+
type: 'boolean',
|
|
3319
|
+
default: false,
|
|
3320
|
+
description: 'Inspect uploads and must-use plugin paths with strict file, time, symlink and result limits.',
|
|
3321
|
+
},
|
|
3322
|
+
},
|
|
3323
|
+
},
|
|
3324
|
+
readOnlyHint: true,
|
|
3325
|
+
},
|
|
3326
|
+
{
|
|
3327
|
+
name: 'wordpress_update_core_security',
|
|
3328
|
+
description: 'Update WordPress core to an exact, catalog-approved security release on the current release branch. Requires a recent recoverable backup, then explicit two-step approval. The first call returns respira_approval_required and an approval_token; repeat the same arguments with that token. The result verifies the running version, REST/frontend boot and official checksums. WordPress core has no automatic rollback, and the receipt says so.',
|
|
3329
|
+
inputSchema: {
|
|
3330
|
+
type: 'object',
|
|
3331
|
+
properties: {
|
|
3332
|
+
advisory_id: {
|
|
3333
|
+
type: 'string',
|
|
3334
|
+
description: 'Reviewed security advisory ID, currently wordpress-wp2shell-2026.',
|
|
3335
|
+
},
|
|
3336
|
+
target_version: {
|
|
3337
|
+
type: 'string',
|
|
3338
|
+
description: 'Exact approved patch version on the current branch: 6.8.6, 6.9.5 or 7.0.2 for wp2shell.',
|
|
3339
|
+
},
|
|
3340
|
+
backup_confirmed: {
|
|
3341
|
+
type: 'boolean',
|
|
3342
|
+
description: 'True only after the operator confirms a recent recoverable backup exists.',
|
|
3343
|
+
},
|
|
3344
|
+
approval_token: {
|
|
3345
|
+
type: 'string',
|
|
3346
|
+
description: 'Approval token returned by the first blocked call. Echo it unchanged on the second call.',
|
|
3347
|
+
},
|
|
3348
|
+
},
|
|
3349
|
+
required: ['advisory_id', 'target_version', 'backup_confirmed'],
|
|
3350
|
+
},
|
|
3351
|
+
idempotentHint: true,
|
|
3352
|
+
},
|
|
3283
3353
|
// Accessibility tools
|
|
3284
3354
|
{
|
|
3285
3355
|
name: 'wordpress_list_accessibility_scans',
|
|
@@ -6992,6 +7062,15 @@ Allowlist: css, scss, less, json. PHP / JS theme writes are intentionally out of
|
|
|
6992
7062
|
return await client.analyzeAEO(args.page_id);
|
|
6993
7063
|
case 'wordpress_check_structured_data':
|
|
6994
7064
|
return await client.checkStructuredData(args.page_id);
|
|
7065
|
+
case 'wordpress_run_security_audit':
|
|
7066
|
+
return await client.runSecurityAudit(args.advisory_ids || [], args.deep_scan === true);
|
|
7067
|
+
case 'wordpress_update_core_security':
|
|
7068
|
+
return await client.updateCoreSecurity({
|
|
7069
|
+
advisoryId: args.advisory_id,
|
|
7070
|
+
targetVersion: args.target_version,
|
|
7071
|
+
backupConfirmed: args.backup_confirmed === true,
|
|
7072
|
+
approvalToken: args.approval_token,
|
|
7073
|
+
});
|
|
6995
7074
|
// Accessibility
|
|
6996
7075
|
case 'wordpress_list_accessibility_scans':
|
|
6997
7076
|
return await client.listAccessibilityScans({
|
|
@@ -7608,12 +7687,26 @@ Allowlist: css, scss, less, json. PHP / JS theme writes are intentionally out of
|
|
|
7608
7687
|
// Widget shortcuts and Bricks tools — dynamic dispatch.
|
|
7609
7688
|
default: {
|
|
7610
7689
|
// Check if this is a Bricks deep tool.
|
|
7611
|
-
|
|
7690
|
+
//
|
|
7691
|
+
// `client` (resolved above via resolveClient(args), the whole switch
|
|
7692
|
+
// statement's site_id-aware client) — NOT this.currentSite, which is
|
|
7693
|
+
// only the globally active site. Every case above this default: uses
|
|
7694
|
+
// `client.callRestV2`; this fallback used `this.currentSite!` instead,
|
|
7695
|
+
// so any respira_*_bricks_typography (and every other tool that only
|
|
7696
|
+
// resolves here, e.g. the rest of bricks-tools.ts / elementor-tools.ts)
|
|
7697
|
+
// silently ignored a supplied site_id and read/wrote the active site
|
|
7698
|
+
// instead — while still reporting the requested site_id back in the
|
|
7699
|
+
// response, since that came from `args`, not from which site actually
|
|
7700
|
+
// executed the call. Reported by E.M. (wellnessist) 2026-07-29: a
|
|
7701
|
+
// `respira_update_bricks_typography` call with site_id set to site B
|
|
7702
|
+
// (site A active) overwrote site A's global variables/theme styles,
|
|
7703
|
+
// a cross-site write with no rollback for either store.
|
|
7704
|
+
const bricksResult = await dispatchBricksTool(name, args, (method, path, body) => client.callRestV2(method, path, body));
|
|
7612
7705
|
if (bricksResult !== null) {
|
|
7613
7706
|
return bricksResult;
|
|
7614
7707
|
}
|
|
7615
|
-
// Check if this is an Elementor deep tool.
|
|
7616
|
-
const elementorResult = await dispatchElementorTool(name, args, (method, path, body) =>
|
|
7708
|
+
// Check if this is an Elementor deep tool. Same site_id fix as above.
|
|
7709
|
+
const elementorResult = await dispatchElementorTool(name, args, (method, path, body) => client.callRestV2(method, path, body));
|
|
7617
7710
|
if (elementorResult !== null) {
|
|
7618
7711
|
return elementorResult;
|
|
7619
7712
|
}
|