@respira/wordpress-mcp-server 6.19.2 → 6.19.4
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.map +1 -1
- package/dist/server.js +97 -60
- package/dist/server.js.map +1 -1
- package/dist/setup.d.ts.map +1 -1
- package/dist/setup.js +7 -2
- package/dist/setup.js.map +1 -1
- package/dist/wordpress-client.d.ts +27 -14
- package/dist/wordpress-client.d.ts.map +1 -1
- package/dist/wordpress-client.js +122 -15
- package/dist/wordpress-client.js.map +1 -1
- package/package.json +2 -2
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;AAoBH,OAAO,KAAK,EAAE,mBAAmB,EAAe,MAAM,kBAAkB,CAAC;AAiLzE,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;IAmUvE,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;IAyIzB;;;;;;;;;OASG;YACW,kBAAkB;IA6FhC,OAAO,CAAC,eAAe;IAmBvB,OAAO,CAAC,aAAa;
|
|
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;AAiLzE,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;IAmUvE,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;IAyIzB;;;;;;;;;OASG;YACW,kBAAkB;IA6FhC,OAAO,CAAC,eAAe;IAmBvB,OAAO,CAAC,aAAa;YAuOP,kBAAkB;YA6BlB,yBAAyB;IASvC;;;OAGG;IACH,OAAO,CAAC,oBAAoB;YAyBd,QAAQ;IA28EtB;;;;;;OAMG;IACH,yEAAyE;IACzE,OAAO,CAAC,mBAAmB,CAAoD;IAC/E,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,gBAAgB,CAAU;YAEpC,oBAAoB;YAqDpB,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;IAsvB9B;;OAEG;IACH,OAAO,CAAC,iBAAiB;IA+SzB;;OAEG;IACH,OAAO,CAAC,sBAAsB;IA6UxB,GAAG;CAyCV"}
|
package/dist/server.js
CHANGED
|
@@ -968,13 +968,30 @@ Allowlist: css, scss, less, json. PHP / JS theme writes are intentionally out of
|
|
|
968
968
|
}
|
|
969
969
|
// Per-call watchdog: a single hung handler must never wedge the stdio
|
|
970
970
|
// loop for every subsequent tool call. If the deadline fires, the race
|
|
971
|
-
// rejects with ToolTimeoutError
|
|
972
|
-
//
|
|
973
|
-
//
|
|
971
|
+
// rejects with ToolTimeoutError, AND we destroy the WP client's HTTP
|
|
972
|
+
// agents to free the orphaned socket so the next tool call doesn't
|
|
973
|
+
// queue behind it at the agent layer. v6.19.3 bug fc2bfdf3 — Mauricio
|
|
974
|
+
// Piper (Fischers Fritze) reported that after one tool deadlocked
|
|
975
|
+
// every subsequent call, including diagnose_connection, also hung 4
|
|
976
|
+
// min; only killing Claude Desktop recovered. Per-client agents (now
|
|
977
|
+
// bounded at maxSockets:16, keepAlive:false) plus this destroy-on-
|
|
978
|
+
// trip make orphans cost at most one socket for at most the axios
|
|
979
|
+
// 30s response timeout, instead of indefinitely blocking the pool.
|
|
974
980
|
const toolTimeoutMs = getMaxToolTimeoutMs();
|
|
975
981
|
let timeoutHandle;
|
|
982
|
+
const siteForAbort = this.currentSite;
|
|
976
983
|
const toolTimeout = new Promise((_, reject) => {
|
|
977
|
-
timeoutHandle = setTimeout(() =>
|
|
984
|
+
timeoutHandle = setTimeout(() => {
|
|
985
|
+
try {
|
|
986
|
+
if (siteForAbort && typeof siteForAbort.abortInFlightRequests === 'function') {
|
|
987
|
+
siteForAbort.abortInFlightRequests();
|
|
988
|
+
}
|
|
989
|
+
}
|
|
990
|
+
catch {
|
|
991
|
+
// Don't block the rejection on cleanup.
|
|
992
|
+
}
|
|
993
|
+
reject(new ToolTimeoutError(name, toolTimeoutMs));
|
|
994
|
+
}, toolTimeoutMs);
|
|
978
995
|
});
|
|
979
996
|
try {
|
|
980
997
|
this.currentSite.setCurrentToolName(name);
|
|
@@ -1254,42 +1271,6 @@ Allowlist: css, scss, less, json. PHP / JS theme writes are intentionally out of
|
|
|
1254
1271
|
},
|
|
1255
1272
|
readOnlyHint: true,
|
|
1256
1273
|
},
|
|
1257
|
-
{
|
|
1258
|
-
name: 'wordpress_search_abilities',
|
|
1259
|
-
description: 'SEARCH the curated WordPress abilities directory across all known plugins (Elementor, Yoast SEO, WooCommerce, Jetpack, ACF, Akismet, plus Respira\'s own 151 tools — 163 total today, growing weekly via auto-pull). ' +
|
|
1260
|
-
'Use this whenever the user asks "is there an ability that does X", "can WordPress do Y", "what plugins expose Z as MCP", "how do i automate W on my site". ' +
|
|
1261
|
-
'Each result is enriched with per-site context: is_installed (is the plugin active on THIS site), is_inhaled (has the admin opted the ability into Respira\'s MCP surface), install_url (wp.org page when not installed), inhale_admin_url (Respira admin tab when installed but not inhaled), and how_to_invoke (the exact tool call to make when ready). ' +
|
|
1262
|
-
'After finding a matching ability, if is_inhaled is true you can call it via wordpress_invoke_ability. Otherwise surface install_url or inhale_admin_url to the user so they can opt in. ' +
|
|
1263
|
-
'No auth gates: this surface is open to every agent connected to Respira. The directory itself is global; the enrichment is per-site.',
|
|
1264
|
-
inputSchema: {
|
|
1265
|
-
type: 'object',
|
|
1266
|
-
properties: {
|
|
1267
|
-
q: {
|
|
1268
|
-
type: 'string',
|
|
1269
|
-
description: 'Free-text query — matches against ability_name, label, description, plugin_name, category. Optional; an empty query returns the top of the directory.',
|
|
1270
|
-
},
|
|
1271
|
-
plugin: {
|
|
1272
|
-
type: 'string',
|
|
1273
|
-
description: 'Restrict to a single plugin by wp.org slug (e.g. "wordpress-seo") or display name ("Yoast SEO"). Optional.',
|
|
1274
|
-
},
|
|
1275
|
-
category: {
|
|
1276
|
-
type: 'string',
|
|
1277
|
-
description: 'Restrict to a single category (e.g. "SEO", "E-commerce", "Page Builder", "Custom Fields"). Optional.',
|
|
1278
|
-
},
|
|
1279
|
-
kind: {
|
|
1280
|
-
type: 'string',
|
|
1281
|
-
enum: ['read', 'write'],
|
|
1282
|
-
description: 'Restrict to read-only or write abilities. Optional.',
|
|
1283
|
-
},
|
|
1284
|
-
limit: {
|
|
1285
|
-
type: 'integer',
|
|
1286
|
-
description: 'Max results to return (default 20, max 100).',
|
|
1287
|
-
default: 20,
|
|
1288
|
-
},
|
|
1289
|
-
},
|
|
1290
|
-
},
|
|
1291
|
-
readOnlyHint: true,
|
|
1292
|
-
},
|
|
1293
1274
|
{
|
|
1294
1275
|
name: 'wordpress_invoke_ability',
|
|
1295
1276
|
description: 'Invoke an inhaled WordPress Abilities API ability through the Respira safety wrapper. ' +
|
|
@@ -4379,14 +4360,6 @@ Allowlist: css, scss, less, json. PHP / JS theme writes are intentionally out of
|
|
|
4379
4360
|
return await client.getDiviMigrationReadiness();
|
|
4380
4361
|
case 'wordpress_abilities_gap_report':
|
|
4381
4362
|
return await client.getAbilitiesGapReport();
|
|
4382
|
-
case 'wordpress_search_abilities':
|
|
4383
|
-
return await client.searchAbilities({
|
|
4384
|
-
q: args?.q,
|
|
4385
|
-
plugin: args?.plugin,
|
|
4386
|
-
category: args?.category,
|
|
4387
|
-
kind: args?.kind,
|
|
4388
|
-
limit: args?.limit,
|
|
4389
|
-
});
|
|
4390
4363
|
case 'wordpress_invoke_ability':
|
|
4391
4364
|
return await client.invokeInhaledAbility(args?.ability, args?.args || {});
|
|
4392
4365
|
case 'wordpress_list_pages':
|
|
@@ -4464,8 +4437,40 @@ Allowlist: css, scss, less, json. PHP / JS theme writes are intentionally out of
|
|
|
4464
4437
|
return await client.deletePost(args.id, args.force);
|
|
4465
4438
|
case 'wordpress_list_media':
|
|
4466
4439
|
return await client.listMedia(args);
|
|
4467
|
-
case 'wordpress_upload_media':
|
|
4440
|
+
case 'wordpress_upload_media': {
|
|
4441
|
+
// v6.19.4: pre-flight size + transport check before the upload.
|
|
4442
|
+
// The MCP-quality dashboard showed upload_media at 50.7% success,
|
|
4443
|
+
// p95 = 120 seconds, 34.8% of calls timing out >60s — meaning
|
|
4444
|
+
// the watchdog was firing on every other call. Almost all of the
|
|
4445
|
+
// long tail is the agent passing a base64 payload of a large
|
|
4446
|
+
// image without realising the per-call timeout will kill it.
|
|
4447
|
+
// Surface a clear "too large for one-shot upload, use chunked
|
|
4448
|
+
// workflow" error BEFORE the upload starts instead of waiting
|
|
4449
|
+
// 120 seconds to fail.
|
|
4450
|
+
const sizeBytes = typeof args.file === 'string' ? Math.floor(args.file.length * 0.75) : 0;
|
|
4451
|
+
const MAX_SAFE_BYTES = 5 * 1024 * 1024; // 5MB after base64 → ~3.75MB binary
|
|
4452
|
+
const HARD_LIMIT_BYTES = 25 * 1024 * 1024; // 25MB → server-side rejected
|
|
4453
|
+
if (sizeBytes > HARD_LIMIT_BYTES) {
|
|
4454
|
+
return {
|
|
4455
|
+
success: false,
|
|
4456
|
+
error: 'file_too_large',
|
|
4457
|
+
message: `Refused to upload: payload is ~${(sizeBytes / 1024 / 1024).toFixed(1)}MB after base64 decode, hard limit is ${HARD_LIMIT_BYTES / 1024 / 1024}MB per call. WordPress would reject this and your call would time out at ~2 minutes. Resize the image client-side (recommended: 2000px max dimension, <2MB) and retry. For PDFs and other documents, the operator should upload manually via wp-admin Media Library.`,
|
|
4458
|
+
preflight: { size_bytes: sizeBytes, hard_limit_bytes: HARD_LIMIT_BYTES, recommended_max_bytes: MAX_SAFE_BYTES },
|
|
4459
|
+
};
|
|
4460
|
+
}
|
|
4461
|
+
if (sizeBytes > MAX_SAFE_BYTES) {
|
|
4462
|
+
// Soft warning — still attempt the upload, but tell the agent
|
|
4463
|
+
// up front this is high-risk for timeout so it can plan around
|
|
4464
|
+
// it (e.g. set expectations with the user, retry on a smaller
|
|
4465
|
+
// crop, etc) instead of looping.
|
|
4466
|
+
const r = await client.uploadMedia(args.file, args.filename, args.mime_type, args.title, args.alt, args.caption);
|
|
4467
|
+
if (r && typeof r === 'object') {
|
|
4468
|
+
r.preflight_warning = `Large payload (~${(sizeBytes / 1024 / 1024).toFixed(1)}MB after decode). Uploads above ${MAX_SAFE_BYTES / 1024 / 1024}MB succeed in ~50% of calls in current telemetry due to per-call watchdog timeout on slow hosts. If this upload times out, ask the user to crop / compress the image client-side before retrying.`;
|
|
4469
|
+
}
|
|
4470
|
+
return r;
|
|
4471
|
+
}
|
|
4468
4472
|
return await client.uploadMedia(args.file, args.filename, args.mime_type, args.title, args.alt, args.caption);
|
|
4473
|
+
}
|
|
4469
4474
|
case 'wordpress_extract_builder_content':
|
|
4470
4475
|
return await client.extractBuilderContent(args.builder, args.page_id);
|
|
4471
4476
|
case 'wordpress_find_builder_targets':
|
|
@@ -4788,8 +4793,29 @@ Allowlist: css, scss, less, json. PHP / JS theme writes are intentionally out of
|
|
|
4788
4793
|
return await client.callRestV2('POST', `/builder/elements/find/${post_id}`, rest);
|
|
4789
4794
|
}
|
|
4790
4795
|
case 'wordpress_update_element': {
|
|
4796
|
+
// v6.19.4: surface the new plugin-side write_was_noop warning
|
|
4797
|
+
// prominently in the tool result. The plugin v7.0.61+ adds
|
|
4798
|
+
// `write_diag` + `warning` keys to the response when post_content
|
|
4799
|
+
// didn't change despite a successful write. Hoist them to the
|
|
4800
|
+
// top of the response payload (above `changes`) and prepend the
|
|
4801
|
+
// warning_hint to the message so the agent sees it before any
|
|
4802
|
+
// other content. The dashboard shows 81% in-session retry rate
|
|
4803
|
+
// on this tool — exactly the silent-fail signature where agents
|
|
4804
|
+
// ignore deeply-nested signals and just retry. Front-load.
|
|
4791
4805
|
const { post_id, ...rest } = args;
|
|
4792
|
-
|
|
4806
|
+
const r = await client.callRestV2('POST', `/builder/elements/update/${post_id}`, rest);
|
|
4807
|
+
if (r && typeof r === 'object' && r.warning === 'write_was_noop') {
|
|
4808
|
+
const hint = r.warning_hint || 'Write succeeded at the API layer but post_content did not change. Verify with respira_extract_builder_content and escalate to the user instead of retrying.';
|
|
4809
|
+
const original = typeof r.message === 'string' ? r.message : '';
|
|
4810
|
+
r.message = `⚠️ NO-OP DETECTED — ${hint}\n\nOriginal message: ${original}`.trim();
|
|
4811
|
+
// Also surface as a top-level error-shaped field so MCP clients
|
|
4812
|
+
// that branch on `r.success === false` don't silently treat
|
|
4813
|
+
// this as a clean success. Keep `success: true` so existing
|
|
4814
|
+
// call sites don't break, but make the agent-visible payload
|
|
4815
|
+
// unambiguous.
|
|
4816
|
+
r.agent_must_escalate = true;
|
|
4817
|
+
}
|
|
4818
|
+
return r;
|
|
4793
4819
|
}
|
|
4794
4820
|
case 'wordpress_move_element': {
|
|
4795
4821
|
const { post_id, ...rest } = args;
|
|
@@ -4921,22 +4947,30 @@ Allowlist: css, scss, less, json. PHP / JS theme writes are intentionally out of
|
|
|
4921
4947
|
},
|
|
4922
4948
|
{
|
|
4923
4949
|
name: 'wordpress_move_element',
|
|
4924
|
-
description: 'Move an element to a different container or position within the page.',
|
|
4950
|
+
description: 'Move an element to a different container or position within the page. Locate the element using the same identifier_type/identifier_value pattern as respira_find_element / respira_remove_element. The destination is a container PATH (e.g. "0.1" for sections[0].rows[1], or "root"/empty for top level), not a container element id.',
|
|
4925
4951
|
inputSchema: {
|
|
4926
4952
|
type: 'object',
|
|
4927
4953
|
properties: {
|
|
4928
4954
|
post_id: { type: 'number', description: 'Page/post ID' },
|
|
4929
|
-
|
|
4930
|
-
|
|
4955
|
+
identifier_type: {
|
|
4956
|
+
type: 'string',
|
|
4957
|
+
enum: ['id', 'css_class', 'text', 'widget_type', 'global_id', 'admin_label', 'type', 'path'],
|
|
4958
|
+
description: 'How to locate the element to move (same enum as respira_find_element).',
|
|
4959
|
+
},
|
|
4960
|
+
identifier_value: {
|
|
4931
4961
|
type: 'string',
|
|
4932
|
-
description: '
|
|
4962
|
+
description: 'Value matching identifier_type.',
|
|
4963
|
+
},
|
|
4964
|
+
target_container_path: {
|
|
4965
|
+
type: 'string',
|
|
4966
|
+
description: 'Dot-separated container path inside the target page (e.g. "0.1.0"). Pass "root" or "" to move to the top level.',
|
|
4933
4967
|
},
|
|
4934
4968
|
position: {
|
|
4935
4969
|
type: 'number',
|
|
4936
|
-
description: 'Position index within the target container (0-based)',
|
|
4970
|
+
description: 'Position index within the target container (0-based). Pass -1 to append.',
|
|
4937
4971
|
},
|
|
4938
4972
|
},
|
|
4939
|
-
required: ['post_id', '
|
|
4973
|
+
required: ['post_id', 'identifier_type', 'identifier_value', 'target_container_path', 'position'],
|
|
4940
4974
|
},
|
|
4941
4975
|
},
|
|
4942
4976
|
{
|
|
@@ -5002,19 +5036,22 @@ Allowlist: css, scss, less, json. PHP / JS theme writes are intentionally out of
|
|
|
5002
5036
|
},
|
|
5003
5037
|
{
|
|
5004
5038
|
name: 'wordpress_reorder_elements',
|
|
5005
|
-
description: 'Reorder child elements within a container.',
|
|
5039
|
+
description: 'Reorder child elements within a container. The container is identified by its PATH inside the page (e.g. "0.1" for sections[0].rows[1], or "root"/"" for the top level).',
|
|
5006
5040
|
inputSchema: {
|
|
5007
5041
|
type: 'object',
|
|
5008
5042
|
properties: {
|
|
5009
5043
|
post_id: { type: 'number', description: 'Page/post ID' },
|
|
5010
|
-
|
|
5044
|
+
container_path: {
|
|
5045
|
+
type: 'string',
|
|
5046
|
+
description: 'Dot-separated container path (e.g. "0.1.0"). Pass "root" or "" for the top-level container.',
|
|
5047
|
+
},
|
|
5011
5048
|
new_order: {
|
|
5012
5049
|
type: 'array',
|
|
5013
|
-
description: 'Array of element IDs in the desired order',
|
|
5050
|
+
description: 'Array of element IDs in the desired order.',
|
|
5014
5051
|
items: { type: 'string' },
|
|
5015
5052
|
},
|
|
5016
5053
|
},
|
|
5017
|
-
required: ['post_id', '
|
|
5054
|
+
required: ['post_id', 'container_path', 'new_order'],
|
|
5018
5055
|
},
|
|
5019
5056
|
},
|
|
5020
5057
|
// --- Composite Tools ---
|