关于 color-engine-mcp
color-engine-mcp 为设计与开发工作流提供纯内存 CSS 颜色工具,包括解析、转换、对比度计算、色域映射和色阶生成。它支持 WCAG 2.1,并可选计算 APCA 对比度。
主要功能
- 01解析并转换 CSS 颜色格式
- 02计算 WCAG 2.1 与可选 APCA 对比度
- 03将宽色域颜色映射到 sRGB
- 04生成色阶并输出设计令牌
- 05求解满足对比度目标的前景色
适用场景
- 01检查文字与背景的可访问性对比度
- 02将 P3 或 OKLCH 颜色映射到 sRGB
- 03为基础色生成设计系统色阶
- 04查找满足 WCAG 目标的文字颜色
MCP 相关
作为标准 stdio MCP 服务,它向 MCP 客户端提供六种颜色工具。所有操作仅在内存中运行,不进行网络 I/O 或文件系统写入。
配置
本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。
工具
6收录的工具列表 · 更新于 2 周前
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| input | string | 是 | CSS 颜色字符串,例如 "tomato"、"#ff6347"、"oklch(0.6 0.18 27)"(最多 256 个字符) |
详细介绍
color-engine MCP Server
一个 Model Context Protocol(MCP)服务,提供 6 种 CSS 颜色工具:解析、转换、色域映射、WCAG 对比度(可选 APCA)、色调/阴影渐变生成(可选设计令牌输出),以及对比度目标求解。所有工具都纯粹在内存中运行,不进行网络 I/O,也不写入文件系统。
| 工具 | 用途 |
|---|---|
| parse_color | 将任意 CSS 颜色字符串解析为十六进制、RGB、OKLCH 和色域信息 |
| convert_color | 将颜色转换为规范的 hex / rgb / hsl / oklch 字符串 |
| contrast | WCAG 2.1 对比度及等级标记,可选提供 APCA Lc |
| gamut_map | 通过感知式 OKLCH 色度降低,将宽色域颜色映射到 sRGB |
| generate_ramp | 生成由浅色到深色的渐变,提供每个色块的对比度信息和设计令牌输出 |
| solve_for_contrast | 查找满足相对于背景的 WCAG 对比度目标的前景色 |
本 README 中显示的所有 JSON 输出,均为通过 MCP Inspector CLI(
npx @modelcontextprotocol/inspector --cli)从构建后的服务中捕获的真实响应。
安装
需要 Node.js >= 20。npm 包提供了 color-engine-mcp 可执行文件,因此无需克隆或构建,客户端可通过 npx 启动它。这是一个标准的 stdio MCP 服务,仅使用 tools 原语,因此任何兼容 MCP 的客户端都可以用相同方式注册:将启动命令指定给客户端,然后由客户端自行启动并管理进程。
对于 Claude Code,只需运行一条命令即可注册服务:
claude mcp add color-engine -- npx -y color-engine-mcp
(默认作用范围是本地项目;添加 --scope user 可将其注册到你的所有项目,添加 --scope project 则会写入共享的 .mcp.json。)
大多数其他使用 JSON 配置的客户端都可以直接使用以下配置格式:
{
"mcpServers": {
"color-engine": {
"command": "npx",
"args": ["-y", "color-engine-mcp"]
}
}
}
以下是一些常见客户端中配置文件的位置:
| 客户端 | 配置文件 |
|---|---|
| Claude Code | 项目根目录中的 .mcp.json |
| Claude Desktop | claude_desktop_config.json(例如 macOS 上的 ~/Library/Application Support/Claude/) |
| Cursor | .cursor/mcp.json(每个项目单独配置)或 ~/.cursor/mcp.json(全局配置) |
| VS Code | .vscode/mcp.json — VS Code 使用 servers 而不是 mcpServers 作为顶层键 |
| Windsurf | ~/.codeium/windsurf/mcp_config.json |
其他客户端(Zed、JetBrains、基于 MCP SDK 构建的自定义 Agent 等)仅在文件位置以及偶尔使用的顶层键名称上有所不同,请参阅它们的 MCP 服务注册文档。
已作为 io.github.aimsise/color-engine-mcp 列入官方 MCP Registry。
连接后,可以尝试向你的 Agent 提出以下请求:
- “从 #3b82f6 生成一个包含 11 个步骤的 Tailwind 渐变”
- “为背景色 #1e293b 查找对比度为 4.5:1 的无障碍文本颜色”
如需从克隆的仓库运行,请参阅开发。
颜色输入处理
每个工具都会通过同一个解析边界处理颜色字符串,因此以下规则统一适用。
接受的格式。 culori 能解析的任何 CSS 颜色字符串,包括命名颜色、十六进制(#rgb、#rgba、#rrggbb、#rrggbbaa)、rgb()/rgba()、hsl()/hsla() 和 oklch(),以及广色域 / CSS Color 4 格式:
color(display-p3 r g b)lab(L a b)/lch(L C h)oklab(L a b)hwb(h w b)color(rec2020 r g b)color(a98-rgb r g b)color(xyz-d65 x y z)
超出 sRGB 的广色域输入可以正常解析,并会报告 inGamut: false:
{ "input": "color(display-p3 1 0 0)" }
{
"hex": "#ff0000",
"rgb": { "r": 255, "g": 0, "b": 0 },
"oklch": { "l": 0.6485740751442981, "c": 0.2994852863383699, "h": 28.958132730803953 },
"inGamut": false
}
空白字符。 解析前会去除首尾空白字符——" #ff0000 " 会被解析为 #ff0000。
CSS Color 4 通道钳制(仅限旧式色彩空间)。 按照 CSS Color 4 的规定,十六进制 / rgb() / hsl() 输入中超出范围的通道会在解析边界处被钳制:rgb(-50 0 0) 的效果与 rgb(0 0 0) 完全相同,hsl() 的饱和度 / 明度也会钳制(色相会循环)。其他模式(oklch()、lab()、color(display-p3 …) 等)中的输入不会被钳制——其超出色域的值会原样传递,这正是 gamut_map 有用之处。
none 通道。 所有六个工具都会将 CSS Color 4 的 none 通道规范化为 0——oklch(0.5 none 30) 的效果与 oklch(0.5 0 30) 完全相同。
分量大小。 如果可解析的分量数值异常巨大(超过 1e6,例如 oklch(0.5 1e30 30)),则会被拒绝,并返回 COMPONENT_OUT_OF_RANGE——其中两个工具(gamut_map、solve_for_contrast)对此的处理方式有所不同;请参阅错误代码表。实际的超出色域值比这一限制低许多个数量级,因此不会受到影响。
Alpha 策略。 contrast 和 solve_for_contrast 会拒绝半透明颜色——任何显式设置的 alpha < 1,包括 rgba()/hsla() 函数式 alpha 和 4 / 8 位十六进制(#00000080)——并返回 ALPHA_UNSUPPORTED,因为半透明图层的实际颜色取决于未知的背景;请先将颜色与背景合成。其他工具都接受半透明输入,并且仅忽略 alpha 通道(计算时使用不透明颜色;输出不会包含 alpha 分量)。
长度上限。 去除首尾空白字符后,超过 256 个字符的颜色字符串会被拒绝,并返回 INPUT_TOO_LONG。
工具
parse_color
解析任意 CSS 颜色字符串,并返回十六进制、RGB、OKLCH 和色域信息。
输入架构
| 字段 | 类型 | 必填 | 描述 |
|---|---|---|---|
| input | 字符串 | 是 | 任意 CSS 颜色字符串,例如 "#ff0000"、"red"、"oklch(0.6 0.2 30)" |
{ "input": "#ff0000" } 的输出
{
"hex": "#ff0000",
"rgb": { "r": 255, "g": 0, "b": 0 },
"oklch": { "l": 0.6279553639214311, "c": 0.2576833038053608, "h": 29.233880279627854 },
"inGamut": true
}
rgb 通道是经过 sRGB 钳制的 0–255 整数投影(与 hex 一致)。对于超出色域的输入(例如广色域 oklch(...)),通道会被钳制到 [0, 255],而不会报告原始超范围值——可使用 inGamut 标志判断输入是否超出 sRGB 色域。与之相反,oklch 块是输入的 原始(无损、未舍入) OKLCH,且对于非彩色,oklch.h 为 0。
convert_color
将 CSS 颜色字符串转换为规范的 hex、rgb、hsl 或 oklch 格式字符串。
输入架构
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| input | string | 是 | 任意 CSS 颜色字符串 |
| to | string | 是 | 目标格式:"hex"、"rgb"、"hsl" 或 "oklch" |
{ "input": "#ff0000", "to": "oklch" } 的输出
{ "result": "oklch(0.62796 0.25768 29.23)" }
超出色域输入的原始值与钳制值
对于超出 sRGB 色域的颜色,目标格式会有所不同:
to: "oklch"返回原始、无损的 OKLCH 三元组,因此超出色域的颜色可以忠实地往返转换。例如,{ "input": "oklch(0.7 0.4 30)", "to": "oklch" }→"oklch(0.70000 0.40000 30.00)"。to: "hex"、"rgb"和"hsl"均基于钳制到 sRGB 色域的投影值派生,并返回色域内的近似值。例如,{ "input": "oklch(0.7 0.4 30)", "to": "rgb" }→"rgb(255, 0, 0)"。rgb通道值为[0, 255]范围内的整数。
(L/C 保留 5 位小数,H 保留 2 位小数,可确保在整个 sRGB 立方体范围内精确往返转换为 hex。)
contrast
计算两个完全不透明的 CSS 颜色字符串之间的 WCAG 2.1 对比度,并返回等级标志。还可选择计算 APCA Lc 值(参见 APCA(可选的感知对比度))。
输入架构
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| a | string | 是 | 第一个 CSS 颜色字符串(在 APCA 中视为文本/前景色),例如 "#000000" |
| b | string | 是 | 第二个 CSS 颜色字符串(在 APCA 中视为背景色),例如 "#ffffff" |
| apca | boolean | 否 | 为 true 时,额外返回文本 a 在背景 b 上的有符号 APCA-W3 apcaLc |
{ "a": "#000000", "b": "#ffffff" } 的输出
{
"ratio": 21,
"aaNormal": true,
"aaLarge": true,
"aaaNormal": true,
"aaaLarge": true
}
ratio 是保留 2 位小数的显示值。四个等级布尔值根据未舍入的原始比率计算(因此,原始值接近边界的 4.4999 虽显示为 4.50,仍会得到 aaNormal: false)。
WCAG 2.1 等级阈值:
aaNormal/aaLarge要求比率 ≥ 4.5 / ≥ 3.0aaaNormal/aaaLarge要求比率 ≥ 7.0 / ≥ 4.5
错误。 无法解析的字符串会产生带参数名称的错误:对于 a,为 PARSE_FAILED: could not parse the foreground color;对于 b,为 PARSE_FAILED: could not parse the background color。半透明输入会产生 ALPHA_UNSUPPORTED(参见 Alpha 策略)。
APCA(可选的感知对比度)
传入 apca: true 可额外获取 apcaLc,即文本 a 在背景 b 上的有符号 APCA-W3(SAPC-4g)亮度对比度 Lc,结果保留 2 位小数(对于 APCA,参数顺序很重要,不同于对称的 WCAG 比率)。符号表示极性:深色文字在浅色背景上为正值,浅色文字在深色背景上为负值;比较时使用 |Lc| 的绝对值。
{ "a": "#1a1a1a", "b": "#ffffff", "apca": true }
{ "ratio": 17.4, "aaNormal": true, "aaLarge": true, "aaaNormal": true, "aaaLarge": true, "apcaLc": 104.27 }
反转颜色对会使符号翻转:{ "a": "#ffffff", "b": "#1a1a1a", "apca": true } → "apcaLc": -106.55(WCAG ratio 仍为 17.4,因为 WCAG 比率具有对称性)。
常见引用的 APCA 指南阈值(按 |Lc|):
| |Lc| | 常见指南用途 |
|---|---|
| 45 | 大号/粗体文字的最低值 |
| 60 | 其他内容文字的最低值 |
| 75 | 正文文字 |
| 90 | 正文文字的理想值 |
免责声明: APCA 是 WCAG 3 的候选方法,尚未成为规范性 WCAG 标准;如需声明符合标准,请使用 WCAG 2.1 等级标志。
gamut_map
通过感知式 OKLCH 色度缩减,将任意 CSS 颜色字符串映射到 sRGB 色域。适用于将广色域颜色(P3、Rec2020、任意 OKLCH)转换为可显示的 sRGB 颜色。
输入架构
| 字段 | 类型 | 必填 | 描述 |
|---|---|---|---|
| input | string | 是 | 任意 CSS 颜色字符串,例如 "oklch(0.6 0.4 30)" |
输出:对于 { "input": "oklch(0.6 0.4 30)" }
{
"hex": "#f70000",
"oklch": { "l": 0.6137129506655941, "c": 0.25183888824211537, "h": 29.233880279628004 },
"clamped": true
}
当输入超出 sRGB 色域且必须进行映射时,clamped 为 true。返回的 oklch 是映射后色域内结果的原始 OKLCH 值(感知式缩减了色度),而非原始输入的值。
恒等性与幂等性。 对于已位于 sRGB 色域内的输入,将原样返回:clamped 为 false,且 hex 与输入的规范十六进制颜色完全一致(例如 { "input": "#3b82f6" } → { "hex": "#3b82f6", …, "clamped": false })。此工具也具有幂等性:其输出的十六进制颜色始终位于色域内,因此将结果再次输入时会走恒等路径,并返回相同的十六进制颜色。
generate_ramp
根据基础 CSS 颜色生成从浅色到深色的色阶。返回按顺序排列的色板,每个色板都包含十六进制颜色、OKLCH、相对于白色和黑色的 WCAG 对比度,以及色域标志。还可选择将色阶输出为设计令牌(参见设计令牌)。
输入架构
| 字段 | 类型 | 必填 | 描述 |
|---|---|---|---|
| base | string | 是 | 任意 CSS 颜色字符串,例如 "#3b82f6" |
| steps | integer | 否 | 色板数量(2–512)。默认值:5 |
| lightnessMin | number | 否 | 亮度下限(OKLCH L,0–1)。默认值:0.05 |
| lightnessMax | number | 否 | 亮度上限(OKLCH L,0–1)。默认值:0.97 |
| deltaL | number | 否 | 以基础颜色的 L 为中心的总亮度跨度——端点为基础 L ± deltaL/2(覆盖固定范围) |
| tokenFormat | string | 否 | "tailwind" 或 "css-variables"——指定后,输出会包含一个 tokens 字符串 |
| tokenName | string | 否 | 输出令牌的基础名称(字母/数字/连字符,必须以字母开头,长度为 1–64 个字符)。默认值:"color" |
输出:对于 { "base": "#3b82f6", "steps": 5 }(显示 5 个色板中的第一个)
{
"swatches": [
{
"step": 0,
"hex": "#f5f5f5",
"oklch": { "l": 0.97, "c": 0, "h": 259.81 },
"vsWhite": { "ratio": 1.09, "tier": "FAIL" },
"vsBlack": { "ratio": 19.26, "tier": "AAA" },
"inGamut": true
}
]
}
色板索引从零开始:色板按亮度从高到低排列(step: 0 最浅,step: steps - 1 最深),OKLCH 亮度严格递减。(上方完整的 5 个色板响应中,位于色域内的基础颜色 #3b82f6 会在 step: 2 原样出现——色阶会将最接近基础颜色亮度的步骤作为锚点。)
每个色板都包含 vsWhite/vsBlack,其结构均为 { ratio, tier },其中 ratio 是相对于白色/黑色的 WCAG 对比度,tier 为 "AAA"(原始比率 ≥ 7.0)、"AA"(≥ 4.5)或 "FAIL"。
显示精度处理。 色板数值会按显示精度取整:对比度 ratio 保留 2 位小数,oklch.l/oklch.c 保留 5 位小数,oklch.h 保留 2 位小数。tier 分类仍基于未取整的原始比率(原始值为 6.9999 时会显示为 7.00,但分类为 "AA")。
阈值附近的等级量化。 每个色板的对比度均根据其 8 位 hex 计算(这是权威的显示颜色),因此在距离等级阈值(3.0/4.5/7.0)约 0.01 的范围内,量化可能导致等级与浮点精度计算的结果不同。如果需要确保留有余量,请将目标比率设为明显高于阈值的值(例如,按 4.6 而非 4.5 求解)。
验证规则
数值约束已在工具架构中声明,因此 SDK 会在处理程序运行前拒绝超出范围的调用——通过 MCP 调用时,这些拒绝会作为 SDK 的验证错误返回,而非下方列出的工具级错误代码(参见架构层与工具层的强制校验):
steps必须是[2, 512]范围内的整数 → 否则返回STEPS_OUT_OF_RANGE。- 如果提供
deltaL,则必须是大于0的有限数值 → 否则返回INVALID_DELTA_L。 - 解析后的明度范围必须满足
lightnessMin < lightnessMax(端点会先被限制在[0, 1]范围内)→ 否则返回INVALID_LIGHTNESS_RANGE。 - 基础颜色的 OKLCH 色度必须 ≤ 100 → 否则返回
BASE_CHROMA_OUT_OF_RANGE。 - 如果
base解析失败,则转发解析错误代码(PARSE_FAILED、INPUT_TOO_LONG、COMPONENT_OUT_OF_RANGE或NON_FINITE_COMPONENTS)。
负载大小。 steps: 512 的调用会在工具结果中返回约 200 KB 的 JSON(美化打印后约为 280 KB)。在 LLM 上下文中,建议使用较小的步数(5–11)——只需极少的令牌,就能覆盖几乎所有设计系统的使用场景。
关于 swatch.oklch 与 swatch.hex 的说明
每个色板报告的 oklch 是经过 目标明度/色度限制 的投影(目标明度下将色度降低至 sRGB 色域内),这能保持整个渐变中的明度严格单调。因此,它可能与根据 swatch.hex 计算出的精确 OKLCH 略有不同(ΔL 最大约为 0.013)。这是有意为之的权衡:优先保证明度单调,而非精确还原十六进制颜色;hex 仍是权威的显示颜色。
设计令牌
传入 tokenFormat(以及可选的 tokenName),即可在 swatches 之外额外获得一个 tokens 字符串。
- 步数恰好为 11 时,令牌键使用 Tailwind 标准刻度
50, 100, 200, …, 900, 950(色板0——最浅色——对应50)。 - 其他任何步数都使用从零开始的
step索引作为键。
tokenFormat: "tailwind" — 美化打印的 JSON 对象字符串。对于 { "base": "#3b82f6", "steps": 11, "tokenFormat": "tailwind", "tokenName": "blue" },tokens 输出如下:
{
"blue": {
"50": "#f5f5f5",
"100": "#c1d8fe",
"200": "#8fbaff",
"300": "#629bfa",
"400": "#3b82f6",
"500": "#155ecf",
"600": "#0043ab",
"700": "#002b80",
"800": "#001750",
"900": "#000721",
"950": "#000000"
}
}
tokenFormat: "css-variables" — 一个 :root 块。对于 { "base": "#3b82f6", "steps": 5, "tokenFormat": "css-variables", "tokenName": "brand" },tokens 输出如下:
:root {
--brand-0: #f5f5f5;
--brand-1: #78abfe;
--brand-2: #3b82f6;
--brand-3: #002068;
--brand-4: #000000;
}
tokenName 会在架构边界处进行验证(/^[a-z][a-z0-9-]*$/i,1–64 个字符),因此始终可以安全地嵌入 CSS 自定义属性名称或 JSON 键中。
solve_for_contrast
查找满足背景色一个或多个 WCAG 2.1 对比度目标的前景色。通过二分搜索 OKLCH 明度(色相和色度保持不变)。
输入架构
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| background | string | 是 | 背景 CSS 颜色字符串 |
| target | number | 二选一 | 单个 WCAG 2.1 对比度目标(例如 4.5、7) |
| targets | number[] | 二选一 | 多个对比度目标(1–50 项;不接受空数组) |
| prefer | "lighter","darker","either" | 否 | 搜索方向。默认值:"either" |
| hue | number | 否 | 搜索期间保持不变的固定 OKLCH 色相(度) |
| chroma | number | 否 | 搜索期间保持不变的固定 OKLCH 色度(≥ 0) |
hue/chroma 默认值——传入 chroma 以保持饱和度
省略时,chroma 默认为背景自身的色度,hue 默认为背景的色相(若背景为无彩色,则默认为 0)。有两点值得注意:
- 仅传入
hue不会增加饱和度。 对于无彩色背景(白色/灰色/黑色),默认色度为0,因此无论传入什么色相,结果都是无彩色灰色。对于#ffffff,当target: 4.5时:{ "hue": 260 }→{ "met": true, "color": "#767676", "ratio": 4.54 }(与完全不传色相时得到的灰色相同),而{ "hue": 260, "chroma": 0.15 }→{ "met": true, "color": "#4075cf", "ratio": 4.5 }(真正的蓝色)。 - 仅传入
chroma时,色相固定为背景的色相(若背景为无彩色,则为0)。如果 sRGB 色域要求降低色度,系统仍可能按明度降低固定的色度。
target 与 targets 的优先级及响应结构
传入单个 target 或 targets(数组)。如果同时传入两者,则以 targets 为准,并忽略单个 target。两种模式会返回不同的响应结构:
- 单个
target→{ met, color, ratio }(另可能包含nearMiss标记)。 targets数组 →{ results: [ { met, color, ratio, nearMiss? }, ... ] },按请求顺序为每个目标返回一项。
已注册的输出架构是两种结构的全可选超集;每个响应只会填充其中一种结构。
输入 { "background": "#ffffff", "target": 4.5, "prefer": "darker" } 的输出
{ "met": true, "color": "#767676", "ratio": 4.54 }
输入 { "background": "#1e293b", "targets": [4.5, 7] } 的输出
{
"results": [
{ "met": true, "color": "#8290a6", "ratio": 4.52 },
{ "met": true, "color": "#a6b5cc", "ratio": 7.04 }
]
}
当 met 通过接近上限的容差判定时,可能会出现 nearMiss: true(无论是在单个结果中,还是在 results 内的某一项中):在搜索方向上可达到的最佳原始对比度与目标相差不超过 0.03,且低于目标。使用方向性 prefer("lighter"/"darker")时,只搜索对应的一侧,因此另一侧仍可能严格满足目标;使用 "either" 模式时,始终优先选择严格满足目标的一侧,所以只有在两侧都无法严格满足目标时,才会出现 nearMiss。
错误。 无法解析的背景会返回带有 PARSE_FAILED: could not parse the background color 的 isError——单个 target 和 targets 两种路径均如此。半透明背景会产生 ALPHA_UNSUPPORTED(请参阅 Alpha policy)。
错误处理
每个工具都是全定义的:格式错误或超出范围的输入不会导致服务崩溃,也不会泄露堆栈跟踪。发生故障时,工具会返回一个 isError: true 的 MCP 结果,其中包含一个文本内容块,文本遵循统一的 <CODE>: message 格式,<CODE> 是来自封闭集合的 UPPER_SNAKE_CASE 值。消息是静态且安全的字符串,不会包含原始输入、文件路径、堆栈或库内部信息。出错时,工具不会设置 structuredContent。
错误文本示例:
PARSE_FAILED: could not parse the provided color string
任何意外内部故障的兜底错误始终为:
INTERNAL_ERROR: unexpected internal error
架构层与工具层的强制校验
工具的 zod 输入架构中声明的约束,由 MCP SDK 在工具处理程序运行前强制校验——处理程序不会执行,结果也不包含 structuredContent。在 MCP SDK 1.29 中,这种拒绝会以内嵌方式返回为带错误标记的工具结果(isError: true),其文本以 MCP error -32602: Input validation error: … 开头——该文本由 SDK 生成,不受上述统一 CODE: message 格式和不包含内部信息的保证约束(这些保证仅适用于工具层错误);未来的 SDK 版本可能会改为返回真正的 -32602 协议错误,因此不要针对具体的返回方式或措辞进行模式匹配。对应的工具层错误代码会予以保留,作为纵深防御,供直接调用库的调用方使用(即导入处理程序或 src/lib 函数并绕过 SDK 校验的代码)。由架构强制校验的约束:
INPUT_TOO_LONG— 每个颜色字符串字段都声明了.max(256)。STEPS_OUT_OF_RANGE—steps声明为整数,范围为2..512。INVALID_DELTA_L—deltaL声明为有限且> 0。TOO_MANY_TARGETS—targets声明了.max(50)。EMPTY_TARGETS—targets声明了.min(1)(空数组会在处理程序运行前被拒绝)。INVALID_TARGET、INVALID_CHROMA和INVALID_HUE所依据的有限性/符号约束同样由架构声明(而且 JSON 本身无法表示非有限数字)。
错误代码
| 代码 | 含义 |
|---|---|
INPUT_TOO_LONG | 颜色字符串超过了 256 个字符的上限(用于防范 DoS,在解析前执行;由 schema 强制执行)。 |
PARSE_FAILED | 无法将提供的颜色字符串解析为任何 CSS 颜色。在 contrast 和 solve_for_contrast 中,静态消息会指出失败的参数:could not parse the foreground color / could not parse the background color。 |
COMPONENT_OUT_OF_RANGE | 可解析的颜色分量幅度异常(> 1e6),例如 oklch(0.5 1e30 30)——由 parse_color、convert_color、contrast 和 generate_ramp 返回。例外:gamut_map 会以 CHROMA_OUT_OF_RANGE 拒绝相同输入(其色度范围检查会先触发),而 solve_for_contrast 会将其报告为带参数名称的 PARSE_FAILED。 |
ALPHA_UNSUPPORTED | contrast / solve_for_contrast 收到了半透明颜色(显式 alpha < 1,包括 4 位/8 位十六进制颜色)。静态消息:contrast requires fully opaque colors (alpha = 1); composite the color over its backdrop first。 |
NON_FINITE_COMPONENTS | 颜色解析结果包含非有限的 RGB/OKLCH 分量(例如色度溢出)。 |
NON_FINITE_LUMINANCE | 对比度计算产生了非有限的亮度值。 |
NON_FINITE_OKLCH_COMPONENTS | 色域映射期间,OKLCH 明度/色度为非有限值。 |
NULL_OKLCH_CHANNELS | 色域映射期间,OKLCH 通道解析为 null。 |
NON_FINITE_OKLCH_HUE | 色域映射期间,彩色的 OKLCH 色相为非有限值。 |
CHROMA_OUT_OF_RANGE | OKLCH 色度超过了色域映射器支持的最大值(100)。对于 oklch(0.5 1e30 30),gamut_map 会返回此错误——而不是 COMPONENT_OUT_OF_RANGE。 |
GAMUT_MAP_COLLAPSE | 色域映射导致通道变为 null/非有限值。 |
STEPS_OUT_OF_RANGE | generate_ramp 的 steps 不是 [2, 512] 范围内的整数(由 schema 强制执行)。 |
INVALID_LIGHTNESS_RANGE | generate_ramp 解析出的 lightnessMin >= lightnessMax。 |
INVALID_DELTA_L | generate_ramp 的 deltaL 不是大于 0 的有限数值(由 schema 强制执行)。 |
BASE_CHROMA_OUT_OF_RANGE | generate_ramp 的基础 OKLCH 色度超过了 100。 |
MISSING_BACKGROUND | 调用 solve_for_contrast 时未提供 background。 |
MISSING_TARGET | 调用 solve_for_contrast 时既未提供 target,也未提供 targets。 |
EMPTY_TARGETS | solve_for_contrast 的 targets 是空数组(通过 .min(1) 由 schema 强制执行)。 |
TOO_MANY_TARGETS | solve_for_contrast 的 targets 超过了 50 项(由 schema 强制执行)。 |
INVALID_TARGET | solve_for_contrast 的某个 target 不是大于等于 0 的有限数值。 |
INVALID_CHROMA | solve_for_contrast 的 chroma 不是大于等于 0 的有限数值。 |
INVALID_HUE | solve_for_contrast 的 hue 不是有限数值。 |
INVALID_GEOMETRY | solve_for_contrast 解析出的色相/色度为非有限值,或色度为负数。通过 MCP 调用时,显式指定的 hue/chroma 会先经过 schema 验证,因此此错误代码实际上是为直接调用库的情况提供的纵深防御。 |
INTERNAL_ERROR | 用于捕获任何意外内部错误。 |
工具注解和服务元数据
六个工具都注册了 MCP 工具注解,声明它们为只读且无副作用(readOnlyHint: true、idempotentHint: true、destructiveHint: false、openWorldHint: false),并附有便于阅读的 title。服务本身注册了一个 title("Color Engine")和一段 instructions 字符串,其中概述了六个工具、优先采用 OKLCH 的设计、舍入规则,以及供连接的客户端使用的错误格式(特别是引导 Agent 在需要达到某个比率而不只是测量比率时使用 solve_for_contrast)。每个工具结果都包含 structuredContent 及与之等效的纯文本内容块,因此无法处理结构化输出的客户端也不会遗漏任何信息。
准确性与性能
准确性(均已在测试套件中验证——运行 npm test 即可复现):
- WCAG 比率通过双重基准验证:一套基于原理实现的 WCAG 2.1 亮度算法,以及独立的
colorjs.io实现;通过跨越各阈值(3.0 / 4.5 / 7.0)的颜色对检查等级分类。 - 在覆盖 sRGB 色域的数千个确定性网格点和带种子的模糊测试样本中,hex ↔ OKLCH 往返转换的字节结果完全一致。
- APCA 与独立的
colorjs.ioAPCA 实现的差异不超过 0.1 Lc,两种极性下均如此。 - gamut_map 对色域内颜色返回逐位完全相同的结果,且对自身输出具有幂等性。
性能(每次调用的中位数,使用 Node v25.9.0 通过 npm run build && npm run bench 测得——仅供参考,不作保证):
| 调用 | 中位数(毫秒) |
|---|---|
parse_color | 0.005 |
convert_color | 0.002 |
contrast | 0.004 |
gamut_map(输入超出色域) | 0.015 |
generate_ramp(steps=5) | 0.037 |
solve_for_contrast(单个目标) | 0.067 |
generate_ramp(steps=512,最坏情况) | 3.3 |
solve_for_contrast(50 个目标,最坏情况) | 2.7 |
冷启动(从启动进程到收到 initialize 响应)中位数约为 135 ms。
开发
构建
npm install
npm run build
编译后的服务位于 dist/server.js。
从源代码运行。 若要让 MCP 客户端指向本地克隆,而非 npm 包,请先构建,然后使用:
{
"mcpServers": {
"color-engine": {
"command": "node",
"args": ["/absolute/path/to/color-engine-mcp/dist/server.js"]
}
}
}
将 /absolute/path/to/color-engine-mcp/dist/server.js 替换为克隆中 dist/server.js 的实际路径。
测试
npm test
运行完整测试套件(包括通过 npx @modelcontextprotocol/inspector --cli 运行的 MCP Inspector CLI 集成测试)。pretest 脚本会自动运行 tsc,确保在 Inspector 测试运行前 dist/server.js 是最新的。
基准测试
npm run build
npm run bench
对构建后的 dist/ 处理程序运行 scripts/bench.mjs(每个用例预热 20 次、计时 200 次,并报告中位数)——这是 准确性与性能 中数据的来源。
更新日志
请参阅 CHANGELOG.md 查看发布历史。
许可证
MIT——请参阅 LICENSE。
评论
一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。