This commit is contained in:
Your Name committed 2026-07-24 19:10:13 -04:00
1 parent bd8f5882e0
commit e509e3f0a2
8 files changed
+581 -12

No files matched your search

+68 -4
View File
@@ -18,6 +18,7 @@ const WEBSITE_PAGE_SECTION_TYPE_LIST = [
"BANNER",
"HERO_ARTICLE",
"RICH_TEXT",
"ARTICLE_MEDIA",
"SCENT_NOTES_BREAKDOWN",
"QUOTE_CALLOUT",
"PRODUCT_CAROUSEL",
@@ -299,6 +300,32 @@ const PAGE_BLOCK_METADATA: Record<string, BlockMetadata> = {
},
},
},
ARTICLE_MEDIA: {
type: "ARTICLE_MEDIA",
requiredFields: ["body"],
optionalFields: ["heading", "imageUrl", "imageAlt", "imageCaption", "imagePosition", "width", "background"],
renderedFields: ["heading", "body", "imageUrl", "imageAlt", "imageCaption", "imagePosition", "width", "background"],
storedOnlyFields: [],
discoveryToolsNeeded: [],
warnings: [
"body is rendered as plain text with preserved line breaks; do not send HTML.",
"imageAlt is required whenever imageUrl is provided.",
],
minimalPayload: { body: localizedObjectExample },
recommendedPayload: {
heading: { th: "ความแม่นยำในทุกฟันสเตอร์", en: "Precision in every sprocket tooth" },
body: {
th: "Fine Blanking ช่วยควบคุมผิวตัดและรูปทรงของฟันสเตอร์ให้สม่ำเสมอ เพื่อการส่งกำลังที่ราบรื่นและแม่นยำ",
en: "Fine blanking controls the cut surface and tooth profile for smooth, precise power transmission.",
},
imageUrl: imageUrlExample,
imageAlt: { th: "สเตอร์ที่ผลิตด้วยเทคโนโลยี Fine Blanking", en: "A sprocket made with fine blanking technology" },
imageCaption: { th: "ผิวตัดเรียบและรูปทรงสม่ำเสมอ", en: "A smooth cut surface and consistent profile" },
imagePosition: "right",
width: "standard",
background: "default",
},
},
SCENT_NOTES_BREAKDOWN: {
type: "SCENT_NOTES_BREAKDOWN",
requiredFields: [],
@@ -524,6 +551,10 @@ const ADD_PAGE_SECTION_GUIDANCE: Record<string, { required: string[]; example: s
required: ["content"],
example: `{ "title": ${localizedStringExample}, "content": ${localizedStringExample} }`
},
ARTICLE_MEDIA: {
required: ["body"],
example: `{ "heading": ${localizedStringExample}, "body": ${localizedStringExample}, "imageUrl": "https://example.com/article.jpg", "imageAlt": ${localizedStringExample}, "imagePosition": "right" }`
},
SCENT_NOTES_BREAKDOWN: {
required: [],
example: `{ "title": ${localizedStringExample}, "topNotes": ${localizedStringListExample} }`
@@ -783,6 +814,23 @@ const SECTION_TOOL_CONFIGS = [
},
required: ["sectionId", "content"]
},
{
name: "update_article_media",
sectionType: "ARTICLE_MEDIA",
description: `Update an ARTICLE_MEDIA section. This tool performs a partial update/merge (PATCH). Omitted fields are preserved. body is localized plain text; do not send HTML. imageAlt is required when imageUrl is present. Example payload: { "sectionId": 35, "heading": ${localizedStringExample}, "body": ${localizedStringExample}, "imageUrl": "https://...", "imageAlt": ${localizedStringExample}, "imagePosition": "right" }`,
properties: {
sectionId: { type: "number", description: "The numeric ID of the section to update" },
heading: { ...localizedStringSchema, description: `Optional localized heading, for example ${localizedStringExample}` },
body: { ...localizedStringSchema, description: `Localized plain text body, for example ${localizedStringExample}` },
imageUrl: { type: "string", description: "Optional image URL or website-relative media path" },
imageAlt: { ...localizedStringSchema, description: `Localized image alternative text, required with imageUrl` },
imageCaption: { ...localizedStringSchema, description: `Optional localized image caption` },
imagePosition: { type: "string", enum: ["left", "right", "top"], description: "Responsive image placement" },
width: { type: "string", enum: ["narrow", "standard", "wide"], description: "Content width preset" },
background: { type: "string", enum: ["default", "muted"], description: "Theme-aware background preset" },
},
required: ["sectionId"]
},
{
name: "update_scent_notes_breakdown",
sectionType: "SCENT_NOTES_BREAKDOWN",
@@ -1020,7 +1068,7 @@ export const SECTION_TOOL_DEFINITIONS = [
},
{
name: "create_page_with_section",
description: "Create or update a dynamic CMS page and add one website-renderable section in one operation. Use this when the user asks to create a page with a single block.",
description: "Create a new dynamic CMS page and its first website-renderable section atomically. This tool is create-only: if the slug already exists, use get_dynamic_page_detail to find the page ID, then use add_page_section.",
inputSchema: {
type: "object",
properties: {
@@ -1052,7 +1100,7 @@ export const SECTION_TOOL_DEFINITIONS = [
},
{
name: "add_page_section",
description: `${buildAddPageSectionDescription()}\n\nRender target guard: generic CMS pages are rendered by apps/website DynamicPageRenderer. PRODUCT_MAIN, PRODUCT_DETAILS, PRODUCT_SPECS, PRODUCT_REVIEWS, and RELATED_PRODUCTS are product detail native blocks and are rejected by default here. Use product detail CMS tools for those blocks.`,
description: `Add a new website-renderable block to an existing dynamic CMS page. Use this instead of create_page_with_section when the page or slug already exists. Call get_dynamic_page_detail first to obtain pageId, and get_page_block_schema before composing payload.\n\n${buildAddPageSectionDescription()}\n\nRender target guard: generic CMS pages are rendered by apps/website DynamicPageRenderer. PRODUCT_MAIN, PRODUCT_DETAILS, PRODUCT_SPECS, PRODUCT_REVIEWS, and RELATED_PRODUCTS are product detail native blocks and are rejected by default here. Use product detail CMS tools for those blocks.`,
inputSchema: {
type: "object",
properties: {
@@ -1398,6 +1446,20 @@ export async function handleSectionTool(api: AxiosInstance, name: string, params
throw new Error(`Validation Error: 'pageId' is required for tool '${name}'`);
}
assertWebsitePageSectionType(params.type, params.allowProductNative === true);
const validation = validatePayloadAgainstMetadata(params.type, params.payload);
if (!validation.valid) {
return {
content: [{
type: "text",
text: JSON.stringify({
success: false,
error: "Payload failed MCP preflight validation.",
validation,
}, null, 2)
}],
isError: true,
};
}
const payload: any = {
type: params.type,
payload: params.payload,
@@ -1414,8 +1476,10 @@ export async function handleSectionTool(api: AxiosInstance, name: string, params
pageId: params.pageId,
pageSlug: params.pageSlug ?? undefined,
preview: params.pageSlug ? buildCmsPreview(params.pageSlug, params.lang || defaultLang) : undefined,
renderability: buildRenderability(params.type),
section: res.data,
sectionId: res.data?.sectionId ?? res.data?.section?.id ?? res.data?.id ?? null,
type: params.type,
order: res.data?.section?.order ?? res.data?.order ?? params.order ?? null,
warnings: buildRenderability(params.type).warnings,
}, null, 2)
}]
};