Skip to main content
Glama

optimize_for_accessibility

Adjust color palettes to meet WCAG accessibility standards for text, background, accent, and interactive elements while maintaining original hues when possible.

Instructions

Optimize colors for accessibility compliance while preserving hue when possible

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
paletteYesArray of colors to optimize
use_casesYesUse cases for the colors
target_standardNoTarget accessibility standardWCAG_AA
preserve_hueNoPreserve original hues when possible
preserve_brand_colorsNoColors that should not be modified

Implementation Reference

  • Core handler function that validates input palette and use cases, optimizes colors for accessibility using WCAG standards while optionally preserving hue, generates optimization results, recommended pairings, and compliance summaries.
    export async function optimizeForAccessibility(
      params: OptimizeForAccessibilityParams
    ): Promise<ToolResponse | ErrorResponse> {
      const startTime = Date.now();
    
      try {
        // Validate required parameters
        if (
          !params.palette ||
          !Array.isArray(params.palette) ||
          params.palette.length === 0
        ) {
          return createErrorResponse(
            'optimize_for_accessibility',
            'MISSING_PALETTE',
            'Palette array parameter is required and must not be empty',
            Date.now() - startTime,
            {
              details: { provided: params.palette },
              suggestions: ['Provide an array of colors to optimize'],
            }
          );
        }
    
        if (
          !params.use_cases ||
          !Array.isArray(params.use_cases) ||
          params.use_cases.length === 0
        ) {
          return createErrorResponse(
            'optimize_for_accessibility',
            'MISSING_USE_CASES',
            'Use cases array parameter is required and must not be empty',
            Date.now() - startTime,
            {
              details: { provided: params.use_cases },
              suggestions: [
                'Specify use cases: text, background, accent, interactive',
              ],
            }
          );
        }
    
        // Validate use cases
        const validUseCases = ['text', 'background', 'accent', 'interactive'];
        const invalidUseCases = params.use_cases.filter(
          uc => !validUseCases.includes(uc)
        );
        if (invalidUseCases.length > 0) {
          return createErrorResponse(
            'optimize_for_accessibility',
            'INVALID_USE_CASES',
            `Invalid use cases: ${invalidUseCases.join(', ')}`,
            Date.now() - startTime,
            {
              details: { invalid: invalidUseCases, valid: validUseCases },
              suggestions: [
                'Use only valid use cases: text, background, accent, interactive',
              ],
            }
          );
        }
    
        // Set defaults
        const targetStandard = params.target_standard || 'WCAG_AA';
        const preserveHue = params.preserve_hue ?? true;
        const preserveBrandColors = params.preserve_brand_colors || [];
    
        // Validate all color inputs
        const validatedColors: UnifiedColor[] = [];
        const preserveBrandSet = new Set(preserveBrandColors);
    
        for (let i = 0; i < params.palette.length; i++) {
          const colorInput = params.palette[i];
          if (!colorInput) {
            return createErrorResponse(
              'optimize_for_accessibility',
              'INVALID_COLOR_FORMAT',
              `Color at index ${i} is undefined or null`,
              Date.now() - startTime,
              {
                details: { index: i, provided: colorInput },
                suggestions: ['Ensure all colors in the palette are valid strings'],
              }
            );
          }
    
          const validation = validateColorInput(colorInput);
    
          if (!validation.isValid) {
            return createErrorResponse(
              'optimize_for_accessibility',
              'INVALID_COLOR_FORMAT',
              `Invalid color format at index ${i}: ${colorInput}`,
              Date.now() - startTime,
              {
                details: {
                  index: i,
                  provided: colorInput,
                  error: validation.error,
                },
                suggestions: [
                  'Use valid color formats like #FF0000 or rgb(255, 0, 0)',
                ],
              }
            );
          }
    
          try {
            validatedColors.push(new UnifiedColor(colorInput));
          } catch (error) {
            return createErrorResponse(
              'optimize_for_accessibility',
              'COLOR_PARSING_ERROR',
              `Failed to parse color at index ${i}: ${colorInput}`,
              Date.now() - startTime,
              {
                details: {
                  index: i,
                  provided: colorInput,
                  error: error instanceof Error ? error.message : 'Unknown error',
                },
                suggestions: ['Check the color format and try again'],
              }
            );
          }
        }
    
        // Optimize each color for each use case
        const optimizationResults: ColorOptimizationResult[] = [];
        let totalOptimized = 0;
        let totalPreserved = 0;
        let totalContrastImprovement = 0;
        let complianceBefore = 0;
        let complianceAfter = 0;
    
        for (let i = 0; i < validatedColors.length; i++) {
          const originalColor = validatedColors[i];
          const originalColorString = params.palette[i];
    
          if (!originalColor || !originalColorString) {
            continue; // Skip invalid entries
          }
    
          const shouldPreserve = preserveBrandSet.has(originalColorString);
    
          for (const useCase of params.use_cases) {
            const optimizationResult = await optimizeColorForUseCase(
              originalColor,
              originalColorString,
              useCase,
              targetStandard,
              preserveHue,
              shouldPreserve
            );
    
            optimizationResults.push(optimizationResult);
    
            if (optimizationResult.optimization_applied) {
              totalOptimized++;
            } else {
              totalPreserved++;
            }
    
            totalContrastImprovement +=
              optimizationResult.contrast_improvement.improvement_percentage;
    
            // Count compliance rates
            if (targetStandard === 'WCAG_AA') {
              if (optimizationResult.accessibility_compliance.wcag_aa_before)
                complianceBefore++;
              if (optimizationResult.accessibility_compliance.wcag_aa_after)
                complianceAfter++;
            } else {
              if (optimizationResult.accessibility_compliance.wcag_aaa_before)
                complianceBefore++;
              if (optimizationResult.accessibility_compliance.wcag_aaa_after)
                complianceAfter++;
            }
          }
        }
    
        // Generate recommended color pairings
        const recommendedPairings = generateRecommendedPairings(
          optimizationResults,
          targetStandard
        );
    
        // Calculate summary statistics
        const totalResults = optimizationResults.length;
        const averageContrastImprovement =
          totalResults > 0 ? totalContrastImprovement / totalResults : 0;
        const complianceRateBefore =
          totalResults > 0 ? (complianceBefore / totalResults) * 100 : 0;
        const complianceRateAfter =
          totalResults > 0 ? (complianceAfter / totalResults) * 100 : 0;
    
        // Generate accessibility notes and recommendations
        const accessibilityNotes = generateAccessibilityNotes(
          optimizationResults,
          targetStandard
        );
        const recommendations = generateOptimizationRecommendations(
          optimizationResults,
          complianceRateBefore,
          complianceRateAfter,
          preserveHue
        );
    
        const responseData: OptimizeForAccessibilityResponse = {
          target_standard: targetStandard,
          preserve_hue: preserveHue,
          optimization_results: optimizationResults,
          summary: {
            total_colors: validatedColors.length,
            colors_optimized: totalOptimized,
            colors_preserved: totalPreserved,
            average_contrast_improvement:
              Math.round(averageContrastImprovement * 100) / 100,
            compliance_rate_before: Math.round(complianceRateBefore * 100) / 100,
            compliance_rate_after: Math.round(complianceRateAfter * 100) / 100,
          },
          recommended_pairings: recommendedPairings,
          accessibility_notes: accessibilityNotes,
          recommendations: recommendations,
        };
    
        const executionTime = Date.now() - startTime;
    
        return createSuccessResponse(
          'optimize_for_accessibility',
          responseData,
          executionTime,
          {
            colorSpaceUsed: 'sRGB',
            accessibilityNotes: accessibilityNotes.slice(0, 5),
            recommendations: recommendations.slice(0, 5),
          }
        );
      } catch (error) {
        const executionTime = Date.now() - startTime;
        return createErrorResponse(
          'optimize_for_accessibility',
          'OPTIMIZATION_ERROR',
          `Failed to optimize colors for accessibility: ${error instanceof Error ? error.message : 'Unknown error'}`,
          executionTime,
          {
            details: {
              palette_size: params.palette?.length || 0,
              use_cases: params.use_cases,
            },
            suggestions: [
              'Check input parameters and try again',
              'Ensure all colors are in valid formats',
            ],
          }
        );
      }
    }
  • TypeScript interfaces defining the input parameters (OptimizeForAccessibilityParams), per-color optimization results (ColorOptimizationResult), and full response structure (OptimizeForAccessibilityResponse).
    export interface OptimizeForAccessibilityParams {
      palette: string[];
      use_cases: ('text' | 'background' | 'accent' | 'interactive')[];
      target_standard?: 'WCAG_AA' | 'WCAG_AAA';
      preserve_hue?: boolean;
      preserve_brand_colors?: string[]; // Colors that should not be modified
    }
    
    export interface ColorOptimizationResult {
      original_color: string;
      optimized_color: string;
      use_case: string;
      optimization_applied: boolean;
      changes_made: string[];
      contrast_improvement: {
        before: number;
        after: number;
        improvement_percentage: number;
      };
      accessibility_compliance: {
        wcag_aa_before: boolean;
        wcag_aa_after: boolean;
        wcag_aaa_before: boolean;
        wcag_aaa_after: boolean;
      };
      hue_preservation: {
        hue_changed: boolean;
        hue_difference: number; // degrees
      };
    }
    
    export interface OptimizeForAccessibilityResponse {
      target_standard: string;
      preserve_hue: boolean;
      optimization_results: ColorOptimizationResult[];
      summary: {
        total_colors: number;
        colors_optimized: number;
        colors_preserved: number;
        average_contrast_improvement: number;
        compliance_rate_before: number;
        compliance_rate_after: number;
      };
      recommended_pairings: Array<{
        foreground: string;
        background: string;
        contrast_ratio: number;
        use_case: string;
        compliant: boolean;
      }>;
      accessibility_notes: string[];
      recommendations: string[];
    }
  • Registers the optimizeForAccessibilityTool in the central ToolRegistry singleton.
    toolRegistry.registerTool(optimizeForAccessibilityTool);
  • MCP tool definition including JSON Schema for parameters (input validation) and handler wrapper that calls the core optimizeForAccessibility function.
    export const optimizeForAccessibilityTool = {
      name: 'optimize_for_accessibility',
      description:
        'Optimize colors for accessibility compliance while preserving hue when possible',
      parameters: {
        type: 'object',
        properties: {
          palette: {
            type: 'array',
            items: { type: 'string' },
            description: 'Array of colors to optimize',
          },
          use_cases: {
            type: 'array',
            items: {
              type: 'string',
              enum: ['text', 'background', 'accent', 'interactive'],
            },
            description: 'Use cases for the colors',
          },
          target_standard: {
            type: 'string',
            enum: ['WCAG_AA', 'WCAG_AAA'],
            description: 'Target accessibility standard',
            default: 'WCAG_AA',
          },
          preserve_hue: {
            type: 'boolean',
            description: 'Preserve original hues when possible',
            default: true,
          },
          preserve_brand_colors: {
            type: 'array',
            items: { type: 'string' },
            description: 'Colors that should not be modified',
            default: [],
          },
        },
        required: ['palette', 'use_cases'],
      },
      handler: async (params: unknown) =>
        optimizeForAccessibility(params as OptimizeForAccessibilityParams),
    };
  • Helper function that optimizes a single color for a specific use case, calculating contrast improvements, compliance, and hue preservation metrics.
    async function optimizeColorForUseCase(
      originalColor: UnifiedColor,
      originalColorString: string,
      useCase: string,
      targetStandard: string,
      preserveHue: boolean,
      shouldPreserve: boolean
    ): Promise<ColorOptimizationResult> {
      const originalHsl = originalColor.hsl;
      let optimizedColor = originalColor;
      let optimizationApplied = false;
      const changesMade: string[] = [];
    
      // Skip optimization if color should be preserved
      if (shouldPreserve) {
        changesMade.push('Color preserved as brand color');
      } else {
        // Determine optimization strategy based on use case
        switch (useCase) {
          case 'text':
            optimizedColor = optimizeForText(
              originalColor,
              targetStandard,
              preserveHue
            );
            break;
          case 'background':
            optimizedColor = optimizeForBackground(
              originalColor,
              targetStandard,
              preserveHue
            );
            break;
          case 'accent':
            optimizedColor = optimizeForAccent(
              originalColor,
              targetStandard,
              preserveHue
            );
            break;
          case 'interactive':
            optimizedColor = optimizeForInteractive(
              originalColor,
              targetStandard,
              preserveHue
            );
            break;
        }
    
        // Check if optimization was applied
        optimizationApplied = !colorsEqual(originalColor, optimizedColor);
    
        if (optimizationApplied) {
          const optimizedHsl = optimizedColor.hsl;
    
          if (Math.abs(originalHsl.l - optimizedHsl.l) > 5) {
            changesMade.push(
              `Lightness adjusted from ${Math.round(originalHsl.l)}% to ${Math.round(optimizedHsl.l)}%`
            );
          }
    
          if (Math.abs(originalHsl.s - optimizedHsl.s) > 5) {
            changesMade.push(
              `Saturation adjusted from ${Math.round(originalHsl.s)}% to ${Math.round(optimizedHsl.s)}%`
            );
          }
    
          if (!preserveHue && Math.abs(originalHsl.h - optimizedHsl.h) > 5) {
            changesMade.push(
              `Hue adjusted from ${Math.round(originalHsl.h)}° to ${Math.round(optimizedHsl.h)}°`
            );
          }
        }
      }
    
      // Calculate contrast improvements (using white background as reference)
      const whiteBackground = UnifiedColor.fromHex('#FFFFFF');
      const contrastBefore = ColorAnalyzer.checkContrast(
        originalColor,
        whiteBackground
      ).ratio;
      const contrastAfter = ColorAnalyzer.checkContrast(
        optimizedColor,
        whiteBackground
      ).ratio;
      const improvementPercentage =
        contrastBefore > 0
          ? ((contrastAfter - contrastBefore) / contrastBefore) * 100
          : 0;
    
      // Check accessibility compliance
      const accessibilityBefore = ColorAnalyzer.analyzeAccessibility(originalColor);
      const accessibilityAfter = ColorAnalyzer.analyzeAccessibility(optimizedColor);
    
      // Calculate hue preservation
      const hueDifference = Math.abs(originalHsl.h - optimizedColor.hsl.h);
      const hueChanged = hueDifference > 5;
    
      return {
        original_color: originalColorString,
        optimized_color: optimizedColor.hex,
        use_case: useCase,
        optimization_applied: optimizationApplied,
        changes_made: changesMade,
        contrast_improvement: {
          before: Math.round(contrastBefore * 100) / 100,
          after: Math.round(contrastAfter * 100) / 100,
          improvement_percentage: Math.round(improvementPercentage * 100) / 100,
        },
        accessibility_compliance: {
          wcag_aa_before: accessibilityBefore.wcag_aa_normal,
          wcag_aa_after: accessibilityAfter.wcag_aa_normal,
          wcag_aaa_before: accessibilityBefore.wcag_aaa_normal,
          wcag_aaa_after: accessibilityAfter.wcag_aaa_normal,
        },
        hue_preservation: {
          hue_changed: hueChanged,
          hue_difference: Math.round(hueDifference * 100) / 100,
        },
      };
    }
Behavior2/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations are provided, so the description carries the full burden of behavioral disclosure. While it mentions 'preserving hue when possible,' it doesn't describe what 'optimize' entails operationally (e.g., does it modify colors algorithmically, return suggestions, or apply transformations?), what permissions or inputs are needed, or what the output looks like. For a tool with 5 parameters and no annotations, this is insufficient.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is a single, efficient sentence that states the core purpose without unnecessary words. It's appropriately sized and front-loaded with the essential information, making it easy to parse quickly.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness2/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the tool's complexity (5 parameters, no annotations, no output schema), the description is incomplete. It doesn't explain what 'optimize' means in practice, what the tool returns, or how it differs from sibling accessibility tools. For a color optimization tool with multiple parameters, more context is needed to guide effective use.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so the schema already documents all parameters thoroughly. The description mentions 'preserving hue when possible,' which aligns with the 'preserve_hue' parameter but doesn't add meaningful semantic context beyond what's in the schema descriptions. Baseline 3 is appropriate when the schema does the heavy lifting.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool's purpose: 'Optimize colors for accessibility compliance while preserving hue when possible.' It specifies the action (optimize), resource (colors), and goal (accessibility compliance). However, it doesn't explicitly differentiate from sibling tools like 'check_contrast' or 'simulate_colorblindness' which also relate to accessibility.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description provides no guidance on when to use this tool versus alternatives. With many sibling tools related to color analysis and accessibility (e.g., 'check_contrast', 'simulate_colorblindness'), there's no indication of when this optimization tool is preferred over those other options or what specific scenarios it addresses.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Install Server

Other Tools

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/keyurgolani/ColorMcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server