@respira/wordpress-mcp-server 7.2.6 → 7.2.8

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/dist/server.d.ts CHANGED
@@ -149,6 +149,17 @@ export declare class RespiraWordPressServer {
149
149
  * the plugin source (the first release tag that contains each route).
150
150
  */
151
151
  private static readonly TOOL_MIN_PLUGIN_VERSION;
152
+ /**
153
+ * Capability negotiation Phase 2 (7.2.7): the feature flag each version-skewed
154
+ * tool needs. From plugin 7.4.20 the compatibility endpoint declares these
155
+ * flags from its LIVE registered REST routes, so this is an exact "does the
156
+ * route exist on this install" signal — more precise than the version map
157
+ * above and self-maintaining. The version map stays as the fallback for
158
+ * plugins older than 7.4.20, which do not declare per-capability features yet.
159
+ */
160
+ private static readonly TOOL_REQUIRED_FEATURE;
161
+ /** First plugin version that declares per-capability features (Phase 2). */
162
+ private static readonly FEATURE_DECL_MIN_PLUGIN_VERSION;
152
163
  /** True when version `a` is strictly older than `b` (both "x.y.z"). */
153
164
  private static versionLt;
154
165
  private filterToolsByContext;
@@ -1 +1 @@
1
- {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAoBH,OAAO,KAAK,EAAE,mBAAmB,EAAe,MAAM,kBAAkB,CAAC;AAkQzE,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;IAEpC,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;IAsVvE,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;IAgDxB;;;;;;;;;;;;;;OAcG;YACW,WAAW;IAiJzB;;;;;;;;;OASG;YACW,kBAAkB;IAiHhC;;;;;;;;;;;;;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;IAuwFtB;;;;;;OAMG;IACH,yEAAyE;IACzE,OAAO,CAAC,mBAAmB,CAAoD;IAC/E,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,gBAAgB,CAAU;IAElD;;;;;;;;;OASG;IACH,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,uBAAuB,CAQ7C;IAEF,uEAAuE;IACvE,OAAO,CAAC,MAAM,CAAC,SAAS;YAWV,oBAAoB;YAwEpB,2BAA2B;IAazC;;;;OAIG;YACW,cAAc;IAY5B,OAAO,CAAC,mBAAmB;IAwT3B;;;;;;;;;;;;OAYG;IACH,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,qBAAqB,CAe3C;IAEF;;;;OAIG;IACH,OAAO,CAAC,0BAA0B;YAmCpB,cAAc;IAqG5B;;;;;;;;;;;;;;;;;;;OAmBG;IACH,OAAO,CAAC,eAAe;YAuCT,gBAAgB;IA22B9B;;OAEG;IACH,OAAO,CAAC,iBAAiB;IAwUzB;;OAEG;IACH,OAAO,CAAC,sBAAsB;IA6UxB,GAAG;CAyCV"}
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAoBH,OAAO,KAAK,EAAE,mBAAmB,EAAe,MAAM,kBAAkB,CAAC;AAqTzE,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;IAEpC,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;IAsVvE,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;IAgDxB;;;;;;;;;;;;;;OAcG;YACW,WAAW;IAiJzB;;;;;;;;;OASG;YACW,kBAAkB;IAiHhC;;;;;;;;;;;;;OAaG;IACH,OAAO,CAAC,6BAA6B;IA0BrC;;;;;;;;;;;;OAYG;IACH,OAAO,CAAC,yBAAyB;IA+BjC,OAAO,CAAC,eAAe;IAmBvB,OAAO,CAAC,aAAa;YA2QP,kBAAkB;YA6BlB,yBAAyB;IASvC;;;OAGG;IACH,OAAO,CAAC,oBAAoB;YAyBd,QAAQ;IAuwFtB;;;;;;OAMG;IACH,yEAAyE;IACzE,OAAO,CAAC,mBAAmB,CAAoD;IAC/E,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,gBAAgB,CAAU;IAElD;;;;;;;;;OASG;IACH,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,uBAAuB,CAQ7C;IAEF;;;;;;;OAOG;IACH,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,qBAAqB,CAQ3C;IAEF,4EAA4E;IAC5E,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,+BAA+B,CAAY;IAEnE,uEAAuE;IACvE,OAAO,CAAC,MAAM,CAAC,SAAS;YAWV,oBAAoB;YAqFpB,2BAA2B;IAazC;;;;OAIG;YACW,cAAc;IAY5B,OAAO,CAAC,mBAAmB;IAwT3B;;;;;;;;;;;;OAYG;IACH,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,qBAAqB,CAe3C;IAEF;;;;OAIG;IACH,OAAO,CAAC,0BAA0B;YAmCpB,cAAc;IAqG5B;;;;;;;;;;;;;;;;;;;OAmBG;IACH,OAAO,CAAC,eAAe;YAuCT,gBAAgB;IA22B9B;;OAEG;IACH,OAAO,CAAC,iBAAiB;IAwUzB;;OAEG;IACH,OAAO,CAAC,sBAAsB;IA6UxB,GAAG;CAyCV"}
package/dist/server.js CHANGED
@@ -17,6 +17,58 @@ import { getBricksTools, dispatchBricksTool } from './bricks-tools.js';
17
17
  import { getElementorTools, dispatchElementorTool } from './elementor-tools.js';
18
18
  import { getAcfTools, ACF_TOOL_NAMES } from './acf-tools.js';
19
19
  import { getUsageEmitter, deriveToolKind } from './usage-emitter.js';
20
+ /**
21
+ * Per-tool argument aliases. AI clients (especially Cowork / Claude desktop)
22
+ * frequently pass a natural-but-wrong key for a value: `pageId` / `page_id`
23
+ * instead of `id`, `filePath` instead of `file`. Because the dispatch never
24
+ * validated required params, a misnamed id silently produced a broken request
25
+ * like `/wp-json/respira/v2/pages/undefined`, and a misnamed file produced an
26
+ * empty upload that failed with "Could not connect — no response received".
27
+ *
28
+ * This map fills the canonical key a handler reads from the first present
29
+ * synonym, and ONLY when the canonical key is absent — an explicit value is
30
+ * never overwritten. Keyed by the canonical (wordpress_*) tool name, i.e.
31
+ * after normalizeToolName().
32
+ */
33
+ const TOOL_ARG_ALIASES = {
34
+ wordpress_read_page: { id: ['page_id', 'pageId', 'post_id', 'postId'] },
35
+ wordpress_read_post: { id: ['post_id', 'postId', 'page_id', 'pageId'] },
36
+ wordpress_create_page_duplicate: {
37
+ original_id: ['page_id', 'pageId', 'id', 'original_page_id', 'originalId'],
38
+ },
39
+ wordpress_create_post_duplicate: {
40
+ original_id: ['post_id', 'postId', 'id', 'original_post_id', 'originalId'],
41
+ },
42
+ wordpress_get_snapshot: { snapshot_uuid: ['snapshot_id', 'snapshotId', 'uuid', 'id'] },
43
+ wordpress_find_element: { post_id: ['page_id', 'pageId', 'id', 'postId'] },
44
+ wordpress_upload_media: {
45
+ file: ['filePath', 'file_path', 'path', 'url', 'file_url', 'fileUrl'],
46
+ filename: ['file_name', 'fileName', 'name'],
47
+ },
48
+ };
49
+ /**
50
+ * Fill canonical argument keys from known synonyms, in place. No-op for tools
51
+ * without an alias entry and for args that already carry the canonical key.
52
+ */
53
+ function normalizeToolArgs(canonicalName, args) {
54
+ if (!args || typeof args !== 'object')
55
+ return;
56
+ const aliases = TOOL_ARG_ALIASES[canonicalName];
57
+ if (!aliases)
58
+ return;
59
+ const record = args;
60
+ const isSet = (v) => v !== undefined && v !== null && v !== '';
61
+ for (const [canonical, synonyms] of Object.entries(aliases)) {
62
+ if (isSet(record[canonical]))
63
+ continue;
64
+ for (const syn of synonyms) {
65
+ if (isSet(record[syn])) {
66
+ record[canonical] = record[syn];
67
+ break;
68
+ }
69
+ }
70
+ }
71
+ }
20
72
  /**
21
73
  * Read the server version from package.json at module load time so the
22
74
  * MCP handshake, the `instructions` block, and the version-checker all
@@ -1163,6 +1215,12 @@ Allowlist: css, scss, less, json. PHP / JS theme writes are intentionally out of
1163
1215
  // (wordpress_redeem_token) because normalizeToolName rewrites the
1164
1216
  // respira_* alias to wordpress_* before dispatch.
1165
1217
  const canonical = this.normalizeToolName(name).canonical;
1218
+ // Tolerate natural-but-wrong arg names from AI clients (pageId/page_id
1219
+ // vs id, filePath vs file). Fills canonical keys from synonyms in place
1220
+ // so a misnamed required param no longer yields /pages/undefined or an
1221
+ // empty "no response received" upload. No-op for tools without aliases;
1222
+ // an explicit canonical value is never overwritten.
1223
+ normalizeToolArgs(canonical, args);
1166
1224
  if (canonical === 'wordpress_redeem_token') {
1167
1225
  // redeemInstallToken returns a plain object. Wrap it in the standard
1168
1226
  // MCP content envelope — the early-exit path bypasses the normal
@@ -4272,6 +4330,25 @@ Allowlist: css, scss, less, json. PHP / JS theme writes are intentionally out of
4272
4330
  wordpress_write_theme_file: '7.0.42',
4273
4331
  wordpress_append_theme_file: '7.0.42',
4274
4332
  };
4333
+ /**
4334
+ * Capability negotiation Phase 2 (7.2.7): the feature flag each version-skewed
4335
+ * tool needs. From plugin 7.4.20 the compatibility endpoint declares these
4336
+ * flags from its LIVE registered REST routes, so this is an exact "does the
4337
+ * route exist on this install" signal — more precise than the version map
4338
+ * above and self-maintaining. The version map stays as the fallback for
4339
+ * plugins older than 7.4.20, which do not declare per-capability features yet.
4340
+ */
4341
+ static TOOL_REQUIRED_FEATURE = {
4342
+ wordpress_run_pagespeed_audit: 'pagespeed',
4343
+ wordpress_analyze_pagespeed: 'pagespeed',
4344
+ wordpress_abilities_gap_report: 'abilities',
4345
+ wordpress_invoke_ability: 'abilities',
4346
+ wordpress_read_theme_file: 'theme_files',
4347
+ wordpress_write_theme_file: 'theme_files',
4348
+ wordpress_append_theme_file: 'theme_files',
4349
+ };
4350
+ /** First plugin version that declares per-capability features (Phase 2). */
4351
+ static FEATURE_DECL_MIN_PLUGIN_VERSION = '7.4.20';
4275
4352
  /** True when version `a` is strictly older than `b` (both "x.y.z"). */
4276
4353
  static versionLt(a, b) {
4277
4354
  const pa = a.split('.').map((n) => parseInt(n, 10) || 0);
@@ -4312,6 +4389,12 @@ Allowlist: css, scss, less, json. PHP / JS theme writes are intentionally out of
4312
4389
  /* non-fatal — fall through with whatever version we have (possibly null) */
4313
4390
  }
4314
4391
  const pluginVersion = this.currentSite.pluginVersion ?? null;
4392
+ // Phase 2 (7.2.7): plugin 7.4.20+ declares per-capability features from its
4393
+ // live REST routes; trust those exactly. Older plugins do not, so they fall
4394
+ // back to the version map in the per-tool gate below.
4395
+ const pluginFeatures = this.currentSite.pluginFeatures ?? [];
4396
+ const declaresFeatures = !!pluginVersion &&
4397
+ !RespiraWordPressServer.versionLt(pluginVersion, RespiraWordPressServer.FEATURE_DECL_MIN_PLUGIN_VERSION);
4315
4398
  const detectedBuilder = (context?.page_builder?.name || '').toLowerCase();
4316
4399
  const hasWooCommerce = Boolean(context?.woocommerce?.active || context?.addons?.woocommerce?.installed);
4317
4400
  return tools.filter((tool) => {
@@ -4336,10 +4419,16 @@ Allowlist: css, scss, less, json. PHP / JS theme writes are intentionally out of
4336
4419
  if (name.startsWith('woocommerce_')) {
4337
4420
  return hasWooCommerce;
4338
4421
  }
4339
- // Capability negotiation: hide tools whose plugin route only exists from a
4340
- // newer plugin version than the connected site runs (avoids rest_no_route
4341
- // 404s on version-skewed sites). Conservative: only when the plugin version
4342
- // is KNOWN and below the tool's verified minimum.
4422
+ // Capability negotiation: hide tools whose plugin route the connected site
4423
+ // does not expose (avoids rest_no_route 404s on version-skewed sites).
4424
+ // Prefer the plugin's declared per-capability features (7.4.20+, computed
4425
+ // from its live routes — exact), and fall back to the verified version map
4426
+ // for older plugins that do not declare them. Both are conservative: a tool
4427
+ // is hidden only on a positive signal that its route is absent.
4428
+ const reqFeature = RespiraWordPressServer.TOOL_REQUIRED_FEATURE[name];
4429
+ if (reqFeature && declaresFeatures) {
4430
+ return pluginFeatures.includes(reqFeature);
4431
+ }
4343
4432
  const minPv = RespiraWordPressServer.TOOL_MIN_PLUGIN_VERSION[name];
4344
4433
  if (minPv && pluginVersion && RespiraWordPressServer.versionLt(pluginVersion, minPv)) {
4345
4434
  return false;