@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 +280 -71
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
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
|
-
|
|
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:
|
|
1876
|
+
formattedOutput = { content: normalizedOutput?.content ?? normalizedOutput?.markdown ?? null, title: normalizedOutput?.title ?? null };
|
|
1817
1877
|
break;
|
|
1818
|
-
case "summary":
|
|
1819
|
-
|
|
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 =
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
2875
|
-
|
|
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
|
-
|
|
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:
|
|
3017
|
-
usd:
|
|
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(
|
|
3023
|
-
usd:
|
|
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
|
|
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: {
|
|
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
|
|
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
|
|
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: {
|
|
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
|
|
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
|
|
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: {
|
|
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
|
|
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
|
|
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:
|
|
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
|
}
|