- JavaScript 99.4%
- Dockerfile 0.6%
|
|
||
|---|---|---|
| .forgejo/workflows | ||
| examples | ||
| lib | ||
| test | ||
| tools | ||
| .dockerignore | ||
| .gitignore | ||
| app.js | ||
| Dockerfile | ||
| index.js | ||
| package-lock.json | ||
| package.json | ||
| README.md | ||
| vitest.config.js | ||
color-theory-mcp
MCP server exposing color tools, semantic context, and accessibility scores to AI agents - powered by David Aerne's color-name-list (31,000+ named colors), pro-color-harmonies, and rampensau. You should give those projects a star and a follow, this is simply a vibe coded wrapper for them.
Tools
Lookup & Description
| Tool | Description |
|---|---|
find_color |
Find closest named colors to any input — first match includes full color space data (RGB, HSL, CMYK, OKLCH, Lab, HWB) |
search_colors |
Filter colors by keyword and/or hue range — first match includes full color space data |
list_color_groups |
List hue-based color groups with counts |
describe_color |
Comprehensive color analysis: all color spaces, descriptive words, color family with meanings/usage, WCAG accessibility, closest named colors |
Conversion & Generation
| Tool | Description |
|---|---|
convert_color |
Convert any color format → all color spaces (hex, RGB, HSL, CMYK, OKLCH, Lab, HWB) |
generate_palette |
Generate 6-color harmony palettes (analogous, complementary, triadic, tetradic, splitComplementary, tintsShades) with style variants, modifiers, and pairwise WCAG contrast data |
generate_ramp |
Generate color ramps/gradients with configurable hue cycling, saturation/lightness ranges, and curve methods — ideal for infographics and charts |
generate_swatch |
Generate PNG swatch images with color squares and name/hex labels — single colors or palette strips |
Accessibility
| Tool | Description |
|---|---|
check_contrast |
Check WCAG 2.2 contrast ratio between any two colors — returns ratio, human-friendly score (AAA/AA/FAIL), and pass/fail for all WCAG thresholds |
Setup
Running the server
npm install
node index.js
The server starts on port 3013 by default (configurable via MCP_PORT environment variable).
- Health check:
GET http://localhost:3013/health - MCP endpoint:
POST http://localhost:3013/mcp
Docker
docker pull git.brads.house/brad/color-mcp:latest
docker run -d --name color-mcp -p 3013:3013 git.brads.house/brad/color-mcp:latest
Or build locally:
docker build -t color-mcp .
docker run -d --name color-mcp -p 3013:3013 color-mcp
Connecting to Claude Desktop
Add to your Claude Desktop config (~/Library/Application Support/Claude/claude_desktop_config.json on macOS, %APPDATA%\Claude\claude_desktop_config.json on Windows):
{
"mcpServers": {
"color": {
"url": "http://localhost:3013/mcp"
}
}
}
Connecting to Claude Code
Add to your project's .mcp.json:
{
"mcpServers": {
"color": {
"type": "url",
"url": "http://localhost:3013/mcp"
}
}
}
Or add it via the CLI:
claude mcp add color --transport http http://localhost:3013/mcp
Other MCP clients
Any MCP-compatible client can connect using the HTTP+SSE transport. Point it at http://localhost:3013/mcp — the server speaks JSON-RPC 2.0.
Examples
Generate a harmony palette
{
"name": "generate_palette",
"arguments": {
"color": "#e85d26",
"harmony": "analogous"
}
}
Split complementary palette
{
"name": "generate_palette",
"arguments": {
"color": "#ff1493",
"harmony": "splitComplementary",
"style": "square"
}
}
Generate a color ramp
{
"name": "generate_ramp",
"arguments": {
"color": "#006994",
"total": 7,
"hCycles": 0,
"sRange": [0.6, 0.8],
"lRange": [0.15, 0.9]
}
}
Generate a swatch strip
{
"name": "generate_swatch",
"arguments": {
"colors": ["#ff5733", "#006994", "#2ecc71", "#9b59b6", "#f39c12"]
}
}
Find closest named colors
{
"name": "find_color",
"arguments": { "color": "ocean" }
}
{
"query": "ocean",
"hex": "#005493",
"results": [
{
"name": "Ocean", "hex": "#005493", "distance": 0,
"rgb": { "r": 0, "g": 84, "b": 147 },
"hsl": { "h": 206, "s": 100, "l": 29 },
"cmyk": { "c": 100, "m": 43, "y": 0, "k": 42 },
"oklch": { "l": 0.439, "c": 0.124, "h": 249.93 },
"lab": { "l": 34.24, "a": -2.28, "b": -41.51 },
"hwb": { "h": 205.71, "w": 0, "b": 42.35 }
},
{ "name": "Standing Waters", "hex": "#005599", "distance": 0.84 },
{ "name": "Blue Olympus", "hex": "#015193", "distance": 1.22 }
]
}
Convert a color to all formats
All tools accept any color format as input: hex, color name, rgb(), hsl(), cmyk(), oklch(), lab(), or hwb().
{
"name": "convert_color",
"arguments": { "color": "rgb(255, 87, 51)" }
}
{
"input": "rgb(255, 87, 51)",
"hex": "#ff5733",
"rgb": { "r": 255, "g": 87, "b": 51 },
"hsl": { "h": 11, "s": 100, "l": 60 },
"cmyk": { "c": 0, "m": 66, "y": 80, "k": 0 },
"oklch": { "l": 0.68, "c": 0.21, "h": 33.69 },
"lab": { "l": 61.03, "a": 63.55, "b": 55.96 },
"hwb": { "h": 10.59, "w": 20, "b": 0 },
"nearestName": { "name": "Poppy Surprise", "hex": "#ff5630", "exact": false, "distance": 0.47 }
}
Describe a color
{
"name": "describe_color",
"arguments": { "color": "#006994" }
}
{
"input": "#006994",
"hex": "#006994",
"name": "Sea Blue",
"rgb": { "r": 0, "g": 105, "b": 148 },
"hsl": { "h": 197, "s": 100, "l": 29 },
"cmyk": { "c": 100, "m": 29, "y": 0, "k": 42 },
"oklch": { "l": 0.492, "c": 0.105, "h": 235.54 },
"lab": { "l": 41.09, "a": -13.73, "b": -31.3 },
"hwb": { "h": 197.43, "w": 0, "b": 41.96 },
"descriptors": ["dark", "dim", "gloomy", "dull", "shady", "cold", "cool", "blue", "blueish"],
"bestContrast": "white",
"family": {
"name": "blue",
"description": "Blue often feels calm, steady, and trustworthy...",
"meanings": ["mystery", "trust", "reliability", "calmness", "..."],
"usage": ["backgrounds", "contrast", "security", "finance", "technology", "..."]
},
"accessibility": {
"luminance": 0.1224,
"contrastOnWhite": {
"ratio": 6.09,
"aa": { "normalText": true, "largeText": true, "uiComponents": true },
"aaa": { "normalText": false, "largeText": true }
},
"contrastOnBlack": {
"ratio": 3.45,
"aa": { "normalText": false, "largeText": true, "uiComponents": true },
"aaa": { "normalText": false, "largeText": false }
}
},
"closestNamed": [
{ "name": "Hydra", "hex": "#006995", "distance": 0.3 },
{ "name": "Port au Prince", "hex": "#006a93", "distance": 0.78 }
]
}
Check contrast between two colors
{
"name": "check_contrast",
"arguments": { "foreground": "#006994", "background": "#ffffff" }
}
{
"foreground": { "hex": "#006994", "luminance": 0.1224 },
"background": { "hex": "#ffffff", "luminance": 1 },
"contrast": {
"ratio": 6.09,
"score": "AA",
"wcag": {
"aa": { "normalText": true, "largeText": true, "uiComponents": true },
"aaa": { "normalText": false, "largeText": true }
}
}
}
Architecture
- Transport: JSON-RPC 2.0 over HTTP (
POST /mcp) - Port: 3013 (configurable via
MCP_PORT) - Data: color-name-list npm package (bundled at build time)
- Color spaces: hex, RGB, HSL, CMYK, OKLCH, CIELab, HWB
- Color matching: CIEDE2000 perceptual distance (via culori)
- Accessibility: culori (WCAG 2.2 luminance + contrast ratio)
- Palettes: pro-color-harmonies (OKLCH perceptual color space)
- Ramps: rampensau (HSL with configurable easing curves)
- Images: @napi-rs/canvas (Skia-based PNG rendering)
Project structure
app.js ← Express app + JSON-RPC dispatch (importable)
index.js ← Server entrypoint (calls app.listen)
tools/
lookup.js ← find/search/describe tools
convert.js ← color space conversion
palette.js ← harmony palette generation
ramp.js ← gradient ramp generation
swatch.js ← PNG swatch image generation
accessibility.js ← WCAG contrast checking
lib/
color-math.js ← Color math utilities + universal input parser
color-convert.js ← OKLCH/Lab/HWB conversions + WCAG accessibility (via culori)
swatch-renderer.js ← Canvas drawing for swatch images
test/ ← Vitest test suite
Testing
npm test # run all tests
npm run test:watch # watch mode




