mcp-color-convert
Version:
Comprehensive color conversion, manipulation, analysis, and accessibility MCP server
362 lines • 20.6 kB
JavaScript
#!/usr/bin/env node
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { McpError } from "@modelcontextprotocol/sdk/types.js";
import { convert, lighten, darken, saturate, desaturate, rotate, luminance, chroma, opacity, name, palette, scheme, swatch, random, contrast, compare, textColor, isValidColor } from "colorizr";
import { z } from "zod";
// Optional: Configuration schema for session
export const configSchema = z.object({
debug: z.boolean().default(false).describe("Enable debug logging"),
});
// NOTE: MCP server tools require Zod schemas for inputSchema, not JSON Schema format.
// The registerTool method expects: (name, {title, description, inputSchema}, handler)
// where inputSchema uses Zod objects like: { field: z.string().describe("description") }
const server = new McpServer({
name: "color-converter",
version: "0.0.1",
description: "Comprehensive color toolkit for conversion, manipulation, analysis, generation, and accessibility testing.",
});
server.registerTool("convert", {
title: "Color Converter",
description: "Converts a color from one format to another. Supports all major color formats including hex, rgb, rgba, hsl, hsla, oklch, oklab with full alpha/transparency support. Input can be any valid color string (hex, rgb, rgba, hsl, hsla, oklch, oklab, or named colors like 'red', 'blue'). Alpha values are preserved when converting between alpha-supporting formats, removed when converting to non-alpha formats. Available output formats: 'hex' (#FF0000), 'rgb' (rgb(255,0,0)), 'hsl' (hsl(0,100%,50%)), 'oklch' (oklch(63.3% 0.254 19.9)), 'oklab' (oklab(0.633 0.025 0.013)). Example: convert color '#FF0000' to format 'oklch'.",
inputSchema: {
color: z.string().describe("The color string to convert - accepts hex (#FF0000), rgb(255,0,0), rgba(255,0,0,0.5), hsl(0,100%,50%), hsla(0,100%,50%,0.5), oklch(63.3% 0.254 19.9), oklch(63.3% 0.254 19.9 / 0.5), oklab(0.633 0.025 0.013), oklab(0.633 0.025 0.013 / 0.5), or named colors ('red', 'blue', 'green'). Alpha values supported in all applicable formats."),
format: z.string().describe("The target color format: 'hex', 'rgb', 'hsl', 'oklch', or 'oklab'."),
},
}, async (args) => {
const { color, format } = args;
try {
const convertedColor = convert(color, format);
return {
content: [{ type: "text", text: JSON.stringify({ color: convertedColor }) }],
};
}
catch (error) {
throw new McpError(-32000, `Invalid color or format: ${error.message}`);
}
});
// Color Manipulation Tools
server.registerTool("lighten", {
title: "Lighten Color",
description: "Increases the lightness of a color by a specified percentage amount. Perfect for creating lighter variations of colors for hover states, highlights, or design system variations.",
inputSchema: {
color: z.string().describe("The color string to lighten (e.g., '#FF0000', 'rgb(255,0,0)', 'rgba(255,0,0,0.5)', 'hsl(0,100%,50%)', 'hsla(0,100%,50%,0.5)', 'oklch(63.3% 0.254 19.9 / 0.5)', 'oklab(0.633 0.025 0.013 / 0.5)'). Supports all formats including alpha variants."),
amount: z.number().describe("The amount to increase lightness by (0-100). For example, 10 makes the color 10% lighter."),
},
}, async ({ color, amount }) => {
try {
const result = lighten(color, amount);
return {
content: [{ type: "text", text: JSON.stringify({ lightened: result }) }],
};
}
catch (error) {
throw new McpError(-32000, `Invalid color or amount: ${error.message}`);
}
});
server.registerTool("darken", {
title: "Darken Color",
description: "Decreases the lightness of a color by a specified percentage amount. Ideal for creating darker variations for active states, shadows, or depth in design systems.",
inputSchema: {
color: z.string().describe("The color string to darken (e.g., '#FF0000', 'rgb(255,0,0)', 'rgba(255,0,0,0.5)', 'hsl(0,100%,50%)', 'hsla(0,100%,50%,0.5)', 'oklch(63.3% 0.254 19.9 / 0.5)', 'oklab(0.633 0.025 0.013 / 0.5)'). Supports all formats including alpha variants."),
amount: z.number().describe("The amount to decrease lightness by (0-100). For example, 10 makes the color 10% darker."),
},
}, async ({ color, amount }) => {
try {
const result = darken(color, amount);
return {
content: [{ type: "text", text: JSON.stringify({ darkened: result }) }],
};
}
catch (error) {
throw new McpError(-32000, `Invalid color or amount: ${error.message}`);
}
});
server.registerTool("saturate", {
title: "Saturate Color",
description: "Increases the saturation (intensity/vibrancy) of a color by a specified percentage amount. Use this to make colors more vivid and eye-catching.",
inputSchema: {
color: z.string().describe("The color string to saturate (e.g., '#FF0000', 'rgb(255,0,0)', 'rgba(255,0,0,0.5)', 'hsl(0,100%,50%)', 'hsla(0,100%,50%,0.5)', 'oklch(63.3% 0.254 19.9 / 0.5)', 'oklab(0.633 0.025 0.013 / 0.5)'). Supports all formats including alpha variants."),
amount: z.number().describe("The amount to increase saturation by (0-100). For example, 20 makes the color 20% more saturated."),
},
}, async ({ color, amount }) => {
try {
const result = saturate(color, amount);
return {
content: [{ type: "text", text: JSON.stringify({ saturated: result }) }],
};
}
catch (error) {
throw new McpError(-32000, `Invalid color or amount: ${error.message}`);
}
});
server.registerTool("desaturate", {
title: "Desaturate Color",
description: "Decreases the saturation (intensity/vibrancy) of a color by a specified percentage amount. Perfect for creating muted tones, subtle backgrounds, or less prominent UI elements.",
inputSchema: {
color: z.string().describe("The color string to desaturate (e.g., '#FF0000', 'rgb(255,0,0)', 'rgba(255,0,0,0.5)', 'hsl(0,100%,50%)', 'hsla(0,100%,50%,0.5)', 'oklch(63.3% 0.254 19.9 / 0.5)', 'oklab(0.633 0.025 0.013 / 0.5)'). Supports all formats including alpha variants."),
amount: z.number().describe("The amount to decrease saturation by (0-100). For example, 20 makes the color 20% less saturated."),
},
}, async ({ color, amount }) => {
try {
const result = desaturate(color, amount);
return {
content: [{ type: "text", text: JSON.stringify({ desaturated: result }) }],
};
}
catch (error) {
throw new McpError(-32000, `Invalid color or amount: ${error.message}`);
}
});
server.registerTool("rotate", {
title: "Rotate Color Hue",
description: "Rotates the hue of a color by specified degrees around the color wheel. Perfect for creating color harmonies, analogous colors, or systematic color variations.",
inputSchema: {
color: z.string().describe("The color string to rotate (e.g., '#FF0000', 'rgb(255,0,0)', 'rgba(255,0,0,0.5)', 'hsl(0,100%,50%)', 'hsla(0,100%,50%,0.5)', 'oklch(63.3% 0.254 19.9 / 0.5)', 'oklab(0.633 0.025 0.013 / 0.5)'). Supports all formats including alpha variants."),
degrees: z.number().describe("The number of degrees to rotate the hue (0-360). Positive values rotate clockwise, negative counter-clockwise. For example, 180 creates the complementary color."),
},
}, async ({ color, degrees }) => {
try {
const result = rotate(color, degrees);
return {
content: [{ type: "text", text: JSON.stringify({ rotated: result }) }],
};
}
catch (error) {
throw new McpError(-32000, `Invalid color or degrees: ${error.message}`);
}
});
// Color Analysis Tools
server.registerTool("luminance", {
title: "Get Color Luminance",
description: "Calculates the WCAG relative brightness of a color (0-1 scale, where 0 is pure black and 1 is pure white). Critical for accessibility compliance and determining if colors are light or dark.",
inputSchema: {
color: z.string().describe("The color string to analyze (e.g., '#FF0000', 'rgb(255,0,0)', 'rgba(255,0,0,0.5)', 'hsl(0,100%,50%)', 'hsla(0,100%,50%,0.5)', 'oklch(63.3% 0.254 19.9 / 0.5)', 'oklab(0.633 0.025 0.013 / 0.5)'). Supports all formats including alpha variants."),
},
}, async ({ color }) => {
try {
const result = luminance(color);
return {
content: [{ type: "text", text: JSON.stringify({ luminance: result }) }],
};
}
catch (error) {
throw new McpError(-32000, `Invalid color: ${error.message}`);
}
});
server.registerTool("chroma", {
title: "Get Color Chroma",
description: "Calculates the chroma (color intensity/purity) of a color (0-1 scale, where 0 is grayscale and 1 is maximum intensity). Useful for understanding color vibrancy and saturation levels.",
inputSchema: {
color: z.string().describe("The color string to analyze (e.g., '#FF0000', 'rgb(255,0,0)', 'rgba(255,0,0,0.5)', 'hsl(0,100%,50%)', 'hsla(0,100%,50%,0.5)', 'oklch(63.3% 0.254 19.9 / 0.5)', 'oklab(0.633 0.025 0.013 / 0.5)'). Supports all formats including alpha variants."),
},
}, async ({ color }) => {
try {
const result = chroma(color);
return {
content: [{ type: "text", text: JSON.stringify({ chroma: result }) }],
};
}
catch (error) {
throw new McpError(-32000, `Invalid color: ${error.message}`);
}
});
server.registerTool("opacity", {
title: "Get Color Opacity",
description: "Extracts the opacity/alpha value from any color format (0-1 scale, where 0 is fully transparent and 1 is fully opaque). Works with rgba, hsla, oklch, oklab, and any format that supports transparency. Essential for working with transparent colors and overlays.",
inputSchema: {
color: z.string().describe("The color string to analyze (e.g., '#FF0000', 'rgb(255,0,0)', 'hsl(0,100%,50%)', 'rgba(255,0,0,0.5)', 'hsla(0,100%,50%,0.5)', 'oklch(63.3% 0.254 19.9 / 0.5)', 'oklab(0.633 0.025 0.013 / 0.5)'). Works with any color format that supports transparency."),
},
}, async ({ color }) => {
try {
const result = opacity(color);
return {
content: [{ type: "text", text: JSON.stringify({ opacity: result }) }],
};
}
catch (error) {
throw new McpError(-32000, `Invalid color: ${error.message}`);
}
});
server.registerTool("name", {
title: "Get Color Name",
description: "Attempts to identify the common name of a color. Returns the color name if recognizable (e.g., 'red', 'blue', 'forestgreen'), otherwise returns the hex code. Useful for user-friendly color identification.",
inputSchema: {
color: z.string().describe("The color string to name (e.g., '#FF0000', 'rgb(255,0,0)', 'hsl(0,100%,50%)')."),
},
}, async ({ color }) => {
try {
const result = name(color);
return {
content: [{ type: "text", text: JSON.stringify({ name: result }) }],
};
}
catch (error) {
throw new McpError(-32000, `Invalid color: ${error.message}`);
}
});
// Color Generation Tools
server.registerTool("palette", {
title: "Generate Color Palette",
description: "Generates a harmonious color palette from a base color. Creates 6 evenly spaced colors around the color wheel. Perfect for creating color schemes, themes, or design system foundations.",
inputSchema: {
color: z.string().describe("The base color to generate palette from (e.g., '#FF0000', 'rgb(255,0,0)', 'rgba(255,0,0,0.5)', 'hsl(0,100%,50%)', 'hsla(0,100%,50%,0.5)', 'oklch(63.3% 0.254 19.9 / 0.5)', 'oklab(0.633 0.025 0.013 / 0.5)'). Supports all formats including alpha variants."),
type: z.string().optional().describe("Optional palette type: 'default' (rainbow-style) or 'monochromatic' (same hue, varying lightness). Defaults to 'default'."),
},
}, async ({ color, type }) => {
try {
const options = type ? { type } : {};
const result = palette(color, options);
return {
content: [{ type: "text", text: JSON.stringify({ palette: result }) }],
};
}
catch (error) {
throw new McpError(-32000, `Invalid color or type: ${error.message}`);
}
});
server.registerTool("scheme", {
title: "Generate Color Scheme",
description: "Generates specific color harmonies from a base color. Supports complementary, triadic, tetradic, and other classic color theory relationships. Essential for creating balanced, professional color combinations.",
inputSchema: {
color: z.string().describe("The base color to generate scheme from (e.g., '#FF0000', 'rgb(255,0,0)', 'rgba(255,0,0,0.5)', 'hsl(0,100%,50%)', 'hsla(0,100%,50%,0.5)', 'oklch(63.3% 0.254 19.9 / 0.5)', 'oklab(0.633 0.025 0.013 / 0.5)'). Supports all formats including alpha variants."),
type: z.string().describe("Scheme type: 'complementary' (2 colors), 'triadic' (3 colors), 'tetradic' (4 colors), 'analogous' (adjacent colors), or 'split-complementary'."),
},
}, async ({ color, type }) => {
try {
const result = scheme(color, type);
return {
content: [{ type: "text", text: JSON.stringify({ scheme: result }) }],
};
}
catch (error) {
throw new McpError(-32000, `Invalid color or scheme type: ${error.message}`);
}
});
server.registerTool("swatch", {
title: "Generate Color Swatch",
description: "Creates a complete 11-shade swatch from a base color (50, 100, 200...950). This is the industry standard for design systems like Tailwind CSS. Perfect for creating consistent light-to-dark variations.",
inputSchema: {
color: z.string().describe("The base color to generate swatch from (e.g., '#FF0000', 'rgb(255,0,0)', 'rgba(255,0,0,0.5)', 'hsl(0,100%,50%)', 'hsla(0,100%,50%,0.5)', 'oklch(63.3% 0.254 19.9 / 0.5)', 'oklab(0.633 0.025 0.013 / 0.5)'). Supports all formats including alpha variants."),
lightnessFactor: z.number().optional().describe("Optional multiplier for lightness adjustments (default: 1.0). Higher values create more contrast between shades."),
maxLightness: z.number().optional().describe("Optional maximum lightness cap (0-1, default: 0.95). Prevents shades from becoming too light."),
minLightness: z.number().optional().describe("Optional minimum lightness floor (0-1, default: 0.05). Prevents shades from becoming too dark."),
},
}, async ({ color, lightnessFactor, maxLightness, minLightness }) => {
try {
const options = {};
if (lightnessFactor !== undefined)
options.lightnessFactor = lightnessFactor;
if (maxLightness !== undefined)
options.maxLightness = maxLightness;
if (minLightness !== undefined)
options.minLightness = minLightness;
const result = swatch(color, options);
return {
content: [{ type: "text", text: JSON.stringify({ swatch: result }) }],
};
}
catch (error) {
throw new McpError(-32000, `Invalid color or options: ${error.message}`);
}
});
server.registerTool("random", {
title: "Generate Random Color",
description: "Generates a random color in the specified format. Useful for testing, prototyping, or when you need inspiration for new color choices.",
inputSchema: {
format: z.string().optional().describe("Optional format for the random color: 'hex', 'rgb', 'hsl', 'oklch', 'oklab'. Defaults to 'hex'."),
},
}, async ({ format }) => {
try {
const result = random((format || 'hex'));
return {
content: [{ type: "text", text: JSON.stringify({ random: result }) }],
};
}
catch (error) {
throw new McpError(-32000, `Invalid format: ${error.message}`);
}
});
// Accessibility Tools
server.registerTool("contrast", {
title: "Calculate Color Contrast",
description: "Calculates the WCAG contrast ratio between two colors (1-21 scale, where higher values mean better contrast). Essential for accessibility compliance - AA requires 4.5:1 for normal text, AAA requires 7:1.",
inputSchema: {
foreground: z.string().describe("The foreground/text color (e.g., '#000000', 'rgb(0,0,0)', 'rgba(0,0,0,0.8)', 'hsl(0,0%,0%)', 'hsla(0,0%,0%,0.8)', 'oklch(0% 0 0 / 0.8)', 'oklab(0 0 0 / 0.8)'). Supports all formats including alpha variants."),
background: z.string().describe("The background color (e.g., '#FFFFFF', 'rgb(255,255,255)', 'rgba(255,255,255,0.8)', 'hsl(0,0%,100%)', 'hsla(0,0%,100%,0.8)', 'oklch(100% 0 0 / 0.8)', 'oklab(1 0 0 / 0.8)'). Supports all formats including alpha variants."),
},
}, async ({ foreground, background }) => {
try {
const result = contrast(foreground, background);
return {
content: [{ type: "text", text: JSON.stringify({ contrast: result }) }],
};
}
catch (error) {
throw new McpError(-32000, `Invalid colors: ${error.message}`);
}
});
server.registerTool("compare", {
title: "WCAG Color Compliance Analysis",
description: "Performs comprehensive WCAG accessibility analysis between two colors. Returns contrast ratio, compliance levels (AA/AAA), and recommendations. Critical for ensuring your color combinations meet accessibility standards.",
inputSchema: {
foreground: z.string().describe("The foreground/text color to analyze (e.g., '#000000', 'rgb(0,0,0)', 'rgba(0,0,0,0.8)', 'hsl(0,0%,0%)', 'hsla(0,0%,0%,0.8)', 'oklch(0% 0 0 / 0.8)', 'oklab(0 0 0 / 0.8)'). Supports all formats including alpha variants."),
background: z.string().describe("The background color to analyze (e.g., '#FFFFFF', 'rgb(255,255,255)', 'rgba(255,255,255,0.8)', 'hsl(0,0%,100%)', 'hsla(0,0%,100%,0.8)', 'oklch(100% 0 0 / 0.8)', 'oklab(1 0 0 / 0.8)'). Supports all formats including alpha variants."),
},
}, async ({ foreground, background }) => {
try {
const result = compare(foreground, background);
return {
content: [{ type: "text", text: JSON.stringify({ analysis: result }) }],
};
}
catch (error) {
throw new McpError(-32000, `Invalid colors: ${error.message}`);
}
});
server.registerTool("text_color", {
title: "Get Optimal Text Color",
description: "Determines the best text color (black or white) for maximum readability on a given background color. Automatically calculates which provides better contrast. Perfect for dynamic UI elements with variable backgrounds.",
inputSchema: {
background: z.string().describe("The background color to analyze (e.g., '#FF0000', 'rgb(255,0,0)', 'rgba(255,0,0,0.5)', 'hsl(0,100%,50%)', 'hsla(0,100%,50%,0.5)', 'oklch(63.3% 0.254 19.9 / 0.5)', 'oklab(0.633 0.025 0.013 / 0.5)'). Supports all formats including alpha variants."),
},
}, async ({ background }) => {
try {
const result = textColor(background);
return {
content: [{ type: "text", text: JSON.stringify({ textColor: result }) }],
};
}
catch (error) {
throw new McpError(-32000, `Invalid background color: ${error.message}`);
}
});
// Utility Tools
server.registerTool("is_valid_color", {
title: "Validate Color",
description: "Checks if a color string is valid and parseable. Supports hex, rgb, hsl, oklab, oklch formats, and named colors. Essential for input validation and error handling.",
inputSchema: {
color: z.string().describe("The color string to validate (e.g., '#FF0000', 'rgb(255,0,0)', 'rgba(255,0,0,0.5)', 'hsl(0,100%,50%)', 'hsla(0,100%,50%,0.5)', 'oklch(63.3% 0.254 19.9 / 0.5)', 'oklab(0.633 0.025 0.013 / 0.5)', 'red', 'invalidcolor'). Supports all formats including alpha variants."),
},
}, async ({ color }) => {
try {
const result = isValidColor(color);
return {
content: [{ type: "text", text: JSON.stringify({ valid: result }) }],
};
}
catch (error) {
throw new McpError(-32000, `Validation error: ${error.message}`);
}
});
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
// Smithery requires default export function
export default function createServer({ config }) {
return server.server;
}
// Auto-connect when run as a standalone server (not when imported by Smithery)
// This handles: node dist/index.js, npx mcp-color-convert, and MCP clients
if (!process.env.SMITHERY) {
const transport = new StdioServerTransport();
server.connect(transport);
}
//# sourceMappingURL=index.js.map