@respira/wordpress-mcp-server 6.11.12 → 6.12.0
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 +13 -0
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +261 -145
- package/dist/server.js.map +1 -1
- package/dist/wordpress-client.d.ts +5 -3
- package/dist/wordpress-client.d.ts.map +1 -1
- package/dist/wordpress-client.js +70 -6
- package/dist/wordpress-client.js.map +1 -1
- package/package.json +2 -2
package/dist/server.js
CHANGED
|
@@ -6,11 +6,37 @@
|
|
|
6
6
|
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
7
7
|
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
8
8
|
import { CallToolRequestSchema, ListToolsRequestSchema, } from '@modelcontextprotocol/sdk/types.js';
|
|
9
|
+
import { readFileSync } from 'fs';
|
|
10
|
+
import { dirname, resolve } from 'path';
|
|
11
|
+
import { fileURLToPath } from 'url';
|
|
9
12
|
import { WordPressClient } from './wordpress-client.js';
|
|
10
13
|
import { RespiraVersionChecker } from './version-checker.js';
|
|
11
14
|
import { getBricksTools, dispatchBricksTool } from './bricks-tools.js';
|
|
12
15
|
import { getElementorTools, dispatchElementorTool } from './elementor-tools.js';
|
|
13
16
|
import { getAcfTools, ACF_TOOL_NAMES } from './acf-tools.js';
|
|
17
|
+
/**
|
|
18
|
+
* Read the server version from package.json at module load time so the
|
|
19
|
+
* MCP handshake, the `instructions` block, and the version-checker all
|
|
20
|
+
* report the actual installed version instead of a stale hard-coded
|
|
21
|
+
* string. K.B. on the customer site reported v6.11.13 self-identifying
|
|
22
|
+
* as 6.11.4 because the constant below was last hand-bumped at the
|
|
23
|
+
* 6.11.4 release and never tracked subsequent npm publishes. Mirrors
|
|
24
|
+
* the existing MCP_CLIENT_VERSION helper in wordpress-client.ts.
|
|
25
|
+
*/
|
|
26
|
+
const MCP_SERVER_VERSION = (() => {
|
|
27
|
+
try {
|
|
28
|
+
const currentDir = dirname(fileURLToPath(import.meta.url));
|
|
29
|
+
const packageJsonPath = resolve(currentDir, '../package.json');
|
|
30
|
+
const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf8'));
|
|
31
|
+
if (typeof packageJson.version === 'string' && packageJson.version.trim()) {
|
|
32
|
+
return packageJson.version.trim();
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
catch {
|
|
36
|
+
// Fall through to the literal fallback below.
|
|
37
|
+
}
|
|
38
|
+
return 'unknown';
|
|
39
|
+
})();
|
|
14
40
|
/**
|
|
15
41
|
* Thrown by the per-call watchdog when a tool handler exceeds its deadline.
|
|
16
42
|
* Surfaces as a structured `tool_timeout` response so the stdio loop stays
|
|
@@ -32,6 +58,43 @@ class ToolTimeoutError extends Error {
|
|
|
32
58
|
function getMaxToolTimeoutMs() {
|
|
33
59
|
return Math.max(5000, parseInt(process.env.RESPIRA_MAX_TOOL_TIMEOUT_MS || '120000', 10));
|
|
34
60
|
}
|
|
61
|
+
/**
|
|
62
|
+
* v6.12.0: Tool names that do NOT receive the optional site_id input parameter.
|
|
63
|
+
* These tools operate on global Respira config or pre-site setup state, so a
|
|
64
|
+
* per-call site override doesn't apply:
|
|
65
|
+
* - list_sites: returns all configured sites
|
|
66
|
+
* - get_active_site: returns the global current site (a per-call override
|
|
67
|
+
* would be meaningless here; if you want a specific site's summary, look
|
|
68
|
+
* at list_sites)
|
|
69
|
+
* - switch_site: already takes site_id with the explicit semantics of
|
|
70
|
+
* flipping the global current site (different intent from per-call override)
|
|
71
|
+
* - redeem_token: pre-site setup; no site exists yet
|
|
72
|
+
*/
|
|
73
|
+
const SITE_AGNOSTIC_TOOLS = new Set([
|
|
74
|
+
'wordpress_list_sites',
|
|
75
|
+
'wordpress_get_active_site',
|
|
76
|
+
'wordpress_switch_site',
|
|
77
|
+
'wordpress_redeem_token',
|
|
78
|
+
'respira_list_sites',
|
|
79
|
+
'respira_get_active_site',
|
|
80
|
+
'respira_switch_site',
|
|
81
|
+
'respira_redeem_token',
|
|
82
|
+
]);
|
|
83
|
+
/**
|
|
84
|
+
* v6.12.0: Schema fragment auto-injected into every non-agnostic tool's
|
|
85
|
+
* inputSchema.properties. Lets callers override the active site on a single
|
|
86
|
+
* tool call without flipping the global current site.
|
|
87
|
+
*
|
|
88
|
+
* Solves the Cowork multi-chat contamination: when Cowork runs many chat
|
|
89
|
+
* sessions through one shared MCP server process, switch_site mutates the
|
|
90
|
+
* global current site for all sessions. Passing site_id per call sidesteps
|
|
91
|
+
* the shared state entirely. T.S. (studioscaler) reported the symptom
|
|
92
|
+
* 2026-05-16 across his multi-site workflow.
|
|
93
|
+
*/
|
|
94
|
+
const SITE_ID_PROPERTY = {
|
|
95
|
+
type: 'string',
|
|
96
|
+
description: 'Optional. Override the active site for this single call without changing the global current site. Use this when running multiple Cowork chats against different WordPress sites in parallel so each chat can pin its own target site per tool call.',
|
|
97
|
+
};
|
|
35
98
|
export class RespiraWordPressServer {
|
|
36
99
|
server;
|
|
37
100
|
currentSite = null;
|
|
@@ -43,7 +106,7 @@ export class RespiraWordPressServer {
|
|
|
43
106
|
allowedSites = null;
|
|
44
107
|
/** Whether the plugin version warning has already been shown this session. */
|
|
45
108
|
versionWarningShown = false;
|
|
46
|
-
static MCP_SERVER_VERSION =
|
|
109
|
+
static MCP_SERVER_VERSION = MCP_SERVER_VERSION;
|
|
47
110
|
/**
|
|
48
111
|
* Normalize a tool name: respira_* → wordpress_* for switch dispatch.
|
|
49
112
|
* Tracks whether the deprecated wordpress_* name was used.
|
|
@@ -333,6 +396,35 @@ Use respira_get_builder_info first to detect which builder is active. Then use t
|
|
|
333
396
|
}
|
|
334
397
|
return this.getSiteSummary(this.currentSite);
|
|
335
398
|
}
|
|
399
|
+
/**
|
|
400
|
+
* Resolve which WordPressClient should service this tool call.
|
|
401
|
+
*
|
|
402
|
+
* Order:
|
|
403
|
+
* 1) explicit args.site_id (per-call override; introduced in v6.12.0)
|
|
404
|
+
* 2) the global current site (`this.currentSite`)
|
|
405
|
+
*
|
|
406
|
+
* Throws if neither is set, or if the supplied site_id is unknown / not
|
|
407
|
+
* allowed for this MCP configuration group.
|
|
408
|
+
*
|
|
409
|
+
* @since 6.12.0
|
|
410
|
+
*/
|
|
411
|
+
resolveClient(args) {
|
|
412
|
+
if (args && typeof args.site_id === 'string' && args.site_id.length > 0) {
|
|
413
|
+
const explicit = this.sites.get(args.site_id);
|
|
414
|
+
if (!explicit) {
|
|
415
|
+
const available = Array.from(this.sites.keys()).join(', ') || '(none configured)';
|
|
416
|
+
throw new Error(`Site with ID "${args.site_id}" not found in this MCP configuration. Available: ${available}`);
|
|
417
|
+
}
|
|
418
|
+
if (!this.isSiteAllowed(explicit)) {
|
|
419
|
+
throw new Error(`Site "${args.site_id}" is not in this MCP configuration group.`);
|
|
420
|
+
}
|
|
421
|
+
return explicit;
|
|
422
|
+
}
|
|
423
|
+
if (!this.currentSite) {
|
|
424
|
+
throw new Error('No active site. Either pass site_id on this tool call or run respira_switch_site / respira_redeem_token first.');
|
|
425
|
+
}
|
|
426
|
+
return this.currentSite;
|
|
427
|
+
}
|
|
336
428
|
/** Check if a site is visible given RESPIRA_SITES filtering. */
|
|
337
429
|
isSiteAllowed(site) {
|
|
338
430
|
if (!this.allowedSites)
|
|
@@ -1137,20 +1229,20 @@ Use respira_get_builder_info first to detect which builder is active. Then use t
|
|
|
1137
1229
|
},
|
|
1138
1230
|
{
|
|
1139
1231
|
name: 'wordpress_extract_builder_content',
|
|
1140
|
-
description: 'Extract the full structured content from a page as builder-native JSON. Use this to see the complete page layout with all sections, columns, and widgets. For targeted searches, prefer find_element (faster). For a lightweight overview, prefer find_builder_targets. Returns the raw builder data structure that can be modified and passed back to inject_builder_content.',
|
|
1232
|
+
description: 'Extract the full structured content from a page as builder-native JSON. Use this to see the complete page layout with all sections, columns, and widgets. For targeted searches, prefer find_element (faster). For a lightweight overview, prefer find_builder_targets. Returns the raw builder data structure that can be modified and passed back to inject_builder_content. Works on any post type (pages, posts, custom post types). The `builder` arg is optional — if omitted, the active site builder is auto-detected via get_builder_info.',
|
|
1141
1233
|
inputSchema: {
|
|
1142
1234
|
type: 'object',
|
|
1143
1235
|
properties: {
|
|
1144
1236
|
builder: {
|
|
1145
1237
|
type: 'string',
|
|
1146
|
-
description: 'Builder name. Use exactly: gutenberg, divi, elementor, bricks, beaver, oxygen, breakdance, brizy, thrive, visual-composer, wpbakery.
|
|
1238
|
+
description: 'Optional. Builder name. Use exactly: gutenberg, divi, elementor, bricks, beaver, oxygen, breakdance, brizy, thrive, visual-composer, wpbakery. Omit to auto-detect the active site builder.',
|
|
1147
1239
|
},
|
|
1148
1240
|
page_id: {
|
|
1149
1241
|
type: 'number',
|
|
1150
|
-
description: 'Page ID',
|
|
1242
|
+
description: 'Page or post ID (works with any post type, including custom post types)',
|
|
1151
1243
|
},
|
|
1152
1244
|
},
|
|
1153
|
-
required: ['
|
|
1245
|
+
required: ['page_id'],
|
|
1154
1246
|
},
|
|
1155
1247
|
readOnlyHint: true,
|
|
1156
1248
|
},
|
|
@@ -1183,7 +1275,7 @@ Use respira_get_builder_info first to detect which builder is active. Then use t
|
|
|
1183
1275
|
},
|
|
1184
1276
|
{
|
|
1185
1277
|
name: 'wordpress_inject_builder_content',
|
|
1186
|
-
description: 'REPLACE (or append to) the entire page builder layout. WARNING: By default this REPLACES all existing content — use mode:"append" to add content without destroying existing elements. For editing a single module, use wordpress_update_module instead. Use exactly: gutenberg, divi, elementor, bricks, beaver, oxygen, breakdance, brizy, thrive, visual-composer, wpbakery. For Divi, divi_version is required ("4" or "5").',
|
|
1278
|
+
description: 'REPLACE (or append to) the entire page builder layout. WARNING: By default this REPLACES all existing content — use mode:"append" to add content without destroying existing elements. For editing a single module, use wordpress_update_module instead. Use exactly: gutenberg, divi, elementor, bricks, beaver, oxygen, breakdance, brizy, thrive, visual-composer, wpbakery. For Divi, divi_version is required ("4" or "5"). Starting in plugin v7.0.16, calling this against a page that already has content with mode="replace" (the default) without also passing confirm_replace=true returns a 409 respira_replace_confirmation_required — pass mode="append" to add to existing content, pass mode="replace" AND confirm_replace=true to overwrite, or pass edit_target="live" to overwrite the live page directly (plugin v7.0.22+ accepts an authorized live edit as the confirmation). The gate prevents silent data loss.',
|
|
1187
1279
|
inputSchema: {
|
|
1188
1280
|
type: 'object',
|
|
1189
1281
|
properties: {
|
|
@@ -1201,9 +1293,13 @@ Use respira_get_builder_info first to detect which builder is active. Then use t
|
|
|
1201
1293
|
},
|
|
1202
1294
|
mode: {
|
|
1203
1295
|
type: 'string',
|
|
1204
|
-
description: 'replace (default): OVERWRITES all existing page content. append: adds new content after existing elements, preserving the current layout.',
|
|
1296
|
+
description: 'replace (default): OVERWRITES all existing page content. append: adds new content after existing elements, preserving the current layout. As of plugin v7.0.16, replace against a page with existing content requires confirm_replace=true.',
|
|
1205
1297
|
enum: ['replace', 'append'],
|
|
1206
1298
|
},
|
|
1299
|
+
confirm_replace: {
|
|
1300
|
+
type: 'boolean',
|
|
1301
|
+
description: 'Required when mode="replace" (or unset, since replace is the default) AND the page already has content. Pass true only when you genuinely want to overwrite the existing structure. Without it, the plugin returns 409 respira_replace_confirmation_required so the agent can decide between mode="append" (preserve) and mode="replace"+confirm_replace=true (overwrite).',
|
|
1302
|
+
},
|
|
1207
1303
|
edit_target: {
|
|
1208
1304
|
type: 'string',
|
|
1209
1305
|
description: 'When editing an original, choose ask, live, or duplicate. Defaults to ask when direct editing is enabled.',
|
|
@@ -2821,6 +2917,18 @@ Use respira_get_builder_info first to detect which builder is active. Then use t
|
|
|
2821
2917
|
if (await this.isAcfAvailable()) {
|
|
2822
2918
|
tools.push(...getAcfTools());
|
|
2823
2919
|
}
|
|
2920
|
+
// v6.12.0: inject optional site_id into every non-agnostic tool schema.
|
|
2921
|
+
// Agnostic tools (list_sites, get_active_site, switch_site, redeem_token)
|
|
2922
|
+
// skip the injection because they operate on global state.
|
|
2923
|
+
for (const t of tools) {
|
|
2924
|
+
if (SITE_AGNOSTIC_TOOLS.has(t.name))
|
|
2925
|
+
continue;
|
|
2926
|
+
if (!t.inputSchema || t.inputSchema.type !== 'object')
|
|
2927
|
+
continue;
|
|
2928
|
+
const props = (t.inputSchema.properties ||= {});
|
|
2929
|
+
if (!props.site_id)
|
|
2930
|
+
props.site_id = SITE_ID_PROPERTY;
|
|
2931
|
+
}
|
|
2824
2932
|
// Generate respira_* aliases for all wordpress_* tools.
|
|
2825
2933
|
const allTools = this.generateDualTools(tools);
|
|
2826
2934
|
// Context-aware tool filtering: expose only relevant tools based on
|
|
@@ -3288,9 +3396,11 @@ Use respira_get_builder_info first to detect which builder is active. Then use t
|
|
|
3288
3396
|
return args;
|
|
3289
3397
|
}
|
|
3290
3398
|
async handleToolCall(name, args) {
|
|
3291
|
-
|
|
3292
|
-
|
|
3293
|
-
|
|
3399
|
+
// v6.12.0: relaxed top-of-handler guard. dispatchToolCall / resolveClient
|
|
3400
|
+
// now decides whether a site is needed for THIS call (some tools are
|
|
3401
|
+
// site-agnostic, others accept a per-call site_id override). The old
|
|
3402
|
+
// eager `if (!this.currentSite) throw` blocked the new "call with just
|
|
3403
|
+
// site_id on a server with no default site" path.
|
|
3294
3404
|
// Normalize respira_* ↔ wordpress_* names.
|
|
3295
3405
|
const { canonical, deprecated } = this.normalizeToolName(name);
|
|
3296
3406
|
// mcp-v6.11.1: snake_case sweep across tool catalog. Schemas advertise
|
|
@@ -3308,14 +3418,15 @@ Use respira_get_builder_info first to detect which builder is active. Then use t
|
|
|
3308
3418
|
return result;
|
|
3309
3419
|
}
|
|
3310
3420
|
async dispatchToolCall(name, args) {
|
|
3311
|
-
|
|
3312
|
-
|
|
3313
|
-
|
|
3421
|
+
// v6.12.0: per-call site resolution. Agnostic tools work without a
|
|
3422
|
+
// resolved client; everything else requires either args.site_id or a
|
|
3423
|
+
// global currentSite. resolveClient throws cleanly if neither is set.
|
|
3424
|
+
const client = SITE_AGNOSTIC_TOOLS.has(name) ? this.currentSite : this.resolveClient(args);
|
|
3314
3425
|
switch (name) {
|
|
3315
3426
|
case 'wordpress_get_site_context':
|
|
3316
3427
|
return args.detail === 'full'
|
|
3317
|
-
? await
|
|
3318
|
-
: await
|
|
3428
|
+
? await client.getSiteContext()
|
|
3429
|
+
: await client.getCompactSiteContext();
|
|
3319
3430
|
case 'wordpress_list_sites': {
|
|
3320
3431
|
const allSites = Array.from(this.sites.values());
|
|
3321
3432
|
const visibleSites = allSites.filter((site) => this.isSiteAllowed(site));
|
|
@@ -3329,21 +3440,21 @@ Use respira_get_builder_info first to detect which builder is active. Then use t
|
|
|
3329
3440
|
site: this.getActiveSiteSummary(),
|
|
3330
3441
|
};
|
|
3331
3442
|
case 'wordpress_get_theme_docs':
|
|
3332
|
-
return await
|
|
3443
|
+
return await client.getThemeDocs();
|
|
3333
3444
|
case 'wordpress_get_builder_info':
|
|
3334
|
-
return await
|
|
3445
|
+
return await client.getBuilderInfo({ debug: Boolean(args?.debug) });
|
|
3335
3446
|
case 'wordpress_list_pages':
|
|
3336
|
-
return await
|
|
3447
|
+
return await client.listPages(args);
|
|
3337
3448
|
case 'wordpress_read_page':
|
|
3338
|
-
return await
|
|
3449
|
+
return await client.getPage(args.id, args.include);
|
|
3339
3450
|
case 'wordpress_create_page_duplicate':
|
|
3340
3451
|
return {
|
|
3341
|
-
...(await
|
|
3342
|
-
respira_approvals_url:
|
|
3452
|
+
...(await client.duplicatePage(args.original_id, args.suffix, args.include)),
|
|
3453
|
+
respira_approvals_url: client.getApprovalsUrl(),
|
|
3343
3454
|
};
|
|
3344
3455
|
case 'wordpress_update_page': {
|
|
3345
|
-
const approvalsUrl =
|
|
3346
|
-
const page = await
|
|
3456
|
+
const approvalsUrl = client.getApprovalsUrl();
|
|
3457
|
+
const page = await client.updatePage(args.id, args);
|
|
3347
3458
|
// Check if Respira created a duplicate
|
|
3348
3459
|
if (page.__respira_duplicate_info) {
|
|
3349
3460
|
const info = page.__respira_duplicate_info;
|
|
@@ -3364,19 +3475,19 @@ Use respira_get_builder_info first to detect which builder is active. Then use t
|
|
|
3364
3475
|
};
|
|
3365
3476
|
}
|
|
3366
3477
|
case 'wordpress_delete_page':
|
|
3367
|
-
return await
|
|
3478
|
+
return await client.deletePage(args.id, args.force);
|
|
3368
3479
|
case 'wordpress_list_posts':
|
|
3369
|
-
return await
|
|
3480
|
+
return await client.listPosts(args);
|
|
3370
3481
|
case 'wordpress_read_post':
|
|
3371
|
-
return await
|
|
3482
|
+
return await client.getPost(args.id, args.include);
|
|
3372
3483
|
case 'wordpress_create_post_duplicate':
|
|
3373
3484
|
return {
|
|
3374
|
-
...(await
|
|
3375
|
-
respira_approvals_url:
|
|
3485
|
+
...(await client.duplicatePost(args.original_id, args.suffix, args.include)),
|
|
3486
|
+
respira_approvals_url: client.getApprovalsUrl(),
|
|
3376
3487
|
};
|
|
3377
3488
|
case 'wordpress_update_post': {
|
|
3378
|
-
const approvalsUrl =
|
|
3379
|
-
const post = await
|
|
3489
|
+
const approvalsUrl = client.getApprovalsUrl();
|
|
3490
|
+
const post = await client.updatePost(args.id, args);
|
|
3380
3491
|
// Check if Respira created a duplicate
|
|
3381
3492
|
if (post.__respira_duplicate_info) {
|
|
3382
3493
|
const info = post.__respira_duplicate_info;
|
|
@@ -3397,27 +3508,27 @@ Use respira_get_builder_info first to detect which builder is active. Then use t
|
|
|
3397
3508
|
};
|
|
3398
3509
|
}
|
|
3399
3510
|
case 'wordpress_delete_post':
|
|
3400
|
-
return await
|
|
3511
|
+
return await client.deletePost(args.id, args.force);
|
|
3401
3512
|
case 'wordpress_list_media':
|
|
3402
|
-
return await
|
|
3513
|
+
return await client.listMedia(args);
|
|
3403
3514
|
case 'wordpress_upload_media':
|
|
3404
|
-
return await
|
|
3515
|
+
return await client.uploadMedia(args.file, args.filename, args.mime_type, args.title, args.alt, args.caption);
|
|
3405
3516
|
case 'wordpress_extract_builder_content':
|
|
3406
|
-
return await
|
|
3517
|
+
return await client.extractBuilderContent(args.builder, args.page_id);
|
|
3407
3518
|
case 'wordpress_find_builder_targets':
|
|
3408
|
-
return await
|
|
3519
|
+
return await client.findBuilderTargets(args.builder, args.page_id, args.query, args.limit);
|
|
3409
3520
|
case 'wordpress_inject_builder_content':
|
|
3410
3521
|
return {
|
|
3411
|
-
...(await
|
|
3412
|
-
respira_approvals_url:
|
|
3522
|
+
...(await client.injectBuilderContent(args.builder, args.page_id, args.content, args.divi_version, args.edit_target, args.mode, args.confirm_replace)),
|
|
3523
|
+
respira_approvals_url: client.getApprovalsUrl(),
|
|
3413
3524
|
};
|
|
3414
3525
|
case 'wordpress_update_module':
|
|
3415
3526
|
return {
|
|
3416
|
-
...(await
|
|
3417
|
-
respira_approvals_url:
|
|
3527
|
+
...(await client.updateModule(args.builder, args.page_id, args.module_identifier, args.updates, args.edit_target)),
|
|
3528
|
+
respira_approvals_url: client.getApprovalsUrl(),
|
|
3418
3529
|
};
|
|
3419
3530
|
case 'wordpress_validate_security':
|
|
3420
|
-
return await
|
|
3531
|
+
return await client.validateSecurity(args.content);
|
|
3421
3532
|
case 'wordpress_switch_site': {
|
|
3422
3533
|
const newSite = this.sites.get(args.site_id);
|
|
3423
3534
|
if (!newSite) {
|
|
@@ -3435,7 +3546,7 @@ Use respira_get_builder_info first to detect which builder is active. Then use t
|
|
|
3435
3546
|
};
|
|
3436
3547
|
}
|
|
3437
3548
|
case 'wordpress_diagnose_connection':
|
|
3438
|
-
return await
|
|
3549
|
+
return await client.diagnoseConnection({ post_id: args.post_id });
|
|
3439
3550
|
// Canonical name (normalizeToolName rewrites respira_* → wordpress_*
|
|
3440
3551
|
// before this switch runs). The early-route guard in setRequestHandler
|
|
3441
3552
|
// catches the redeem call before we ever land here when no site is
|
|
@@ -3445,245 +3556,245 @@ Use respira_get_builder_info first to detect which builder is active. Then use t
|
|
|
3445
3556
|
return await this.redeemInstallToken(String(args.token || ''));
|
|
3446
3557
|
// Page Speed Analysis
|
|
3447
3558
|
case 'wordpress_analyze_performance':
|
|
3448
|
-
return await
|
|
3559
|
+
return await client.analyzePerformance(args.page_id);
|
|
3449
3560
|
case 'wordpress_get_core_web_vitals':
|
|
3450
|
-
return await
|
|
3561
|
+
return await client.getCoreWebVitals(args.page_id);
|
|
3451
3562
|
case 'wordpress_analyze_images':
|
|
3452
|
-
return await
|
|
3563
|
+
return await client.analyzeImages(args.page_id);
|
|
3453
3564
|
// SEO Analysis
|
|
3454
3565
|
case 'wordpress_analyze_seo':
|
|
3455
|
-
return await
|
|
3566
|
+
return await client.analyzeSEO(args.page_id);
|
|
3456
3567
|
case 'wordpress_check_seo_issues':
|
|
3457
|
-
return await
|
|
3568
|
+
return await client.checkSEOIssues(args.page_id);
|
|
3458
3569
|
case 'wordpress_analyze_readability':
|
|
3459
|
-
return await
|
|
3570
|
+
return await client.analyzeReadability(args.page_id);
|
|
3460
3571
|
case 'wordpress_analyze_rankmath':
|
|
3461
|
-
return await
|
|
3572
|
+
return await client.analyzeRankMath(args.post_id);
|
|
3462
3573
|
// AEO Analysis
|
|
3463
3574
|
case 'wordpress_analyze_aeo':
|
|
3464
|
-
return await
|
|
3575
|
+
return await client.analyzeAEO(args.page_id);
|
|
3465
3576
|
case 'wordpress_check_structured_data':
|
|
3466
|
-
return await
|
|
3577
|
+
return await client.checkStructuredData(args.page_id);
|
|
3467
3578
|
// Accessibility
|
|
3468
3579
|
case 'wordpress_list_accessibility_scans':
|
|
3469
|
-
return await
|
|
3580
|
+
return await client.listAccessibilityScans();
|
|
3470
3581
|
case 'wordpress_get_accessibility_scan':
|
|
3471
|
-
return await
|
|
3582
|
+
return await client.getAccessibilityScan(args.scan_id);
|
|
3472
3583
|
case 'wordpress_scan_page_accessibility':
|
|
3473
|
-
return await
|
|
3584
|
+
return await client.scanPageAccessibility(args.page_id, args.standard);
|
|
3474
3585
|
case 'wordpress_apply_accessibility_fixes':
|
|
3475
|
-
return await
|
|
3586
|
+
return await client.applyAccessibilityFixes(args.scan_id, args.rule_ids);
|
|
3476
3587
|
// Plugin Management (EXPERIMENTAL)
|
|
3477
3588
|
case 'wordpress_list_plugins':
|
|
3478
|
-
return await
|
|
3589
|
+
return await client.listPlugins();
|
|
3479
3590
|
case 'wordpress_install_plugin':
|
|
3480
|
-
return await
|
|
3591
|
+
return await client.installPlugin(args.slug_or_url, args.source || 'wordpress.org', args.approval_token);
|
|
3481
3592
|
case 'wordpress_activate_plugin':
|
|
3482
|
-
return await
|
|
3593
|
+
return await client.activatePlugin(args.slug, args.approval_token);
|
|
3483
3594
|
case 'wordpress_deactivate_plugin':
|
|
3484
|
-
return await
|
|
3595
|
+
return await client.deactivatePlugin(args.slug, args.approval_token);
|
|
3485
3596
|
case 'wordpress_update_plugin':
|
|
3486
|
-
return await
|
|
3597
|
+
return await client.updatePlugin(args.slug, args.approval_token);
|
|
3487
3598
|
case 'wordpress_delete_plugin':
|
|
3488
|
-
return await
|
|
3599
|
+
return await client.deletePlugin(args.slug, args.approval_token);
|
|
3489
3600
|
// Users Management
|
|
3490
3601
|
case 'wordpress_list_users':
|
|
3491
|
-
return await
|
|
3602
|
+
return await client.listUsers(args);
|
|
3492
3603
|
case 'wordpress_get_user':
|
|
3493
|
-
return await
|
|
3604
|
+
return await client.getUser(args.id);
|
|
3494
3605
|
case 'wordpress_create_user':
|
|
3495
|
-
return await
|
|
3606
|
+
return await client.createUser(args);
|
|
3496
3607
|
case 'wordpress_update_user':
|
|
3497
|
-
return await
|
|
3608
|
+
return await client.updateUser(args.id, args);
|
|
3498
3609
|
case 'wordpress_delete_user':
|
|
3499
|
-
return await
|
|
3610
|
+
return await client.deleteUser(args.id, args.reassign);
|
|
3500
3611
|
// Comments
|
|
3501
3612
|
case 'wordpress_list_comments':
|
|
3502
|
-
return await
|
|
3613
|
+
return await client.listComments(args);
|
|
3503
3614
|
case 'wordpress_get_comment':
|
|
3504
|
-
return await
|
|
3615
|
+
return await client.getComment(args.id);
|
|
3505
3616
|
case 'wordpress_create_comment':
|
|
3506
|
-
return await
|
|
3617
|
+
return await client.createComment(args);
|
|
3507
3618
|
case 'wordpress_update_comment':
|
|
3508
|
-
return await
|
|
3619
|
+
return await client.updateComment(args.id, args);
|
|
3509
3620
|
case 'wordpress_delete_comment':
|
|
3510
|
-
return await
|
|
3621
|
+
return await client.deleteComment(args.id);
|
|
3511
3622
|
// Taxonomies
|
|
3512
3623
|
case 'wordpress_list_taxonomies':
|
|
3513
|
-
return await
|
|
3624
|
+
return await client.listTaxonomies();
|
|
3514
3625
|
case 'wordpress_get_taxonomy':
|
|
3515
|
-
return await
|
|
3626
|
+
return await client.getTaxonomy(args.taxonomy);
|
|
3516
3627
|
case 'wordpress_list_terms':
|
|
3517
|
-
return await
|
|
3628
|
+
return await client.listTerms(args.taxonomy, args);
|
|
3518
3629
|
case 'wordpress_get_term':
|
|
3519
|
-
return await
|
|
3630
|
+
return await client.getTerm(args.taxonomy, args.id);
|
|
3520
3631
|
case 'wordpress_create_term':
|
|
3521
|
-
return await
|
|
3632
|
+
return await client.createTerm(args.taxonomy, args);
|
|
3522
3633
|
case 'wordpress_update_term':
|
|
3523
|
-
return await
|
|
3634
|
+
return await client.updateTerm(args.taxonomy, args.id, args);
|
|
3524
3635
|
case 'wordpress_delete_term':
|
|
3525
|
-
return await
|
|
3636
|
+
return await client.deleteTerm(args.taxonomy, args.id);
|
|
3526
3637
|
// Custom Post Types
|
|
3527
3638
|
case 'wordpress_list_post_types':
|
|
3528
|
-
return await
|
|
3639
|
+
return await client.listPostTypes();
|
|
3529
3640
|
case 'wordpress_get_post_type':
|
|
3530
|
-
return await
|
|
3641
|
+
return await client.getPostType(args.type);
|
|
3531
3642
|
case 'wordpress_list_custom_posts':
|
|
3532
|
-
return await
|
|
3643
|
+
return await client.listCustomPosts(args.type, args);
|
|
3533
3644
|
case 'wordpress_get_custom_post':
|
|
3534
|
-
return await
|
|
3645
|
+
return await client.getCustomPost(args.type, args.id, args.include);
|
|
3535
3646
|
case 'wordpress_create_custom_post':
|
|
3536
|
-
return await
|
|
3647
|
+
return await client.createCustomPost(args.type, args);
|
|
3537
3648
|
case 'wordpress_update_custom_post':
|
|
3538
|
-
return await
|
|
3649
|
+
return await client.updateCustomPost(args.type, args.id, args);
|
|
3539
3650
|
case 'wordpress_delete_custom_post':
|
|
3540
|
-
return await
|
|
3651
|
+
return await client.deleteCustomPost(args.type, args.id);
|
|
3541
3652
|
// Options
|
|
3542
3653
|
case 'wordpress_list_options':
|
|
3543
|
-
return await
|
|
3654
|
+
return await client.listOptions(args.search);
|
|
3544
3655
|
case 'wordpress_get_option':
|
|
3545
|
-
return await
|
|
3656
|
+
return await client.getOption(args.option);
|
|
3546
3657
|
case 'wordpress_update_option':
|
|
3547
|
-
return await
|
|
3658
|
+
return await client.updateOption(args.option, args.value);
|
|
3548
3659
|
case 'wordpress_delete_option':
|
|
3549
|
-
return await
|
|
3660
|
+
return await client.deleteOption(args.option);
|
|
3550
3661
|
// Media enhancements
|
|
3551
3662
|
case 'wordpress_get_media':
|
|
3552
|
-
return await
|
|
3663
|
+
return await client.getMedia(args.id);
|
|
3553
3664
|
case 'wordpress_update_media':
|
|
3554
|
-
return await
|
|
3665
|
+
return await client.updateMedia(args.id, args);
|
|
3555
3666
|
case 'wordpress_update_media_batch':
|
|
3556
|
-
return await
|
|
3667
|
+
return await client.updateMediaBatch(args.items);
|
|
3557
3668
|
case 'wordpress_delete_media':
|
|
3558
|
-
return await
|
|
3669
|
+
return await client.deleteMedia(args.id);
|
|
3559
3670
|
// Menu Management
|
|
3560
3671
|
case 'wordpress_list_menus':
|
|
3561
|
-
return await
|
|
3672
|
+
return await client.listMenus();
|
|
3562
3673
|
case 'wordpress_get_menu':
|
|
3563
|
-
return await
|
|
3674
|
+
return await client.getMenu(args.id);
|
|
3564
3675
|
case 'wordpress_create_menu':
|
|
3565
|
-
return await
|
|
3676
|
+
return await client.createMenu(args);
|
|
3566
3677
|
case 'wordpress_update_menu':
|
|
3567
|
-
return await
|
|
3678
|
+
return await client.updateMenu(args.id, args);
|
|
3568
3679
|
case 'wordpress_delete_menu':
|
|
3569
|
-
return await
|
|
3680
|
+
return await client.deleteMenu(args.id);
|
|
3570
3681
|
// Menu Locations
|
|
3571
3682
|
case 'wordpress_list_menu_locations':
|
|
3572
|
-
return await
|
|
3683
|
+
return await client.listMenuLocations();
|
|
3573
3684
|
case 'wordpress_assign_menu_location':
|
|
3574
|
-
return await
|
|
3685
|
+
return await client.assignMenuToLocation(args.location, args.menu_id);
|
|
3575
3686
|
// Menu Items
|
|
3576
3687
|
case 'wordpress_list_menu_items':
|
|
3577
|
-
return await
|
|
3688
|
+
return await client.listMenuItems(args.menu_id);
|
|
3578
3689
|
case 'wordpress_create_menu_item':
|
|
3579
|
-
return await
|
|
3690
|
+
return await client.createMenuItem(args.menu_id, args);
|
|
3580
3691
|
case 'wordpress_get_menu_item':
|
|
3581
|
-
return await
|
|
3692
|
+
return await client.getMenuItem(args.item_id);
|
|
3582
3693
|
case 'wordpress_update_menu_item':
|
|
3583
|
-
return await
|
|
3694
|
+
return await client.updateMenuItem(args.item_id, args);
|
|
3584
3695
|
case 'wordpress_delete_menu_item':
|
|
3585
|
-
return await
|
|
3696
|
+
return await client.deleteMenuItem(args.item_id);
|
|
3586
3697
|
case 'wordpress_list_snapshots':
|
|
3587
|
-
return await
|
|
3698
|
+
return await client.listSnapshots(args);
|
|
3588
3699
|
case 'wordpress_get_snapshot':
|
|
3589
|
-
return await
|
|
3700
|
+
return await client.getSnapshot(args.snapshot_uuid);
|
|
3590
3701
|
case 'wordpress_diff_snapshots':
|
|
3591
|
-
return await
|
|
3702
|
+
return await client.diffSnapshots(args.snapshot_uuid_a, args.snapshot_uuid_b);
|
|
3592
3703
|
case 'wordpress_restore_snapshot':
|
|
3593
|
-
return await
|
|
3704
|
+
return await client.restoreSnapshot(args.snapshot_uuid);
|
|
3594
3705
|
case 'wordpress_apply_builder_patch':
|
|
3595
|
-
return await
|
|
3706
|
+
return await client.applyBuilderPatch(args.builder, args.post_id, args.operations, args.include, args.edit_target);
|
|
3596
3707
|
case 'woocommerce_list_products':
|
|
3597
|
-
return await
|
|
3708
|
+
return await client.woocommerceListProducts(args);
|
|
3598
3709
|
case 'woocommerce_get_product':
|
|
3599
|
-
return await
|
|
3710
|
+
return await client.woocommerceGetProduct(args.id);
|
|
3600
3711
|
case 'woocommerce_create_product':
|
|
3601
|
-
return await
|
|
3712
|
+
return await client.woocommerceCreateProduct(args);
|
|
3602
3713
|
case 'woocommerce_update_product': {
|
|
3603
3714
|
const { id, ...payload } = args;
|
|
3604
|
-
return await
|
|
3715
|
+
return await client.woocommerceUpdateProduct(id, payload);
|
|
3605
3716
|
}
|
|
3606
3717
|
case 'woocommerce_duplicate_product':
|
|
3607
|
-
return await
|
|
3718
|
+
return await client.woocommerceDuplicateProduct(args.id);
|
|
3608
3719
|
case 'woocommerce_list_categories':
|
|
3609
|
-
return await
|
|
3720
|
+
return await client.woocommerceListCategories(args);
|
|
3610
3721
|
case 'woocommerce_get_category':
|
|
3611
|
-
return await
|
|
3722
|
+
return await client.woocommerceGetCategory(args.id);
|
|
3612
3723
|
case 'woocommerce_create_category':
|
|
3613
|
-
return await
|
|
3724
|
+
return await client.woocommerceCreateCategory(args);
|
|
3614
3725
|
case 'woocommerce_update_category': {
|
|
3615
3726
|
const { id, ...payload } = args;
|
|
3616
|
-
return await
|
|
3727
|
+
return await client.woocommerceUpdateCategory(id, payload);
|
|
3617
3728
|
}
|
|
3618
3729
|
case 'woocommerce_delete_category':
|
|
3619
|
-
return await
|
|
3730
|
+
return await client.woocommerceDeleteCategory(args.id);
|
|
3620
3731
|
case 'woocommerce_list_tags':
|
|
3621
|
-
return await
|
|
3732
|
+
return await client.woocommerceListTags(args);
|
|
3622
3733
|
case 'woocommerce_get_tag':
|
|
3623
|
-
return await
|
|
3734
|
+
return await client.woocommerceGetTag(args.id);
|
|
3624
3735
|
case 'woocommerce_create_tag':
|
|
3625
|
-
return await
|
|
3736
|
+
return await client.woocommerceCreateTag(args);
|
|
3626
3737
|
case 'woocommerce_update_tag': {
|
|
3627
3738
|
const { id, ...payload } = args;
|
|
3628
|
-
return await
|
|
3739
|
+
return await client.woocommerceUpdateTag(id, payload);
|
|
3629
3740
|
}
|
|
3630
3741
|
case 'woocommerce_delete_tag':
|
|
3631
|
-
return await
|
|
3742
|
+
return await client.woocommerceDeleteTag(args.id);
|
|
3632
3743
|
case 'woocommerce_list_orders':
|
|
3633
|
-
return await
|
|
3744
|
+
return await client.woocommerceListOrders(args);
|
|
3634
3745
|
case 'woocommerce_get_order':
|
|
3635
|
-
return await
|
|
3746
|
+
return await client.woocommerceGetOrder(args.id);
|
|
3636
3747
|
case 'woocommerce_update_order_status':
|
|
3637
|
-
return await
|
|
3748
|
+
return await client.woocommerceUpdateOrderStatus(args.id, args.status);
|
|
3638
3749
|
case 'woocommerce_get_stock_status':
|
|
3639
|
-
return await
|
|
3750
|
+
return await client.woocommerceGetStockStatus();
|
|
3640
3751
|
case 'woocommerce_update_stock': {
|
|
3641
3752
|
const { id, ...payload } = args;
|
|
3642
|
-
return await
|
|
3753
|
+
return await client.woocommerceUpdateStock(id, payload);
|
|
3643
3754
|
}
|
|
3644
3755
|
case 'woocommerce_sales_report':
|
|
3645
|
-
return await
|
|
3756
|
+
return await client.woocommerceSalesReport(args);
|
|
3646
3757
|
// --- v5.2.0 Elemental tools ---
|
|
3647
3758
|
case 'wordpress_find_element': {
|
|
3648
3759
|
const { post_id, ...rest } = args;
|
|
3649
|
-
return await
|
|
3760
|
+
return await client.callRestV2('POST', `/builder/elements/find/${post_id}`, rest);
|
|
3650
3761
|
}
|
|
3651
3762
|
case 'wordpress_update_element': {
|
|
3652
3763
|
const { post_id, ...rest } = args;
|
|
3653
|
-
return await
|
|
3764
|
+
return await client.callRestV2('POST', `/builder/elements/update/${post_id}`, rest);
|
|
3654
3765
|
}
|
|
3655
3766
|
case 'wordpress_move_element': {
|
|
3656
3767
|
const { post_id, ...rest } = args;
|
|
3657
|
-
return await
|
|
3768
|
+
return await client.callRestV2('POST', `/builder/elements/move/${post_id}`, rest);
|
|
3658
3769
|
}
|
|
3659
3770
|
case 'wordpress_duplicate_element': {
|
|
3660
3771
|
const { post_id, ...rest } = args;
|
|
3661
|
-
return await
|
|
3772
|
+
return await client.callRestV2('POST', `/builder/elements/duplicate/${post_id}`, rest);
|
|
3662
3773
|
}
|
|
3663
3774
|
case 'wordpress_remove_element': {
|
|
3664
3775
|
const { post_id, ...rest } = args;
|
|
3665
|
-
return await
|
|
3776
|
+
return await client.callRestV2('POST', `/builder/elements/remove/${post_id}`, rest);
|
|
3666
3777
|
}
|
|
3667
3778
|
case 'wordpress_batch_update': {
|
|
3668
3779
|
const { post_id, ...rest } = args;
|
|
3669
|
-
return await
|
|
3780
|
+
return await client.callRestV2('POST', `/builder/elements/batch/${post_id}`, rest);
|
|
3670
3781
|
}
|
|
3671
3782
|
case 'wordpress_reorder_elements': {
|
|
3672
3783
|
const { post_id, ...rest } = args;
|
|
3673
|
-
return await
|
|
3784
|
+
return await client.callRestV2('POST', `/builder/elements/reorder/${post_id}`, rest);
|
|
3674
3785
|
}
|
|
3675
3786
|
case 'wordpress_build_page':
|
|
3676
|
-
return await
|
|
3787
|
+
return await client.callRestV2('POST', '/builder/build', args);
|
|
3677
3788
|
case 'wordpress_convert_html_to_builder':
|
|
3678
|
-
return await
|
|
3789
|
+
return await client.callRestV2('POST', '/builder/convert', args);
|
|
3679
3790
|
case 'wordpress_bulk_pages_operation':
|
|
3680
|
-
return await
|
|
3791
|
+
return await client.callRestV2('POST', '/builder/bulk', args);
|
|
3681
3792
|
case 'wordpress_search_stock_images':
|
|
3682
|
-
return await
|
|
3793
|
+
return await client.callRestV2('GET', '/stock-images/search', args);
|
|
3683
3794
|
case 'wordpress_sideload_image':
|
|
3684
|
-
return await
|
|
3795
|
+
return await client.callRestV2('POST', '/stock-images/sideload', args);
|
|
3685
3796
|
case 'wordpress_get_server_compatibility':
|
|
3686
|
-
return await
|
|
3797
|
+
return await client.callRestV2('GET', '/server/compatibility');
|
|
3687
3798
|
// Widget shortcuts and Bricks tools — dynamic dispatch.
|
|
3688
3799
|
default: {
|
|
3689
3800
|
// Check if this is a Bricks deep tool.
|
|
@@ -3715,7 +3826,7 @@ Use respira_get_builder_info first to detect which builder is active. Then use t
|
|
|
3715
3826
|
throw new Error(`Widget shortcut "${shortcutName}" requires a post_id parameter.`);
|
|
3716
3827
|
}
|
|
3717
3828
|
const { post_id: _pid, ...settings } = args;
|
|
3718
|
-
return await
|
|
3829
|
+
return await client.callRestV2('POST', `/builder/widget/${shortcutName}/${postId}`, settings);
|
|
3719
3830
|
}
|
|
3720
3831
|
// Return isError result instead of throwing — unknown tool is an execution
|
|
3721
3832
|
// error, not a protocol error. This lets LLMs self-correct gracefully.
|
|
@@ -3757,7 +3868,7 @@ Use respira_get_builder_info first to detect which builder is active. Then use t
|
|
|
3757
3868
|
},
|
|
3758
3869
|
{
|
|
3759
3870
|
name: 'wordpress_update_element',
|
|
3760
|
-
description: 'Update settings or content on a specific element in a page. This is the PRIMARY tool for making content changes — use it for text edits, style changes, image swaps, link updates, etc. Works with all 12 page builders. First use find_element to locate the element, then pass the same identifier here with the updates object containing the new values.',
|
|
3871
|
+
description: 'Update settings or content on a specific element in a page. This is the PRIMARY tool for making content changes — use it for text edits, style changes, image swaps, link updates, etc. Works with all 12 page builders. First use find_element to locate the element, then pass the same identifier here with the updates object containing the new values. Response includes target_id, original_id, edit_target ("live" or "duplicate"), is_duplicate, duplicate_created and post_status so the caller can never mistake a duplicate-routed write for a live-page change.',
|
|
3761
3872
|
inputSchema: {
|
|
3762
3873
|
type: 'object',
|
|
3763
3874
|
properties: {
|
|
@@ -3771,6 +3882,11 @@ Use respira_get_builder_info first to detect which builder is active. Then use t
|
|
|
3771
3882
|
type: 'object',
|
|
3772
3883
|
description: 'Key-value pairs of settings to update on the matched element',
|
|
3773
3884
|
},
|
|
3885
|
+
editTarget: {
|
|
3886
|
+
type: 'string',
|
|
3887
|
+
enum: ['live', 'duplicate'],
|
|
3888
|
+
description: 'Optional. "duplicate" (default) routes the write to the Respira duplicate of the post (or auto-creates one) so changes go through the Respira → Changes approval flow before they reach the public page. "live" writes straight to the published original — requires Respira → Settings → Allow direct edit, or the post to be a draft / existing Respira duplicate. The response always reports the resolved target so you know exactly which post received the write.',
|
|
3889
|
+
},
|
|
3774
3890
|
},
|
|
3775
3891
|
required: ['post_id', 'identifier_type', 'identifier_value', 'updates'],
|
|
3776
3892
|
},
|