@morya-ui/mcp 0.3.4 → 0.3.5
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/LICENSE +21 -0
- package/bin/morya-ui-mcp.js +0 -0
- package/data/catalog.json +31 -31
- package/data/example-coverage.json +2 -2
- package/data/golden-pages/list-page-dense.vue +3 -1
- package/data/golden-pages/list-page-rail.vue +3 -1
- package/data/golden-pages/list-page.vue +3 -1
- package/dist/__tests__/page-snippets.test.js +29 -0
- package/dist/__tests__/tools.test.js +40 -22
- package/dist/decisions.d.ts +2 -0
- package/dist/decisions.js +34 -6
- package/dist/golden-pages.d.ts +6 -1
- package/dist/golden-pages.js +34 -33
- package/dist/index.js +8 -8
- package/dist/page-snippets.js +712 -38
- package/dist/patterns.d.ts +4 -2
- package/dist/patterns.js +35 -6
- package/dist/style-presets.d.ts +13 -32
- package/dist/style-presets.js +100 -291
- package/dist/tools.d.ts +3 -11
- package/dist/tools.js +59 -77
- package/package.json +11 -13
package/dist/tools.js
CHANGED
|
@@ -4,7 +4,7 @@ import { listGoldenPages, readGoldenPageSource, resolveGoldenPageId } from './go
|
|
|
4
4
|
import { filterPageSnippets, findPageSnippet, pageSnippets, scorePageSnippet } from './page-snippets.js';
|
|
5
5
|
import { designRules, findPattern, pagePatterns, scorePattern } from './patterns.js';
|
|
6
6
|
import { countCatalogResources, countCatalogResourceTemplates } from './resources.js';
|
|
7
|
-
import {
|
|
7
|
+
import { resolveListCraftVariant, resolveStyleDirection, styleResolution, } from './style-presets.js';
|
|
8
8
|
function inspectButtonIconOnlyUsage(code, issues) {
|
|
9
9
|
const pairedTagRe = /<MButton\b([^>]*)>([\s\S]*?)<\/MButton>/gi;
|
|
10
10
|
let match = pairedTagRe.exec(code);
|
|
@@ -65,6 +65,7 @@ function mapDecisionOption(option, locale) {
|
|
|
65
65
|
avoidWhen: zh ? option.avoidWhen : option.avoidWhenEn,
|
|
66
66
|
recipe,
|
|
67
67
|
antiPatterns: zh ? option.antiPatterns : option.antiPatternsEn,
|
|
68
|
+
...(option.relatedSnippets?.length ? { relatedSnippets: option.relatedSnippets } : {}),
|
|
68
69
|
};
|
|
69
70
|
}
|
|
70
71
|
function generatedPageCode(patternId, intent, locale) {
|
|
@@ -734,34 +735,30 @@ export function createToolHandlers(catalog = loadCatalog()) {
|
|
|
734
735
|
const stylePayload = {
|
|
735
736
|
resolution: styleDirection.resolution,
|
|
736
737
|
confidence: styleDirection.confidence,
|
|
737
|
-
|
|
738
|
-
? {
|
|
739
|
-
id: styleDirection.preset.id,
|
|
740
|
-
title: locale === 'en-US' ? styleDirection.preset.titleEn : styleDirection.preset.title,
|
|
741
|
-
summary: locale === 'en-US' ? styleDirection.preset.summaryEn : styleDirection.preset.summary,
|
|
742
|
-
apply: locale === 'en-US' ? styleDirection.preset.applyEn : styleDirection.preset.apply,
|
|
743
|
-
avoid: locale === 'en-US' ? styleDirection.preset.avoidEn : styleDirection.preset.avoid,
|
|
744
|
-
}
|
|
745
|
-
: null,
|
|
746
|
-
candidates: styleDirection.candidates.map((item) => ({
|
|
747
|
-
id: item.id,
|
|
748
|
-
title: locale === 'en-US' ? item.titleEn : item.title,
|
|
749
|
-
score: item.score,
|
|
750
|
-
})),
|
|
738
|
+
summary: styleDirection.summary ?? null,
|
|
751
739
|
guidance: locale === 'en-US' ? styleDirection.guidanceEn : styleDirection.guidanceZh,
|
|
752
740
|
priority: locale === 'en-US' ? styleResolution.priorityEn : styleResolution.priorityZh,
|
|
741
|
+
hardRules: locale === 'en-US' ? styleResolution.hardRulesEn : styleResolution.hardRulesZh,
|
|
753
742
|
note: locale === 'en-US'
|
|
754
|
-
? '
|
|
755
|
-
: '
|
|
743
|
+
? 'No named style-preset catalog. Follow an explicit user reference/description; if styleDirection.resolution is ask, ask before inventing the look. Prefer snippets for composition; golden pages are optional block-order checks, not aesthetics.'
|
|
744
|
+
: '没有命名风格预设表。有明确参考/描述就跟用户;若 styleDirection.resolution 为 ask,先询问再写完整视觉。优先用 snippets 拼装;黄金样例仅可选核对块顺序,不是审美模板。',
|
|
756
745
|
};
|
|
746
|
+
const craftText = [args.style || '', args.intent || ''].join(' ');
|
|
757
747
|
const resolvedGoldenId = resolveGoldenPageId({
|
|
758
748
|
patternId: best.pattern.id,
|
|
759
749
|
canonicalGoldenId: best.pattern.goldenPage,
|
|
760
|
-
styleId:
|
|
750
|
+
styleId: craftText || null,
|
|
761
751
|
});
|
|
762
752
|
const resolvedGoldenPath = resolvedGoldenId
|
|
763
753
|
? `docs/golden-pages/${resolvedGoldenId}.vue`
|
|
764
754
|
: best.pattern.goldenPage;
|
|
755
|
+
let suggestedSnippets = [...(best.pattern.suggestedSnippets ?? [])];
|
|
756
|
+
if (best.pattern.id === 'admin-list') {
|
|
757
|
+
const listCraft = resolveListCraftVariant(craftText);
|
|
758
|
+
if (listCraft === 'list-page-dense') {
|
|
759
|
+
suggestedSnippets = suggestedSnippets.map((id) => id === 'list-filters-stack' ? 'list-filters-dense' : id);
|
|
760
|
+
}
|
|
761
|
+
}
|
|
765
762
|
const result = {
|
|
766
763
|
intent: args.intent,
|
|
767
764
|
pageType: args.pageType,
|
|
@@ -778,23 +775,35 @@ export function createToolHandlers(catalog = loadCatalog()) {
|
|
|
778
775
|
interactionRules: best.pattern.interactionRules,
|
|
779
776
|
avoid: best.pattern.avoid,
|
|
780
777
|
alternatives: ranked.slice(1, 3).filter((item) => item.score > 0).map((item) => ({ id: item.pattern.id, score: item.score })),
|
|
778
|
+
suggestedSnippets,
|
|
781
779
|
nextStep: (() => {
|
|
782
780
|
const goldenId = resolvedGoldenId;
|
|
781
|
+
const snippets = suggestedSnippets;
|
|
782
|
+
const snippetHint = snippets.length > 0
|
|
783
|
+
? locale === 'en-US'
|
|
784
|
+
? ` Compose with get_page_snippet for: ${snippets.join(', ')}. Use recommend_component for L2 recipes (relatedSnippets).`
|
|
785
|
+
: ` 用 get_page_snippet 拼装:${snippets.join(', ')}。选型用 recommend_component(含 relatedSnippets)。`
|
|
786
|
+
: locale === 'en-US'
|
|
787
|
+
? ' Compose with get_page_snippet + recommend_component (relatedSnippets).'
|
|
788
|
+
: ' 用 get_page_snippet + recommend_component(relatedSnippets)拼装。';
|
|
783
789
|
const styleHint = locale === 'en-US'
|
|
784
|
-
? styleDirection.resolution === '
|
|
785
|
-
? '
|
|
786
|
-
: `
|
|
787
|
-
: styleDirection.resolution === '
|
|
788
|
-
? '
|
|
789
|
-
: `
|
|
790
|
+
? styleDirection.resolution === 'ask'
|
|
791
|
+
? ' Style is uncertain — ask the user for a short description or reference before inventing the look.'
|
|
792
|
+
: ` Follow styleDirection (${styleDirection.resolution}): “${styleDirection.summary || 'user/reference'}”; honor explicit user words over companions.`
|
|
793
|
+
: styleDirection.resolution === 'ask'
|
|
794
|
+
? ' 风格不确定 — 先请用户说明气质或贴参考,再写完整视觉。'
|
|
795
|
+
: ` 按 styleDirection(${styleDirection.resolution})落实「${styleDirection.summary || '用户参考/描述'}」;用户明确指定优先于 companion。`;
|
|
796
|
+
const goldenHint = goldenId
|
|
797
|
+
? locale === 'en-US'
|
|
798
|
+
? ` Optional: get_golden_page("${goldenId}") only to check whole-page block order (not the default clone target).`
|
|
799
|
+
: ` 可选:仅当需核对整页块顺序时再 get_golden_page("${goldenId}")(默认不要整页克隆)。`
|
|
800
|
+
: locale === 'en-US'
|
|
801
|
+
? ' No golden page for this pattern — follow get_pattern structure + snippets.'
|
|
802
|
+
: ' 该模式暂无黄金样例:按 get_pattern 结构 + snippets 拼装。';
|
|
790
803
|
if (locale === 'en-US') {
|
|
791
|
-
return
|
|
792
|
-
? `Call get_golden_page("${goldenId}") and mirror structure.${styleHint} Look up unfamiliar APIs with get_component/get_example, then always run validate_usage and validate_page.`
|
|
793
|
-
: `No golden page for this pattern: use get_pattern + get_design_rules.${styleHint} Look up APIs with get_component/get_example, then always run validate_usage and validate_page.`;
|
|
804
|
+
return `${snippetHint.trim()}${styleHint}${goldenHint} Look up unfamiliar APIs with get_component/get_example, then always run validate_usage and validate_page.`;
|
|
794
805
|
}
|
|
795
|
-
return
|
|
796
|
-
? `调用 get_golden_page("${goldenId}") 并镜像结构。${styleHint} 不熟悉的 API 用 get_component/get_example 核对,然后必须跑 validate_usage 与 validate_page。`
|
|
797
|
-
: `该模式暂无黄金样例:用 get_pattern + get_design_rules。${styleHint} API 用 get_component/get_example 核对,然后必须跑 validate_usage 与 validate_page。`;
|
|
806
|
+
return `${snippetHint.trim()}${styleHint}${goldenHint} 不熟悉的 API 用 get_component/get_example 核对,然后必须跑 validate_usage 与 validate_page。`;
|
|
798
807
|
})(),
|
|
799
808
|
};
|
|
800
809
|
if (args.includeScaffold) {
|
|
@@ -807,8 +816,8 @@ export function createToolHandlers(catalog = loadCatalog()) {
|
|
|
807
816
|
files: { component: golden.source },
|
|
808
817
|
warnings: [
|
|
809
818
|
locale === 'en-US'
|
|
810
|
-
? 'Scaffold is the golden
|
|
811
|
-
: '
|
|
819
|
+
? 'Scaffold is an optional structure reference from the golden sample. Prefer composing suggestedSnippets; remap domain copy/data only if you use this source. Never invent props. Then validate_usage + validate_page.'
|
|
820
|
+
: '脚手架是黄金样例的可选结构参考。默认优先拼装 suggestedSnippets;若使用本源码只替换域文案与数据。禁止臆造 prop。完成后必须 validate_usage + validate_page。',
|
|
812
821
|
],
|
|
813
822
|
};
|
|
814
823
|
}
|
|
@@ -821,8 +830,8 @@ export function createToolHandlers(catalog = loadCatalog()) {
|
|
|
821
830
|
files: { component: componentSource },
|
|
822
831
|
warnings: [
|
|
823
832
|
locale === 'en-US'
|
|
824
|
-
? 'No golden page for this pattern — structural fallback only. Prefer
|
|
825
|
-
: '该模式暂无黄金样例,仅为结构占位。优先
|
|
833
|
+
? 'No golden page for this pattern — structural fallback only. Prefer suggestedSnippets + get_component. Must validate_usage before delivery.'
|
|
834
|
+
: '该模式暂无黄金样例,仅为结构占位。优先 suggestedSnippets + get_component。交付前必须 validate_usage。',
|
|
826
835
|
],
|
|
827
836
|
};
|
|
828
837
|
}
|
|
@@ -1155,12 +1164,9 @@ export function createToolHandlers(catalog = loadCatalog()) {
|
|
|
1155
1164
|
const styles = {
|
|
1156
1165
|
resolution: locale === 'en-US' ? styleResolution.priorityEn : styleResolution.priorityZh,
|
|
1157
1166
|
hardRules: locale === 'en-US' ? styleResolution.hardRulesEn : styleResolution.hardRulesZh,
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
summary: locale === 'en-US' ? preset.summaryEn : preset.summary,
|
|
1162
|
-
lanes: preset.lanes,
|
|
1163
|
-
})),
|
|
1167
|
+
note: locale === 'en-US'
|
|
1168
|
+
? 'No named style-preset catalog. Follow user reference/description; ask when uncertain.'
|
|
1169
|
+
: '没有命名风格预设表。跟用户参考/描述;不确定就先问。',
|
|
1164
1170
|
};
|
|
1165
1171
|
if (locale === 'zh-CN') {
|
|
1166
1172
|
return textResult({
|
|
@@ -1226,45 +1232,23 @@ export function createToolHandlers(catalog = loadCatalog()) {
|
|
|
1226
1232
|
'Form controls should have a visible label or an equivalent accessible name.',
|
|
1227
1233
|
'Overlays teleport to body by default; only change appendTo for a clear layout constraint.',
|
|
1228
1234
|
'Prefer documented component variants over deep CSS overrides.',
|
|
1229
|
-
'Resolve visual style
|
|
1235
|
+
'Resolve visual style: follow explicit user reference/description first; else infer from clear prompt cues; if uncertain ask once. No named preset catalog. Golden pages lock structure, not aesthetics.',
|
|
1230
1236
|
],
|
|
1231
1237
|
});
|
|
1232
1238
|
}
|
|
1233
|
-
function
|
|
1239
|
+
function getStyleDirection(args = {}) {
|
|
1234
1240
|
const locale = resolveLocale(args.mode);
|
|
1241
|
+
const direction = resolveStyleDirection({ intent: args.intent, style: args.style });
|
|
1235
1242
|
return textResult({
|
|
1236
|
-
resolution:
|
|
1243
|
+
resolution: direction.resolution,
|
|
1244
|
+
confidence: direction.confidence,
|
|
1245
|
+
summary: direction.summary ?? null,
|
|
1246
|
+
guidance: locale === 'en-US' ? direction.guidanceEn : direction.guidanceZh,
|
|
1247
|
+
priority: locale === 'en-US' ? styleResolution.priorityEn : styleResolution.priorityZh,
|
|
1237
1248
|
hardRules: locale === 'en-US' ? styleResolution.hardRulesEn : styleResolution.hardRulesZh,
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
summary: locale === 'en-US' ? preset.summaryEn : preset.summary,
|
|
1242
|
-
lanes: preset.lanes,
|
|
1243
|
-
keywords: preset.keywords,
|
|
1244
|
-
listGoldenPage: preset.listGoldenPage ?? null,
|
|
1245
|
-
})),
|
|
1246
|
-
});
|
|
1247
|
-
}
|
|
1248
|
-
function getStylePreset(args) {
|
|
1249
|
-
const locale = resolveLocale(args.mode);
|
|
1250
|
-
const preset = findStylePreset(args.style);
|
|
1251
|
-
if (!preset) {
|
|
1252
|
-
return textResult({
|
|
1253
|
-
error: `Style preset not found: ${args.style}`,
|
|
1254
|
-
available: stylePresets.map((item) => item.id),
|
|
1255
|
-
});
|
|
1256
|
-
}
|
|
1257
|
-
return textResult({
|
|
1258
|
-
id: preset.id,
|
|
1259
|
-
title: locale === 'en-US' ? preset.titleEn : preset.title,
|
|
1260
|
-
summary: locale === 'en-US' ? preset.summaryEn : preset.summary,
|
|
1261
|
-
lanes: preset.lanes,
|
|
1262
|
-
keywords: preset.keywords,
|
|
1263
|
-
listGoldenPage: preset.listGoldenPage ?? null,
|
|
1264
|
-
cues: locale === 'en-US' ? preset.cuesEn : preset.cues,
|
|
1265
|
-
apply: locale === 'en-US' ? preset.applyEn : preset.apply,
|
|
1266
|
-
avoid: locale === 'en-US' ? preset.avoidEn : preset.avoid,
|
|
1267
|
-
resolution: locale === 'en-US' ? styleResolution.priorityEn : styleResolution.priorityZh,
|
|
1249
|
+
note: locale === 'en-US'
|
|
1250
|
+
? 'No named style-preset catalog. Pass style as free-text user description when they specified one.'
|
|
1251
|
+
: '没有命名风格预设表。用户有描述时把原文放进 style;不确定时 resolution 为 ask,应先问用户。',
|
|
1268
1252
|
});
|
|
1269
1253
|
}
|
|
1270
1254
|
function recommendComponent(args) {
|
|
@@ -1485,8 +1469,7 @@ export function createToolHandlers(catalog = loadCatalog()) {
|
|
|
1485
1469
|
'get_pattern',
|
|
1486
1470
|
'recommend_page',
|
|
1487
1471
|
'get_design_rules',
|
|
1488
|
-
'
|
|
1489
|
-
'get_style_preset',
|
|
1472
|
+
'get_style_direction',
|
|
1490
1473
|
'recommend_component',
|
|
1491
1474
|
'list_golden_pages',
|
|
1492
1475
|
'get_golden_page',
|
|
@@ -1510,8 +1493,7 @@ export function createToolHandlers(catalog = loadCatalog()) {
|
|
|
1510
1493
|
getPattern,
|
|
1511
1494
|
recommendPage,
|
|
1512
1495
|
getDesignRules,
|
|
1513
|
-
|
|
1514
|
-
getStylePreset,
|
|
1496
|
+
getStyleDirection,
|
|
1515
1497
|
recommendComponent,
|
|
1516
1498
|
listGoldenPageCatalog,
|
|
1517
1499
|
getGoldenPage,
|
package/package.json
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@morya-ui/mcp",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.3.
|
|
5
|
-
"packageManager": "pnpm@10.12.4",
|
|
4
|
+
"version": "0.3.5",
|
|
6
5
|
"description": "MCP server for morya-ui — component docs, examples, and usage guidance for AI coding agents.",
|
|
7
6
|
"license": "MIT",
|
|
8
7
|
"homepage": "https://github.com/morya-space/morya-ui/tree/main/packages/ui-mcp#readme",
|
|
@@ -30,6 +29,14 @@
|
|
|
30
29
|
"engines": {
|
|
31
30
|
"node": ">=20.19.0"
|
|
32
31
|
},
|
|
32
|
+
"dependencies": {
|
|
33
|
+
"@modelcontextprotocol/sdk": "^1.30.0",
|
|
34
|
+
"zod": "^3.25.76"
|
|
35
|
+
},
|
|
36
|
+
"devDependencies": {
|
|
37
|
+
"@types/node": "^22.19.15",
|
|
38
|
+
"typescript": "^5.9.3"
|
|
39
|
+
},
|
|
33
40
|
"scripts": {
|
|
34
41
|
"generate": "node ./scripts/generate-catalog.mjs",
|
|
35
42
|
"generate:recipes": "node --experimental-strip-types ./scripts/generate-decision-recipes.mjs",
|
|
@@ -41,15 +48,6 @@
|
|
|
41
48
|
"build": "pnpm run generate && tsc -p tsconfig.json && pnpm run generate:recipes",
|
|
42
49
|
"start": "node ./dist/index.js",
|
|
43
50
|
"test": "vitest run --config ../../vitest.config.ts src/__tests__",
|
|
44
|
-
"typecheck": "tsc -p tsconfig.json --noEmit"
|
|
45
|
-
"prepublishOnly": "pnpm run build"
|
|
46
|
-
},
|
|
47
|
-
"dependencies": {
|
|
48
|
-
"@modelcontextprotocol/sdk": "^1.30.0",
|
|
49
|
-
"zod": "^3.25.76"
|
|
50
|
-
},
|
|
51
|
-
"devDependencies": {
|
|
52
|
-
"@types/node": "^22.19.15",
|
|
53
|
-
"typescript": "^5.9.3"
|
|
51
|
+
"typecheck": "tsc -p tsconfig.json --noEmit"
|
|
54
52
|
}
|
|
55
|
-
}
|
|
53
|
+
}
|