@wisewandtools/mcp-server 2.2.1 → 2.2.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/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,115 @@ 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: null });
2401
+ try {
2402
+ const updated = await this.apiClient.getArticle(entity_id);
2403
+ const stillPresent = updated.data?.input?.publishwordpress_author;
2404
+ if (stillPresent) {
2405
+ logger.warn("publishwordpress_author still present after clear attempt", { entity_id, value: stillPresent });
2406
+ warnings.push(`publishwordpress_author may not have been fully cleared (still shows "${stillPresent}"). Publishing may still fail.`);
2407
+ } else {
2408
+ logger.info("Cleared stored publishwordpress_author before publishing", { entity_id, cleared_author: storedAuthor });
2409
+ }
2410
+ } catch (verifyError) {
2411
+ logger.warn("Could not verify publishwordpress_author was cleared", { error: verifyError.message });
2412
+ }
2413
+ } catch (clearError) {
2414
+ logger.warn("Failed to clear publishwordpress_author", { error: clearError.message });
2415
+ warnings.push(`Could not clear stored publishwordpress_author (${storedAuthor}). Publishing may fail if the WP connection user lacks edit_others_posts.`);
2416
+ }
2417
+ } else {
2418
+ warnings.push(
2419
+ `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.`
2420
+ );
2421
+ }
2422
+ }
2423
+ } catch (fetchError) {
2424
+ logger.warn("Could not pre-validate article before publishing", { entity_id, error: fetchError.message });
2425
+ }
2426
+ const options = { connection_id };
2427
+ if (status) options.status = status;
2428
+ if (date) options.date = date;
2429
+ if (category_id) options.category_id = category_id;
2430
+ if (author_id) options.author_id = author_id;
2299
2431
  const result2 = await this.apiClient.publishToWordPress(entity_id, options);
2300
2432
  this.metrics.recordAPICall("publish_wordpress", result2.success ? "success" : "error");
2301
2433
  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) }] };
2434
+ const response = {
2435
+ success: true,
2436
+ message: "Content published to WordPress successfully",
2437
+ entity_id,
2438
+ wordpress_url: result2.url,
2439
+ status: status || "draft"
2440
+ };
2441
+ if (warnings.length > 0) response.warnings = warnings;
2442
+ return { content: [{ type: "text", text: JSON.stringify(response, null, 2) }] };
2303
2443
  } else {
2304
2444
  throw new Error(result2.error || "Publishing failed");
2305
2445
  }
2306
2446
  } catch (error) {
2307
2447
  logger.error("Failed to publish to WordPress", { error: error.message });
2308
2448
  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 };
2449
+ const errorMsg = (error.message || "").toLowerCase();
2450
+ const hints = [];
2451
+ if (errorMsg.includes("rest_cannot_edit_others") || errorMsg.includes("403") || errorMsg.includes("cannot edit others")) {
2452
+ hints.push("This error is likely caused by a stored publishwordpress_author value. Try again with clear_author: true to clear it before publishing.");
2453
+ }
2454
+ const response = { error: true, message: error.message || "Failed to publish to WordPress" };
2455
+ if (hints.length > 0) response.hints = hints;
2456
+ return { content: [{ type: "text", text: JSON.stringify(response, null, 2) }], isError: true };
2310
2457
  }
2311
2458
  }, "handler")
2312
2459
  };
@@ -2871,16 +3018,8 @@ var BulkOperationsToolHandler = class {
2871
3018
  },
2872
3019
  handler: /* @__PURE__ */ __name(async (args) => {
2873
3020
  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;
3021
+ const articles = resolveArray(args, "articles");
3022
+ const auto_generate = args.auto_generate ?? false;
2884
3023
  logger.info("Validating and transforming article data types", { count: articles.length });
2885
3024
  const validatedArticles = articles.map((article, index) => {
2886
3025
  try {
@@ -2994,17 +3133,23 @@ var BulkOperationsToolHandler = class {
2994
3133
  },
2995
3134
  handler: /* @__PURE__ */ __name(async (args) => {
2996
3135
  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;
3136
+ const articles = resolveArray(args, "articles");
3007
3137
  const result2 = await this.apiClient.calculateBulkCost(articles);
3138
+ logger.debug("bulk_estimate_cost raw API response keys", { keys: Object.keys(result2 || {}) });
3139
+ const breakdown = result2?.breakdown ?? result2?.items ?? result2?.estimates ?? [];
3140
+ let totalCredits;
3141
+ if (typeof result2?.credits === "number" && !isNaN(result2.credits)) {
3142
+ totalCredits = result2.credits;
3143
+ } else if (typeof result2?.total === "number" && !isNaN(result2.total)) {
3144
+ totalCredits = result2.total;
3145
+ } else if (Array.isArray(breakdown) && breakdown.length > 0) {
3146
+ totalCredits = breakdown.reduce((sum, item) => sum + (item?.credits ?? item?.cost ?? 0), 0);
3147
+ logger.info("bulk_estimate_cost: totalCredits derived from breakdown sum", { totalCredits });
3148
+ } else {
3149
+ totalCredits = 0;
3150
+ logger.warn("bulk_estimate_cost: could not extract credits from API response, defaulting to 0", { resultKeys: Object.keys(result2 || {}) });
3151
+ }
3152
+ const totalCost = typeof result2?.cost === "number" && !isNaN(result2.cost) ? result2.cost : typeof result2?.price === "number" && !isNaN(result2.price) ? result2.price : null;
3008
3153
  this.metrics.recordAPICall("bulk_estimate_cost", "success");
3009
3154
  return {
3010
3155
  content: [
@@ -3013,16 +3158,15 @@ var BulkOperationsToolHandler = class {
3013
3158
  text: JSON.stringify({
3014
3159
  success: true,
3015
3160
  total_cost: {
3016
- credits: result2.total,
3017
- usd: (result2.total * 0.01).toFixed(2)
3018
- // Assuming 1 credit = $0.01
3161
+ credits: totalCredits,
3162
+ usd: totalCost !== null ? totalCost.toFixed(2) : null
3019
3163
  },
3020
3164
  article_count: articles.length,
3021
3165
  average_cost_per_article: {
3022
- credits: Math.round(result2.total / articles.length),
3023
- usd: (result2.total / articles.length * 0.01).toFixed(2)
3166
+ credits: articles.length > 0 ? Math.round(totalCredits / articles.length) : 0,
3167
+ usd: totalCost !== null && articles.length > 0 ? (totalCost / articles.length).toFixed(2) : null
3024
3168
  },
3025
- breakdown: result2.breakdown
3169
+ breakdown
3026
3170
  }, null, 2)
3027
3171
  }
3028
3172
  ]
@@ -3647,7 +3791,7 @@ var CategoryPagesToolHandler = class {
3647
3791
  country: { type: "string", enum: [...COUNTRY_ENUM2], description: "Target country for localization" },
3648
3792
  project_id: { type: "string", format: "uuid", description: "Project UUID" },
3649
3793
  persona_id: { type: "string", format: "uuid", description: "Persona UUID" },
3650
- apply_project_brief_config: { type: "boolean", description: "Apply project configuration", default: false },
3794
+ 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
3795
  use_faq: { type: "boolean", description: "Generate FAQ section", default: false },
3652
3796
  use_toc: { type: "boolean", description: "Generate table of contents", default: false },
3653
3797
  use_image: { type: "boolean", description: "Generate featured image", default: false },
@@ -3660,7 +3804,7 @@ var CategoryPagesToolHandler = class {
3660
3804
  publishwordpress_date: { type: "string", description: "Publication date (ISO 8601 or WordPress format)" },
3661
3805
  publishwordpress_status: { type: "string", enum: ["draft", "publish", "future"], description: "WordPress post status" },
3662
3806
  publishwordpress_category: { type: "number", description: "WordPress category ID" },
3663
- publishwordpress_author: { type: "number", description: "WordPress author ID" },
3807
+ 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
3808
  publishwordpress_connection: { type: "string", format: "uuid", description: "WordPress connection UUID" }
3665
3809
  },
3666
3810
  required: ["name", "subject"]
@@ -3668,9 +3812,21 @@ var CategoryPagesToolHandler = class {
3668
3812
  handler: /* @__PURE__ */ __name(async (args) => {
3669
3813
  try {
3670
3814
  const validated = CreateCategoryPageSchema.parse(args);
3815
+ if (validated.apply_project_brief_config && validated.project_id && (!validated.lang || !validated.country)) {
3816
+ try {
3817
+ const project = await this.apiClient.getProject(validated.project_id);
3818
+ if (!validated.lang && project.default_lang) {
3819
+ validated.lang = project.default_lang;
3820
+ }
3821
+ if (!validated.country && project.default_country) {
3822
+ validated.country = project.default_country;
3823
+ }
3824
+ } catch (e) {
3825
+ logger.warn("Could not fetch project for lang/country defaults", { error: e.message });
3826
+ }
3827
+ }
3671
3828
  const input = this.buildApiInput(validated);
3672
3829
  const categoryPage = await this.apiClient.createCategoryPage(input);
3673
- await this.cache.set(`category_page:${categoryPage.id}`, categoryPage, 300);
3674
3830
  this.metrics.recordAPICall("create_category_page", "success");
3675
3831
  const displayName = categoryPage.name || validated.name || args.name || "Unnamed";
3676
3832
  return {
@@ -3838,10 +3994,19 @@ var CategoryPagesToolHandler = class {
3838
3994
  return {
3839
3995
  name: "estimate_category_page_cost",
3840
3996
  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"] },
3997
+ inputSchema: {
3998
+ type: "object",
3999
+ properties: {
4000
+ subject: { type: "string", description: "Category page subject", minLength: 1 },
4001
+ length: { oneOf: [{ type: "number", maximum: 5e3 }, { type: "string", const: "auto" }], default: "auto" },
4002
+ use_image: { type: "boolean", default: false }
4003
+ },
4004
+ required: ["subject"]
4005
+ },
3842
4006
  handler: /* @__PURE__ */ __name(async (args) => {
3843
4007
  try {
3844
- const cost = await this.apiClient.calculateCategoryPageCost(args);
4008
+ const costInput = args.data || args;
4009
+ const cost = await this.apiClient.calculateCategoryPageCost(costInput);
3845
4010
  this.metrics.recordAPICall("estimate_category_page_cost", "success");
3846
4011
  return { content: [{ type: "text", text: JSON.stringify({ success: true, cost_estimate: cost }, null, 2) }] };
3847
4012
  } catch (error) {
@@ -3859,7 +4024,7 @@ var CategoryPagesToolHandler = class {
3859
4024
  inputSchema: { type: "object", properties: { items: { type: "array", items: { type: "object" }, description: "Array of category page configurations" } }, required: ["items"] },
3860
4025
  handler: /* @__PURE__ */ __name(async (args) => {
3861
4026
  try {
3862
- const { items } = args;
4027
+ const items = resolveArray(args, "items");
3863
4028
  const result2 = await this.apiClient.createBulkCategoryPages(items);
3864
4029
  this.metrics.recordAPICall("bulk_create_category_pages", "success");
3865
4030
  return { content: [{ type: "text", text: JSON.stringify({ success: true, message: `Bulk created ${items.length} category pages`, result: result2 }, null, 2) }] };
@@ -3963,7 +4128,7 @@ var ProductPagesToolHandler = class {
3963
4128
  country: { type: "string", enum: [...COUNTRY_ENUM3], description: "Target country for localization" },
3964
4129
  project_id: { type: "string", format: "uuid", description: "Project UUID" },
3965
4130
  persona_id: { type: "string", format: "uuid", description: "Persona UUID" },
3966
- apply_project_brief_config: { type: "boolean", description: "Apply project configuration", default: false },
4131
+ 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
4132
  use_faq: { type: "boolean", description: "Generate FAQ section", default: false },
3968
4133
  use_toc: { type: "boolean", description: "Generate table of contents", default: false },
3969
4134
  use_image: { type: "boolean", description: "Generate featured image", default: false },
@@ -3976,7 +4141,7 @@ var ProductPagesToolHandler = class {
3976
4141
  publishwordpress_date: { type: "string", description: "Publication date (ISO 8601 or WordPress format)" },
3977
4142
  publishwordpress_status: { type: "string", enum: ["draft", "publish", "future"], description: "WordPress post status" },
3978
4143
  publishwordpress_category: { type: "number", description: "WordPress category ID" },
3979
- publishwordpress_author: { type: "number", description: "WordPress author ID" },
4144
+ 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
4145
  publishwordpress_connection: { type: "string", format: "uuid", description: "WordPress connection UUID" }
3981
4146
  },
3982
4147
  required: ["name", "subject"]
@@ -3984,9 +4149,21 @@ var ProductPagesToolHandler = class {
3984
4149
  handler: /* @__PURE__ */ __name(async (args) => {
3985
4150
  try {
3986
4151
  const validated = CreateProductPageSchema.parse(args);
4152
+ if (validated.apply_project_brief_config && validated.project_id && (!validated.lang || !validated.country)) {
4153
+ try {
4154
+ const project = await this.apiClient.getProject(validated.project_id);
4155
+ if (!validated.lang && project.default_lang) {
4156
+ validated.lang = project.default_lang;
4157
+ }
4158
+ if (!validated.country && project.default_country) {
4159
+ validated.country = project.default_country;
4160
+ }
4161
+ } catch (e) {
4162
+ logger.warn("Could not fetch project for lang/country defaults", { error: e.message });
4163
+ }
4164
+ }
3987
4165
  const input = this.buildApiInput(validated);
3988
4166
  const productPage = await this.apiClient.createProductPage(input);
3989
- await this.cache.set(`product_page:${productPage.id}`, productPage, 300);
3990
4167
  this.metrics.recordAPICall("create_product_page", "success");
3991
4168
  const displayName = productPage.name || validated.name || args.name || "Unnamed";
3992
4169
  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 +4312,19 @@ var ProductPagesToolHandler = class {
4135
4312
  return {
4136
4313
  name: "estimate_product_page_cost",
4137
4314
  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"] },
4315
+ inputSchema: {
4316
+ type: "object",
4317
+ properties: {
4318
+ subject: { type: "string", description: "Product page subject", minLength: 1 },
4319
+ length: { oneOf: [{ type: "number", maximum: 5e3 }, { type: "string", const: "auto" }], default: "auto" },
4320
+ use_image: { type: "boolean", default: false }
4321
+ },
4322
+ required: ["subject"]
4323
+ },
4139
4324
  handler: /* @__PURE__ */ __name(async (args) => {
4140
4325
  try {
4141
- const cost = await this.apiClient.calculateProductPageCost(args);
4326
+ const costInput = args.data || args;
4327
+ const cost = await this.apiClient.calculateProductPageCost(costInput);
4142
4328
  this.metrics.recordAPICall("estimate_product_page_cost", "success");
4143
4329
  return { content: [{ type: "text", text: JSON.stringify({ success: true, cost_estimate: cost }, null, 2) }] };
4144
4330
  } catch (error) {
@@ -4156,7 +4342,7 @@ var ProductPagesToolHandler = class {
4156
4342
  inputSchema: { type: "object", properties: { items: { type: "array", items: { type: "object" }, description: "Array of product page configurations" } }, required: ["items"] },
4157
4343
  handler: /* @__PURE__ */ __name(async (args) => {
4158
4344
  try {
4159
- const { items } = args;
4345
+ const items = resolveArray(args, "items");
4160
4346
  const result2 = await this.apiClient.createBulkProductPages(items);
4161
4347
  this.metrics.recordAPICall("bulk_create_product_pages", "success");
4162
4348
  return { content: [{ type: "text", text: JSON.stringify({ success: true, message: `Bulk created ${items.length} product pages`, result: result2 }, null, 2) }] };
@@ -4277,7 +4463,7 @@ var DiscoverToolHandler = class {
4277
4463
  keywords_secondary: { type: "string", description: "Secondary keywords" },
4278
4464
  project_id: { type: "string", format: "uuid", description: "Project UUID" },
4279
4465
  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 },
4466
+ 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
4467
  lang: { type: "string", enum: ["fr", "en"], description: "Language code" },
4282
4468
  country: { type: "string", enum: ["fr", "be", "ch", "ca", "us", "gb"], description: "Country code" },
4283
4469
  length: { description: 'Number (max 2000) or "auto"' },
@@ -4319,7 +4505,7 @@ var DiscoverToolHandler = class {
4319
4505
  publishwordpress_date: { type: "string", description: "WordPress publish date" },
4320
4506
  publishwordpress_status: { type: "string", enum: ["draft", "publish", "future"], description: "WordPress post status" },
4321
4507
  publishwordpress_category: { type: "number", description: "WordPress category ID" },
4322
- publishwordpress_author: { type: "number", description: "WordPress author ID" },
4508
+ 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
4509
  publishwordpress_connection: { type: "string", description: "WordPress connection ID" },
4324
4510
  // Additional features
4325
4511
  use_infotable: { type: "boolean", description: "Add info table" },
@@ -4344,6 +4530,19 @@ var DiscoverToolHandler = class {
4344
4530
  apply_project_brief_config_raw_type: typeof args.apply_project_brief_config
4345
4531
  });
4346
4532
  const validated = DiscoverContentSchema.parse(args);
4533
+ if (validated.apply_project_brief_config && validated.project_id && (!validated.lang || !validated.country)) {
4534
+ try {
4535
+ const project = await this.apiClient.getProject(validated.project_id);
4536
+ if (!validated.lang && project.default_lang) {
4537
+ validated.lang = project.default_lang;
4538
+ }
4539
+ if (!validated.country && project.default_country) {
4540
+ validated.country = project.default_country;
4541
+ }
4542
+ } catch (e) {
4543
+ logger.warn("Could not fetch project for lang/country defaults", { error: e.message });
4544
+ }
4545
+ }
4347
4546
  logger.info("discover_content VALIDATED (after boolField transform)", {
4348
4547
  apply_project_brief_config: validated.apply_project_brief_config,
4349
4548
  apply_project_brief_config_type: typeof validated.apply_project_brief_config,
@@ -4363,7 +4562,6 @@ var DiscoverToolHandler = class {
4363
4562
  apply_project_brief_config_value: cleanedInput.apply_project_brief_config
4364
4563
  });
4365
4564
  const discovery = await this.apiClient.discoverArticles(cleanedInput);
4366
- await this.cache.set(`discover:${discovery.id}`, discovery, 300);
4367
4565
  this.metrics.recordAPICall("discover_content", "success");
4368
4566
  return {
4369
4567
  content: [{
@@ -4507,10 +4705,19 @@ var DiscoverToolHandler = class {
4507
4705
  return {
4508
4706
  name: "estimate_discover_cost",
4509
4707
  description: "Estimate the cost of a discover article",
4510
- inputSchema: { type: "object", properties: { data: { type: "object", description: "Discovery configuration for cost estimation" } }, required: ["data"] },
4708
+ inputSchema: {
4709
+ type: "object",
4710
+ properties: {
4711
+ subject: { type: "string", description: "Discover article subject", minLength: 1 },
4712
+ length: { oneOf: [{ type: "number", maximum: 2e3 }, { type: "string", const: "auto" }], default: "auto" },
4713
+ use_image: { type: "boolean", default: false }
4714
+ },
4715
+ required: ["subject"]
4716
+ },
4511
4717
  handler: /* @__PURE__ */ __name(async (args) => {
4512
4718
  try {
4513
- const cost = await this.apiClient.calculateDiscoverCost(args);
4719
+ const costInput = args.data || args;
4720
+ const cost = await this.apiClient.calculateDiscoverCost(costInput);
4514
4721
  this.metrics.recordAPICall("estimate_discover_cost", "success");
4515
4722
  return { content: [{ type: "text", text: JSON.stringify({ success: true, cost_estimate: cost }, null, 2) }] };
4516
4723
  } catch (error) {
@@ -4528,7 +4735,7 @@ var DiscoverToolHandler = class {
4528
4735
  inputSchema: { type: "object", properties: { items: { type: "array", items: { type: "object" }, description: "Array of discover article configurations" } }, required: ["items"] },
4529
4736
  handler: /* @__PURE__ */ __name(async (args) => {
4530
4737
  try {
4531
- const { items } = args;
4738
+ const items = resolveArray(args, "items");
4532
4739
  const result2 = await this.apiClient.createBulkDiscoverArticles(items);
4533
4740
  this.metrics.recordAPICall("bulk_create_discover_articles", "success");
4534
4741
  return { content: [{ type: "text", text: JSON.stringify({ success: true, message: `Bulk created ${items.length} discover articles`, result: result2 }, null, 2) }] };
@@ -4621,8 +4828,10 @@ var RSSToolHandler = class {
4621
4828
  } catch (error) {
4622
4829
  logger.error("Failed to create feed", { error: error.message });
4623
4830
  this.metrics.recordAPICall("create_feed", "error");
4831
+ const errorMessage = error.message || "Failed to create feed";
4832
+ 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
4833
  return {
4625
- content: [{ type: "text", text: JSON.stringify({ error: true, message: error.message || "Failed to create feed", details: error.issues || void 0 }, null, 2) }],
4834
+ content: [{ type: "text", text: JSON.stringify({ error: true, message: errorMessage, guidance, details: error.issues || void 0 }, null, 2) }],
4626
4835
  isError: true
4627
4836
  };
4628
4837
  }