This commit is contained in:
Your Name committed 2026-06-29 17:57:54 -04:00
1 parent 70877882b0
commit ce2f4cf0c8
93 files changed
+4200 -67

No files matched your search

+28 -1
View File
@@ -6,7 +6,11 @@ const TreeNodeEditor = ({ node, type, depth, onChange, onRemove }: any) => {
const childrenKey = type === 'root' ? 'columns' : 'links';
const labelKey = type === 'column' ? 'title' : 'label';
const hasUrl = type !== 'column';
// Image field name by node type
const imageFieldKey = type === 'root' ? 'rootImageUrl' : type === 'column' ? 'iconUrl' : 'imageUrl';
const imageFieldLabel = type === 'root' ? 'Root Image URL (left panel, 1:1)' : type === 'column' ? 'Column Icon URL (1:1)' : 'Hover Image URL (right panel, 1:1)';
const children = node[childrenKey] || [];
const handleAddChild = () => {
@@ -29,6 +33,7 @@ const TreeNodeEditor = ({ node, type, depth, onChange, onRemove }: any) => {
const languages = ['th', 'en', 'ja', 'zh', 'vi', 'ko'];
const nodeLabel = node[labelKey] || {};
const imageValue: string = node[imageFieldKey] || '';
return (
<div className={`border border-white/10 p-4 rounded-xl mt-4 ${depth > 0 ? 'ml-6 border-l-cyan-500/30' : ''}`}>
@@ -50,7 +55,29 @@ const TreeNodeEditor = ({ node, type, depth, onChange, onRemove }: any) => {
))}
</div>
</div>
{/* Image URL field */}
<div className="flex items-start gap-3">
{/* Preview thumbnail */}
<div className="w-14 h-14 shrink-0 rounded-lg overflow-hidden border border-white/10 bg-white/5 flex items-center justify-center">
{imageValue ? (
<img src={imageValue} alt="preview" className="w-full h-full object-cover" />
) : (
<span className="text-[9px] text-slate-600 text-center leading-tight px-1">No Image</span>
)}
</div>
<div className="flex-1 space-y-1">
<label className="text-[10px] uppercase font-bold text-slate-500">{imageFieldLabel}</label>
<input
className="admin-input w-full text-xs py-1 px-2"
value={imageValue}
placeholder="https://cdn.example.com/image.webp"
onChange={e => onChange({ ...node, [imageFieldKey]: e.target.value || null })}
/>
</div>
</div>
</div>
{hasUrl && (
<div className="w-48 space-y-1">
<label className="text-[10px] uppercase font-bold text-slate-500">url</label>
+2
View File
@@ -7,6 +7,8 @@ export default defineConfig({
plugins: [react(), tailwindcss()],
server: {
host: true,
port: 5174, // กำหนด port ชัดเจน
allowedHosts: true,
},
})
+1
View File
@@ -31,6 +31,7 @@
"@types/bcryptjs": "^3.0.0",
"@types/bun": "^1.3.11",
"@types/pg": "^8.20.0",
"axios": "^1.18.1",
"prisma": "^7.6.0",
"typescript": "^6.0.2"
},
+20 -17
View File
@@ -1,6 +1,6 @@
import { Elysia, t } from "elysia";
import prisma from "../lib/prisma";
import { adminOnly } from "../middleware/auth";
import prisma from "../lib/prisma.js";
import { adminOnly } from "../middleware/auth.js";
export const menusRoute = new Elysia({ prefix: "/menus" })
.get("/", async () => {
@@ -29,27 +29,30 @@ export const menusRoute = new Elysia({ prefix: "/menus" })
return menu;
})
.use(adminOnly)
.post("/", async ({ body, set }) => {
.post("/", async ({ body, set }: { body: any; set: any }) => {
try {
const { name, rootImage, items } = body as {
name: string;
rootImage?: string | null;
items?: { label: string; url: string; order: number; columnImage?: string | null; hoverImage?: string | null }[]
};
const data: any = {
name,
rootImage,
};
if (items) {
data.items = {
create: items.map(item => ({
label: item.label,
url: item.url,
order: item.order,
columnImage: item.columnImage,
hoverImage: item.hoverImage
}))
};
}
const menu = await prisma.menu.create({
data: {
name,
rootImage,
items: items ? {
create: items.map(item => ({
label: item.label,
url: item.url,
order: item.order,
columnImage: item.columnImage,
hoverImage: item.hoverImage
}))
} : undefined
},
data,
include: { items: true }
});
return menu;
@@ -71,7 +74,7 @@ export const menusRoute = new Elysia({ prefix: "/menus" })
})))
})
})
.put("/:name", async ({ params: { name }, body, set }) => {
.put("/:name", async ({ params: { name }, body, set }: { params: { name: string }; body: any; set: any }) => {
try {
const { items, payload, isActive, rootImage } = body as {
payload?: any;
+386
View File
@@ -0,0 +1,386 @@
import prisma from "./src/lib/prisma";
import axios from "axios";
import { handleMenuTool } from "../mcp-server/menu-toolkit";
const API_BASE = "http://localhost:3051/api";
const TEST_MENU_NAME_API = "challenger-test-menu-api";
const TEST_MENU_NAME_MCP = "challenger-test-menu-mcp";
async function loginAndGetToken() {
const loginRes = await fetch(`${API_BASE}/auth/login`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ username: "admin", password: "homaroma123" })
});
if (!loginRes.ok) {
throw new Error(`Failed to log in: ${loginRes.statusText}`);
}
const data = await loginRes.json() as { success: boolean; token: string };
if (!data.success || !data.token) {
throw new Error(`Invalid login response: ${JSON.stringify(data)}`);
}
return data.token;
}
async function cleanDatabase() {
console.log("Cleaning up previous test data in the database...");
await prisma.menu.deleteMany({
where: {
name: {
in: [TEST_MENU_NAME_API, TEST_MENU_NAME_MCP]
}
}
});
console.log("Cleanup finished.");
}
async function run() {
const token = await loginAndGetToken();
console.log("Obtained Admin Authorization Token successfully.");
await cleanDatabase();
console.log("\n=============================================");
console.log("PART 1: Direct Elysia API Routes Verification");
console.log("=============================================");
// 1. Post a new menu via API
console.log("1.1 POST /api/menus - Creating a menu with rootImage and items with columnImage & hoverImage...");
const createPayload = {
name: TEST_MENU_NAME_API,
rootImage: "https://example.com/api-root-original.png",
items: [
{
label: "Link 1",
url: "/link1",
order: 1,
columnImage: "https://example.com/api-col1-original.png",
hoverImage: "https://example.com/api-hover1-original.png"
},
{
label: "Link 2",
url: "/link2",
order: 2,
columnImage: null,
hoverImage: null
}
]
};
const postRes = await fetch(`${API_BASE}/menus`, {
method: "POST",
headers: {
"Content-Type": "application/json",
"Authorization": `Bearer ${token}`
},
body: JSON.stringify(createPayload)
});
if (!postRes.ok) {
throw new Error(`POST /menus failed: ${postRes.status} ${await postRes.text()}`);
}
const createdMenu = await postRes.json() as any;
console.log("Successfully created menu via API:", JSON.stringify(createdMenu, null, 2));
// 2. Query the backend API and verify stored fields
console.log("\n1.2 GET /api/menus/:name - Querying the backend API to verify fields...");
const getRes = await fetch(`${API_BASE}/menus/${TEST_MENU_NAME_API}`);
if (!getRes.ok) {
throw new Error(`GET /menus/${TEST_MENU_NAME_API} failed: ${getRes.status}`);
}
const retrievedMenu = await getRes.json() as any;
console.log("Retrieved menu from API:", JSON.stringify(retrievedMenu, null, 2));
if (retrievedMenu.rootImage !== createPayload.rootImage) {
throw new Error(`API Verification Error: Menu rootImage mismatch. Expected '${createPayload.rootImage}', got '${retrievedMenu.rootImage}'`);
}
const items = retrievedMenu.items || [];
if (items.length !== 2) {
throw new Error(`API Verification Error: Expected 2 items, got ${items.length}`);
}
const item1 = items.find((i: any) => i.label === "Link 1");
const item2 = items.find((i: any) => i.label === "Link 2");
if (!item1 || item1.columnImage !== "https://example.com/api-col1-original.png" || item1.hoverImage !== "https://example.com/api-hover1-original.png") {
throw new Error(`API Verification Error: Item 1 image fields mismatch. Got: col='${item1?.columnImage}', hover='${item1?.hoverImage}'`);
}
if (!item2 || item2.columnImage !== null || item2.hoverImage !== null) {
throw new Error(`API Verification Error: Item 2 image fields mismatch. Got: col='${item2?.columnImage}', hover='${item2?.hoverImage}'`);
}
console.log("✅ GET API fields are stored and retrieved correctly!");
// 3. Update the menu via PUT /api/menus/:name
console.log("\n1.3 PUT /api/menus/:name - Updating the menu and items...");
const updatePayload = {
rootImage: "https://example.com/api-root-updated.png",
items: [
{
id: item1.id,
label: "Link 1 Updated",
url: "/link1-updated",
order: 10,
columnImage: "https://example.com/api-col1-updated.png",
hoverImage: "https://example.com/api-hover1-updated.png"
},
{
label: "Link 3 Added",
url: "/link3",
order: 3,
columnImage: "https://example.com/api-col3-new.png",
hoverImage: null
}
]
};
const putRes = await fetch(`${API_BASE}/menus/${TEST_MENU_NAME_API}`, {
method: "PUT",
headers: {
"Content-Type": "application/json",
"Authorization": `Bearer ${token}`
},
body: JSON.stringify(updatePayload)
});
if (!putRes.ok) {
throw new Error(`PUT /menus failed: ${putRes.status} ${await putRes.text()}`);
}
const updatedMenu = await putRes.json() as any;
console.log("Successfully updated menu via PUT:", JSON.stringify(updatedMenu, null, 2));
if (updatedMenu.rootImage !== updatePayload.rootImage) {
throw new Error(`API Verification Error: Updated Menu rootImage mismatch. Expected '${updatePayload.rootImage}', got '${updatedMenu.rootImage}'`);
}
const updatedItems = updatedMenu.items || [];
const updatedItem1 = updatedItems.find((i: any) => i.id === item1.id);
const addedItem3 = updatedItems.find((i: any) => i.label === "Link 3 Added");
if (!updatedItem1 || updatedItem1.columnImage !== "https://example.com/api-col1-updated.png" || updatedItem1.hoverImage !== "https://example.com/api-hover1-updated.png") {
throw new Error(`API Verification Error: Updated Item 1 image fields mismatch.`);
}
if (!addedItem3 || addedItem3.columnImage !== "https://example.com/api-col3-new.png" || addedItem3.hoverImage !== null) {
throw new Error(`API Verification Error: Added Item 3 image fields mismatch.`);
}
console.log("✅ PUT API fields update verified successfully!");
// 4. Verify validation errors for incorrect types in Elysia routes
console.log("\n1.4 Verify Elysia Route Validation Errors for Incorrect Types...");
const invalidPayloads = [
{
description: "Invalid rootImage type (number instead of string/null)",
payload: {
name: "challenger-invalid-1",
rootImage: 12345,
items: []
}
},
{
description: "Invalid columnImage type (boolean instead of string/null)",
payload: {
name: "challenger-invalid-2",
rootImage: null,
items: [
{
label: "Link",
url: "/url",
order: 1,
columnImage: true
}
]
}
},
{
description: "Invalid order type (string instead of number)",
payload: {
name: "challenger-invalid-3",
rootImage: null,
items: [
{
label: "Link",
url: "/url",
order: "first",
columnImage: null
}
]
}
}
];
for (const { description, payload } of invalidPayloads) {
const invalidRes = await fetch(`${API_BASE}/menus`, {
method: "POST",
headers: {
"Content-Type": "application/json",
"Authorization": `Bearer ${token}`
},
body: JSON.stringify(payload)
});
console.log(`- Testing: ${description}`);
console.log(` Response Status: ${invalidRes.status} (Expected: 422)`);
if (invalidRes.status !== 422) {
throw new Error(`API Validation Verification Error: Expected 422 status, got ${invalidRes.status}`);
}
const errorBody = await invalidRes.json();
console.log(` Validation Message: ${JSON.stringify((errorBody as any).summary || (errorBody as any).message || errorBody)}`);
}
console.log("✅ Elysia Route Validation Errors verification passed!");
console.log("\n=============================================");
console.log("PART 2: Direct Database Schema Verification");
console.log("=============================================");
console.log("Querying database directly using Prisma client...");
const dbMenu = await prisma.menu.findUnique({
where: { name: TEST_MENU_NAME_API },
include: { items: true }
});
if (!dbMenu) {
throw new Error("DB Verification Error: Menu not found in database.");
}
console.log("Direct DB Menu Record:", JSON.stringify(dbMenu, null, 2));
if (dbMenu.rootImage !== "https://example.com/api-root-updated.png") {
throw new Error(`DB Verification Error: Menu rootImage column mismatch. Expected 'https://example.com/api-root-updated.png', got '${dbMenu.rootImage}'`);
}
const dbItem1 = dbMenu.items.find(i => i.url === "/link1-updated");
if (!dbItem1 || dbItem1.columnImage !== "https://example.com/api-col1-updated.png" || dbItem1.hoverImage !== "https://example.com/api-hover1-updated.png") {
throw new Error("DB Verification Error: Menu item 1 columns mismatch in PostgreSQL.");
}
const dbItem3 = dbMenu.items.find(i => i.url === "/link3");
if (!dbItem3 || dbItem3.columnImage !== "https://example.com/api-col3-new.png" || dbItem3.hoverImage !== null) {
throw new Error("DB Verification Error: Menu item 3 columns mismatch in PostgreSQL.");
}
console.log("✅ PostgreSQL schema verification passed!");
console.log("\n=============================================");
console.log("PART 3: MCP Tools Layer Verification");
console.log("=============================================");
// Create an Axios instance mimicking the MCP server environment
const apiAxios = axios.create({
baseURL: API_BASE,
headers: { Authorization: `Bearer ${token}` }
});
// 1. Test upsert_menu MCP Tool
console.log("3.1 Testing upsert_menu MCP Tool...");
const mcpPayload = {
name: TEST_MENU_NAME_MCP,
rootImage: "https://example.com/mcp-root.png",
items: [
{
label: "MCP Link 1",
url: "/mcp-link1",
order: 1,
columnImage: "https://example.com/mcp-col1.png",
hoverImage: "https://example.com/mcp-hover1.png"
}
]
};
const upsertToolRes = await handleMenuTool(apiAxios, "upsert_menu", mcpPayload);
console.log("upsert_menu MCP response:", JSON.stringify(upsertToolRes, null, 2));
if (upsertToolRes?.isError) {
throw new Error(`MCP Tool Error: upsert_menu failed: ${JSON.stringify(upsertToolRes)}`);
}
// Verify stored fields via direct API / DB
const mcpDbMenu = await prisma.menu.findUnique({
where: { name: TEST_MENU_NAME_MCP },
include: { items: true }
});
if (!mcpDbMenu) {
throw new Error("MCP DB Verification Error: Menu created by MCP not found.");
}
if (mcpDbMenu.rootImage !== mcpPayload.rootImage) {
throw new Error(`MCP DB Verification Error: rootImage mismatch. Got '${mcpDbMenu.rootImage}'`);
}
const mcpItem = mcpDbMenu.items[0];
if (!mcpItem || mcpItem.columnImage !== "https://example.com/mcp-col1.png" || mcpItem.hoverImage !== "https://example.com/mcp-hover1.png") {
throw new Error(`MCP DB Verification Error: Item columns mismatch. Got col='${mcpItem?.columnImage}', hover='${mcpItem?.hoverImage}'`);
}
console.log("✅ MCP upsert_menu successfully created & stored correct fields!");
// 2. Test get_menu MCP Tool
console.log("\n3.2 Testing get_menu MCP Tool...");
const getToolRes = await handleMenuTool(apiAxios, "get_menu", { name: TEST_MENU_NAME_MCP });
console.log("get_menu MCP response:", JSON.stringify(getToolRes, null, 2));
if (getToolRes?.isError) {
throw new Error(`MCP Tool Error: get_menu failed: ${JSON.stringify(getToolRes)}`);
}
const mcpContentText = getToolRes?.content?.[0]?.text;
if (!mcpContentText) {
throw new Error("MCP Tool Error: No content returned by get_menu.");
}
const mcpMenuData = JSON.parse(mcpContentText);
if (mcpMenuData.rootImage !== mcpPayload.rootImage) {
throw new Error(`MCP Tool Verification Error: rootImage mismatch in get_menu response.`);
}
if (mcpMenuData.items?.[0]?.columnImage !== "https://example.com/mcp-col1.png") {
throw new Error(`MCP Tool Verification Error: columnImage mismatch in get_menu response.`);
}
console.log("✅ MCP get_menu successfully retrieved correct fields!");
// 3. Verify validation errors in MCP Tools Layer
console.log("\n3.3 Verify MCP Tool Validation Errors for Incorrect Types...");
const invalidMcpPayloads = [
{
description: "Invalid items parameter type (not an array)",
payload: {
name: TEST_MENU_NAME_MCP,
items: "not-an-array"
}
},
{
description: "Invalid columnImage parameter type (number instead of string/null)",
payload: {
name: TEST_MENU_NAME_MCP,
items: [
{
label: "Invalid Item",
url: "/invalid",
order: 1,
columnImage: 999
}
]
}
}
];
for (const { description, payload } of invalidMcpPayloads) {
console.log(`- Testing MCP Tool: ${description}`);
try {
await handleMenuTool(apiAxios, "upsert_menu", payload);
throw new Error(`Expected MCP Tool to throw validation error, but it succeeded.`);
} catch (e: any) {
console.log(` Caught expected validation error: ${e.message}`);
}
}
console.log("✅ MCP Tool validation error checking passed!");
console.log("\n=============================================");
console.log("ALL TESTS COMPLETED SUCCESSFULLY!");
console.log("=============================================");
}
run()
.catch((err) => {
console.error("\n❌ VERIFICATION TEST FAILED!");
console.error(err);
process.exit(1);
})
.finally(() => {
prisma.$disconnect();
});
+154
View File
@@ -0,0 +1,154 @@
import { Elysia } from "elysia";
import prisma from "./src/lib/prisma.js";
import { menusRoute } from "./src/routes/menus.js";
import { authRoute } from "./src/routes/auth.js";
const TEST_MENU_NAME = "test-image-menu";
async function main() {
console.log("🚀 Starting Menu Image Integration Test...");
// 1. Direct Prisma cleanup
console.log("🧹 Cleaning up old test menu...");
await prisma.menu.deleteMany({
where: { name: TEST_MENU_NAME }
});
// 2. Initialize Elysia mock server
console.log("⚙️ Setting up Elysia test app...");
const app = new Elysia().use(authRoute).use(menusRoute);
// 3. Login to get token
console.log("🔑 Logging in as admin...");
const loginRes = await app.handle(
new Request("http://localhost/auth/login", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ username: "admin", password: "homaroma123" })
})
);
if (loginRes.status !== 200) {
const errText = await loginRes.text();
throw new Error(`Login failed with status ${loginRes.status}: ${errText}`);
}
const { success, token } = (await loginRes.json()) as { success: boolean; token: string };
if (!success || !token) {
throw new Error("Failed to retrieve authentication token.");
}
console.log("✅ Authenticated successfully.");
// 4. POST: Create menu with rootImage, columnImage, and hoverImage
console.log("➕ Creating new menu with image fields via POST...");
const postRes = await app.handle(
new Request("http://localhost/menus", {
method: "POST",
headers: {
"Content-Type": "application/json",
"Authorization": `Bearer ${token}`
},
body: JSON.stringify({
name: TEST_MENU_NAME,
rootImage: "https://example.com/root.jpg",
items: [
{
label: "Product 1",
url: "/product1",
order: 1,
columnImage: "https://example.com/col1.jpg",
hoverImage: "https://example.com/hov1.jpg"
}
]
})
})
);
if (postRes.status !== 200 && postRes.status !== 201) {
const errText = await postRes.text();
throw new Error(`POST /menus failed with status ${postRes.status}: ${errText}`);
}
const createdMenu = (await postRes.json()) as any;
console.log("Created menu payload:", JSON.stringify(createdMenu, null, 2));
// Assertions
if (createdMenu.name !== TEST_MENU_NAME) throw new Error("Incorrect menu name");
if (createdMenu.rootImage !== "https://example.com/root.jpg") throw new Error("Incorrect rootImage");
if (!createdMenu.items || createdMenu.items.length !== 1) throw new Error("Expected 1 menu item");
if (createdMenu.items[0].columnImage !== "https://example.com/col1.jpg") throw new Error("Incorrect columnImage");
if (createdMenu.items[0].hoverImage !== "https://example.com/hov1.jpg") throw new Error("Incorrect hoverImage");
console.log("✅ POST verification successful!");
// 5. PUT: Update menu with new images
console.log("🔄 Updating menu with new images via PUT...");
const putRes = await app.handle(
new Request(`http://localhost/menus/${TEST_MENU_NAME}`, {
method: "PUT",
headers: {
"Content-Type": "application/json",
"Authorization": `Bearer ${token}`
},
body: JSON.stringify({
rootImage: "https://example.com/root-updated.jpg",
items: [
{
label: "Product 1 Updated",
url: "/product1-updated",
order: 1,
columnImage: "https://example.com/col1-updated.jpg",
hoverImage: "https://example.com/hov1-updated.jpg"
}
]
})
})
);
if (putRes.status !== 200) {
const errText = await putRes.text();
throw new Error(`PUT /menus/:name failed with status ${putRes.status}: ${errText}`);
}
const updatedMenu = (await putRes.json()) as any;
console.log("Updated menu payload:", JSON.stringify(updatedMenu, null, 2));
// Assertions
if (updatedMenu.rootImage !== "https://example.com/root-updated.jpg") throw new Error("Incorrect rootImage update");
if (!updatedMenu.items || updatedMenu.items.length !== 1) throw new Error("Expected 1 menu item after update");
if (updatedMenu.items[0].columnImage !== "https://example.com/col1-updated.jpg") throw new Error("Incorrect columnImage update");
if (updatedMenu.items[0].hoverImage !== "https://example.com/hov1-updated.jpg") throw new Error("Incorrect hoverImage update");
console.log("✅ PUT verification successful!");
// 6. GET: Fetch the menu publicly
console.log("🔍 Fetching menu via public GET route...");
const getRes = await app.handle(
new Request(`http://localhost/menus/${TEST_MENU_NAME}`)
);
if (getRes.status !== 200) {
const errText = await getRes.text();
throw new Error(`GET /menus/:name failed with status ${getRes.status}: ${errText}`);
}
const fetchedMenu = (await getRes.json()) as any;
if (fetchedMenu.rootImage !== "https://example.com/root-updated.jpg") throw new Error("Incorrect fetched rootImage");
if (fetchedMenu.items[0].columnImage !== "https://example.com/col1-updated.jpg") throw new Error("Incorrect fetched columnImage");
if (fetchedMenu.items[0].hoverImage !== "https://example.com/hov1-updated.jpg") throw new Error("Incorrect fetched hoverImage");
console.log("✅ GET verification successful!");
// 7. Cleanup
console.log("🧹 Performing final database cleanup...");
await prisma.menu.deleteMany({
where: { name: TEST_MENU_NAME }
});
console.log("✨ All Menu Image Integration Tests Passed Successfully!");
}
main()
.catch((err) => {
console.error("❌ Test failed:", err);
process.exit(1);
})
.finally(async () => {
await prisma.$disconnect();
});
@@ -0,0 +1,91 @@
import { spawn } from "child_process";
async function test() {
console.log("Starting MCP server...");
const mcp = spawn("bun", ["index.ts"], {
cwd: "/home/ti/git/homaroma/apps/mcp-server",
env: { ...process.env, MCP_BACKEND_URL: "http://localhost:3051/api" }
});
mcp.stderr.on("data", (data) => {
console.error(`[MCP STDERR] ${data}`);
});
let responseData = "";
mcp.stdout.on("data", (data) => {
responseData += data.toString();
});
const send = (msg: any) => {
mcp.stdin.write(JSON.stringify(msg) + "\n");
};
const waitResponse = async (ms: number) => {
await new Promise(resolve => setTimeout(resolve, ms));
const lines = responseData.trim().split("\n");
const lastLine = lines[lines.length - 1];
try {
return JSON.parse(lastLine);
} catch (e) {
console.log("Failed to parse JSON line:", lastLine);
return null;
}
};
// 1. Send initialize
console.log("Sending initialize request...");
send({
jsonrpc: "2.0",
id: 1,
method: "initialize",
params: {
protocolVersion: "2024-11-05",
capabilities: {},
clientInfo: { name: "test-client", version: "1.0.0" }
}
});
await waitResponse(1000);
// 2. Send initialized notification
console.log("Sending initialized notification...");
send({
jsonrpc: "2.0",
method: "notifications/initialized"
});
await new Promise(resolve => setTimeout(resolve, 500));
// 3. Try calling upsert_menu with invalid rootImage (number)
console.log("\nSending tools/call for upsert_menu with invalid rootImage (number)...");
responseData = "";
send({
jsonrpc: "2.0",
id: 3,
method: "tools/call",
params: {
name: "upsert_menu",
arguments: {
name: "mcp-test-invalid-root",
rootImage: 99999,
items: [
{
label: "MCP Item",
url: "/item",
order: 1
}
]
}
}
});
await waitResponse(2000);
console.log("upsert_menu invalid rootImage response:", responseData);
mcp.kill();
}
test().catch(err => {
console.error("MCP Challenger Test failed:", err);
process.exit(1);
});
+9 -1
View File
@@ -2,7 +2,15 @@ import type { NextConfig } from "next";
const nextConfig: NextConfig = {
output: "standalone",
/* config options here */
images: {
remotePatterns: [
{
protocol: "https",
hostname: "homaroma.shop",
pathname: "/**",
},
],
},
};
export default nextConfig;