part.compare
Compare UI components side by side to analyze styles, layout, interaction, and accessibility aspects for design evaluation and improvement.
Instructions
2-5個のUIコンポーネントパーツをスタイル・レイアウト・インタラクション・アクセシビリティの観点で並列比較します。 / Compare 2-5 UI component parts side by side on styles, layout, interaction, and accessibility aspects.
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| part_ids | Yes | 比較対象パーツID(2-5個) / 2-5 part IDs to compare | |
| compare_aspects | No | 比較観点(デフォルト: styles, layout) / Aspects to compare (default: styles, layout) |
Implementation Reference
- Main handler for the part.compare tool, implementing the comparison logic for UI component parts.
export async function partCompareHandler(input: unknown): Promise<PartCompareOutput> { if (isDevelopment()) { logger.info("[MCP Tool] part.compare called", { partCount: Array.isArray((input as Record<string, unknown>)?.part_ids) ? ((input as Record<string, unknown>).part_ids as unknown[]).length : 0, }); } // 入力バリデーション / Input validation let validated: PartCompareInput; try { validated = partCompareInputSchema.parse(input); } catch (error) { if (error instanceof ZodError) { const errorMessage = error.errors.map((e) => `${e.path.join(".")}: ${e.message}`).join(", "); logger.warn("[MCP Tool] part.compare validation error", { errors: error.errors, }); return { success: false, error: { code: PART_COMPARE_ERROR_CODES.VALIDATION_ERROR, message: `Validation error: ${errorMessage}`, }, }; } throw error; } try { // DB からパーツを取得(リレーション含む) // Fetch parts from DB (with relations) const dbParts = await prisma.componentPart.findMany({ where: { id: { in: validated.part_ids } }, include: { webPage: { select: { url: true } }, sectionPattern: { select: { sectionType: true } }, }, }); // 見つからなかったパーツを検出 / Detect missing parts const foundIds = new Set(dbParts.map((p) => p.id)); const missingIds = validated.part_ids.filter((id) => !foundIds.has(id)); if (missingIds.length > 0) { logger.warn("[MCP Tool] part.compare parts not found", { missingCount: missingIds.length, missingIds: missingIds.map((id) => truncateId(id)), }); return { success: false, error: { code: PART_COMPARE_ERROR_CODES.PARTS_NOT_FOUND, message: `${missingIds.length} part(s) not found`, }, }; } // パーツ情報を構築 / Build part info const partsInfo: ComparedPartInfo[] = dbParts.map((p) => ({ id: p.id, partType: p.partType, webPageUrl: p.webPage.url, sectionType: p.sectionPattern.sectionType, })); // 比較ロジック用の中間表現 // Intermediate representation for comparison logic const partsForComparison = dbParts.map((p) => ({ id: p.id, computedStyles: (p.computedStyles ?? {}) as Record<string, unknown>, boundingBox: (p.boundingBox ?? {}) as Record<string, unknown>, interactionInfo: (p.interactionInfo ?? {}) as Record<string, unknown>, attributes: (p.attributes ?? {}) as Record<string, unknown>, })); // 比較観点ごとに結果を構築 / Build comparisons per aspect const comparisons: Record<string, ComparisonDetail> = {}; for (const aspect of validated.compare_aspects) { switch (aspect) { case "styles": comparisons.styles = buildStylesComparison(partsForComparison); break; case "layout": comparisons.layout = buildLayoutComparison(partsForComparison); break; case "interaction": comparisons.interaction = buildInteractionComparison(partsForComparison); break; case "accessibility": comparisons.accessibility = buildAccessibilityComparison(partsForComparison); break; } } if (isDevelopment()) { logger.info("[MCP Tool] part.compare completed", { partCount: partsInfo.length, aspects: validated.compare_aspects, }); } return { success: true, data: { parts: partsInfo, comparisons, }, }; } catch (error) { const errorInstance = error instanceof Error ? error : new Error(String(error)); // 全環境でログ出力(isDevelopmentガードなし) // Log in all environments (no isDevelopment guard) logger.warn("[MCP Tool] part.compare error", { code: PART_COMPARE_ERROR_CODES.INTERNAL_ERROR, error: errorInstance.message, }); return { success: false, error: { code: PART_COMPARE_ERROR_CODES.INTERNAL_ERROR, message: sanitizeErrorMessage(error), }, }; } } - MCP tool definition for part.compare, including input schema and metadata.
export const partCompareToolDefinition = { name: "part.compare" as const, description: "2-5個のUIコンポーネントパーツをスタイル・レイアウト・インタラクション・" + "アクセシビリティの観点で並列比較します。" + " / Compare 2-5 UI component parts side by side on styles, layout, " + "interaction, and accessibility aspects.", annotations: { title: "Part Compare", readOnlyHint: true, idempotentHint: true, openWorldHint: false, }, inputSchema: { type: "object" as const, properties: { part_ids: { type: "array", items: { type: "string", format: "uuid" }, minItems: 2, maxItems: 5, description: "比較対象パーツID(2-5個) / 2-5 part IDs to compare", }, compare_aspects: { type: "array", items: { type: "string", enum: ["styles", "layout", "interaction", "accessibility"], }, default: ["styles", "layout"], description: "比較観点(デフォルト: styles, layout) / Aspects to compare (default: styles, layout)", }, }, required: ["part_ids"], }, };