@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.
@@ -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;AAEzE,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;YAuIP,kBAAkB;YA6BlB,yBAAyB;IASvC;;;OAGG;IACH,OAAO,CAAC,oBAAoB;YAyBd,QAAQ;IAk5DtB;;;;;;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"}
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.3.0';
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 this.handleToolCall(name, args || {});
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 post.',
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 */" })',