This commit is contained in:
Your Name committed 2026-06-23 20:02:29 -04:00
1 parent ed69a4b840
commit 992335a52b
22 files changed
+1064 -346

No files matched your search

+61 -30
View File
@@ -80,15 +80,26 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
description: "Ping the backend to verify connection and authentication",
inputSchema: { type: "object", properties: {} },
},
{
name: "list_product_brands",
description: "List all product brands with their translations",
inputSchema: { type: "object", properties: {} }
},
{
name: "list_categories",
description: "List all product categories with their translations",
inputSchema: { type: "object", properties: {} }
},
{
name: "search_products",
description: "Search for products and get their basic info",
description: "Search for products and get their basic info. WARNING: Set a low 'limit' (e.g. 5) to prevent token overflow, as product details are massive.",
inputSchema: {
type: "object",
properties: {
search: { type: "string", description: "Search term" },
limit: { type: "number", description: "Max results" },
lang: { type: "string", description: "Language code (e.g. th, en, all)" }
lang: { type: "string", description: "Language code (e.g. th, en, all)" },
brandId: { type: "number", description: "Filter by brand ID" }
}
}
},
@@ -118,14 +129,14 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
}
},
{
name: "update_scent_translation",
description: "Add or update translations for a specific scent.",
name: "update_product_brand_translation",
description: "Add or update translations for a specific product brand.",
inputSchema: {
type: "object",
properties: {
id: { type: "number", description: "Scent ID" },
id: { type: "number", description: "Brand ID" },
lang: { type: "string", description: "Language code (th, en, ja, zh, vi, ko)" },
name: { type: "string", description: "Translated scent name" }
name: { type: "string", description: "Translated brand name" }
},
required: ["id", "lang", "name"]
}
@@ -240,9 +251,25 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
required: ["id", "lang"]
}
},
{
name: "create_product",
description: "Create a new product. Minimum required fields are categoryId and brandId.",
inputSchema: {
type: "object",
properties: {
categoryId: { type: "number" },
brandId: { type: "number" },
name: { type: "string" },
price: { type: "number" },
description: { type: "string" },
details: { type: "string" }
},
required: ["categoryId", "brandId"]
}
},
{
name: "update_product_content",
description: "Update product basic content (name, description, details)",
description: "Update product basic content for the default/Thai translation. Note: If the Thai translation record does not exist yet, the 'name' field is strictly required, otherwise the backend will silently drop the update.",
inputSchema: {
type: "object",
properties: {
@@ -256,7 +283,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
},
{
name: "update_product_translation",
description: "Update product translation for a specific language. Use this to add missing translations.",
description: "Update product translation for a specific language. Use this to add missing translations. Note: If adding a missing translation, the 'name' field is strictly required by the backend, otherwise the update will silently fail.",
inputSchema: {
type: "object",
properties: {
@@ -355,7 +382,8 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
inputSchema: {
type: "object",
properties: {
slug: { type: "string", description: "Slug of the page" }
slug: { type: "string", description: "Slug of the page" },
includeInactive: { type: "boolean", description: "Include inactive sections" }
},
required: ["slug"]
}
@@ -367,16 +395,15 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
type: "object",
properties: {
title: { type: "string", description: "Title of the page" },
slug: { type: "string", description: "Unique slug identifier (e.g. about-us)" },
slug: { type: "string", description: "Unique slug identifier. Do not use reserved system slugs (e.g., shop, category, cart, checkout, account, article, etc.)." },
isPublished: { type: "boolean", description: "Publish status" },
status: { type: "string", description: "Publish status (e.g. 'published' or 'draft')" },
metadata: { type: "object", description: "SEO metadata" },
id: { type: "number", description: "Numeric page ID (if updating existing page)" }
id: { type: "number", description: "Numeric page ID. Required if you are updating an existing page. Use list_dynamic_pages_metadata or get_dynamic_page_detail to find the ID first." }
},
required: ["title", "slug"]
}
},
...SECTION_TOOL_DEFINITIONS,
{
name: "update_footer_content",
description: "Update the content and links of the 4 Footer Blocks (Brand, Explore, Collections, Newsletter)",
@@ -465,11 +492,20 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
const res = await api.get(`/menus/${params.name}`);
return { content: [{ type: "text", text: JSON.stringify(res.data, null, 2) }] };
}
case "list_product_brands": {
const res = await api.get(`/products/brands`);
return { content: [{ type: "text", text: JSON.stringify(res.data, null, 2) }] };
}
case "list_categories": {
const res = await api.get(`/categories`);
return { content: [{ type: "text", text: JSON.stringify(res.data, null, 2) }] };
}
case "search_products": {
const search = params.search ? `&search=${encodeURIComponent(params.search)}` : "";
const limit = params.limit ? `&limit=${params.limit}` : "&limit=10";
const lang = params.lang ? `&lang=${params.lang}` : "";
const res = await api.get(`/products?${search}${limit}${lang}`);
const brandId = params.brandId ? `&brandId=${params.brandId}` : "";
const res = await api.get(`/products?${search}${limit}${lang}${brandId}`);
return { content: [{ type: "text", text: JSON.stringify(res.data, null, 2) }] };
}
case "get_product_detail": {
@@ -477,6 +513,10 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
const res = await api.get(`/products/${params.id}${lang}`);
return { content: [{ type: "text", text: JSON.stringify(res.data, null, 2) }] };
}
case "create_product": {
const res = await api.post(`/products`, params);
return { content: [{ type: "text", text: JSON.stringify(res.data, null, 2) }] };
}
case "update_category_translation": {
const { id, lang, name } = params as any;
const res = await api.patch(`/categories/${id}/translations`, {
@@ -486,9 +526,9 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
content: [{ type: "text", text: JSON.stringify(res.data, null, 2) }]
};
}
case "update_scent_translation": {
case "update_product_brand_translation": {
const { id, lang, name } = params as any;
const res = await api.patch(`/products/scents/${id}/translations`, {
const res = await api.patch(`/products/brands/${id}/translations`, {
name: { [lang]: name }
});
return { content: [{ type: "text", text: JSON.stringify(res.data, null, 2) }] };
@@ -636,22 +676,12 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
});
const uploadedUrl = uploadRes.data.url;
// 3. Get current product to append image
const prodRes = await api.get(`/products/${params.productId}`);
const product = prodRes.data;
const newImages = [...product.images, { url: uploadedUrl, order: product.images.length }];
// 4. Update product (using PUT to sync variants and images)
// Ensure we pass required fields
const putRes = await api.put(`/products/${params.productId}`, {
categoryId: product.categoryId,
description: product.description,
details: product.details,
variants: product.variants,
images: newImages
// 3. Post the new image URL directly to the product
const postRes = await api.post(`/products/${params.productId}/images`, {
url: uploadedUrl
});
return { content: [{ type: "text", text: `Image uploaded and product updated. New image URL: ${uploadedUrl}` }] };
return { content: [{ type: "text", text: JSON.stringify({ success: true, imageUrl: uploadedUrl }, null, 2) }] };
}
case "generate_mock_review": {
const res = await api.post("/reviews", {
@@ -708,7 +738,8 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
return { content: [{ type: "text", text: JSON.stringify(metadata, null, 2) }] };
}
case "get_dynamic_page_detail": {
const res = await api.get(`/pages/${params.slug}`);
const url = `/pages/${params.slug}` + (params.includeInactive ? "?includeInactive=true" : "");
const res = await api.get(url);
return { content: [{ type: "text", text: JSON.stringify(res.data, null, 2) }] };
}
case "create_dynamic_page": {
+2 -2
View File
@@ -203,7 +203,7 @@ function buildSectionUpdateResult(toolName: string, sectionType: string, updated
export const SECTION_TOOL_DEFINITIONS = [
{
name: "add_page_section",
description: "Add a block section to a dynamic page layout",
description: "Add a block section to a dynamic page layout. For the payload field, ensure it matches the section type. Examples: For TEXT, use { \"text\": \"...\" }. For RICH_TEXT, use { \"title\": \"...\", \"content\": \"...\" }.",
inputSchema: {
type: "object",
properties: {
@@ -218,7 +218,7 @@ export const SECTION_TOOL_DEFINITIONS = [
},
{
name: "update_page_section",
description: "Update an existing block section payload, order or active state",
description: "Update an existing block section payload, order or active state. If you are modifying the payload, it is strongly recommended to also provide the 'type' field to ensure correct validation.",
inputSchema: {
type: "object",
properties: {