@wisewandtools/mcp-server 2.2.1 → 2.2.3

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/index.js CHANGED
@@ -1203,6 +1203,27 @@ function validateUuid(value) {
1203
1203
  return str;
1204
1204
  }
1205
1205
  __name(validateUuid, "validateUuid");
1206
+ function resolveArray(args, key) {
1207
+ let value = args[key];
1208
+ if (value === void 0 || value === null) {
1209
+ throw new ToolInputError(`Missing required parameter: ${key}`);
1210
+ }
1211
+ if (typeof value === "string") {
1212
+ try {
1213
+ value = JSON.parse(value);
1214
+ } catch {
1215
+ throw new ToolInputError(`Invalid ${key} format: expected JSON array`);
1216
+ }
1217
+ }
1218
+ if (!Array.isArray(value)) {
1219
+ throw new ToolInputError(`Invalid ${key} format: expected array, got ${typeof value}`);
1220
+ }
1221
+ if (value.length === 0) {
1222
+ throw new ToolInputError(`${key} must not be empty`);
1223
+ }
1224
+ return value;
1225
+ }
1226
+ __name(resolveArray, "resolveArray");
1206
1227
 
1207
1228
  // src/handlers/tools/ArticleToolHandler.ts
1208
1229
  var boolField = z2.union([z2.boolean(), z2.string()]).transform(
@@ -1501,7 +1522,7 @@ var ArticleToolHandler = class {
1501
1522
  publishwordpress_date: { type: "string", description: "WordPress publish date (ISO 8601)" },
1502
1523
  publishwordpress_status: { type: "string", enum: ["draft", "publish", "future"], description: "WordPress post status", default: "draft" },
1503
1524
  publishwordpress_category: { type: "number", description: "WordPress category ID" },
1504
- publishwordpress_author: { type: "number", description: "WordPress author ID" },
1525
+ publishwordpress_author: { type: "number", description: "WordPress author ID. CAUTION: If the WP connection user lacks edit_others_posts capability, setting this will cause publishing to fail with rest_cannot_edit_others. Omit unless the WP user has admin/editor role." },
1505
1526
  publishwordpress_connection: { type: "string", format: "uuid", description: "WordPress connection ID" },
1506
1527
  // Shopify Publishing
1507
1528
  use_publishshopify: { type: "boolean", description: "Automatically publish to Shopify" },
@@ -1573,21 +1594,38 @@ var ArticleToolHandler = class {
1573
1594
  const validated = CreateArticleSchema.parse(args);
1574
1595
  const apiInput = this.buildApiInput(validated);
1575
1596
  const article = await this.apiClient.createArticle(apiInput);
1576
- await this.cache.set(`article:${article.id}`, article, 300);
1577
1597
  this.metrics.recordAPICall("create_article", "success");
1598
+ const warnings = [];
1599
+ if (validated.use_publishwordpress && !validated.publishwordpress_connection) {
1600
+ warnings.push("Warning: use_publishwordpress is true but publishwordpress_connection is not set. The article may not be published to WordPress. Provide a publishwordpress_connection UUID.");
1601
+ }
1602
+ if (validated.publishwordpress_author) {
1603
+ warnings.push(
1604
+ 'Warning: publishwordpress_author is set. If the WP connection user lacks edit_others_posts capability, auto-publish will silently fail with "rest_cannot_edit_others". Omit publishwordpress_author unless the WP user has admin/editor role.'
1605
+ );
1606
+ }
1607
+ const hasAutoPublish = validated.use_publishwordpress || validated.use_publishshopify || validated.use_publishprestashop || validated.use_publishwoocommerce;
1608
+ const next_steps = hasAutoPublish ? [
1609
+ `Generation auto-triggered. Use 'get_article' with id: ${article.id} to check progress`,
1610
+ `Use 'get_article_output' with id: ${article.id} to get content once ready`
1611
+ ] : [
1612
+ `Use 'generate_article' with id: ${article.id} to start content generation`,
1613
+ `Use 'get_article' with id: ${article.id} to check status`
1614
+ ];
1615
+ const response = {
1616
+ success: true,
1617
+ article_id: article.id,
1618
+ title: article.title || "Article created",
1619
+ message: `Article "${article.title || validated.subject}" created successfully.`,
1620
+ next_steps
1621
+ };
1622
+ if (warnings.length > 0) {
1623
+ response.warnings = warnings;
1624
+ }
1578
1625
  return {
1579
1626
  content: [{
1580
1627
  type: "text",
1581
- text: JSON.stringify({
1582
- success: true,
1583
- article_id: article.id,
1584
- title: article.title || "Article created",
1585
- message: `Article "${article.title || validated.subject}" created successfully.`,
1586
- next_steps: [
1587
- `Use 'generate_article' with id: ${article.id} to start content generation`,
1588
- `Use 'get_article' with id: ${article.id} to check status`
1589
- ]
1590
- }, null, 2)
1628
+ text: JSON.stringify(response, null, 2)
1591
1629
  }]
1592
1630
  };
1593
1631
  } catch (error) {
@@ -1655,6 +1693,22 @@ var ArticleToolHandler = class {
1655
1693
  }
1656
1694
  throw new Error(`Generation timeout after ${max_wait_time} seconds`);
1657
1695
  } catch (error) {
1696
+ const errorMsg = (error.message || "").toLowerCase();
1697
+ if (errorMsg.includes("already running") || errorMsg.includes("in queue") || errorMsg.includes("already in progress")) {
1698
+ const article_id = args.id || args.article_id;
1699
+ return {
1700
+ content: [{
1701
+ type: "text",
1702
+ text: JSON.stringify({
1703
+ success: true,
1704
+ article_id,
1705
+ status: "already_running",
1706
+ message: "Article generation is already in progress. Use get_article to check status.",
1707
+ next_steps: [`Use 'get_article' with id: ${article_id} to check progress`]
1708
+ }, null, 2)
1709
+ }]
1710
+ };
1711
+ }
1658
1712
  logger.error("Failed to generate article", { error: error.message });
1659
1713
  this.metrics.recordAPICall("generate_article", "error");
1660
1714
  return { content: [{ type: "text", text: JSON.stringify({ error: true, message: error.message || "Failed to generate article" }, null, 2) }], isError: true };
@@ -1807,19 +1861,30 @@ var ArticleToolHandler = class {
1807
1861
  await this.cache.set(cacheKey, output, 600);
1808
1862
  }
1809
1863
  this.metrics.recordAPICall("get_article_output", "success");
1864
+ const rawOutput = output;
1865
+ const normalizedOutput = rawOutput?.data?.output ?? rawOutput?.data ?? rawOutput?.output ?? rawOutput;
1866
+ logger.debug("get_article_output raw response keys", { keys: Object.keys(rawOutput || {}), normalizedKeys: Object.keys(normalizedOutput || {}) });
1810
1867
  let formattedOutput;
1811
1868
  switch (format) {
1812
- case "html":
1813
- formattedOutput = { html: output.html_content, title: output.title };
1869
+ case "html": {
1870
+ const html = normalizedOutput?.html_content ?? normalizedOutput?.content ?? null;
1871
+ const title = normalizedOutput?.title ?? null;
1872
+ formattedOutput = html || title ? { html, title } : { ...normalizedOutput, _note: "Fields not found at expected paths (html_content, title)" };
1814
1873
  break;
1874
+ }
1815
1875
  case "markdown":
1816
- formattedOutput = { content: output.content, title: output.title };
1876
+ formattedOutput = { content: normalizedOutput?.content ?? normalizedOutput?.markdown ?? null, title: normalizedOutput?.title ?? null };
1817
1877
  break;
1818
- case "summary":
1819
- formattedOutput = { title: output.title, excerpt: output.excerpt, word_count: output.word_count, seo_score: output.seo_score };
1878
+ case "summary": {
1879
+ const title = normalizedOutput?.title ?? null;
1880
+ const excerpt = normalizedOutput?.excerpt ?? null;
1881
+ const word_count = normalizedOutput?.word_count ?? null;
1882
+ const seo_score = normalizedOutput?.seo_score ?? null;
1883
+ formattedOutput = title || excerpt ? { title, excerpt, word_count, seo_score } : { ...normalizedOutput, _note: "Fields not found at expected paths (title, excerpt)" };
1820
1884
  break;
1885
+ }
1821
1886
  default:
1822
- formattedOutput = output;
1887
+ formattedOutput = normalizedOutput;
1823
1888
  }
1824
1889
  return { content: [{ type: "text", text: JSON.stringify({ success: true, article_id, format, output: formattedOutput, from_cache: !!cached }, null, 2) }] };
1825
1890
  } catch (error) {
@@ -2280,33 +2345,104 @@ var PublishingToolHandler = class {
2280
2345
  publishToWordPressTool() {
2281
2346
  return {
2282
2347
  name: "publish_to_wordpress",
2283
- description: "Publish content to WordPress",
2348
+ description: "Publish content to WordPress. connection_id must reference a WordPress connection with sufficient permissions.",
2284
2349
  inputSchema: {
2285
2350
  type: "object",
2286
2351
  properties: {
2287
2352
  entity_id: { type: "string", format: "uuid", description: "ID of the article/content to publish" },
2288
2353
  status: { type: "string", enum: ["draft", "publish", "future"], description: "Publishing status", default: "draft" },
2289
- schedule_date: { type: "string", format: "date-time", description: "Schedule date for future publishing (ISO 8601)" },
2354
+ date: { type: "string", format: "date-time", description: 'Publication date (ISO 8601). Required when status is "future".' },
2290
2355
  category_id: { type: "number", description: "WordPress category ID" },
2291
- author_id: { type: "number", description: "WordPress author ID" },
2292
- connection_id: { type: "string", format: "uuid", description: "WordPress connection ID" }
2356
+ author_id: { type: "number", description: "WordPress author ID. CAUTION: If the WP connection user lacks edit_others_posts capability, setting this will cause publishing to fail with rest_cannot_edit_others. Omit unless the WP user has admin/editor role." },
2357
+ connection_id: { type: "string", format: "uuid", description: "WordPress connection ID (must have sufficient WP permissions)" },
2358
+ clear_author: { type: "boolean", description: 'Clear the stored publishwordpress_author from article config before publishing. Use this to prevent "rest_cannot_edit_others" errors.', default: false }
2293
2359
  },
2294
- required: ["entity_id"]
2360
+ required: ["entity_id", "connection_id"]
2295
2361
  },
2296
2362
  handler: /* @__PURE__ */ __name(async (args) => {
2297
2363
  try {
2298
- const { entity_id, ...options } = args;
2364
+ const entity_id = resolveId(args, "entity_id");
2365
+ const { connection_id, status, date, category_id, author_id } = args;
2366
+ const clear_author = args.clear_author ?? false;
2367
+ const warnings = [];
2368
+ try {
2369
+ const article = await this.apiClient.getArticle(entity_id);
2370
+ const articleStatus = article.status || "unknown";
2371
+ if (articleStatus !== "completed" && articleStatus !== "success") {
2372
+ const statusMessages = {
2373
+ draft: "Article has not been generated yet.",
2374
+ pending: "Article generation is still pending.",
2375
+ processing: "Article is currently being generated.",
2376
+ failed: "Article generation failed previously.",
2377
+ error: "Article encountered an error during generation."
2378
+ };
2379
+ return {
2380
+ content: [{
2381
+ type: "text",
2382
+ text: JSON.stringify({
2383
+ error: true,
2384
+ message: statusMessages[articleStatus] || `Article status is "${articleStatus}" \u2014 content must be generated before publishing.`,
2385
+ article_status: articleStatus,
2386
+ entity_id,
2387
+ next_steps: [
2388
+ `Use 'generate_article' with id: ${entity_id} to generate content first`,
2389
+ `Use 'get_article' with id: ${entity_id} to check current status`
2390
+ ]
2391
+ }, null, 2)
2392
+ }],
2393
+ isError: true
2394
+ };
2395
+ }
2396
+ const storedAuthor = article.data?.input?.publishwordpress_author;
2397
+ if (storedAuthor && !author_id) {
2398
+ if (clear_author) {
2399
+ try {
2400
+ await this.apiClient.updateArticle(entity_id, { publishwordpress_author: void 0 });
2401
+ logger.info("Cleared stored publishwordpress_author before publishing", { entity_id, cleared_author: storedAuthor });
2402
+ } catch (clearError) {
2403
+ logger.warn("Failed to clear publishwordpress_author", { error: clearError.message });
2404
+ warnings.push(`Could not clear stored publishwordpress_author (${storedAuthor}). Publishing may fail if the WP connection user lacks edit_others_posts.`);
2405
+ }
2406
+ } else {
2407
+ warnings.push(
2408
+ `Article has stored publishwordpress_author=${storedAuthor}. If the WP connection user lacks edit_others_posts capability, publishing will fail with "rest_cannot_edit_others". Use clear_author: true to clear it before publishing.`
2409
+ );
2410
+ }
2411
+ }
2412
+ } catch (fetchError) {
2413
+ logger.warn("Could not pre-validate article before publishing", { entity_id, error: fetchError.message });
2414
+ }
2415
+ const options = { connection_id };
2416
+ if (status) options.status = status;
2417
+ if (date) options.date = date;
2418
+ if (category_id) options.category_id = category_id;
2419
+ if (author_id) options.author_id = author_id;
2299
2420
  const result2 = await this.apiClient.publishToWordPress(entity_id, options);
2300
2421
  this.metrics.recordAPICall("publish_wordpress", result2.success ? "success" : "error");
2301
2422
  if (result2.success) {
2302
- return { content: [{ type: "text", text: JSON.stringify({ success: true, message: "Content published to WordPress successfully", entity_id, wordpress_url: result2.url, status: options.status || "draft" }, null, 2) }] };
2423
+ const response = {
2424
+ success: true,
2425
+ message: "Content published to WordPress successfully",
2426
+ entity_id,
2427
+ wordpress_url: result2.url,
2428
+ status: status || "draft"
2429
+ };
2430
+ if (warnings.length > 0) response.warnings = warnings;
2431
+ return { content: [{ type: "text", text: JSON.stringify(response, null, 2) }] };
2303
2432
  } else {
2304
2433
  throw new Error(result2.error || "Publishing failed");
2305
2434
  }
2306
2435
  } catch (error) {
2307
2436
  logger.error("Failed to publish to WordPress", { error: error.message });
2308
2437
  this.metrics.recordAPICall("publish_wordpress", "error");
2309
- return { content: [{ type: "text", text: JSON.stringify({ error: true, message: error.message || "Failed to publish to WordPress" }, null, 2) }], isError: true };
2438
+ const errorMsg = (error.message || "").toLowerCase();
2439
+ const hints = [];
2440
+ if (errorMsg.includes("rest_cannot_edit_others") || errorMsg.includes("403") || errorMsg.includes("cannot edit others")) {
2441
+ hints.push("This error is likely caused by a stored publishwordpress_author value. Try again with clear_author: true to clear it before publishing.");
2442
+ }
2443
+ const response = { error: true, message: error.message || "Failed to publish to WordPress" };
2444
+ if (hints.length > 0) response.hints = hints;
2445
+ return { content: [{ type: "text", text: JSON.stringify(response, null, 2) }], isError: true };
2310
2446
  }
2311
2447
  }, "handler")
2312
2448
  };
@@ -2871,16 +3007,8 @@ var BulkOperationsToolHandler = class {
2871
3007
  },
2872
3008
  handler: /* @__PURE__ */ __name(async (args) => {
2873
3009
  try {
2874
- let processedArgs = { ...args };
2875
- if (typeof args.articles === "string") {
2876
- logger.info("Converting articles from string to array");
2877
- try {
2878
- processedArgs.articles = JSON.parse(args.articles);
2879
- } catch (error) {
2880
- throw new Error("Invalid articles format: expected array or JSON string");
2881
- }
2882
- }
2883
- const { articles, auto_generate = false } = processedArgs;
3010
+ const articles = resolveArray(args, "articles");
3011
+ const auto_generate = args.auto_generate ?? false;
2884
3012
  logger.info("Validating and transforming article data types", { count: articles.length });
2885
3013
  const validatedArticles = articles.map((article, index) => {
2886
3014
  try {
@@ -2994,17 +3122,24 @@ var BulkOperationsToolHandler = class {
2994
3122
  },
2995
3123
  handler: /* @__PURE__ */ __name(async (args) => {
2996
3124
  try {
2997
- let processedArgs = { ...args };
2998
- if (typeof args.articles === "string") {
2999
- logger.info("Converting articles from string to array (bulk cost estimate)");
3000
- try {
3001
- processedArgs.articles = JSON.parse(args.articles);
3002
- } catch (error) {
3003
- throw new Error("Invalid articles format: expected array or JSON string");
3004
- }
3005
- }
3006
- const { articles } = processedArgs;
3125
+ const articles = resolveArray(args, "articles");
3007
3126
  const result2 = await this.apiClient.calculateBulkCost(articles);
3127
+ logger.debug("bulk_estimate_cost raw API response keys", { keys: Object.keys(result2 || {}) });
3128
+ const breakdown = result2?.breakdown ?? result2?.items ?? result2?.estimates ?? [];
3129
+ let total;
3130
+ if (typeof result2?.total === "number" && !isNaN(result2.total)) {
3131
+ total = result2.total;
3132
+ } else if (typeof result2?.credits === "number" && !isNaN(result2.credits)) {
3133
+ total = result2.credits;
3134
+ } else if (typeof result2?.cost === "number" && !isNaN(result2.cost)) {
3135
+ total = result2.cost;
3136
+ } else if (Array.isArray(breakdown) && breakdown.length > 0) {
3137
+ total = breakdown.reduce((sum, item) => sum + (item?.credits ?? item?.cost ?? 0), 0);
3138
+ logger.info("bulk_estimate_cost: total derived from breakdown sum", { total });
3139
+ } else {
3140
+ total = 0;
3141
+ logger.warn("bulk_estimate_cost: could not extract total from API response, defaulting to 0", { resultKeys: Object.keys(result2 || {}) });
3142
+ }
3008
3143
  this.metrics.recordAPICall("bulk_estimate_cost", "success");
3009
3144
  return {
3010
3145
  content: [
@@ -3013,16 +3148,15 @@ var BulkOperationsToolHandler = class {
3013
3148
  text: JSON.stringify({
3014
3149
  success: true,
3015
3150
  total_cost: {
3016
- credits: result2.total,
3017
- usd: (result2.total * 0.01).toFixed(2)
3018
- // Assuming 1 credit = $0.01
3151
+ credits: total,
3152
+ usd: (total * 0.01).toFixed(2)
3019
3153
  },
3020
3154
  article_count: articles.length,
3021
3155
  average_cost_per_article: {
3022
- credits: Math.round(result2.total / articles.length),
3023
- usd: (result2.total / articles.length * 0.01).toFixed(2)
3156
+ credits: articles.length > 0 ? Math.round(total / articles.length) : 0,
3157
+ usd: articles.length > 0 ? (total / articles.length * 0.01).toFixed(2) : "0.00"
3024
3158
  },
3025
- breakdown: result2.breakdown
3159
+ breakdown
3026
3160
  }, null, 2)
3027
3161
  }
3028
3162
  ]
@@ -3647,7 +3781,7 @@ var CategoryPagesToolHandler = class {
3647
3781
  country: { type: "string", enum: [...COUNTRY_ENUM2], description: "Target country for localization" },
3648
3782
  project_id: { type: "string", format: "uuid", description: "Project UUID" },
3649
3783
  persona_id: { type: "string", format: "uuid", description: "Persona UUID" },
3650
- apply_project_brief_config: { type: "boolean", description: "Apply project configuration", default: false },
3784
+ apply_project_brief_config: { type: "boolean", description: "Apply project configuration. When true with a project_id, lang/country will be inherited from the project if not explicitly provided.", default: false },
3651
3785
  use_faq: { type: "boolean", description: "Generate FAQ section", default: false },
3652
3786
  use_toc: { type: "boolean", description: "Generate table of contents", default: false },
3653
3787
  use_image: { type: "boolean", description: "Generate featured image", default: false },
@@ -3660,7 +3794,7 @@ var CategoryPagesToolHandler = class {
3660
3794
  publishwordpress_date: { type: "string", description: "Publication date (ISO 8601 or WordPress format)" },
3661
3795
  publishwordpress_status: { type: "string", enum: ["draft", "publish", "future"], description: "WordPress post status" },
3662
3796
  publishwordpress_category: { type: "number", description: "WordPress category ID" },
3663
- publishwordpress_author: { type: "number", description: "WordPress author ID" },
3797
+ publishwordpress_author: { type: "number", description: "WordPress author ID. CAUTION: If the WP connection user lacks edit_others_posts capability, setting this will cause publishing to fail with rest_cannot_edit_others. Omit unless the WP user has admin/editor role." },
3664
3798
  publishwordpress_connection: { type: "string", format: "uuid", description: "WordPress connection UUID" }
3665
3799
  },
3666
3800
  required: ["name", "subject"]
@@ -3668,9 +3802,21 @@ var CategoryPagesToolHandler = class {
3668
3802
  handler: /* @__PURE__ */ __name(async (args) => {
3669
3803
  try {
3670
3804
  const validated = CreateCategoryPageSchema.parse(args);
3805
+ if (validated.apply_project_brief_config && validated.project_id && (!validated.lang || !validated.country)) {
3806
+ try {
3807
+ const project = await this.apiClient.getProject(validated.project_id);
3808
+ if (!validated.lang && project.default_lang) {
3809
+ validated.lang = project.default_lang;
3810
+ }
3811
+ if (!validated.country && project.default_country) {
3812
+ validated.country = project.default_country;
3813
+ }
3814
+ } catch (e) {
3815
+ logger.warn("Could not fetch project for lang/country defaults", { error: e.message });
3816
+ }
3817
+ }
3671
3818
  const input = this.buildApiInput(validated);
3672
3819
  const categoryPage = await this.apiClient.createCategoryPage(input);
3673
- await this.cache.set(`category_page:${categoryPage.id}`, categoryPage, 300);
3674
3820
  this.metrics.recordAPICall("create_category_page", "success");
3675
3821
  const displayName = categoryPage.name || validated.name || args.name || "Unnamed";
3676
3822
  return {
@@ -3838,10 +3984,19 @@ var CategoryPagesToolHandler = class {
3838
3984
  return {
3839
3985
  name: "estimate_category_page_cost",
3840
3986
  description: "Estimate the cost of generating a category page",
3841
- inputSchema: { type: "object", properties: { data: { type: "object", description: "Category page configuration for cost estimation" } }, required: ["data"] },
3987
+ inputSchema: {
3988
+ type: "object",
3989
+ properties: {
3990
+ subject: { type: "string", description: "Category page subject", minLength: 1 },
3991
+ length: { oneOf: [{ type: "number", maximum: 5e3 }, { type: "string", const: "auto" }], default: "auto" },
3992
+ use_image: { type: "boolean", default: false }
3993
+ },
3994
+ required: ["subject"]
3995
+ },
3842
3996
  handler: /* @__PURE__ */ __name(async (args) => {
3843
3997
  try {
3844
- const cost = await this.apiClient.calculateCategoryPageCost(args);
3998
+ const costInput = args.data || args;
3999
+ const cost = await this.apiClient.calculateCategoryPageCost(costInput);
3845
4000
  this.metrics.recordAPICall("estimate_category_page_cost", "success");
3846
4001
  return { content: [{ type: "text", text: JSON.stringify({ success: true, cost_estimate: cost }, null, 2) }] };
3847
4002
  } catch (error) {
@@ -3859,7 +4014,7 @@ var CategoryPagesToolHandler = class {
3859
4014
  inputSchema: { type: "object", properties: { items: { type: "array", items: { type: "object" }, description: "Array of category page configurations" } }, required: ["items"] },
3860
4015
  handler: /* @__PURE__ */ __name(async (args) => {
3861
4016
  try {
3862
- const { items } = args;
4017
+ const items = resolveArray(args, "items");
3863
4018
  const result2 = await this.apiClient.createBulkCategoryPages(items);
3864
4019
  this.metrics.recordAPICall("bulk_create_category_pages", "success");
3865
4020
  return { content: [{ type: "text", text: JSON.stringify({ success: true, message: `Bulk created ${items.length} category pages`, result: result2 }, null, 2) }] };
@@ -3963,7 +4118,7 @@ var ProductPagesToolHandler = class {
3963
4118
  country: { type: "string", enum: [...COUNTRY_ENUM3], description: "Target country for localization" },
3964
4119
  project_id: { type: "string", format: "uuid", description: "Project UUID" },
3965
4120
  persona_id: { type: "string", format: "uuid", description: "Persona UUID" },
3966
- apply_project_brief_config: { type: "boolean", description: "Apply project configuration", default: false },
4121
+ apply_project_brief_config: { type: "boolean", description: "Apply project configuration. When true with a project_id, lang/country will be inherited from the project if not explicitly provided.", default: false },
3967
4122
  use_faq: { type: "boolean", description: "Generate FAQ section", default: false },
3968
4123
  use_toc: { type: "boolean", description: "Generate table of contents", default: false },
3969
4124
  use_image: { type: "boolean", description: "Generate featured image", default: false },
@@ -3976,7 +4131,7 @@ var ProductPagesToolHandler = class {
3976
4131
  publishwordpress_date: { type: "string", description: "Publication date (ISO 8601 or WordPress format)" },
3977
4132
  publishwordpress_status: { type: "string", enum: ["draft", "publish", "future"], description: "WordPress post status" },
3978
4133
  publishwordpress_category: { type: "number", description: "WordPress category ID" },
3979
- publishwordpress_author: { type: "number", description: "WordPress author ID" },
4134
+ publishwordpress_author: { type: "number", description: "WordPress author ID. CAUTION: If the WP connection user lacks edit_others_posts capability, setting this will cause publishing to fail with rest_cannot_edit_others. Omit unless the WP user has admin/editor role." },
3980
4135
  publishwordpress_connection: { type: "string", format: "uuid", description: "WordPress connection UUID" }
3981
4136
  },
3982
4137
  required: ["name", "subject"]
@@ -3984,9 +4139,21 @@ var ProductPagesToolHandler = class {
3984
4139
  handler: /* @__PURE__ */ __name(async (args) => {
3985
4140
  try {
3986
4141
  const validated = CreateProductPageSchema.parse(args);
4142
+ if (validated.apply_project_brief_config && validated.project_id && (!validated.lang || !validated.country)) {
4143
+ try {
4144
+ const project = await this.apiClient.getProject(validated.project_id);
4145
+ if (!validated.lang && project.default_lang) {
4146
+ validated.lang = project.default_lang;
4147
+ }
4148
+ if (!validated.country && project.default_country) {
4149
+ validated.country = project.default_country;
4150
+ }
4151
+ } catch (e) {
4152
+ logger.warn("Could not fetch project for lang/country defaults", { error: e.message });
4153
+ }
4154
+ }
3987
4155
  const input = this.buildApiInput(validated);
3988
4156
  const productPage = await this.apiClient.createProductPage(input);
3989
- await this.cache.set(`product_page:${productPage.id}`, productPage, 300);
3990
4157
  this.metrics.recordAPICall("create_product_page", "success");
3991
4158
  const displayName = productPage.name || validated.name || args.name || "Unnamed";
3992
4159
  return { content: [{ type: "text", text: JSON.stringify({ success: true, product_page_id: productPage.id, name: displayName, message: `Product page "${displayName}" created successfully`, next_steps: [`Use 'generate_product_page' with id: ${productPage.id}`, `Use 'get_product_page' with id: ${productPage.id}`] }, null, 2) }] };
@@ -4135,10 +4302,19 @@ var ProductPagesToolHandler = class {
4135
4302
  return {
4136
4303
  name: "estimate_product_page_cost",
4137
4304
  description: "Estimate the cost of generating a product page",
4138
- inputSchema: { type: "object", properties: { data: { type: "object", description: "Product page configuration for cost estimation" } }, required: ["data"] },
4305
+ inputSchema: {
4306
+ type: "object",
4307
+ properties: {
4308
+ subject: { type: "string", description: "Product page subject", minLength: 1 },
4309
+ length: { oneOf: [{ type: "number", maximum: 5e3 }, { type: "string", const: "auto" }], default: "auto" },
4310
+ use_image: { type: "boolean", default: false }
4311
+ },
4312
+ required: ["subject"]
4313
+ },
4139
4314
  handler: /* @__PURE__ */ __name(async (args) => {
4140
4315
  try {
4141
- const cost = await this.apiClient.calculateProductPageCost(args);
4316
+ const costInput = args.data || args;
4317
+ const cost = await this.apiClient.calculateProductPageCost(costInput);
4142
4318
  this.metrics.recordAPICall("estimate_product_page_cost", "success");
4143
4319
  return { content: [{ type: "text", text: JSON.stringify({ success: true, cost_estimate: cost }, null, 2) }] };
4144
4320
  } catch (error) {
@@ -4156,7 +4332,7 @@ var ProductPagesToolHandler = class {
4156
4332
  inputSchema: { type: "object", properties: { items: { type: "array", items: { type: "object" }, description: "Array of product page configurations" } }, required: ["items"] },
4157
4333
  handler: /* @__PURE__ */ __name(async (args) => {
4158
4334
  try {
4159
- const { items } = args;
4335
+ const items = resolveArray(args, "items");
4160
4336
  const result2 = await this.apiClient.createBulkProductPages(items);
4161
4337
  this.metrics.recordAPICall("bulk_create_product_pages", "success");
4162
4338
  return { content: [{ type: "text", text: JSON.stringify({ success: true, message: `Bulk created ${items.length} product pages`, result: result2 }, null, 2) }] };
@@ -4277,7 +4453,7 @@ var DiscoverToolHandler = class {
4277
4453
  keywords_secondary: { type: "string", description: "Secondary keywords" },
4278
4454
  project_id: { type: "string", format: "uuid", description: "Project UUID" },
4279
4455
  persona_id: { type: "string", format: "uuid", description: "Persona UUID" },
4280
- apply_project_brief_config: { type: "boolean", description: "Apply project brief configuration settings. When true, inherits all feature flags and settings from the project brief (use_faq, use_toc, use_inline_images, internal links, etc.). Recommended for consistency.", default: false },
4456
+ apply_project_brief_config: { type: "boolean", description: "Apply project brief configuration settings. When true, inherits all feature flags and settings from the project brief (use_faq, use_toc, use_inline_images, internal links, etc.). When true with a project_id, lang/country will be inherited from the project if not explicitly provided. Recommended for consistency.", default: false },
4281
4457
  lang: { type: "string", enum: ["fr", "en"], description: "Language code" },
4282
4458
  country: { type: "string", enum: ["fr", "be", "ch", "ca", "us", "gb"], description: "Country code" },
4283
4459
  length: { description: 'Number (max 2000) or "auto"' },
@@ -4319,7 +4495,7 @@ var DiscoverToolHandler = class {
4319
4495
  publishwordpress_date: { type: "string", description: "WordPress publish date" },
4320
4496
  publishwordpress_status: { type: "string", enum: ["draft", "publish", "future"], description: "WordPress post status" },
4321
4497
  publishwordpress_category: { type: "number", description: "WordPress category ID" },
4322
- publishwordpress_author: { type: "number", description: "WordPress author ID" },
4498
+ publishwordpress_author: { type: "number", description: "WordPress author ID. CAUTION: If the WP connection user lacks edit_others_posts capability, setting this will cause publishing to fail with rest_cannot_edit_others. Omit unless the WP user has admin/editor role." },
4323
4499
  publishwordpress_connection: { type: "string", description: "WordPress connection ID" },
4324
4500
  // Additional features
4325
4501
  use_infotable: { type: "boolean", description: "Add info table" },
@@ -4344,6 +4520,19 @@ var DiscoverToolHandler = class {
4344
4520
  apply_project_brief_config_raw_type: typeof args.apply_project_brief_config
4345
4521
  });
4346
4522
  const validated = DiscoverContentSchema.parse(args);
4523
+ if (validated.apply_project_brief_config && validated.project_id && (!validated.lang || !validated.country)) {
4524
+ try {
4525
+ const project = await this.apiClient.getProject(validated.project_id);
4526
+ if (!validated.lang && project.default_lang) {
4527
+ validated.lang = project.default_lang;
4528
+ }
4529
+ if (!validated.country && project.default_country) {
4530
+ validated.country = project.default_country;
4531
+ }
4532
+ } catch (e) {
4533
+ logger.warn("Could not fetch project for lang/country defaults", { error: e.message });
4534
+ }
4535
+ }
4347
4536
  logger.info("discover_content VALIDATED (after boolField transform)", {
4348
4537
  apply_project_brief_config: validated.apply_project_brief_config,
4349
4538
  apply_project_brief_config_type: typeof validated.apply_project_brief_config,
@@ -4363,7 +4552,6 @@ var DiscoverToolHandler = class {
4363
4552
  apply_project_brief_config_value: cleanedInput.apply_project_brief_config
4364
4553
  });
4365
4554
  const discovery = await this.apiClient.discoverArticles(cleanedInput);
4366
- await this.cache.set(`discover:${discovery.id}`, discovery, 300);
4367
4555
  this.metrics.recordAPICall("discover_content", "success");
4368
4556
  return {
4369
4557
  content: [{
@@ -4507,10 +4695,19 @@ var DiscoverToolHandler = class {
4507
4695
  return {
4508
4696
  name: "estimate_discover_cost",
4509
4697
  description: "Estimate the cost of a discover article",
4510
- inputSchema: { type: "object", properties: { data: { type: "object", description: "Discovery configuration for cost estimation" } }, required: ["data"] },
4698
+ inputSchema: {
4699
+ type: "object",
4700
+ properties: {
4701
+ subject: { type: "string", description: "Discover article subject", minLength: 1 },
4702
+ length: { oneOf: [{ type: "number", maximum: 2e3 }, { type: "string", const: "auto" }], default: "auto" },
4703
+ use_image: { type: "boolean", default: false }
4704
+ },
4705
+ required: ["subject"]
4706
+ },
4511
4707
  handler: /* @__PURE__ */ __name(async (args) => {
4512
4708
  try {
4513
- const cost = await this.apiClient.calculateDiscoverCost(args);
4709
+ const costInput = args.data || args;
4710
+ const cost = await this.apiClient.calculateDiscoverCost(costInput);
4514
4711
  this.metrics.recordAPICall("estimate_discover_cost", "success");
4515
4712
  return { content: [{ type: "text", text: JSON.stringify({ success: true, cost_estimate: cost }, null, 2) }] };
4516
4713
  } catch (error) {
@@ -4528,7 +4725,7 @@ var DiscoverToolHandler = class {
4528
4725
  inputSchema: { type: "object", properties: { items: { type: "array", items: { type: "object" }, description: "Array of discover article configurations" } }, required: ["items"] },
4529
4726
  handler: /* @__PURE__ */ __name(async (args) => {
4530
4727
  try {
4531
- const { items } = args;
4728
+ const items = resolveArray(args, "items");
4532
4729
  const result2 = await this.apiClient.createBulkDiscoverArticles(items);
4533
4730
  this.metrics.recordAPICall("bulk_create_discover_articles", "success");
4534
4731
  return { content: [{ type: "text", text: JSON.stringify({ success: true, message: `Bulk created ${items.length} discover articles`, result: result2 }, null, 2) }] };
@@ -4621,8 +4818,10 @@ var RSSToolHandler = class {
4621
4818
  } catch (error) {
4622
4819
  logger.error("Failed to create feed", { error: error.message });
4623
4820
  this.metrics.recordAPICall("create_feed", "error");
4821
+ const errorMessage = error.message || "Failed to create feed";
4822
+ const guidance = "Please verify the feed URL is a valid RSS/Atom feed and is accessible. If the URL is correct, this may be a temporary Wisewand service issue \u2014 try again later.";
4624
4823
  return {
4625
- content: [{ type: "text", text: JSON.stringify({ error: true, message: error.message || "Failed to create feed", details: error.issues || void 0 }, null, 2) }],
4824
+ content: [{ type: "text", text: JSON.stringify({ error: true, message: errorMessage, guidance, details: error.issues || void 0 }, null, 2) }],
4626
4825
  isError: true
4627
4826
  };
4628
4827
  }