sort_colors
Sort colors by hue, saturation, lightness, brightness, temperature, or frequency. Optionally group similar colors together for organized color management.
Instructions
Sort colors by various properties including hue, saturation, lightness, brightness, temperature, or frequency. Optionally group similar colors together.
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| colors | Yes | Array of colors to sort (2-100 colors) | |
| sort_by | Yes | Property to sort by | |
| direction | No | Sort direction | ascending |
| group_similar | No | Group similar colors together |
Implementation Reference
- src/tools/sort-colors.ts:171-507 (handler)Core handler function that implements the entire 'sort_colors' tool: input validation, color parsing using UnifiedColor, metric calculations (hue, saturation, lightness, brightness, temperature, frequency), sorting by specified criteria and direction, optional grouping of similar colors using HSL distance threshold, generates detailed output with analysis, accessibility notes, recommendations, and export formats (CSS, SCSS, JSON).
async function sortColorsHandler( params: unknown ): Promise<ToolResponse | ErrorResponse> { const startTime = Date.now(); try { // Validate parameters const { error, value } = sortColorsSchema.validate(params); if (error) { return createErrorResponse( 'sort_colors', 'INVALID_PARAMETERS', `Invalid parameters: ${error.details.map(d => d.message).join(', ')}`, Date.now() - startTime, { details: error.details, } ); } const { colors: colorStrings, sort_by, direction, group_similar, } = value as SortColorsParams; // Parse colors and calculate metrics const colorsWithMetrics: ColorWithMetrics[] = []; const parsedColors: UnifiedColor[] = []; for (let i = 0; i < colorStrings.length; i++) { const colorString = colorStrings[i]; if (!colorString) continue; try { const color = new UnifiedColor(colorString); parsedColors.push(color); const hsl = color.hsl; const rgb = color.rgb; // Calculate brightness using perceived brightness formula const brightness = 0.299 * rgb.r + 0.587 * rgb.g + 0.114 * rgb.b; colorsWithMetrics.push({ original: colorString, color, index: i, hue: hsl.h, saturation: hsl.s, lightness: hsl.l, brightness, temperature: calculateTemperatureValue(hsl.h), frequency: 0, // Will be calculated below }); } catch (error) { return createErrorResponse( 'sort_colors', 'INVALID_COLOR', `Invalid color at index ${i}: ${colorString}`, Date.now() - startTime, { details: { colorIndex: i, providedColor: colorString, error: error instanceof Error ? error.message : 'Unknown error', }, suggestions: [ 'Ensure all colors are in valid format (hex, rgb, hsl, etc.)', 'Check color syntax and values', ], } ); } } // Calculate frequency if needed if (sort_by === 'frequency') { const frequencyMap = calculateColorFrequency(parsedColors); colorsWithMetrics.forEach(item => { item.frequency = frequencyMap.get(item.color.hex.toLowerCase()) || 0; }); } // Sort colors const sortedColors = [...colorsWithMetrics].sort((a, b) => { let valueA: number, valueB: number; switch (sort_by) { case 'hue': valueA = a.hue; valueB = b.hue; break; case 'saturation': valueA = a.saturation; valueB = b.saturation; break; case 'lightness': valueA = a.lightness; valueB = b.lightness; break; case 'brightness': valueA = a.brightness; valueB = b.brightness; break; case 'temperature': valueA = a.temperature; valueB = b.temperature; break; case 'frequency': valueA = a.frequency; valueB = b.frequency; break; default: valueA = a.hue; valueB = b.hue; } const comparison = valueA - valueB; return direction === 'ascending' ? comparison : -comparison; }); // Group similar colors if requested const result: SortResult = { sorted_colors: sortedColors.map(item => ({ original: item.original, hex: item.color.hex, rgb: `rgb(${item.color.rgb.r}, ${item.color.rgb.g}, ${item.color.rgb.b})`, hsl: `hsl(${item.color.hsl.h}, ${item.color.hsl.s}%, ${item.color.hsl.l}%)`, hsv: `hsv(${item.color.hsv.h}, ${item.color.hsv.s}%, ${item.color.hsv.v}%)`, metrics: { hue: Math.round(item.hue * 10) / 10, saturation: Math.round(item.saturation * 10) / 10, lightness: Math.round(item.lightness * 10) / 10, brightness: Math.round(item.brightness * 10) / 10, temperature: Math.round(item.temperature * 10) / 10, frequency: item.frequency, }, original_index: item.index, })), sort_criteria: sort_by, direction, total_colors: colorStrings.length, }; if (group_similar) { const groups = groupSimilarColors(sortedColors); result.groups = groups.map((group, groupIndex) => ({ group_id: groupIndex + 1, colors: group.map(item => ({ original: item.original, hex: item.color.hex, rgb: `rgb(${item.color.rgb.r}, ${item.color.rgb.g}, ${item.color.rgb.b})`, hsl: `hsl(${item.color.hsl.h}, ${item.color.hsl.s}%, ${item.color.hsl.l}%)`, hsv: `hsv(${item.color.hsv.h}, ${item.color.hsv.s}%, ${item.color.hsv.v}%)`, metrics: { hue: Math.round(item.hue * 10) / 10, saturation: Math.round(item.saturation * 10) / 10, lightness: Math.round(item.lightness * 10) / 10, brightness: Math.round(item.brightness * 10) / 10, temperature: Math.round(item.temperature * 10) / 10, frequency: item.frequency, }, original_index: item.index, })), group_size: group.length, average_metrics: { hue: Math.round( (group.reduce((sum, item) => sum + item.hue, 0) / group.length) * 10 ) / 10, saturation: Math.round( (group.reduce((sum, item) => sum + item.saturation, 0) / group.length) * 10 ) / 10, lightness: Math.round( (group.reduce((sum, item) => sum + item.lightness, 0) / group.length) * 10 ) / 10, brightness: Math.round( (group.reduce((sum, item) => sum + item.brightness, 0) / group.length) * 10 ) / 10, temperature: Math.round( (group.reduce((sum, item) => sum + item.temperature, 0) / group.length) * 10 ) / 10, }, })); result.total_groups = groups.length; } const executionTime = Date.now() - startTime; // Generate analysis const analysis = { color_distribution: { hue_range: { min: Math.min(...colorsWithMetrics.map(c => c.hue)), max: Math.max(...colorsWithMetrics.map(c => c.hue)), }, saturation_range: { min: Math.min(...colorsWithMetrics.map(c => c.saturation)), max: Math.max(...colorsWithMetrics.map(c => c.saturation)), }, lightness_range: { min: Math.min(...colorsWithMetrics.map(c => c.lightness)), max: Math.max(...colorsWithMetrics.map(c => c.lightness)), }, brightness_range: { min: Math.min(...colorsWithMetrics.map(c => c.brightness)), max: Math.max(...colorsWithMetrics.map(c => c.brightness)), }, }, dominant_characteristics: { most_common_hue_range: getMostCommonHueRange(colorsWithMetrics), average_saturation: Math.round( (colorsWithMetrics.reduce((sum, c) => sum + c.saturation, 0) / colorsWithMetrics.length) * 10 ) / 10, average_lightness: Math.round( (colorsWithMetrics.reduce((sum, c) => sum + c.lightness, 0) / colorsWithMetrics.length) * 10 ) / 10, }, }; // Generate accessibility notes const accessibilityNotes: string[] = []; const darkColors = colorsWithMetrics.filter(c => c.lightness < 30).length; const lightColors = colorsWithMetrics.filter(c => c.lightness > 70).length; const midColors = colorsWithMetrics.length - darkColors - lightColors; accessibilityNotes.push( `Color distribution: ${darkColors} dark, ${midColors} medium, ${lightColors} light colors` ); if (darkColors > lightColors * 2) { accessibilityNotes.push( 'Palette is dark-heavy - ensure sufficient light colors for contrast' ); } else if (lightColors > darkColors * 2) { accessibilityNotes.push( 'Palette is light-heavy - ensure sufficient dark colors for contrast' ); } // Generate recommendations const recommendations: string[] = []; if (sort_by === 'hue') { recommendations.push( 'Hue sorting creates rainbow-like color progressions' ); } else if (sort_by === 'lightness') { recommendations.push( 'Lightness sorting helps identify contrast relationships' ); } else if (sort_by === 'saturation') { recommendations.push('Saturation sorting groups vivid and muted colors'); } if (group_similar) { recommendations.push( 'Similar color grouping helps identify redundant colors in palettes' ); } // Add export formats const exportFormats = { css: result.sorted_colors .map((c, i: number) => `--color-${i + 1}: ${c.hex};`) .join('\n'), scss: result.sorted_colors .map((c, i: number) => `$color-${i + 1}: ${c.hex};`) .join('\n'), json: { sort_criteria: sort_by, direction, colors: result.sorted_colors.map(c => ({ hex: c.hex, metrics: c.metrics, })), }, }; return createSuccessResponse( 'sort_colors', { ...result, analysis, }, executionTime, { colorSpaceUsed: 'hsl', accessibilityNotes: accessibilityNotes, recommendations, exportFormats, } ); } catch (error) { const executionTime = Date.now() - startTime; logger.error('Error in sort_colors tool', { error: error as Error }); return createErrorResponse( 'sort_colors', 'PROCESSING_ERROR', 'An error occurred while sorting colors', executionTime, { details: { error: error instanceof Error ? error.message : 'Unknown error', }, suggestions: [ 'Check that all input colors are valid', 'Verify sort criteria and direction parameters', 'Try with fewer colors if processing large sets', ], } ); } } - src/tools/sort-colors.ts:56-82 (schema)Joi validation schema defining input parameters for the sort_colors tool: colors array (2-100 strings), sort_by enum, direction, group_similar boolean.
const sortColorsSchema = Joi.object({ colors: Joi.array() .items(Joi.string().required()) .min(2) .max(100) .required() .messages({ 'array.min': 'At least 2 colors are required for sorting', 'array.max': 'Maximum 100 colors can be sorted at once', }), sort_by: Joi.string() .valid( 'hue', 'saturation', 'lightness', 'brightness', 'temperature', 'frequency' ) .required() .messages({ 'any.only': 'Sort criteria must be one of: hue, saturation, lightness, brightness, temperature, frequency', }), direction: Joi.string().valid('ascending', 'descending').default('ascending'), group_similar: Joi.boolean().default(false), }); - src/tools/sort-colors.ts:544-579 (schema)JSON Schema (MCP tool parameters) defining the input structure for sort_colors tool.
parameters: { type: 'object', properties: { colors: { type: 'array', items: { type: 'string' }, minItems: 2, maxItems: 100, description: 'Array of colors to sort (2-100 colors)', }, sort_by: { type: 'string', enum: [ 'hue', 'saturation', 'lightness', 'brightness', 'temperature', 'frequency', ], description: 'Property to sort by', }, direction: { type: 'string', enum: ['ascending', 'descending'], default: 'ascending', description: 'Sort direction', }, group_similar: { type: 'boolean', default: false, description: 'Group similar colors together', }, }, required: ['colors', 'sort_by'], }, - src/tools/index.ts:136-136 (registration)Registration of the sortColorsTool in the central tool registry.
toolRegistry.registerTool(sortColorsTool); - src/tools/index.ts:91-91 (registration)Import of the sortColorsTool for registration.
import { sortColorsTool } from './sort-colors';