@respira/wordpress-mcp-server 6.6.0 → 6.6.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 +211 -9
- package/dist/server.js.map +1 -1
- package/dist/wordpress-client.d.ts +24 -0
- package/dist/wordpress-client.d.ts.map +1 -1
- package/dist/wordpress-client.js +121 -19
- 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;AAcH,OAAO,KAAK,EAAE,mBAAmB,EAAe,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAcH,OAAO,KAAK,EAAE,mBAAmB,EAAe,MAAM,kBAAkB,CAAC;AAuBzE,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,CAAW;IAErD;;;OAGG;IACH,OAAO,CAAC,iBAAiB;IAWzB;;;OAGG;IACH,OAAO,CAAC,iBAAiB;gBA4Bb,WAAW,EAAE,mBAAmB,EAAE,EAAE,YAAY,CAAC,EAAE,MAAM,EAAE;IAuPvE,OAAO,CAAC,cAAc;IAItB,OAAO,CAAC,oBAAoB;IAQ5B,gEAAgE;IAChE,OAAO,CAAC,aAAa;IAUrB,OAAO,CAAC,eAAe;IAmBvB,OAAO,CAAC,aAAa;YA0LP,kBAAkB;YA6BlB,yBAAyB;IASvC;;;OAGG;IACH,OAAO,CAAC,oBAAoB;YAyBd,QAAQ;IA+hEtB;;;;;;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;YAwTb,cAAc;YAoBd,gBAAgB;IAkkB9B;;OAEG;IACH,OAAO,CAAC,iBAAiB;IAuQzB;;OAEG;IACH,OAAO,CAAC,sBAAsB;IA6UxB,GAAG;CAKV"}
|
package/dist/server.js
CHANGED
|
@@ -11,6 +11,27 @@ import { RespiraVersionChecker } from './version-checker.js';
|
|
|
11
11
|
import { getBricksTools, dispatchBricksTool } from './bricks-tools.js';
|
|
12
12
|
import { getElementorTools, dispatchElementorTool } from './elementor-tools.js';
|
|
13
13
|
import { getAcfTools, ACF_TOOL_NAMES } from './acf-tools.js';
|
|
14
|
+
/**
|
|
15
|
+
* Thrown by the per-call watchdog when a tool handler exceeds its deadline.
|
|
16
|
+
* Surfaces as a structured `tool_timeout` response so the stdio loop stays
|
|
17
|
+
* responsive and every subsequent tool call keeps working.
|
|
18
|
+
*/
|
|
19
|
+
class ToolTimeoutError extends Error {
|
|
20
|
+
toolName;
|
|
21
|
+
timeoutMs;
|
|
22
|
+
constructor(toolName, timeoutMs) {
|
|
23
|
+
super(`Tool "${toolName}" did not complete within ${timeoutMs}ms. ` +
|
|
24
|
+
`The WordPress server may be unresponsive, the operation may legitimately take longer than configured, ` +
|
|
25
|
+
`or the handler may be stuck. Other tools should continue to work. ` +
|
|
26
|
+
`Raise RESPIRA_MAX_TOOL_TIMEOUT_MS if the operation genuinely needs more time.`);
|
|
27
|
+
this.toolName = toolName;
|
|
28
|
+
this.timeoutMs = timeoutMs;
|
|
29
|
+
this.name = 'ToolTimeoutError';
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
function getMaxToolTimeoutMs() {
|
|
33
|
+
return Math.max(5000, parseInt(process.env.RESPIRA_MAX_TOOL_TIMEOUT_MS || '120000', 10));
|
|
34
|
+
}
|
|
14
35
|
export class RespiraWordPressServer {
|
|
15
36
|
server;
|
|
16
37
|
currentSite = null;
|
|
@@ -22,7 +43,7 @@ export class RespiraWordPressServer {
|
|
|
22
43
|
allowedSites = null;
|
|
23
44
|
/** Whether the plugin version warning has already been shown this session. */
|
|
24
45
|
versionWarningShown = false;
|
|
25
|
-
static MCP_SERVER_VERSION = '6.
|
|
46
|
+
static MCP_SERVER_VERSION = '6.6.4';
|
|
26
47
|
/**
|
|
27
48
|
* Normalize a tool name: respira_* → wordpress_* for switch dispatch.
|
|
28
49
|
* Tracks whether the deprecated wordpress_* name was used.
|
|
@@ -351,9 +372,25 @@ Use respira_get_builder_info first to detect which builder is active. Then use t
|
|
|
351
372
|
if (!this.currentSite) {
|
|
352
373
|
throw new Error('No WordPress site configured');
|
|
353
374
|
}
|
|
375
|
+
// Per-call watchdog: a single hung handler must never wedge the stdio
|
|
376
|
+
// loop for every subsequent tool call. If the deadline fires, the race
|
|
377
|
+
// rejects with ToolTimeoutError; the losing handler keeps running in
|
|
378
|
+
// the background but the client gets a clean structured error and the
|
|
379
|
+
// next tool call is free to proceed.
|
|
380
|
+
const toolTimeoutMs = getMaxToolTimeoutMs();
|
|
381
|
+
let timeoutHandle;
|
|
382
|
+
const toolTimeout = new Promise((_, reject) => {
|
|
383
|
+
timeoutHandle = setTimeout(() => reject(new ToolTimeoutError(name, toolTimeoutMs)), toolTimeoutMs);
|
|
384
|
+
});
|
|
354
385
|
try {
|
|
355
386
|
this.currentSite.setCurrentToolName(name);
|
|
356
|
-
const result = await
|
|
387
|
+
const result = await Promise.race([
|
|
388
|
+
this.handleToolCall(name, args || {}),
|
|
389
|
+
toolTimeout,
|
|
390
|
+
]);
|
|
391
|
+
if (timeoutHandle) {
|
|
392
|
+
clearTimeout(timeoutHandle);
|
|
393
|
+
}
|
|
357
394
|
this.currentSite.setCurrentToolName(null);
|
|
358
395
|
// Handle soft errors (e.g. unknown tool) — return isError without throwing.
|
|
359
396
|
if (result && result.__respira_is_error) {
|
|
@@ -384,7 +421,31 @@ Use respira_get_builder_info first to detect which builder is active. Then use t
|
|
|
384
421
|
};
|
|
385
422
|
}
|
|
386
423
|
catch (error) {
|
|
424
|
+
if (timeoutHandle) {
|
|
425
|
+
clearTimeout(timeoutHandle);
|
|
426
|
+
}
|
|
387
427
|
this.currentSite?.setCurrentToolName(null);
|
|
428
|
+
if (error instanceof ToolTimeoutError) {
|
|
429
|
+
const activeSite = this.getActiveSiteSummary();
|
|
430
|
+
return {
|
|
431
|
+
content: [
|
|
432
|
+
{
|
|
433
|
+
type: 'text',
|
|
434
|
+
text: JSON.stringify({
|
|
435
|
+
error: 'tool_timeout',
|
|
436
|
+
tool: error.toolName,
|
|
437
|
+
timeout_ms: error.timeoutMs,
|
|
438
|
+
message: error.message,
|
|
439
|
+
hint: 'Other tools should continue to work. ' +
|
|
440
|
+
'If this operation genuinely needs more time, raise RESPIRA_MAX_TOOL_TIMEOUT_MS. ' +
|
|
441
|
+
'For uploads specifically, also see RESPIRA_UPLOAD_TIMEOUT_MS and RESPIRA_MAX_UPLOAD_MB.',
|
|
442
|
+
site: activeSite,
|
|
443
|
+
}, null, 2),
|
|
444
|
+
},
|
|
445
|
+
],
|
|
446
|
+
isError: true,
|
|
447
|
+
};
|
|
448
|
+
}
|
|
388
449
|
// Handle Promise objects that weren't awaited (they would have .then method)
|
|
389
450
|
if (error && typeof error.then === 'function') {
|
|
390
451
|
// Try to await the Promise to get the actual error
|
|
@@ -752,7 +813,7 @@ Use respira_get_builder_info first to detect which builder is active. Then use t
|
|
|
752
813
|
},
|
|
753
814
|
{
|
|
754
815
|
name: 'wordpress_read_post',
|
|
755
|
-
description: 'Get full content of a specific post.',
|
|
816
|
+
description: 'Get full content of a specific post. Response includes author object ({id, login, display_name}), taxonomies map ({category, post_tag, ...} each with term {id, name, slug}), and featured_media ({id, url}) alongside title/content/slug/status/date/url/meta.',
|
|
756
817
|
inputSchema: {
|
|
757
818
|
type: 'object',
|
|
758
819
|
properties: {
|
|
@@ -789,7 +850,7 @@ Use respira_get_builder_info first to detect which builder is active. Then use t
|
|
|
789
850
|
},
|
|
790
851
|
{
|
|
791
852
|
name: 'wordpress_update_post',
|
|
792
|
-
description: 'Update a post. When direct editing is enabled and you target an original post, Respira returns a confirmation_required preflight by default so you can choose live/original or duplicate.',
|
|
853
|
+
description: 'Update a post. Supports author reassignment, taxonomy terms (categories/tags/custom), and featured media. When direct editing is enabled and you target an original post, Respira returns a confirmation_required preflight by default so you can choose live/original or duplicate.',
|
|
793
854
|
inputSchema: {
|
|
794
855
|
type: 'object',
|
|
795
856
|
properties: {
|
|
@@ -813,6 +874,49 @@ Use respira_get_builder_info first to detect which builder is active. Then use t
|
|
|
813
874
|
type: 'string',
|
|
814
875
|
description: 'URL slug (post_name). Example: "my-post" produces /my-post/.',
|
|
815
876
|
},
|
|
877
|
+
author: {
|
|
878
|
+
description: 'Author (user id or user login). Requires edit_others_posts capability when different from the acting user.',
|
|
879
|
+
oneOf: [
|
|
880
|
+
{ type: 'number' },
|
|
881
|
+
{ type: 'string' },
|
|
882
|
+
],
|
|
883
|
+
},
|
|
884
|
+
categories: {
|
|
885
|
+
type: 'array',
|
|
886
|
+
description: 'Shorthand for the built-in "category" taxonomy. Accepts term ids (number) or slugs/names (string). Replaces existing categories unless append_terms=true.',
|
|
887
|
+
items: {
|
|
888
|
+
oneOf: [
|
|
889
|
+
{ type: 'number' },
|
|
890
|
+
{ type: 'string' },
|
|
891
|
+
],
|
|
892
|
+
},
|
|
893
|
+
},
|
|
894
|
+
tags: {
|
|
895
|
+
type: 'array',
|
|
896
|
+
description: 'Shorthand for the built-in "post_tag" taxonomy. Accepts term ids (number) or slugs/names (string).',
|
|
897
|
+
items: {
|
|
898
|
+
oneOf: [
|
|
899
|
+
{ type: 'number' },
|
|
900
|
+
{ type: 'string' },
|
|
901
|
+
],
|
|
902
|
+
},
|
|
903
|
+
},
|
|
904
|
+
taxonomies: {
|
|
905
|
+
type: 'object',
|
|
906
|
+
description: 'Generic map of taxonomy slug to array of term ids/slugs for custom taxonomies (e.g. product_cat). Example: { "product_cat": [12, "featured"] }.',
|
|
907
|
+
},
|
|
908
|
+
featuredMedia: {
|
|
909
|
+
type: 'number',
|
|
910
|
+
description: 'Attachment ID to set as the featured image. Pass 0 to clear.',
|
|
911
|
+
},
|
|
912
|
+
createMissingTerms: {
|
|
913
|
+
type: 'boolean',
|
|
914
|
+
description: 'When true, auto-create taxonomy terms that do not exist by name. Default false (strict: unknown slug returns 400).',
|
|
915
|
+
},
|
|
916
|
+
appendTerms: {
|
|
917
|
+
type: 'boolean',
|
|
918
|
+
description: 'When true, append to existing taxonomy terms instead of replacing. Default false (replace).',
|
|
919
|
+
},
|
|
816
920
|
customCss: {
|
|
817
921
|
type: 'string',
|
|
818
922
|
description: 'Custom CSS for the post. Auto-detects builder: Divi → _et_pb_custom_css, Elementor → _elementor_page_settings.custom_css.',
|
|
@@ -882,7 +986,7 @@ Use respira_get_builder_info first to detect which builder is active. Then use t
|
|
|
882
986
|
},
|
|
883
987
|
{
|
|
884
988
|
name: 'wordpress_upload_media',
|
|
885
|
-
description: 'Upload a media file (image, document, video) to WordPress. Supports base64 encoded files, file URLs, file paths, or raw SVG markup strings (starting with <svg or <?xml).',
|
|
989
|
+
description: 'Upload a media file (image, document, video) to WordPress. Supports base64 encoded files, file URLs, file paths, or raw SVG markup strings (starting with <svg or <?xml). Fails fast with a clear error if the file exceeds 50MB (override via RESPIRA_MAX_UPLOAD_MB) or the upload does not complete within 120 seconds (override via RESPIRA_UPLOAD_TIMEOUT_MS).',
|
|
886
990
|
inputSchema: {
|
|
887
991
|
type: 'object',
|
|
888
992
|
properties: {
|
|
@@ -1887,7 +1991,7 @@ Use respira_get_builder_info first to detect which builder is active. Then use t
|
|
|
1887
1991
|
},
|
|
1888
1992
|
{
|
|
1889
1993
|
name: 'wordpress_list_custom_posts',
|
|
1890
|
-
description: 'List posts of a custom post type.',
|
|
1994
|
+
description: 'List posts of a custom post type. Each row includes author ({id, login, display_name}), taxonomies map, and featured_media alongside id/title/slug/status/date/url.',
|
|
1891
1995
|
inputSchema: {
|
|
1892
1996
|
type: 'object',
|
|
1893
1997
|
properties: {
|
|
@@ -1945,13 +2049,13 @@ Use respira_get_builder_info first to detect which builder is active. Then use t
|
|
|
1945
2049
|
},
|
|
1946
2050
|
{
|
|
1947
2051
|
name: 'wordpress_create_custom_post',
|
|
1948
|
-
description: 'Create a new custom
|
|
2052
|
+
description: 'Create a new post. Supports author, taxonomy terms (categories/tags/custom), and featured media inline so the full "same category and author as X" pattern works in a single call.',
|
|
1949
2053
|
inputSchema: {
|
|
1950
2054
|
type: 'object',
|
|
1951
2055
|
properties: {
|
|
1952
2056
|
type: {
|
|
1953
2057
|
type: 'string',
|
|
1954
|
-
description: 'Post type name',
|
|
2058
|
+
description: 'Post type name (e.g. "post", "page", "product")',
|
|
1955
2059
|
},
|
|
1956
2060
|
title: {
|
|
1957
2061
|
type: 'string',
|
|
@@ -1965,13 +2069,68 @@ Use respira_get_builder_info first to detect which builder is active. Then use t
|
|
|
1965
2069
|
type: 'string',
|
|
1966
2070
|
description: 'Post status (default: draft)',
|
|
1967
2071
|
},
|
|
2072
|
+
slug: {
|
|
2073
|
+
type: 'string',
|
|
2074
|
+
description: 'URL slug (post_name). Example: "my-post" produces /my-post/.',
|
|
2075
|
+
},
|
|
2076
|
+
excerpt: {
|
|
2077
|
+
type: 'string',
|
|
2078
|
+
description: 'Optional post excerpt.',
|
|
2079
|
+
},
|
|
2080
|
+
date: {
|
|
2081
|
+
type: 'string',
|
|
2082
|
+
description: 'Optional publish date in YYYY-MM-DD HH:MM:SS (server local time) for backdating.',
|
|
2083
|
+
},
|
|
2084
|
+
author: {
|
|
2085
|
+
description: 'Author (user id or user login). Requires edit_others_posts capability when different from the acting user.',
|
|
2086
|
+
oneOf: [
|
|
2087
|
+
{ type: 'number' },
|
|
2088
|
+
{ type: 'string' },
|
|
2089
|
+
],
|
|
2090
|
+
},
|
|
2091
|
+
categories: {
|
|
2092
|
+
type: 'array',
|
|
2093
|
+
description: 'Shorthand for the built-in "category" taxonomy. Accepts term ids (number) or slugs/names (string).',
|
|
2094
|
+
items: {
|
|
2095
|
+
oneOf: [
|
|
2096
|
+
{ type: 'number' },
|
|
2097
|
+
{ type: 'string' },
|
|
2098
|
+
],
|
|
2099
|
+
},
|
|
2100
|
+
},
|
|
2101
|
+
tags: {
|
|
2102
|
+
type: 'array',
|
|
2103
|
+
description: 'Shorthand for the built-in "post_tag" taxonomy. Accepts term ids (number) or slugs/names (string).',
|
|
2104
|
+
items: {
|
|
2105
|
+
oneOf: [
|
|
2106
|
+
{ type: 'number' },
|
|
2107
|
+
{ type: 'string' },
|
|
2108
|
+
],
|
|
2109
|
+
},
|
|
2110
|
+
},
|
|
2111
|
+
taxonomies: {
|
|
2112
|
+
type: 'object',
|
|
2113
|
+
description: 'Generic map of taxonomy slug to array of term ids/slugs for custom taxonomies (e.g. product_cat). Example: { "product_cat": [12, "featured"] }.',
|
|
2114
|
+
},
|
|
2115
|
+
featuredMedia: {
|
|
2116
|
+
type: 'number',
|
|
2117
|
+
description: 'Attachment ID to set as the featured image.',
|
|
2118
|
+
},
|
|
2119
|
+
createMissingTerms: {
|
|
2120
|
+
type: 'boolean',
|
|
2121
|
+
description: 'When true, auto-create taxonomy terms that do not exist by name. Default false (strict: unknown slug returns 400 and the post is rolled back).',
|
|
2122
|
+
},
|
|
2123
|
+
meta: {
|
|
2124
|
+
type: 'object',
|
|
2125
|
+
description: 'Post meta data (map of meta key to value).',
|
|
2126
|
+
},
|
|
1968
2127
|
},
|
|
1969
2128
|
required: ['type', 'title'],
|
|
1970
2129
|
},
|
|
1971
2130
|
},
|
|
1972
2131
|
{
|
|
1973
2132
|
name: 'wordpress_update_custom_post',
|
|
1974
|
-
description: 'Update a custom post. When direct editing is enabled and you target an original item, Respira returns a confirmation_required preflight by default so you can choose live/original or duplicate.',
|
|
2133
|
+
description: 'Update a custom post. Supports author reassignment, taxonomy terms (categories/tags/custom), and featured media. When direct editing is enabled and you target an original item, Respira returns a confirmation_required preflight by default so you can choose live/original or duplicate.',
|
|
1975
2134
|
inputSchema: {
|
|
1976
2135
|
type: 'object',
|
|
1977
2136
|
properties: {
|
|
@@ -1999,6 +2158,49 @@ Use respira_get_builder_info first to detect which builder is active. Then use t
|
|
|
1999
2158
|
type: 'string',
|
|
2000
2159
|
description: 'URL slug (post_name).',
|
|
2001
2160
|
},
|
|
2161
|
+
author: {
|
|
2162
|
+
description: 'Author (user id or user login). Requires edit_others_posts capability when different from the acting user.',
|
|
2163
|
+
oneOf: [
|
|
2164
|
+
{ type: 'number' },
|
|
2165
|
+
{ type: 'string' },
|
|
2166
|
+
],
|
|
2167
|
+
},
|
|
2168
|
+
categories: {
|
|
2169
|
+
type: 'array',
|
|
2170
|
+
description: 'Shorthand for the built-in "category" taxonomy. Accepts term ids (number) or slugs/names (string).',
|
|
2171
|
+
items: {
|
|
2172
|
+
oneOf: [
|
|
2173
|
+
{ type: 'number' },
|
|
2174
|
+
{ type: 'string' },
|
|
2175
|
+
],
|
|
2176
|
+
},
|
|
2177
|
+
},
|
|
2178
|
+
tags: {
|
|
2179
|
+
type: 'array',
|
|
2180
|
+
description: 'Shorthand for the built-in "post_tag" taxonomy. Accepts term ids (number) or slugs/names (string).',
|
|
2181
|
+
items: {
|
|
2182
|
+
oneOf: [
|
|
2183
|
+
{ type: 'number' },
|
|
2184
|
+
{ type: 'string' },
|
|
2185
|
+
],
|
|
2186
|
+
},
|
|
2187
|
+
},
|
|
2188
|
+
taxonomies: {
|
|
2189
|
+
type: 'object',
|
|
2190
|
+
description: 'Generic map of taxonomy slug to array of term ids/slugs for custom taxonomies.',
|
|
2191
|
+
},
|
|
2192
|
+
featuredMedia: {
|
|
2193
|
+
type: 'number',
|
|
2194
|
+
description: 'Attachment ID to set as the featured image. Pass 0 to clear.',
|
|
2195
|
+
},
|
|
2196
|
+
createMissingTerms: {
|
|
2197
|
+
type: 'boolean',
|
|
2198
|
+
description: 'When true, auto-create taxonomy terms that do not exist by name. Default false (strict).',
|
|
2199
|
+
},
|
|
2200
|
+
appendTerms: {
|
|
2201
|
+
type: 'boolean',
|
|
2202
|
+
description: 'When true, append to existing taxonomy terms instead of replacing. Default false (replace).',
|
|
2203
|
+
},
|
|
2002
2204
|
meta: {
|
|
2003
2205
|
type: 'object',
|
|
2004
2206
|
description: 'Post meta data (e.g., { "_et_pb_custom_css": "/* CSS */" })',
|