import { expect, test } from "bun:test"; import { join } from "node:path"; import { Client } from "@modelcontextprotocol/sdk/client/index.js"; import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js"; async function connectMcp(baseUrl: string) { const env = { ...process.env, MCP_BACKEND_URL: baseUrl } as Record; delete env.ADMIN_USERNAME; delete env.ADMIN_PASSWORD; delete env.MCP_ADMIN_USERNAME; delete env.MCP_ADMIN_PASSWORD; env.MCP_ADMIN_USERNAME = "contract-test"; env.MCP_ADMIN_PASSWORD = "contract-test"; const transport = new StdioClientTransport({ command: process.execPath, args: [join(import.meta.dir, "index.ts")], cwd: import.meta.dir, env, stderr: "pipe", }); const client = new Client({ name: "mcp-contract-test", version: "1.0.0" }); await client.connect(transport); return client; } test("settings writes send only supported fields and simple product fields are advertised", async () => { const writes: unknown[] = []; let getSettingsCount = 0; const backend = Bun.serve({ port: 0, fetch: async (request) => { const path = new URL(request.url).pathname; if (path === "/api/auth/login") return Response.json({ token: "contract-token" }); if (path === "/api/settings/footer-layout" && request.method === "GET") { return Response.json({ version: "2026-10-11T00:00:00.000Z", layout: { columns: ["brand", "explore"] } }); } if (path === "/api/settings" && request.method === "GET") { getSettingsCount++; return Response.json({ brandName: "Old", footerLayout: { columns: [] }, themeTokens: { brandAccent: "#123" } }); } if (path === "/api/settings" && request.method === "PUT") { const body = await request.json(); writes.push(body); return Response.json({ ...(body as object), themeTokens: { brandAccent: "#123", ...((body as any).themeTokens ?? {}) } }); } return new Response("Not found", { status: 404 }); }, }); let client: Client | undefined; try { client = await connectMcp(`http://localhost:${backend.port}/api`); const tools = (await client.listTools()).tools; expect(tools).toHaveLength(116); expect(tools.find((item) => item.name === "get_footer_layout")?.annotations?.readOnlyHint).toBe(true); expect(tools.find((item) => item.name === "delete_category_section")?.annotations?.destructiveHint).toBe(true); const footerLayout = await client.callTool({ name: "get_footer_layout", arguments: {} }); expect(footerLayout.isError).toBeFalsy(); expect(footerLayout.structuredContent).toEqual({ version: "2026-10-11T00:00:00.000Z", layout: { columns: ["brand", "explore"] } }); for (const name of ["create_product", "bulk_create_products"]) { const tool = tools.find((item) => item.name === name); expect(tool).toBeDefined(); const properties = name === "create_product" ? tool!.inputSchema.properties : (tool!.inputSchema.properties?.products as any).items.properties; for (const field of ["price", "stock", "sku", "barcode"]) { expect(properties?.[field]).toBeDefined(); } } expect((await client.callTool({ name: "update_theme_tokens", arguments: { brandPrimary: "#456" } })).isError).toBeFalsy(); expect((await client.callTool({ name: "update_brand_name", arguments: { brandName: "New" } })).isError).toBeFalsy(); expect((await client.callTool({ name: "update_footer_content", arguments: { brandDescription: "New footer", socialLinks: { facebookUrl: "https://example.com" } } })).isError).toBeFalsy(); expect(getSettingsCount).toBe(0); expect(writes).toEqual([ { themeTokens: { brandPrimary: "#456" } }, { brandName: "New" }, { footerDescription: "New footer", facebookUrl: "https://example.com" }, ]); } finally { await client?.close(); backend.stop(); } });