@trishchuk/coolors-mcp 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/settings.local.json +39 -0
- package/.env +2 -0
- package/.github/ISSUE_TEMPLATE/bug_report.md +73 -0
- package/.github/ISSUE_TEMPLATE/feature_request.md +71 -0
- package/.github/pull_request_template.md +97 -0
- package/.github/workflows/ci.yml +127 -0
- package/.github/workflows/deploy-docs.yml +56 -0
- package/.github/workflows/release.yml +99 -0
- package/.mcp.json +12 -0
- package/.prettierignore +1 -0
- package/CLAUDE.md +201 -0
- package/DOCUMENTATION.md +274 -0
- package/GEMINI.md +54 -0
- package/LICENSE +21 -0
- package/README.md +401 -0
- package/demo/content_based_color.png +0 -0
- package/demo/music-player.html +621 -0
- package/demo/podcast-player.html +903 -0
- package/dist/bin/coolors-mcp.d.ts +1 -0
- package/dist/bin/coolors-mcp.js +154 -0
- package/dist/bin/coolors-mcp.js.map +1 -0
- package/dist/bin/server.d.ts +1 -0
- package/dist/bin/server.js +3292 -0
- package/dist/bin/server.js.map +1 -0
- package/dist/chunk-IQ7NN26V.js +114 -0
- package/dist/chunk-IQ7NN26V.js.map +1 -0
- package/dist/chunk-P3ARRKLS.js +1214 -0
- package/dist/chunk-P3ARRKLS.js.map +1 -0
- package/dist/color/index.d.ts +716 -0
- package/dist/color/index.js +153 -0
- package/dist/color/index.js.map +1 -0
- package/dist/coolors-mcp.d.ts +136 -0
- package/dist/coolors-mcp.js +7 -0
- package/dist/coolors-mcp.js.map +1 -0
- package/docs/.vitepress/cache/deps/@braintree_sanitize-url.js +93 -0
- package/docs/.vitepress/cache/deps/@braintree_sanitize-url.js.map +7 -0
- package/docs/.vitepress/cache/deps/_metadata.json +127 -0
- package/docs/.vitepress/cache/deps/chunk-BUSYA2B4.js +9 -0
- package/docs/.vitepress/cache/deps/chunk-BUSYA2B4.js.map +7 -0
- package/docs/.vitepress/cache/deps/chunk-JD3CXNQ6.js +12683 -0
- package/docs/.vitepress/cache/deps/chunk-JD3CXNQ6.js.map +7 -0
- package/docs/.vitepress/cache/deps/chunk-SYPOPCWC.js +9719 -0
- package/docs/.vitepress/cache/deps/chunk-SYPOPCWC.js.map +7 -0
- package/docs/.vitepress/cache/deps/cytoscape-cose-bilkent.js +4710 -0
- package/docs/.vitepress/cache/deps/cytoscape-cose-bilkent.js.map +7 -0
- package/docs/.vitepress/cache/deps/cytoscape.js +30278 -0
- package/docs/.vitepress/cache/deps/cytoscape.js.map +7 -0
- package/docs/.vitepress/cache/deps/dayjs.js +285 -0
- package/docs/.vitepress/cache/deps/dayjs.js.map +7 -0
- package/docs/.vitepress/cache/deps/debug.js +468 -0
- package/docs/.vitepress/cache/deps/debug.js.map +7 -0
- package/docs/.vitepress/cache/deps/package.json +3 -0
- package/docs/.vitepress/cache/deps/prismjs.js +1466 -0
- package/docs/.vitepress/cache/deps/prismjs.js.map +7 -0
- package/docs/.vitepress/cache/deps/prismjs_components_prism-bash.js +228 -0
- package/docs/.vitepress/cache/deps/prismjs_components_prism-bash.js.map +7 -0
- package/docs/.vitepress/cache/deps/prismjs_components_prism-javascript.js +142 -0
- package/docs/.vitepress/cache/deps/prismjs_components_prism-javascript.js.map +7 -0
- package/docs/.vitepress/cache/deps/prismjs_components_prism-json.js +27 -0
- package/docs/.vitepress/cache/deps/prismjs_components_prism-json.js.map +7 -0
- package/docs/.vitepress/cache/deps/prismjs_components_prism-python.js +65 -0
- package/docs/.vitepress/cache/deps/prismjs_components_prism-python.js.map +7 -0
- package/docs/.vitepress/cache/deps/prismjs_components_prism-typescript.js +53 -0
- package/docs/.vitepress/cache/deps/prismjs_components_prism-typescript.js.map +7 -0
- package/docs/.vitepress/cache/deps/prismjs_components_prism-yaml.js +73 -0
- package/docs/.vitepress/cache/deps/prismjs_components_prism-yaml.js.map +7 -0
- package/docs/.vitepress/cache/deps/vitepress___@vue_devtools-api.js +4507 -0
- package/docs/.vitepress/cache/deps/vitepress___@vue_devtools-api.js.map +7 -0
- package/docs/.vitepress/cache/deps/vitepress___@vueuse_core.js +584 -0
- package/docs/.vitepress/cache/deps/vitepress___@vueuse_core.js.map +7 -0
- package/docs/.vitepress/cache/deps/vitepress___@vueuse_integrations_useFocusTrap.js +1146 -0
- package/docs/.vitepress/cache/deps/vitepress___@vueuse_integrations_useFocusTrap.js.map +7 -0
- package/docs/.vitepress/cache/deps/vitepress___mark__js_src_vanilla__js.js +1667 -0
- package/docs/.vitepress/cache/deps/vitepress___mark__js_src_vanilla__js.js.map +7 -0
- package/docs/.vitepress/cache/deps/vitepress___minisearch.js +1814 -0
- package/docs/.vitepress/cache/deps/vitepress___minisearch.js.map +7 -0
- package/docs/.vitepress/cache/deps/vue.js +344 -0
- package/docs/.vitepress/cache/deps/vue.js.map +7 -0
- package/docs/.vitepress/components/ClientGrid.vue +125 -0
- package/docs/.vitepress/components/CodeBlock.vue +231 -0
- package/docs/.vitepress/components/ConfigModal.vue +477 -0
- package/docs/.vitepress/components/DiagramModal.vue +528 -0
- package/docs/.vitepress/components/TroubleshootingModal.vue +472 -0
- package/docs/.vitepress/config.js +162 -0
- package/docs/.vitepress/theme/FundingLayout.vue +251 -0
- package/docs/.vitepress/theme/Layout.vue +134 -0
- package/docs/.vitepress/theme/components/AdBanner.vue +317 -0
- package/docs/.vitepress/theme/components/AdPlaceholder.vue +78 -0
- package/docs/.vitepress/theme/components/FundingEffects.vue +322 -0
- package/docs/.vitepress/theme/components/FundingHero.vue +345 -0
- package/docs/.vitepress/theme/components/SupportSection.vue +511 -0
- package/docs/.vitepress/theme/custom-app.css +339 -0
- package/docs/.vitepress/theme/custom.css +699 -0
- package/docs/.vitepress/theme/index.js +25 -0
- package/docs/README.md +198 -0
- package/docs/concepts/accessibility.md +473 -0
- package/docs/concepts/color-spaces.md +222 -0
- package/docs/concepts/distance-metrics.md +384 -0
- package/docs/concepts/hct.md +261 -0
- package/docs/concepts/image-analysis.md +396 -0
- package/docs/concepts/material-design.md +306 -0
- package/docs/concepts/theme-matching.md +399 -0
- package/docs/examples/basic-colors.md +490 -0
- package/docs/examples/creating-themes.md +898 -0
- package/docs/examples/css-refactoring.md +824 -0
- package/docs/examples/image-extraction.md +882 -0
- package/docs/getting-started.md +366 -0
- package/docs/index.md +190 -0
- package/docs/installation.md +157 -0
- package/docs/tools/README.md +234 -0
- package/docs/tools/accessibility.md +614 -0
- package/docs/tools/color-operations.md +374 -0
- package/docs/tools/image-extraction.md +624 -0
- package/docs/tools/material-design.md +347 -0
- package/docs/tools/theme-matching.md +552 -0
- package/eslint.config.ts +14 -0
- package/examples/theme-matching.md +113 -0
- package/jsr.json +7 -0
- package/mcp-config.json +8 -0
- package/note.md +35 -0
- package/package.json +122 -0
- package/research_results.md +53 -0
- package/src/bin/coolors-mcp.ts +194 -0
- package/src/bin/server.ts +61 -0
- package/src/color/__tests__/conversions-argb.test.ts +198 -0
- package/src/color/__tests__/extract-colors.test.ts +360 -0
- package/src/color/__tests__/image-utils.test.ts +242 -0
- package/src/color/__tests__/reference-colors.test.ts +278 -0
- package/src/color/__tests__/round-trip.test.ts +197 -0
- package/src/color/conversions.test.ts +402 -0
- package/src/color/conversions.ts +393 -0
- package/src/color/dislike/__tests__/dislike-analyzer.test.ts +248 -0
- package/src/color/dislike/dislike-analyzer.ts +114 -0
- package/src/color/extract-colors.ts +228 -0
- package/src/color/hct/__tests__/hct-class.test.ts +232 -0
- package/src/color/hct/harmonization.ts +204 -0
- package/src/color/hct/hct-class.ts +109 -0
- package/src/color/hct/hct-solver.ts +168 -0
- package/src/color/hct/index.ts +39 -0
- package/src/color/hct/tonal-palette.ts +211 -0
- package/src/color/hct/types.ts +88 -0
- package/src/color/image-utils.ts +79 -0
- package/src/color/index.ts +87 -0
- package/src/color/material-theme.ts +157 -0
- package/src/color/metrics.test.ts +276 -0
- package/src/color/metrics.ts +281 -0
- package/src/color/quantize/__tests__/quantizer_celebi.test.ts +195 -0
- package/src/color/quantize/lab_point_provider.ts +55 -0
- package/src/color/quantize/point_provider.ts +27 -0
- package/src/color/quantize/quantizer_celebi.ts +51 -0
- package/src/color/quantize/quantizer_celebi_test.ts +71 -0
- package/src/color/quantize/quantizer_map.ts +47 -0
- package/src/color/quantize/quantizer_wsmeans.ts +232 -0
- package/src/color/quantize/quantizer_wu.ts +472 -0
- package/src/color/score/__tests__/score.test.ts +224 -0
- package/src/color/score/score.ts +175 -0
- package/src/color/types.ts +151 -0
- package/src/color/utils/color_utils.ts +292 -0
- package/src/color/utils/math_utils.ts +145 -0
- package/src/color/utils.test.ts +403 -0
- package/src/color/utils.ts +315 -0
- package/src/constants.ts +5 -0
- package/src/coolors-mcp.ts +37 -0
- package/src/examples/addition.ts +333 -0
- package/src/examples/color-demo.ts +125 -0
- package/src/examples/custom-logger.ts +201 -0
- package/src/examples/oauth-server.ts +113 -0
- package/src/examples/session-context.ts +269 -0
- package/src/session.ts +116 -0
- package/src/theme/__tests__/matcher.test.ts +180 -0
- package/src/theme/__tests__/parser.test.ts +148 -0
- package/src/theme/__tests__/refactor.test.ts +224 -0
- package/src/theme/index.ts +34 -0
- package/src/theme/matcher.ts +395 -0
- package/src/theme/parser.ts +392 -0
- package/src/theme/refactor.ts +360 -0
- package/src/theme/types.ts +152 -0
- package/src/tools/__tests__/gradient-generator.test.ts +206 -0
- package/src/tools/__tests__/palette-with-locks.test.ts +109 -0
- package/src/tools/color-conversion.tool.ts +54 -0
- package/src/tools/color-distance.tool.ts +41 -0
- package/src/tools/colors.ts +31 -0
- package/src/tools/contrast-checker.tool.ts +37 -0
- package/src/tools/dislike-analyzer.tool.ts +247 -0
- package/src/tools/gradient-generator.tool.ts +250 -0
- package/src/tools/image-extraction.tools.ts +289 -0
- package/src/tools/index.ts +39 -0
- package/src/tools/material-theme.tools.ts +250 -0
- package/src/tools/palette-generator.tool.ts +135 -0
- package/src/tools/palette-with-locks.tool.ts +221 -0
- package/src/tools/registry.ts +142 -0
- package/src/tools/simple-tools.ts +37 -0
- package/src/tools/theme-matching.tools.ts +334 -0
- package/src/types.ts +182 -0
- package/src/utils.ts +22 -0
- package/tsconfig.json +8 -0
- package/vitest.config.js +15 -0
package/note.md
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
Phase 1: Core Infrastructure
|
|
2
|
+
|
|
3
|
+
- Integrate Material Color Utilities HCT system into existing codebase
|
|
4
|
+
- Build theme variable parser for CSS custom properties
|
|
5
|
+
- Implement HCT-based color distance calculations
|
|
6
|
+
|
|
7
|
+
Phase 2: Matching Engine
|
|
8
|
+
|
|
9
|
+
- Create efficient color lookup using spatial indexing (k-d tree)
|
|
10
|
+
- Implement multi-factor scoring algorithm (perceptual + semantic +
|
|
11
|
+
accessibility)
|
|
12
|
+
- Add context-aware matching for different UI elements
|
|
13
|
+
|
|
14
|
+
Phase 3: CSS Refactoring
|
|
15
|
+
|
|
16
|
+
- Build CSS parser to extract legacy colors
|
|
17
|
+
- Generate refactored CSS with theme variable replacements
|
|
18
|
+
- Include confidence scores and alternative suggestions
|
|
19
|
+
|
|
20
|
+
Phase 4: MCP Integration
|
|
21
|
+
|
|
22
|
+
- Add new MCP tools: match_theme_color, refactor_css_colors,
|
|
23
|
+
analyze_theme_quality
|
|
24
|
+
- Extend existing tools to support HCT color space
|
|
25
|
+
|
|
26
|
+
This approach leverages Material Color Utilities for sophisticated color
|
|
27
|
+
science while integrating with your existing infrastructure for maximum utility
|
|
28
|
+
in UI development.
|
|
29
|
+
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
[material-color-utilities](researcher/material-color-utilities)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
https://github.com/material-foundation/material-color-utilities#readme
|
package/package.json
ADDED
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@trishchuk/coolors-mcp",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"main": "dist/CoolorsMCP.js",
|
|
5
|
+
"scripts": {
|
|
6
|
+
"build": "tsup",
|
|
7
|
+
"lint": "prettier --check . && eslint . && tsc --noEmit",
|
|
8
|
+
"test": "vitest run",
|
|
9
|
+
"format": "prettier --write . && eslint --fix .",
|
|
10
|
+
"docs:dev": "vitepress dev docs",
|
|
11
|
+
"docs:build": "vitepress build docs",
|
|
12
|
+
"docs:preview": "vitepress preview docs"
|
|
13
|
+
},
|
|
14
|
+
"bin": {
|
|
15
|
+
"coolors-mcp": "dist/bin/server.js"
|
|
16
|
+
},
|
|
17
|
+
"keywords": [
|
|
18
|
+
"mcp",
|
|
19
|
+
"model-context-protocol",
|
|
20
|
+
"color",
|
|
21
|
+
"colors",
|
|
22
|
+
"material-design",
|
|
23
|
+
"theme",
|
|
24
|
+
"css",
|
|
25
|
+
"hct",
|
|
26
|
+
"lab",
|
|
27
|
+
"rgb",
|
|
28
|
+
"hsl",
|
|
29
|
+
"palette",
|
|
30
|
+
"gradient",
|
|
31
|
+
"wcag",
|
|
32
|
+
"accessibility",
|
|
33
|
+
"contrast",
|
|
34
|
+
"image-extraction"
|
|
35
|
+
],
|
|
36
|
+
"type": "module",
|
|
37
|
+
"author": "Taras Trishchuk <x51xxx@gmail.com>",
|
|
38
|
+
"license": "MIT",
|
|
39
|
+
"description": "Advanced color operations MCP server with Material Design 3 support, CSS theme matching, image color extraction, and accessibility compliance. Features HCT color space for perceptually accurate operations.",
|
|
40
|
+
"module": "dist/coolors-mcp.js",
|
|
41
|
+
"types": "dist/coolors-mcp.d.ts",
|
|
42
|
+
"dependencies": {
|
|
43
|
+
"@modelcontextprotocol/sdk": "^1.17.2",
|
|
44
|
+
"@standard-schema/spec": "^1.0.0",
|
|
45
|
+
"culori": "^4.0.2",
|
|
46
|
+
"execa": "^9.6.0",
|
|
47
|
+
"file-type": "^21.0.0",
|
|
48
|
+
"fuse.js": "^7.1.0",
|
|
49
|
+
"mcp-proxy": "^5.5.4",
|
|
50
|
+
"strict-event-emitter-types": "^2.0.0",
|
|
51
|
+
"undici": "^7.13.0",
|
|
52
|
+
"uri-templates": "^0.2.0",
|
|
53
|
+
"xsschema": "0.3.5",
|
|
54
|
+
"yargs": "^18.0.0",
|
|
55
|
+
"zod": "^3.25.76",
|
|
56
|
+
"zod-to-json-schema": "^3.24.6"
|
|
57
|
+
},
|
|
58
|
+
"repository": {
|
|
59
|
+
"type": "git",
|
|
60
|
+
"url": "https://github.com/x51xxx/coolors-mcp"
|
|
61
|
+
},
|
|
62
|
+
"homepage": "https://github.com/x51xxx/coolors-mcp",
|
|
63
|
+
"bugs": {
|
|
64
|
+
"url": "https://github.com/x51xxx/coolors-mcp/issues"
|
|
65
|
+
},
|
|
66
|
+
"release": {
|
|
67
|
+
"branches": [
|
|
68
|
+
"main"
|
|
69
|
+
],
|
|
70
|
+
"plugins": [
|
|
71
|
+
"@semantic-release/commit-analyzer",
|
|
72
|
+
"@semantic-release/release-notes-generator",
|
|
73
|
+
"@semantic-release/npm",
|
|
74
|
+
"@semantic-release/github",
|
|
75
|
+
"@sebbo2002/semantic-release-jsr"
|
|
76
|
+
]
|
|
77
|
+
},
|
|
78
|
+
"devDependencies": {
|
|
79
|
+
"@eslint/js": "^9.33.0",
|
|
80
|
+
"@modelcontextprotocol/inspector": "^0.16.2",
|
|
81
|
+
"@sebbo2002/semantic-release-jsr": "^3.0.1",
|
|
82
|
+
"@tsconfig/node22": "^22.0.2",
|
|
83
|
+
"@types/node": "^24.2.1",
|
|
84
|
+
"@types/uri-templates": "^0.1.34",
|
|
85
|
+
"@types/yargs": "^17.0.33",
|
|
86
|
+
"@valibot/to-json-schema": "^1.3.0",
|
|
87
|
+
"@wong2/mcp-cli": "^1.13.0",
|
|
88
|
+
"arktype": "^2.1.20",
|
|
89
|
+
"eslint": "^9.33.0",
|
|
90
|
+
"eslint-config-prettier": "^10.1.8",
|
|
91
|
+
"eslint-plugin-perfectionist": "^4.15.0",
|
|
92
|
+
"eslint-plugin-prettier": "^5.5.4",
|
|
93
|
+
"eventsource-client": "^1.1.4",
|
|
94
|
+
"get-port-please": "^3.2.0",
|
|
95
|
+
"jiti": "^2.5.1",
|
|
96
|
+
"jsr": "^0.13.5",
|
|
97
|
+
"prettier": "^3.6.2",
|
|
98
|
+
"semantic-release": "^24.2.7",
|
|
99
|
+
"tsup": "^8.5.0",
|
|
100
|
+
"typescript": "^5.9.2",
|
|
101
|
+
"typescript-eslint": "^8.39.0",
|
|
102
|
+
"valibot": "^1.1.0",
|
|
103
|
+
"vitepress": "^1.6.4",
|
|
104
|
+
"vitepress-plugin-mermaid": "^2.0.17",
|
|
105
|
+
"vitest": "^3.2.4"
|
|
106
|
+
},
|
|
107
|
+
"tsup": {
|
|
108
|
+
"entry": [
|
|
109
|
+
"src/coolors-mcp.ts",
|
|
110
|
+
"src/bin/coolors-mcp.ts",
|
|
111
|
+
"src/bin/server.ts",
|
|
112
|
+
"src/color/index.ts"
|
|
113
|
+
],
|
|
114
|
+
"format": [
|
|
115
|
+
"esm"
|
|
116
|
+
],
|
|
117
|
+
"dts": true,
|
|
118
|
+
"splitting": true,
|
|
119
|
+
"sourcemap": true,
|
|
120
|
+
"clean": true
|
|
121
|
+
}
|
|
122
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# Research Results
|
|
2
|
+
|
|
3
|
+
## chromaticity-color-utilities
|
|
4
|
+
|
|
5
|
+
Based on the search results, "chromaticity-color-utilities" seems to be a general term for libraries that provide color science calculations. There are many libraries available for different languages.
|
|
6
|
+
|
|
7
|
+
For JavaScript/Node.js, some popular libraries are:
|
|
8
|
+
|
|
9
|
+
- **Culori**: A comprehensive color library that supports a wide range of color spaces and difference calculations.
|
|
10
|
+
- **Color.js**: A library for color conversion and manipulation.
|
|
11
|
+
- **Chroma.js**: A small library for color conversions and scales.
|
|
12
|
+
|
|
13
|
+
For Python, some popular libraries are:
|
|
14
|
+
|
|
15
|
+
- **Colour**: A powerful color science library with a wide range of features.
|
|
16
|
+
- **ColorPy**: A library for color calculations and visualization.
|
|
17
|
+
- **ColorAide**: A modern color library with a focus on ease of use.
|
|
18
|
+
|
|
19
|
+
The user should specify the programming language and the specific functionality they need to get a more specific recommendation.
|
|
20
|
+
|
|
21
|
+
## encodable
|
|
22
|
+
|
|
23
|
+
"Encodable" is a general term in software engineering that refers to the ability of data or an object to be converted into a specific format for storage, transmission, or processing.
|
|
24
|
+
|
|
25
|
+
Common types of encoding include:
|
|
26
|
+
|
|
27
|
+
- **Data Serialization**: Converting data structures or object states into a format that can be stored or transmitted and reconstructed later (e.g., JSON, XML, Protocol Buffers).
|
|
28
|
+
- **Character Encoding**: Representing text characters in a digital format (e.g., UTF-8, ASCII).
|
|
29
|
+
- **Binary-to-Text Encoding**: Converting binary data into a text-based format (e.g., Base64, Hexadecimal).
|
|
30
|
+
- **URL Encoding**: Converting characters in a URL into a format that can be safely transmitted.
|
|
31
|
+
|
|
32
|
+
The user should provide more context on what they mean by "encodable" to get a more specific answer.
|
|
33
|
+
|
|
34
|
+
## material-color-utilities
|
|
35
|
+
|
|
36
|
+
"Material Color Utilities" (MCU) is a set of color libraries developed by Material Design. It provides algorithms and utilities for generating dynamic color themes and schemes, particularly for Material You.
|
|
37
|
+
|
|
38
|
+
MCU is available for various programming languages, including:
|
|
39
|
+
|
|
40
|
+
- **TypeScript** (via npm): `@material/material-color-utilities`
|
|
41
|
+
- **Dart** (via Pub.dev): `material_color_utilities`
|
|
42
|
+
- **Rust**: `material-color-utilities`
|
|
43
|
+
|
|
44
|
+
The core concepts of MCU include:
|
|
45
|
+
|
|
46
|
+
- **Dynamic Color**: Generating a color palette from a single seed color, often extracted from a user's wallpaper.
|
|
47
|
+
- **Tonal Palettes**: A set of 13 tones for each color in the palette, which provides a range of shades for different UI elements.
|
|
48
|
+
|
|
49
|
+
MCU can be used to:
|
|
50
|
+
|
|
51
|
+
- Create personalized color themes for applications.
|
|
52
|
+
- Ensure color harmony and accessibility in UIs.
|
|
53
|
+
- Implement Material You features in an application.
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { execa } from "execa";
|
|
4
|
+
import yargs from "yargs";
|
|
5
|
+
import { hideBin } from "yargs/helpers";
|
|
6
|
+
|
|
7
|
+
await yargs(hideBin(process.argv))
|
|
8
|
+
.scriptName("coolors-mcp")
|
|
9
|
+
.command(
|
|
10
|
+
"dev <file>",
|
|
11
|
+
"Start a development server",
|
|
12
|
+
(yargs) => {
|
|
13
|
+
return yargs
|
|
14
|
+
.positional("file", {
|
|
15
|
+
demandOption: true,
|
|
16
|
+
describe: "The path to the server file",
|
|
17
|
+
type: "string",
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
.option("watch", {
|
|
21
|
+
alias: "w",
|
|
22
|
+
default: false,
|
|
23
|
+
describe: "Watch for file changes and restart server",
|
|
24
|
+
type: "boolean",
|
|
25
|
+
})
|
|
26
|
+
|
|
27
|
+
.option("verbose", {
|
|
28
|
+
alias: "v",
|
|
29
|
+
default: false,
|
|
30
|
+
describe: "Enable verbose logging",
|
|
31
|
+
type: "boolean",
|
|
32
|
+
});
|
|
33
|
+
},
|
|
34
|
+
|
|
35
|
+
async (argv) => {
|
|
36
|
+
try {
|
|
37
|
+
const command = argv.watch
|
|
38
|
+
? `npx @wong2/mcp-cli npx tsx --watch ${argv.file}`
|
|
39
|
+
: `npx @wong2/mcp-cli npx tsx ${argv.file}`;
|
|
40
|
+
|
|
41
|
+
if (argv.verbose) {
|
|
42
|
+
console.log(`[CoolorsMCP] Starting server: ${command}`);
|
|
43
|
+
console.log(`[CoolorsMCP] File: ${argv.file}`);
|
|
44
|
+
console.log(
|
|
45
|
+
`[CoolorsMCP] Watch mode: ${argv.watch ? "enabled" : "disabled"}`,
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const { stdout } = await execa({
|
|
50
|
+
shell: true,
|
|
51
|
+
stderr: "inherit",
|
|
52
|
+
stdin: "inherit",
|
|
53
|
+
})`${command}`;
|
|
54
|
+
|
|
55
|
+
console.log(stdout);
|
|
56
|
+
} catch (error) {
|
|
57
|
+
console.error(
|
|
58
|
+
"[CoolorsMCP Error] Failed to start development server:",
|
|
59
|
+
error instanceof Error ? error.message : String(error),
|
|
60
|
+
);
|
|
61
|
+
|
|
62
|
+
if (argv.verbose && error instanceof Error && error.stack) {
|
|
63
|
+
console.error("[CoolorsMCP Debug] Stack trace:", error.stack);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
process.exit(1);
|
|
67
|
+
}
|
|
68
|
+
},
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
.command(
|
|
72
|
+
"inspect <file>",
|
|
73
|
+
"Inspect a server file",
|
|
74
|
+
(yargs) => {
|
|
75
|
+
return yargs.positional("file", {
|
|
76
|
+
demandOption: true,
|
|
77
|
+
describe: "The path to the server file",
|
|
78
|
+
type: "string",
|
|
79
|
+
});
|
|
80
|
+
},
|
|
81
|
+
|
|
82
|
+
async (argv) => {
|
|
83
|
+
try {
|
|
84
|
+
await execa({
|
|
85
|
+
stderr: "inherit",
|
|
86
|
+
stdout: "inherit",
|
|
87
|
+
})`npx @modelcontextprotocol/inspector npx tsx ${argv.file}`;
|
|
88
|
+
} catch (error) {
|
|
89
|
+
console.error(
|
|
90
|
+
"[CoolorsMCP Error] Failed to inspect server:",
|
|
91
|
+
error instanceof Error ? error.message : String(error),
|
|
92
|
+
);
|
|
93
|
+
|
|
94
|
+
process.exit(1);
|
|
95
|
+
}
|
|
96
|
+
},
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
.command(
|
|
100
|
+
"validate <file>",
|
|
101
|
+
"Validate a CoolorsMCP server file for syntax and basic structure",
|
|
102
|
+
(yargs) => {
|
|
103
|
+
return yargs
|
|
104
|
+
.positional("file", {
|
|
105
|
+
demandOption: true,
|
|
106
|
+
describe: "The path to the server file",
|
|
107
|
+
type: "string",
|
|
108
|
+
})
|
|
109
|
+
|
|
110
|
+
.option("strict", {
|
|
111
|
+
alias: "s",
|
|
112
|
+
default: false,
|
|
113
|
+
describe: "Enable strict validation (type checking)",
|
|
114
|
+
type: "boolean",
|
|
115
|
+
});
|
|
116
|
+
},
|
|
117
|
+
|
|
118
|
+
async (argv) => {
|
|
119
|
+
try {
|
|
120
|
+
const { existsSync } = await import("fs");
|
|
121
|
+
const { resolve } = await import("path");
|
|
122
|
+
const filePath = resolve(argv.file);
|
|
123
|
+
|
|
124
|
+
if (!existsSync(filePath)) {
|
|
125
|
+
console.error(`[CoolorsMCP Error] File not found: ${filePath}`);
|
|
126
|
+
process.exit(1);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
console.log(`[CoolorsMCP] Validating server file: ${filePath}`);
|
|
130
|
+
|
|
131
|
+
const command = argv.strict
|
|
132
|
+
? `npx tsc --noEmit --strict ${filePath}`
|
|
133
|
+
: `npx tsc --noEmit ${filePath}`;
|
|
134
|
+
|
|
135
|
+
try {
|
|
136
|
+
await execa({
|
|
137
|
+
shell: true,
|
|
138
|
+
stderr: "pipe",
|
|
139
|
+
stdout: "pipe",
|
|
140
|
+
})`${command}`;
|
|
141
|
+
|
|
142
|
+
console.log("[CoolorsMCP] ✓ TypeScript compilation successful");
|
|
143
|
+
} catch (tsError) {
|
|
144
|
+
console.error("[CoolorsMCP] ✗ TypeScript compilation failed");
|
|
145
|
+
|
|
146
|
+
if (tsError instanceof Error && "stderr" in tsError) {
|
|
147
|
+
console.error(tsError.stderr);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
process.exit(1);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
try {
|
|
154
|
+
await execa({
|
|
155
|
+
shell: true,
|
|
156
|
+
stderr: "pipe",
|
|
157
|
+
stdout: "pipe",
|
|
158
|
+
})`node -e "
|
|
159
|
+
(async () => {
|
|
160
|
+
try {
|
|
161
|
+
const { CoolorsMCP } = await import('coolors-mcp');
|
|
162
|
+
await import('file://${filePath}');
|
|
163
|
+
console.log('[CoolorsMCP] ✓ Server structure validation passed');
|
|
164
|
+
} catch (error) {
|
|
165
|
+
console.error('[CoolorsMCP] ✗ Server structure validation failed:', error.message);
|
|
166
|
+
process.exit(1);
|
|
167
|
+
}
|
|
168
|
+
})();
|
|
169
|
+
"`;
|
|
170
|
+
} catch {
|
|
171
|
+
console.error("[CoolorsMCP] ✗ Server structure validation failed");
|
|
172
|
+
console.error(
|
|
173
|
+
"Make sure the file properly imports and uses CoolorsMCP",
|
|
174
|
+
);
|
|
175
|
+
|
|
176
|
+
process.exit(1);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
console.log(
|
|
180
|
+
"[CoolorsMCP] ✓ All validations passed! Server file looks good.",
|
|
181
|
+
);
|
|
182
|
+
} catch (error) {
|
|
183
|
+
console.error(
|
|
184
|
+
"[CoolorsMCP Error] Validation failed:",
|
|
185
|
+
error instanceof Error ? error.message : String(error),
|
|
186
|
+
);
|
|
187
|
+
|
|
188
|
+
process.exit(1);
|
|
189
|
+
}
|
|
190
|
+
},
|
|
191
|
+
)
|
|
192
|
+
|
|
193
|
+
.help()
|
|
194
|
+
.parseAsync();
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Coolors MCP Server - Advanced Color Operations & Theme Intelligence
|
|
5
|
+
*
|
|
6
|
+
* A comprehensive Model Context Protocol server providing:
|
|
7
|
+
* - Color space conversions (RGB, HSL, HSV, LAB, XYZ, HCT)
|
|
8
|
+
* - Material Design 3 theme generation with tonal palettes
|
|
9
|
+
* - Intelligent CSS theme matching with multi-factor scoring
|
|
10
|
+
* - Image color extraction using Celebi quantization
|
|
11
|
+
* - WCAG accessibility compliance checking
|
|
12
|
+
* - Universal color preference analysis (dislike detection)
|
|
13
|
+
* - Context-aware color selection for UI components
|
|
14
|
+
*
|
|
15
|
+
* Built on Google's HCT color space for perceptually uniform operations
|
|
16
|
+
* and Material Color Utilities algorithms for professional-grade results.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
import { CoolorsMcp } from "../coolors-mcp.js";
|
|
20
|
+
import * as tools from "../tools/index.js";
|
|
21
|
+
|
|
22
|
+
// Create the MCP server
|
|
23
|
+
const server = new CoolorsMcp({
|
|
24
|
+
instructions:
|
|
25
|
+
"Advanced color operations server with Material Design 3 support, CSS theme matching, image extraction, and accessibility compliance. Uses HCT color space for perceptually accurate operations.",
|
|
26
|
+
name: "coolors-mcp",
|
|
27
|
+
version: "1.0.0",
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
// Core color operations: conversion, distance metrics, accessibility
|
|
31
|
+
server.addTool(tools.colorConversionTool);
|
|
32
|
+
server.addTool(tools.colorDistanceTool);
|
|
33
|
+
server.addTool(tools.contrastCheckerTool);
|
|
34
|
+
server.addTool(tools.paletteGeneratorTool);
|
|
35
|
+
server.addTool(tools.paletteWithLocksTool);
|
|
36
|
+
server.addTool(tools.gradientGeneratorTool);
|
|
37
|
+
|
|
38
|
+
// Material Design 3: theme generation, harmonization, tonal palettes
|
|
39
|
+
server.addTool(tools.generateMaterialThemeTool);
|
|
40
|
+
server.addTool(tools.harmonizeColorsTool);
|
|
41
|
+
server.addTool(tools.generateTonalPaletteTool);
|
|
42
|
+
|
|
43
|
+
// CSS theme intelligence: matching, refactoring, migration
|
|
44
|
+
server.addTool(tools.matchThemeColorTool);
|
|
45
|
+
server.addTool(tools.refactorCssWithThemeTool);
|
|
46
|
+
server.addTool(tools.matchThemeColorsBatchTool);
|
|
47
|
+
server.addTool(tools.generateThemeCssTool);
|
|
48
|
+
|
|
49
|
+
// Image analysis: color extraction, theme generation from photos
|
|
50
|
+
server.addTool(tools.extractImageColorsTool);
|
|
51
|
+
server.addTool(tools.generateThemeFromImageTool);
|
|
52
|
+
|
|
53
|
+
// Color psychology: detect and fix universally disliked colors
|
|
54
|
+
server.addTool(tools.analyzeColorLikabilityTool);
|
|
55
|
+
server.addTool(tools.fixDislikedColorsBatchTool);
|
|
56
|
+
|
|
57
|
+
// Start the server
|
|
58
|
+
server.start().catch((error) => {
|
|
59
|
+
console.error("Failed to start server:", error);
|
|
60
|
+
process.exit(1);
|
|
61
|
+
});
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for ARGB conversion functions
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { describe, expect, it } from "vitest";
|
|
6
|
+
|
|
7
|
+
import { argbToRgb, rgbToArgb } from "../conversions.js";
|
|
8
|
+
|
|
9
|
+
describe("ARGB Conversions", () => {
|
|
10
|
+
describe("rgbToArgb", () => {
|
|
11
|
+
it("should convert RGB to ARGB with full opacity", () => {
|
|
12
|
+
const argb = rgbToArgb({ b: 64, g: 128, r: 255 });
|
|
13
|
+
|
|
14
|
+
// Check alpha channel (should be 0xff)
|
|
15
|
+
expect((argb >> 24) & 0xff).toBe(0xff);
|
|
16
|
+
|
|
17
|
+
// Check RGB channels
|
|
18
|
+
expect((argb >> 16) & 0xff).toBe(255); // Red
|
|
19
|
+
expect((argb >> 8) & 0xff).toBe(128); // Green
|
|
20
|
+
expect(argb & 0xff).toBe(64); // Blue
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
it("should handle primary colors", () => {
|
|
24
|
+
const red = rgbToArgb({ b: 0, g: 0, r: 255 });
|
|
25
|
+
const green = rgbToArgb({ b: 0, g: 255, r: 0 });
|
|
26
|
+
const blue = rgbToArgb({ b: 255, g: 0, r: 0 });
|
|
27
|
+
|
|
28
|
+
// Use >>> 0 to convert expected values to unsigned
|
|
29
|
+
expect(red).toBe(0xffff0000 >>> 0);
|
|
30
|
+
expect(green).toBe(0xff00ff00 >>> 0);
|
|
31
|
+
expect(blue).toBe(0xff0000ff >>> 0);
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it("should handle black and white", () => {
|
|
35
|
+
const black = rgbToArgb({ b: 0, g: 0, r: 0 });
|
|
36
|
+
const white = rgbToArgb({ b: 255, g: 255, r: 255 });
|
|
37
|
+
|
|
38
|
+
expect(black).toBe(0xff000000 >>> 0);
|
|
39
|
+
expect(white).toBe(0xffffffff >>> 0);
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
it("should clamp values above 255", () => {
|
|
43
|
+
const argb = rgbToArgb({ b: 1000, g: 256, r: 300 });
|
|
44
|
+
|
|
45
|
+
expect((argb >> 16) & 0xff).toBe(255); // Red clamped
|
|
46
|
+
expect((argb >> 8) & 0xff).toBe(255); // Green clamped
|
|
47
|
+
expect(argb & 0xff).toBe(255); // Blue clamped
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it("should clamp negative values to 0", () => {
|
|
51
|
+
const argb = rgbToArgb({ b: -1, g: -100, r: -10 });
|
|
52
|
+
|
|
53
|
+
expect((argb >> 16) & 0xff).toBe(0); // Red clamped
|
|
54
|
+
expect((argb >> 8) & 0xff).toBe(0); // Green clamped
|
|
55
|
+
expect(argb & 0xff).toBe(0); // Blue clamped
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
it("should round floating point values", () => {
|
|
59
|
+
const argb = rgbToArgb({ b: 127.5, g: 127.3, r: 127.7 });
|
|
60
|
+
|
|
61
|
+
expect((argb >> 16) & 0xff).toBe(128); // Rounded up
|
|
62
|
+
expect((argb >> 8) & 0xff).toBe(127); // Rounded down
|
|
63
|
+
expect(argb & 0xff).toBe(128); // Rounded up (0.5 rounds up)
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
it("should produce correct 32-bit integer", () => {
|
|
67
|
+
const argb = rgbToArgb({ b: 0x56, g: 0x34, r: 0x12 });
|
|
68
|
+
|
|
69
|
+
expect(argb).toBe(0xff123456 >>> 0);
|
|
70
|
+
expect(argb.toString(16)).toBe("ff123456");
|
|
71
|
+
});
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
describe("argbToRgb", () => {
|
|
75
|
+
it("should convert ARGB back to RGB", () => {
|
|
76
|
+
const argb = 0xff804020; // Alpha: 255, R: 128, G: 64, B: 32
|
|
77
|
+
const rgb = argbToRgb(argb);
|
|
78
|
+
|
|
79
|
+
expect(rgb.r).toBe(128);
|
|
80
|
+
expect(rgb.g).toBe(64);
|
|
81
|
+
expect(rgb.b).toBe(32);
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
it("should ignore alpha channel", () => {
|
|
85
|
+
const argbFullAlpha = 0xff123456;
|
|
86
|
+
const argbNoAlpha = 0x00123456;
|
|
87
|
+
|
|
88
|
+
const rgb1 = argbToRgb(argbFullAlpha);
|
|
89
|
+
const rgb2 = argbToRgb(argbNoAlpha);
|
|
90
|
+
|
|
91
|
+
// RGB values should be the same regardless of alpha
|
|
92
|
+
expect(rgb1).toEqual(rgb2);
|
|
93
|
+
expect(rgb1.r).toBe(0x12);
|
|
94
|
+
expect(rgb1.g).toBe(0x34);
|
|
95
|
+
expect(rgb1.b).toBe(0x56);
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
it("should handle primary colors", () => {
|
|
99
|
+
const red = argbToRgb(0xffff0000);
|
|
100
|
+
const green = argbToRgb(0xff00ff00);
|
|
101
|
+
const blue = argbToRgb(0xff0000ff);
|
|
102
|
+
|
|
103
|
+
expect(red).toEqual({ b: 0, g: 0, r: 255 });
|
|
104
|
+
expect(green).toEqual({ b: 0, g: 255, r: 0 });
|
|
105
|
+
expect(blue).toEqual({ b: 255, g: 0, r: 0 });
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
it("should handle black and white", () => {
|
|
109
|
+
const black = argbToRgb(0xff000000);
|
|
110
|
+
const white = argbToRgb(0xffffffff);
|
|
111
|
+
|
|
112
|
+
expect(black).toEqual({ b: 0, g: 0, r: 0 });
|
|
113
|
+
expect(white).toEqual({ b: 255, g: 255, r: 255 });
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
it("should handle grayscale values", () => {
|
|
117
|
+
const gray = argbToRgb(0xff808080);
|
|
118
|
+
|
|
119
|
+
expect(gray).toEqual({ b: 128, g: 128, r: 128 });
|
|
120
|
+
});
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
describe("round-trip conversion", () => {
|
|
124
|
+
it("should maintain values through round-trip", () => {
|
|
125
|
+
const originalRgb = { b: 67, g: 45, r: 123 };
|
|
126
|
+
const argb = rgbToArgb(originalRgb);
|
|
127
|
+
const resultRgb = argbToRgb(argb);
|
|
128
|
+
|
|
129
|
+
expect(resultRgb).toEqual(originalRgb);
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
it("should handle all possible byte values", () => {
|
|
133
|
+
// Test a sampling of values
|
|
134
|
+
for (let i = 0; i < 256; i += 17) {
|
|
135
|
+
const originalRgb = { b: (i + 170) % 256, g: (i + 85) % 256, r: i };
|
|
136
|
+
const argb = rgbToArgb(originalRgb);
|
|
137
|
+
const resultRgb = argbToRgb(argb);
|
|
138
|
+
|
|
139
|
+
expect(resultRgb).toEqual(originalRgb);
|
|
140
|
+
}
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
it("should maintain clamped values", () => {
|
|
144
|
+
const originalRgb = { b: 127.8, g: -50, r: 300 };
|
|
145
|
+
const argb = rgbToArgb(originalRgb);
|
|
146
|
+
const resultRgb = argbToRgb(argb);
|
|
147
|
+
|
|
148
|
+
// Values should be clamped and rounded
|
|
149
|
+
expect(resultRgb).toEqual({ b: 128, g: 0, r: 255 });
|
|
150
|
+
});
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
describe("edge cases", () => {
|
|
154
|
+
it("should handle zero values", () => {
|
|
155
|
+
const argb = rgbToArgb({ b: 0, g: 0, r: 0 });
|
|
156
|
+
expect(argb).toBe(0xff000000 >>> 0);
|
|
157
|
+
|
|
158
|
+
const rgb = argbToRgb(0);
|
|
159
|
+
expect(rgb).toEqual({ b: 0, g: 0, r: 0 });
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
it("should handle maximum values", () => {
|
|
163
|
+
const argb = rgbToArgb({ b: 255, g: 255, r: 255 });
|
|
164
|
+
expect(argb).toBe(0xffffffff >>> 0);
|
|
165
|
+
|
|
166
|
+
const rgb = argbToRgb(0xffffffff);
|
|
167
|
+
expect(rgb).toEqual({ b: 255, g: 255, r: 255 });
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
it("should handle single channel values", () => {
|
|
171
|
+
const redOnly = rgbToArgb({ b: 0, g: 0, r: 255 });
|
|
172
|
+
const greenOnly = rgbToArgb({ b: 0, g: 255, r: 0 });
|
|
173
|
+
const blueOnly = rgbToArgb({ b: 255, g: 0, r: 0 });
|
|
174
|
+
|
|
175
|
+
expect(redOnly & 0x00ffffff).toBe(0x00ff0000);
|
|
176
|
+
expect(greenOnly & 0x00ffffff).toBe(0x0000ff00);
|
|
177
|
+
expect(blueOnly & 0x00ffffff).toBe(0x000000ff);
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
it("should preserve bit patterns", () => {
|
|
181
|
+
// Test specific bit patterns
|
|
182
|
+
const patterns = [
|
|
183
|
+
0xff000000 >>> 0, // Black
|
|
184
|
+
0xffffffff >>> 0, // White
|
|
185
|
+
0xff808080 >>> 0, // Gray
|
|
186
|
+
0xffaa55aa >>> 0, // Mixed pattern
|
|
187
|
+
0xff123456 >>> 0, // Sequential
|
|
188
|
+
0xfffedcba >>> 0, // Reverse sequential
|
|
189
|
+
];
|
|
190
|
+
|
|
191
|
+
for (const pattern of patterns) {
|
|
192
|
+
const rgb = argbToRgb(pattern);
|
|
193
|
+
const result = rgbToArgb(rgb);
|
|
194
|
+
expect(result).toBe(pattern);
|
|
195
|
+
}
|
|
196
|
+
});
|
|
197
|
+
});
|
|
198
|
+
});
|