@monoharada/wcf-mcp 0.11.0 → 0.12.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/README.md +8 -3
- package/core/constants.mjs +12 -6
- package/core/prefix.mjs +13 -1
- package/core/register.mjs +170 -16
- package/core.mjs +8 -0
- package/data/component-selector-guide.json +198 -56
- package/data/custom-elements.json +174 -6
- package/data/design-tokens.json +1 -1
- package/data/guidelines-index.json +1 -1
- package/data/llms-full.txt +85 -19
- package/data/pattern-registry.json +18 -0
- package/data/skills-registry.json +41 -0
- package/package.json +1 -1
- package/validator.mjs +314 -0
package/README.md
CHANGED
|
@@ -84,7 +84,7 @@ claude mcp add wcf -- npx @monoharada/wcf-mcp
|
|
|
84
84
|
|
|
85
85
|
| ツール | 説明 |
|
|
86
86
|
|--------|------|
|
|
87
|
-
| `get_design_system_overview` |
|
|
87
|
+
| `get_design_system_overview` | 最初に呼ぶ前提情報(カテゴリ別コンポーネント数、利用可能パターン、`componentPatternMap`、推奨 preloading / workflow、IDE設定テンプレート)を返す |
|
|
88
88
|
|
|
89
89
|
### コンポーネント検索・API
|
|
90
90
|
|
|
@@ -100,7 +100,7 @@ claude mcp add wcf -- npx @monoharada/wcf-mcp
|
|
|
100
100
|
|
|
101
101
|
| ツール | 説明 |
|
|
102
102
|
|--------|------|
|
|
103
|
-
| `validate_markup` | HTML スニペットを検証し、セマンティック検証(下表)で `suggestion`
|
|
103
|
+
| `validate_markup` | HTML スニペットを検証し、セマンティック検証(下表)で `suggestion` 付き診断を返す。severity は `error` / `warning` / `info` |
|
|
104
104
|
| `validate_files` | 複数のマークアップファイルをまとめて検証し、ファイル別診断と集計を返す |
|
|
105
105
|
| `validate_project` | ディレクトリを走査し、include/exclude glob に一致する複数ファイルをまとめて検証する |
|
|
106
106
|
|
|
@@ -126,6 +126,11 @@ claude mcp add wcf -- npx @monoharada/wcf-mcp
|
|
|
126
126
|
| `emptyAriaLabel` | error | 空の `aria-label` 属性(アクセシビリティ違反) | `<dads-button aria-label="">` |
|
|
127
127
|
| `duplicateId` | error | 同一ドキュメント内で `id` が重複している | `<div id="hero">...</div><section id="hero">...</section>` |
|
|
128
128
|
| `forbiddenAttribute` | warning | 禁止属性 | `placeholder` |
|
|
129
|
+
| `sortOnTh` / `sortWrongTarget` / `sortTypeOnWrongElement` | warning | `dads-table` のソート構造誤用 | `th[data-sort]`, `button[data-sort-type]` |
|
|
130
|
+
| `selectionControlWrongElement` | warning | `data-select-row` / `data-select-all` が checkbox 以外 | `<button data-select-row>` |
|
|
131
|
+
| `resourceListWholeLinkMissingInteraction` | warning | `dads-resource-list[href]` に `data-interaction="whole"` がない | `<dads-resource-list href="...">` |
|
|
132
|
+
| `nativePatternReplaceable` | warning / info | 既存 DADS コンポーネントに置換可能な独自パターン | `role="tablist"`, `role="dialog"`, `<dl>` |
|
|
133
|
+
| `customAnimationReplaceable` | warning | スピナー相当の独自 CSS アニメーション | `@keyframes spin` + `animation` |
|
|
129
134
|
|
|
130
135
|
### UI パターン
|
|
131
136
|
|
|
@@ -247,7 +252,7 @@ claude mcp add wcf -- npx @monoharada/wcf-mcp
|
|
|
247
252
|
|
|
248
253
|
| URI | 説明 |
|
|
249
254
|
|-----|------|
|
|
250
|
-
| `wcf://components` |
|
|
255
|
+
| `wcf://components` | コンポーネントカタログのスナップショット。プロトタイピング前の preload を推奨 |
|
|
251
256
|
| `wcf://tokens` | トークン summary(type/category/themes/sample) |
|
|
252
257
|
| `wcf://guidelines/{topic}` | topic 別ガイドライン要約(`accessibility`,`css`,`patterns`,`all`) |
|
|
253
258
|
| `wcf://llms-full` | `llms-full.txt` の全文 |
|
package/core/constants.mjs
CHANGED
|
@@ -115,10 +115,14 @@ export function buildServerInstructions(prefix, installRegistry, patterns) {
|
|
|
115
115
|
'You can generate complete HTML pages using MCP tools alone — no CLI installation required.',
|
|
116
116
|
'',
|
|
117
117
|
'## Quickest Workflow (build a page)',
|
|
118
|
-
'1.
|
|
118
|
+
'1. get_design_system_overview()',
|
|
119
|
+
' → Inspect componentPatternMap before writing custom HTML/CSS',
|
|
120
|
+
'2. Read resource wcf://components',
|
|
121
|
+
' → Preload the component catalog to avoid re-implementing existing components',
|
|
122
|
+
'3. get_pattern_recipe({ patternId: "<id>", include: ["fullPage"] })',
|
|
119
123
|
' → Returns a complete <!DOCTYPE html> page in one call',
|
|
120
|
-
'
|
|
121
|
-
'
|
|
124
|
+
'4. validate_markup({ html: "<the full page HTML>" }) → Validate the full page (catches missing importmap / boot script)',
|
|
125
|
+
'5. Save the fullPageHtml result to a file and serve via HTTP',
|
|
122
126
|
'',
|
|
123
127
|
`## Available Pattern IDs (${patternIds.length})`,
|
|
124
128
|
patternIds.length > 0 ? patternIds.join(', ') : '(none)',
|
|
@@ -127,9 +131,11 @@ export function buildServerInstructions(prefix, installRegistry, patterns) {
|
|
|
127
131
|
componentIds.length > 0 ? componentIds.join(', ') : '(none)',
|
|
128
132
|
'',
|
|
129
133
|
'## Custom Page Construction',
|
|
130
|
-
'1.
|
|
131
|
-
'2.
|
|
132
|
-
'3.
|
|
134
|
+
'1. get_design_system_overview() → review componentPatternMap',
|
|
135
|
+
'2. Read resource wcf://components → preload component catalog context',
|
|
136
|
+
'3. generate_usage_snippet({ component: "<componentId>" }) → Get HTML for each component',
|
|
137
|
+
'4. generate_full_page_html({ html: "<combined fragments>" }) → Wrap into a complete page',
|
|
138
|
+
'5. validate_markup({ html: "<the full page HTML>" }) → Validate the full page (catches missing importmap / boot script)',
|
|
133
139
|
'',
|
|
134
140
|
'## Important Notes',
|
|
135
141
|
'- No CDN exists. All files are served from a local vendor directory.',
|
package/core/prefix.mjs
CHANGED
|
@@ -111,7 +111,7 @@ export function buildDiagnosticSuggestion({ diagnostic, cemIndex, prefix }) {
|
|
|
111
111
|
}
|
|
112
112
|
|
|
113
113
|
if (code === 'forbiddenAttribute' && String(diagnostic?.attrName ?? '').toLowerCase() === 'placeholder') {
|
|
114
|
-
return 'Use
|
|
114
|
+
return 'Use support-text (attribute or slot), a visible label, and aria-describedby/aria-label where needed instead of placeholder. See: https://design.digital.go.jp/dads/components/input-text/accessibility/';
|
|
115
115
|
}
|
|
116
116
|
|
|
117
117
|
if (code === 'ariaLiveNotRecommended') {
|
|
@@ -130,6 +130,18 @@ export function buildDiagnosticSuggestion({ diagnostic, cemIndex, prefix }) {
|
|
|
130
130
|
return diagnostic?.hint ?? 'Provide a meaningful aria-label value or use a visible <label> element.';
|
|
131
131
|
}
|
|
132
132
|
|
|
133
|
+
if (
|
|
134
|
+
code === 'sortOnTh' ||
|
|
135
|
+
code === 'sortWrongTarget' ||
|
|
136
|
+
code === 'sortTypeOnWrongElement' ||
|
|
137
|
+
code === 'selectionControlWrongElement' ||
|
|
138
|
+
code === 'resourceListWholeLinkMissingInteraction' ||
|
|
139
|
+
code === 'nativePatternReplaceable' ||
|
|
140
|
+
code === 'customAnimationReplaceable'
|
|
141
|
+
) {
|
|
142
|
+
return diagnostic?.hint ?? undefined;
|
|
143
|
+
}
|
|
144
|
+
|
|
133
145
|
return undefined;
|
|
134
146
|
}
|
|
135
147
|
|
package/core/register.mjs
CHANGED
|
@@ -249,6 +249,71 @@ function scoreSearchFields(query, terms, fields) {
|
|
|
249
249
|
return score;
|
|
250
250
|
}
|
|
251
251
|
|
|
252
|
+
function normalizeSearchText(value) {
|
|
253
|
+
return String(value ?? '').trim().toLowerCase();
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
function normalizeSearchTokens(value) {
|
|
257
|
+
return normalizeSearchText(value)
|
|
258
|
+
.split(/[^a-z0-9\u3040-\u30ff\u3400-\u9fff-]+/u)
|
|
259
|
+
.filter(Boolean);
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
function includesWholeToken(value, query) {
|
|
263
|
+
return normalizeSearchTokens(value).includes(normalizeSearchText(query));
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
function buildSelectorGuideComponentLookup(selectorGuideData) {
|
|
267
|
+
const lookup = new Map();
|
|
268
|
+
const categories = Array.isArray(selectorGuideData?.categories) ? selectorGuideData.categories : [];
|
|
269
|
+
for (const category of categories) {
|
|
270
|
+
const categoryKey = String(category?.key ?? '');
|
|
271
|
+
const components = Array.isArray(category?.components) ? category.components : [];
|
|
272
|
+
for (const component of components) {
|
|
273
|
+
const id = String(component?.id ?? '').trim();
|
|
274
|
+
const tagName = String(component?.tagName ?? '').trim().toLowerCase();
|
|
275
|
+
const useCase = String(component?.useCase ?? '');
|
|
276
|
+
const keywords = Array.isArray(component?.keywords) ? component.keywords.map((keyword) => String(keyword)) : [];
|
|
277
|
+
const entry = { id, tagName, useCase, keywords, categoryKey };
|
|
278
|
+
if (id) lookup.set(`id:${id}`, entry);
|
|
279
|
+
if (tagName) lookup.set(`tag:${tagName}`, entry);
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
return lookup;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
function scoreSelectorGuideComponent(component, query) {
|
|
286
|
+
const q = normalizeSearchText(query);
|
|
287
|
+
if (!q) return 0;
|
|
288
|
+
|
|
289
|
+
const id = String(component?.id ?? '');
|
|
290
|
+
const tagName = String(component?.tagName ?? '').toLowerCase();
|
|
291
|
+
const useCase = String(component?.useCase ?? '');
|
|
292
|
+
const keywords = Array.isArray(component?.keywords) ? component.keywords : [];
|
|
293
|
+
|
|
294
|
+
if (id.toLowerCase() === q) return 120;
|
|
295
|
+
if (tagName === q || tagName === `dads-${q}`) return 115;
|
|
296
|
+
if (keywords.some((keyword) => normalizeSearchText(keyword) === q)) return 110;
|
|
297
|
+
if (includesWholeToken(useCase, q)) return 90;
|
|
298
|
+
if (keywords.some((keyword) => includesWholeToken(keyword, q))) return 85;
|
|
299
|
+
if (tagName.startsWith(q) || id.toLowerCase().startsWith(q)) return 70;
|
|
300
|
+
if (useCase.toLowerCase().includes(q)) return 40;
|
|
301
|
+
if (keywords.some((keyword) => normalizeSearchText(keyword).includes(q))) return 35;
|
|
302
|
+
if (tagName.includes(q) || id.toLowerCase().includes(q)) return 20;
|
|
303
|
+
return 0;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
function buildOverviewPatternMap(selectorGuideData) {
|
|
307
|
+
const entries = Array.isArray(selectorGuideData?.componentPatternMap) ? selectorGuideData.componentPatternMap : [];
|
|
308
|
+
return entries.map((entry) => ({
|
|
309
|
+
pattern: String(entry?.pattern ?? ''),
|
|
310
|
+
componentIds: Array.isArray(entry?.componentIds) ? entry.componentIds.map((item) => String(item)) : [],
|
|
311
|
+
usage: typeof entry?.usage === 'string' ? entry.usage : undefined,
|
|
312
|
+
note: String(entry?.note ?? ''),
|
|
313
|
+
keywords: Array.isArray(entry?.keywords) ? entry.keywords.map((item) => String(item)) : [],
|
|
314
|
+
})).filter((entry) => entry.pattern && entry.componentIds.length > 0);
|
|
315
|
+
}
|
|
316
|
+
|
|
252
317
|
function detectKnowledgeIntentSources(query, terms) {
|
|
253
318
|
const raw = `${query} ${terms.join(' ')}`.toLowerCase();
|
|
254
319
|
const intents = new Set();
|
|
@@ -432,10 +497,12 @@ async function walkProjectFiles(rootDir) {
|
|
|
432
497
|
function summarizeDiagnostics(diagnostics) {
|
|
433
498
|
const errorCount = diagnostics.filter((diagnostic) => diagnostic.severity === 'error').length;
|
|
434
499
|
const warningCount = diagnostics.filter((diagnostic) => diagnostic.severity === 'warning').length;
|
|
500
|
+
const infoCount = diagnostics.filter((diagnostic) => diagnostic.severity === 'info').length;
|
|
435
501
|
return {
|
|
436
502
|
total: diagnostics.length,
|
|
437
503
|
errorCount,
|
|
438
504
|
warningCount,
|
|
505
|
+
infoCount,
|
|
439
506
|
};
|
|
440
507
|
}
|
|
441
508
|
|
|
@@ -620,6 +687,7 @@ export function registerAll(context) {
|
|
|
620
687
|
llmsFullText,
|
|
621
688
|
tokenSuggestionMap,
|
|
622
689
|
componentTokenRefMap,
|
|
690
|
+
selectorGuideData,
|
|
623
691
|
plugins,
|
|
624
692
|
loadJsonData,
|
|
625
693
|
loadJson,
|
|
@@ -657,6 +725,10 @@ export function registerAll(context) {
|
|
|
657
725
|
detectNonLowercaseAttributes,
|
|
658
726
|
detectCdnReferences,
|
|
659
727
|
detectMissingRuntimeScaffold,
|
|
728
|
+
detectTableAuthoringMisuse = () => [],
|
|
729
|
+
detectResourceListAuthoringMisuse = () => [],
|
|
730
|
+
detectReplaceableNativePatterns = () => [],
|
|
731
|
+
detectReplaceableAnimationPatterns = () => [],
|
|
660
732
|
} = await loadValidator();
|
|
661
733
|
|
|
662
734
|
const p = normalizePrefix(prefix);
|
|
@@ -757,6 +829,32 @@ export function registerAll(context) {
|
|
|
757
829
|
severity: 'warning',
|
|
758
830
|
});
|
|
759
831
|
|
|
832
|
+
const tableAuthoringDiagnostics = detectTableAuthoringMisuse({
|
|
833
|
+
filePath,
|
|
834
|
+
text,
|
|
835
|
+
prefix: p,
|
|
836
|
+
severity: 'warning',
|
|
837
|
+
});
|
|
838
|
+
|
|
839
|
+
const resourceListAuthoringDiagnostics = detectResourceListAuthoringMisuse({
|
|
840
|
+
filePath,
|
|
841
|
+
text,
|
|
842
|
+
prefix: p,
|
|
843
|
+
severity: 'warning',
|
|
844
|
+
});
|
|
845
|
+
|
|
846
|
+
const replaceableNativeDiagnostics = detectReplaceableNativePatterns({
|
|
847
|
+
filePath,
|
|
848
|
+
text,
|
|
849
|
+
prefix: p,
|
|
850
|
+
});
|
|
851
|
+
|
|
852
|
+
const replaceableAnimationDiagnostics = detectReplaceableAnimationPatterns({
|
|
853
|
+
filePath,
|
|
854
|
+
text,
|
|
855
|
+
prefix: p,
|
|
856
|
+
});
|
|
857
|
+
|
|
760
858
|
const allRawDiagnostics = [
|
|
761
859
|
...cemDiagnostics,
|
|
762
860
|
...enumDiagnostics,
|
|
@@ -770,6 +868,10 @@ export function registerAll(context) {
|
|
|
770
868
|
...accessibilityDiagnostics,
|
|
771
869
|
...cdnDiagnostics,
|
|
772
870
|
...scaffoldDiagnostics,
|
|
871
|
+
...tableAuthoringDiagnostics,
|
|
872
|
+
...resourceListAuthoringDiagnostics,
|
|
873
|
+
...replaceableNativeDiagnostics,
|
|
874
|
+
...replaceableAnimationDiagnostics,
|
|
773
875
|
];
|
|
774
876
|
|
|
775
877
|
for (const plugin of plugins) {
|
|
@@ -1023,6 +1125,7 @@ export function registerAll(context) {
|
|
|
1023
1125
|
const cat = getCategory(tagName);
|
|
1024
1126
|
categoryCount[cat] = (categoryCount[cat] ?? 0) + 1;
|
|
1025
1127
|
}
|
|
1128
|
+
const componentPatternMap = buildOverviewPatternMap(selectorGuideData);
|
|
1026
1129
|
|
|
1027
1130
|
const patternList = Object.values(patterns).map((p) => ({
|
|
1028
1131
|
id: p?.id,
|
|
@@ -1037,6 +1140,7 @@ export function registerAll(context) {
|
|
|
1037
1140
|
componentsByCategory: categoryCount,
|
|
1038
1141
|
totalPatterns: patternList.length,
|
|
1039
1142
|
patterns: patternList,
|
|
1143
|
+
componentPatternMap,
|
|
1040
1144
|
setupInfo: {
|
|
1041
1145
|
npmPackage: 'web-components-factory',
|
|
1042
1146
|
installCommand: 'npm install web-components-factory',
|
|
@@ -1118,10 +1222,10 @@ export function registerAll(context) {
|
|
|
1118
1222
|
{ name: 'get_component_selector_guide', purpose: 'Component selection guide by category and use case' },
|
|
1119
1223
|
],
|
|
1120
1224
|
recommendedWorkflow: [
|
|
1121
|
-
'1. get_design_system_overview →
|
|
1122
|
-
'2.
|
|
1123
|
-
'3.
|
|
1124
|
-
'4.
|
|
1225
|
+
'1. get_design_system_overview → start here and inspect componentPatternMap before creating custom HTML/CSS',
|
|
1226
|
+
'2. wcf://components and wcf://tokens resources → preload the component/token catalog',
|
|
1227
|
+
'3. figma_to_wcf (optional) → bootstrap the Figma-to-WCF tool sequence',
|
|
1228
|
+
'4. search_design_system_knowledge → do a broad first-pass search across components, patterns, tokens, guidelines, and skills',
|
|
1125
1229
|
'5. search_guidelines → find relevant guidelines',
|
|
1126
1230
|
'6. get_design_tokens → get correct token values',
|
|
1127
1231
|
'7. get_design_token_detail → inspect one token with references/referencedBy and usage examples',
|
|
@@ -1134,6 +1238,18 @@ export function registerAll(context) {
|
|
|
1134
1238
|
'14. generate_full_page_html → wrap fragment into a complete preview-ready page',
|
|
1135
1239
|
'15. get_install_recipe → get import/install instructions',
|
|
1136
1240
|
],
|
|
1241
|
+
recommendedPreloadResources: [
|
|
1242
|
+
{
|
|
1243
|
+
uri: WCF_RESOURCE_URIS.components,
|
|
1244
|
+
reason: 'Read this before prototyping to avoid re-implementing existing WCF components.',
|
|
1245
|
+
when: 'Before generating page markup',
|
|
1246
|
+
},
|
|
1247
|
+
{
|
|
1248
|
+
uri: WCF_RESOURCE_URIS.tokens,
|
|
1249
|
+
reason: 'Read this before styling to avoid hard-coded token values.',
|
|
1250
|
+
when: 'Before writing CSS or inline styles',
|
|
1251
|
+
},
|
|
1252
|
+
],
|
|
1137
1253
|
experimental: {
|
|
1138
1254
|
plugins: {
|
|
1139
1255
|
enabled: plugins.length > 0,
|
|
@@ -1261,6 +1377,12 @@ export function registerAll(context) {
|
|
|
1261
1377
|
if (related.length > 0) api.relatedComponents = related;
|
|
1262
1378
|
const a11y = extractAccessibilityChecklist(d, { prefix });
|
|
1263
1379
|
if (a11y) api.accessibilityChecklist = a11y;
|
|
1380
|
+
const interactionExamples = cTag ? INTERACTION_EXAMPLES_MAP[cTag] : undefined;
|
|
1381
|
+
if (interactionExamples) api.interactionExamples = interactionExamples;
|
|
1382
|
+
const layoutBehavior = cTag ? LAYOUT_BEHAVIOR_MAP[cTag] : undefined;
|
|
1383
|
+
if (layoutBehavior) api.layoutBehavior = layoutBehavior;
|
|
1384
|
+
const authoringGuidance = d?.custom?.authoringGuidance;
|
|
1385
|
+
if (authoringGuidance) api.authoringGuidance = authoringGuidance;
|
|
1264
1386
|
results.push(api);
|
|
1265
1387
|
}
|
|
1266
1388
|
return buildJsonToolResponse(results);
|
|
@@ -1301,6 +1423,10 @@ export function registerAll(context) {
|
|
|
1301
1423
|
if (accessibilityChecklist) {
|
|
1302
1424
|
api.accessibilityChecklist = accessibilityChecklist;
|
|
1303
1425
|
}
|
|
1426
|
+
const authoringGuidance = decl?.custom?.authoringGuidance;
|
|
1427
|
+
if (authoringGuidance) {
|
|
1428
|
+
api.authoringGuidance = authoringGuidance;
|
|
1429
|
+
}
|
|
1304
1430
|
const interactionExamples = canonicalTag ? INTERACTION_EXAMPLES_MAP[canonicalTag] : undefined;
|
|
1305
1431
|
if (interactionExamples) {
|
|
1306
1432
|
api.interactionExamples = interactionExamples;
|
|
@@ -1686,13 +1812,13 @@ export function registerAll(context) {
|
|
|
1686
1812
|
},
|
|
1687
1813
|
},
|
|
1688
1814
|
async ({ category, useCase }) => {
|
|
1689
|
-
if (!
|
|
1815
|
+
if (!selectorGuideData || !Array.isArray(selectorGuideData.categories)) {
|
|
1690
1816
|
return buildJsonToolErrorResponse({
|
|
1691
1817
|
error: 'Component selector guide not available.',
|
|
1692
1818
|
});
|
|
1693
1819
|
}
|
|
1694
1820
|
|
|
1695
|
-
let categories =
|
|
1821
|
+
let categories = selectorGuideData.categories;
|
|
1696
1822
|
|
|
1697
1823
|
if (typeof category === 'string' && category.trim()) {
|
|
1698
1824
|
const cat = category.trim().toLowerCase();
|
|
@@ -1700,15 +1826,35 @@ export function registerAll(context) {
|
|
|
1700
1826
|
}
|
|
1701
1827
|
|
|
1702
1828
|
if (typeof useCase === 'string' && useCase.trim()) {
|
|
1703
|
-
const
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
|
|
1829
|
+
const scored = [];
|
|
1830
|
+
for (const categoryEntry of categories) {
|
|
1831
|
+
const components = Array.isArray(categoryEntry?.components) ? categoryEntry.components : [];
|
|
1832
|
+
for (const component of components) {
|
|
1833
|
+
const score = scoreSelectorGuideComponent(component, useCase);
|
|
1834
|
+
if (score <= 0) continue;
|
|
1835
|
+
scored.push({
|
|
1836
|
+
categoryKey: categoryEntry.key,
|
|
1837
|
+
component: { ...component, _score: score },
|
|
1838
|
+
});
|
|
1839
|
+
}
|
|
1840
|
+
}
|
|
1841
|
+
|
|
1842
|
+
const grouped = new Map();
|
|
1843
|
+
for (const hit of scored.sort((left, right) => right.component._score - left.component._score)) {
|
|
1844
|
+
const list = grouped.get(hit.categoryKey) ?? [];
|
|
1845
|
+
list.push(hit.component);
|
|
1846
|
+
grouped.set(hit.categoryKey, list);
|
|
1847
|
+
}
|
|
1848
|
+
|
|
1849
|
+
categories = categories
|
|
1850
|
+
.map((categoryEntry) => ({
|
|
1851
|
+
...categoryEntry,
|
|
1852
|
+
components: (grouped.get(categoryEntry.key) ?? []).map((component) => {
|
|
1853
|
+
const { _score, ...rest } = component;
|
|
1854
|
+
return rest;
|
|
1855
|
+
}),
|
|
1856
|
+
}))
|
|
1857
|
+
.filter((categoryEntry) => categoryEntry.components.length > 0);
|
|
1712
1858
|
}
|
|
1713
1859
|
|
|
1714
1860
|
return buildJsonToolResponse({
|
|
@@ -2149,19 +2295,25 @@ export function registerAll(context) {
|
|
|
2149
2295
|
const terms = expandQueryWithSynonyms(q).filter(Boolean);
|
|
2150
2296
|
const limit = Number.isInteger(maxResults) ? maxResults : 10;
|
|
2151
2297
|
const results = [];
|
|
2298
|
+
const selectorGuideLookup = buildSelectorGuideComponentLookup(selectorGuideData);
|
|
2152
2299
|
|
|
2153
2300
|
if (requestedSources.has('components')) {
|
|
2154
2301
|
const page = buildComponentSummaries(indexes, {
|
|
2155
|
-
query: q,
|
|
2156
2302
|
limit: 200,
|
|
2157
2303
|
prefix: p,
|
|
2158
2304
|
});
|
|
2159
2305
|
for (const item of page.items) {
|
|
2306
|
+
const canonicalTagName = toCanonicalTagName(item.tagName, p) ?? item.tagName;
|
|
2307
|
+
const guideEntry =
|
|
2308
|
+
selectorGuideLookup.get(`tag:${String(canonicalTagName).toLowerCase()}`) ??
|
|
2309
|
+
selectorGuideLookup.get(`id:${installRegistry?.tags?.[String(canonicalTagName).toLowerCase()] ?? ''}`);
|
|
2160
2310
|
const score = scoreSearchFields(q, terms, [
|
|
2161
2311
|
{ text: item.tagName, weight: 5 },
|
|
2162
2312
|
{ text: item.className, weight: 4 },
|
|
2163
2313
|
{ text: item.description, weight: 2 },
|
|
2164
2314
|
{ text: item.category, weight: 1 },
|
|
2315
|
+
{ text: guideEntry?.useCase, weight: 4 },
|
|
2316
|
+
{ text: Array.isArray(guideEntry?.keywords) ? guideEntry.keywords.join(' ') : '', weight: 5 },
|
|
2165
2317
|
]);
|
|
2166
2318
|
if (score <= 0) continue;
|
|
2167
2319
|
results.push({
|
|
@@ -2172,6 +2324,8 @@ export function registerAll(context) {
|
|
|
2172
2324
|
metadata: {
|
|
2173
2325
|
className: item.className,
|
|
2174
2326
|
category: item.category,
|
|
2327
|
+
useCase: guideEntry?.useCase,
|
|
2328
|
+
keywords: guideEntry?.keywords ?? [],
|
|
2175
2329
|
},
|
|
2176
2330
|
score: score + getKnowledgeSourceBoost('components', q, terms),
|
|
2177
2331
|
});
|
package/core.mjs
CHANGED
|
@@ -164,6 +164,10 @@ export async function createMcpServer(loadJsonData, loadValidator, options = {})
|
|
|
164
164
|
detectNonLowercaseAttributes = () => [],
|
|
165
165
|
detectCdnReferences = () => [],
|
|
166
166
|
detectMissingRuntimeScaffold = () => [],
|
|
167
|
+
detectTableAuthoringMisuse = () => [],
|
|
168
|
+
detectResourceListAuthoringMisuse = () => [],
|
|
169
|
+
detectReplaceableNativePatterns = () => [],
|
|
170
|
+
detectReplaceableAnimationPatterns = () => [],
|
|
167
171
|
} = await loadValidator();
|
|
168
172
|
const canonicalCemIndex = collectCemCustomElements(manifest);
|
|
169
173
|
const canonicalEnumMap = buildEnumAttributeMap(manifest);
|
|
@@ -251,6 +255,10 @@ export async function createMcpServer(loadJsonData, loadValidator, options = {})
|
|
|
251
255
|
detectNonLowercaseAttributes,
|
|
252
256
|
detectCdnReferences,
|
|
253
257
|
detectMissingRuntimeScaffold,
|
|
258
|
+
detectTableAuthoringMisuse,
|
|
259
|
+
detectResourceListAuthoringMisuse,
|
|
260
|
+
detectReplaceableNativePatterns,
|
|
261
|
+
detectReplaceableAnimationPatterns,
|
|
254
262
|
}),
|
|
255
263
|
selectorGuideData,
|
|
256
264
|
maxToolResultBytes: MAX_TOOL_RESULT_BYTES,
|