@lism-css/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.ja.md +4 -2
- package/README.md +4 -2
- package/dist/data/docs-index.json +278 -51
- package/dist/data/guides/SKILL.md +113 -0
- package/dist/data/guides/base-styles.md +106 -0
- package/dist/data/guides/components-core.md +338 -0
- package/dist/data/guides/components-ui.md +351 -0
- package/dist/data/guides/css-rules.md +146 -0
- package/dist/data/guides/module-class.md +162 -0
- package/dist/data/guides/prop-responsive.md +54 -0
- package/dist/data/guides/property-class.md +400 -0
- package/dist/data/guides/set-class.md +190 -0
- package/dist/data/guides/tokens.md +210 -0
- package/dist/data/guides/utility-class.md +81 -0
- package/dist/index.js +4 -0
- package/dist/lib/load-data.js +2 -11
- package/dist/lib/load-markdown.d.ts +6 -0
- package/dist/lib/load-markdown.js +29 -0
- package/dist/lib/markdown-utils.d.ts +42 -0
- package/dist/lib/markdown-utils.js +158 -0
- package/dist/lib/schemas.d.ts +0 -242
- package/dist/lib/schemas.js +0 -64
- package/dist/lib/search.d.ts +2 -16
- package/dist/lib/search.js +9 -68
- package/dist/lib/types.d.ts +0 -64
- package/dist/tools/convert-css.js +96 -55
- package/dist/tools/get-component.js +60 -29
- package/dist/tools/get-guide.d.ts +2 -0
- package/dist/tools/get-guide.js +45 -0
- package/dist/tools/get-overview.js +26 -39
- package/dist/tools/get-props-system.js +45 -33
- package/dist/tools/get-tokens.js +9 -14
- package/dist/tools/search-docs.js +27 -9
- package/package.json +2 -2
- package/dist/data/components.json +0 -564
- package/dist/data/overview.json +0 -114
- package/dist/data/props-system.json +0 -1147
- package/dist/data/tokens.json +0 -148
|
@@ -1,74 +1,107 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
2
|
+
import { loadMarkdown } from '../lib/load-markdown.js';
|
|
3
|
+
import { parsePropRows } from '../lib/markdown-utils.js';
|
|
4
4
|
import { success, error, READ_ONLY_ANNOTATIONS } from '../lib/response.js';
|
|
5
5
|
// ----------------------------------------------------------------
|
|
6
6
|
// CSS パース
|
|
7
7
|
// ----------------------------------------------------------------
|
|
8
|
+
/** @ルール(@media 等)が含まれていないか検査する */
|
|
9
|
+
function detectAtRules(cssText) {
|
|
10
|
+
const atRuleMatch = cssText.match(/^@(\w[\w-]*)/m);
|
|
11
|
+
if (atRuleMatch) {
|
|
12
|
+
return `@${atRuleMatch[1]} ルールは未対応です。CSS 宣言(property: value;)のみを入力してください。`;
|
|
13
|
+
}
|
|
14
|
+
return null;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* CSS テキストから宣言を抽出する。
|
|
18
|
+
* `;` で分割する際に `url()` 等の括弧内の `;` を無視する。
|
|
19
|
+
*/
|
|
8
20
|
function parseCssDeclarations(cssText) {
|
|
9
21
|
// コメント除去
|
|
10
22
|
let cleaned = cssText.replace(/\/\*[\s\S]*?\*\//g, '');
|
|
11
23
|
// セレクタ + ブレースを除去(裸の宣言リストも受け付ける)
|
|
12
24
|
cleaned = cleaned.replace(/[^{}]*\{/g, '').replace(/\}/g, '');
|
|
25
|
+
// 括弧のネストを考慮して `;` で分割
|
|
26
|
+
const segments = [];
|
|
27
|
+
let current = '';
|
|
28
|
+
let parenDepth = 0;
|
|
29
|
+
for (const ch of cleaned) {
|
|
30
|
+
if (ch === '(')
|
|
31
|
+
parenDepth++;
|
|
32
|
+
if (ch === ')')
|
|
33
|
+
parenDepth = Math.max(0, parenDepth - 1);
|
|
34
|
+
if (ch === ';' && parenDepth === 0) {
|
|
35
|
+
segments.push(current.trim());
|
|
36
|
+
current = '';
|
|
37
|
+
}
|
|
38
|
+
else {
|
|
39
|
+
current += ch;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
if (current.trim())
|
|
43
|
+
segments.push(current.trim());
|
|
13
44
|
const declarations = [];
|
|
14
|
-
for (const
|
|
15
|
-
|
|
16
|
-
if (!trimmed)
|
|
45
|
+
for (const segment of segments) {
|
|
46
|
+
if (!segment)
|
|
17
47
|
continue;
|
|
18
|
-
const colonIdx =
|
|
48
|
+
const colonIdx = segment.indexOf(':');
|
|
19
49
|
if (colonIdx === -1)
|
|
20
50
|
continue;
|
|
21
|
-
const property =
|
|
22
|
-
const value =
|
|
51
|
+
const property = segment.substring(0, colonIdx).trim().toLowerCase();
|
|
52
|
+
const value = segment.substring(colonIdx + 1).trim();
|
|
23
53
|
if (property && value) {
|
|
24
54
|
declarations.push({ property, value });
|
|
25
55
|
}
|
|
26
56
|
}
|
|
27
57
|
return declarations;
|
|
28
58
|
}
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
59
|
+
/** プリセット値列から値を抽出する(例: "-fz:root, -fz:base" → ["root", "base"]) */
|
|
60
|
+
function extractPresetValues(presetColumn, propName) {
|
|
61
|
+
if (!presetColumn || presetColumn === '—' || presetColumn === '-')
|
|
62
|
+
return [];
|
|
63
|
+
const values = [];
|
|
64
|
+
// -{prop}:{value} パターンを全て抽出
|
|
65
|
+
const escaped = propName.replace(/[-/\\^$*+?.()|[\]{}]/g, '\\$&');
|
|
66
|
+
const regex = new RegExp(`-${escaped}:([^,\\s\`〜]+)`, 'g');
|
|
67
|
+
let match;
|
|
68
|
+
while ((match = regex.exec(presetColumn)) !== null) {
|
|
69
|
+
values.push(match[1]);
|
|
70
|
+
}
|
|
71
|
+
return values;
|
|
72
|
+
}
|
|
73
|
+
function buildMappings(md) {
|
|
74
|
+
return parsePropRows(md).map((row) => ({
|
|
75
|
+
prop: row.prop,
|
|
76
|
+
cssProperty: row.cssProperty,
|
|
77
|
+
presetValues: extractPresetValues(row.presetColumn, row.prop),
|
|
78
|
+
sectionName: row.sectionName,
|
|
79
|
+
}));
|
|
80
|
+
}
|
|
81
|
+
function buildCssPropertyMap(mappings) {
|
|
33
82
|
const map = new Map();
|
|
34
|
-
for (const
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
83
|
+
for (const mapping of mappings) {
|
|
84
|
+
const normalized = mapping.cssProperty.toLowerCase();
|
|
85
|
+
// CSS カスタムプロパティ形式はスキップ("--hl" 等)
|
|
86
|
+
if (!normalized.startsWith('(class:')) {
|
|
87
|
+
map.set(normalized, mapping);
|
|
39
88
|
}
|
|
40
89
|
}
|
|
41
90
|
return map;
|
|
42
91
|
}
|
|
43
|
-
/** cssProperty フィールドの正規化 */
|
|
44
|
-
function normalizeCssPropertyName(raw) {
|
|
45
|
-
// "(class: is--container)" → スキップ(CSS プロパティではない)
|
|
46
|
-
if (raw.startsWith('(class:'))
|
|
47
|
-
return null;
|
|
48
|
-
// "--hl (CSS変数)" → "--hl"
|
|
49
|
-
return raw
|
|
50
|
-
.replace(/\s*\(.*\)$/, '')
|
|
51
|
-
.trim()
|
|
52
|
-
.toLowerCase();
|
|
53
|
-
}
|
|
54
92
|
// ----------------------------------------------------------------
|
|
55
93
|
// 値のマッピング
|
|
56
94
|
// ----------------------------------------------------------------
|
|
57
95
|
/** よくある CSS 値 → Lism トークン値の変換テーブル */
|
|
58
96
|
const VALUE_ALIASES = {
|
|
59
|
-
column: 'col',
|
|
60
|
-
'column-reverse': 'col-r',
|
|
61
|
-
'row-reverse': 'row-r',
|
|
62
97
|
'space-between': 'between',
|
|
63
|
-
|
|
64
|
-
'flex-end': 'flex-e',
|
|
65
|
-
currentcolor: 'cc',
|
|
98
|
+
currentcolor: 'current',
|
|
66
99
|
uppercase: 'upper',
|
|
67
100
|
lowercase: 'lower',
|
|
68
101
|
};
|
|
69
|
-
function suggestValue(
|
|
70
|
-
const tokens =
|
|
71
|
-
if (
|
|
102
|
+
function suggestValue(mapping, cssValue) {
|
|
103
|
+
const tokens = mapping.presetValues;
|
|
104
|
+
if (tokens.length === 0)
|
|
72
105
|
return null;
|
|
73
106
|
// 直接一致
|
|
74
107
|
if (tokens.includes(cssValue))
|
|
@@ -124,12 +157,9 @@ function detectComponent(declarations) {
|
|
|
124
157
|
// ----------------------------------------------------------------
|
|
125
158
|
// 変換メイン
|
|
126
159
|
// ----------------------------------------------------------------
|
|
127
|
-
function findCategory(
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
return cat.category;
|
|
131
|
-
}
|
|
132
|
-
return 'unknown';
|
|
160
|
+
function findCategory(mappings, propName) {
|
|
161
|
+
const found = mappings.find((m) => m.prop === propName);
|
|
162
|
+
return found?.sectionName ?? 'unknown';
|
|
133
163
|
}
|
|
134
164
|
function buildExample(conversions, component) {
|
|
135
165
|
const tagName = component?.name ?? 'Lism';
|
|
@@ -167,44 +197,55 @@ function buildExample(conversions, component) {
|
|
|
167
197
|
// ----------------------------------------------------------------
|
|
168
198
|
export function registerConvertCss(server) {
|
|
169
199
|
server.registerTool('convert_css', {
|
|
170
|
-
description: 'Convert CSS code to lism-css props, utility classes, and component suggestions. Accepts CSS declarations (with or without selectors) and returns the equivalent lism-css representation
|
|
200
|
+
description: 'Convert CSS code to lism-css props, utility classes, and component suggestions. Accepts CSS declarations (with or without selectors) and returns the equivalent lism-css representation as structured JSON.\n' +
|
|
201
|
+
'Use this when migrating existing CSS to lism-css in bulk, or when you need to understand how multiple CSS properties map to lism-css at once.\n' +
|
|
202
|
+
'Do NOT use this for single prop lookups (use get_props_system instead). Note: @media and other at-rules are NOT supported — an error will be returned if detected.\n' +
|
|
203
|
+
'Returns JSON with conversions (prop mappings with confidence), component suggestions, and a JSX usage example.',
|
|
171
204
|
inputSchema: {
|
|
172
205
|
css: z
|
|
173
206
|
.string()
|
|
174
|
-
.describe('CSS code to convert. Accepts a full rule block with selector (e.g. ".foo { padding: 1rem; }") or bare declarations (e.g. "padding: 1rem; font-size: 16px;").'),
|
|
207
|
+
.describe('CSS code to convert. Accepts a full rule block with selector (e.g. ".foo { padding: 1rem; }") or bare declarations (e.g. "padding: 1rem; font-size: 16px;"). @media and other at-rules are not supported.'),
|
|
175
208
|
},
|
|
176
209
|
annotations: READ_ONLY_ANNOTATIONS,
|
|
177
210
|
}, ({ css }) => {
|
|
178
211
|
try {
|
|
179
|
-
|
|
180
|
-
const
|
|
212
|
+
// @ ルール検出
|
|
213
|
+
const atRuleError = detectAtRules(css);
|
|
214
|
+
if (atRuleError) {
|
|
215
|
+
return error(atRuleError);
|
|
216
|
+
}
|
|
217
|
+
const md = loadMarkdown('property-class.md');
|
|
218
|
+
const mappings = buildMappings(md);
|
|
219
|
+
const cssPropertyMap = buildCssPropertyMap(mappings);
|
|
181
220
|
const declarations = parseCssDeclarations(css);
|
|
182
221
|
if (declarations.length === 0) {
|
|
183
222
|
return error('CSS 宣言が見つかりません。"property: value;" 形式の CSS を入力してください。');
|
|
184
223
|
}
|
|
185
224
|
// 各宣言を変換
|
|
186
225
|
const conversions = declarations.map((decl) => {
|
|
187
|
-
const
|
|
188
|
-
if (!
|
|
226
|
+
const mapping = cssPropertyMap.get(decl.property);
|
|
227
|
+
if (!mapping) {
|
|
189
228
|
return {
|
|
190
229
|
css: `${decl.property}: ${decl.value}`,
|
|
191
230
|
lismProp: null,
|
|
192
231
|
suggestedValue: null,
|
|
193
232
|
availableTokens: null,
|
|
233
|
+
confidence: 'unmapped',
|
|
194
234
|
note: 'Lism Props に該当なし。style で直接指定してください。',
|
|
195
235
|
};
|
|
196
236
|
}
|
|
197
|
-
const suggested = suggestValue(
|
|
198
|
-
const category = findCategory(
|
|
237
|
+
const suggested = suggestValue(mapping, decl.value);
|
|
238
|
+
const category = findCategory(mappings, mapping.prop);
|
|
199
239
|
return {
|
|
200
240
|
css: `${decl.property}: ${decl.value}`,
|
|
201
|
-
lismProp:
|
|
241
|
+
lismProp: mapping.prop,
|
|
202
242
|
suggestedValue: suggested,
|
|
203
|
-
availableTokens:
|
|
243
|
+
availableTokens: mapping.presetValues.length > 0 ? mapping.presetValues : null,
|
|
244
|
+
confidence: suggested ? 'exact' : 'approximate',
|
|
204
245
|
note: suggested
|
|
205
246
|
? `トークン値 '${suggested}' を使用(カテゴリ: ${category})`
|
|
206
|
-
:
|
|
207
|
-
? `カスタム値。利用可能なトークン: ${
|
|
247
|
+
: mapping.presetValues.length > 0
|
|
248
|
+
? `カスタム値。利用可能なトークン: ${mapping.presetValues.join(', ')}(カテゴリ: ${category})`
|
|
208
249
|
: `カスタム値として指定(カテゴリ: ${category})`,
|
|
209
250
|
};
|
|
210
251
|
});
|
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
2
|
+
import { loadMarkdown } from '../lib/load-markdown.js';
|
|
3
|
+
import { findComponentByHeading, findComponentInTables } from '../lib/markdown-utils.js';
|
|
4
|
+
import { markdownResponse, error, notFound, READ_ONLY_ANNOTATIONS } from '../lib/response.js';
|
|
5
5
|
export function registerGetComponent(server) {
|
|
6
6
|
server.registerTool('get_component', {
|
|
7
|
-
description: '
|
|
7
|
+
description: 'Get detailed information about a specific lism-css component: purpose, props, and usage examples.\n' +
|
|
8
|
+
'Use this when you need documentation for a known component by name (e.g. "Box", "Flex", "Accordion", "Lism", "HTML").\n' +
|
|
9
|
+
'Do NOT use this for broad topic guides (use get_guide with "components-core" or "components-ui") or keyword search across all docs (use search_docs).\n' +
|
|
10
|
+
'If the component is not found, suggestions will be provided — follow up with search_docs for a broader query.\n' +
|
|
11
|
+
'The response is pre-formatted Markdown. Output it verbatim. Do NOT summarize or omit code examples.',
|
|
8
12
|
inputSchema: {
|
|
9
13
|
name: z.string().describe('Component name to look up (e.g. "Box", "Flex", "Accordion").'),
|
|
10
14
|
package: z
|
|
@@ -15,42 +19,69 @@ export function registerGetComponent(server) {
|
|
|
15
19
|
annotations: READ_ONLY_ANNOTATIONS,
|
|
16
20
|
}, ({ name, package: pkg }) => {
|
|
17
21
|
try {
|
|
18
|
-
const data = loadJSON('components.json', z.array(ComponentInfoSchema));
|
|
19
|
-
let candidates = data;
|
|
20
|
-
if (pkg) {
|
|
21
|
-
candidates = data.filter((c) => c.package === pkg);
|
|
22
|
-
}
|
|
23
22
|
const nameLower = name.toLowerCase();
|
|
24
|
-
//
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
23
|
+
// --- @lism-css/ui コンポーネントを検索 ---
|
|
24
|
+
if (!pkg || pkg === '@lism-css/ui') {
|
|
25
|
+
const uiMd = loadMarkdown('components-ui.md');
|
|
26
|
+
// ## ComponentName 形式の見出しで検索(完全一致)
|
|
27
|
+
const uiSection = findComponentByHeading(uiMd, name);
|
|
28
|
+
if (uiSection) {
|
|
29
|
+
return markdownResponse(uiSection);
|
|
30
|
+
}
|
|
31
|
+
// 大文字小文字を無視した見出し検索
|
|
32
|
+
const uiLines = uiMd.split('\n');
|
|
33
|
+
const headingLine = uiLines.find((l) => l.startsWith('## ') && l.slice(3).trim().toLowerCase() === nameLower);
|
|
34
|
+
if (headingLine) {
|
|
35
|
+
const section = findComponentByHeading(uiMd, headingLine.slice(3).trim());
|
|
36
|
+
if (section)
|
|
37
|
+
return markdownResponse(section);
|
|
38
|
+
}
|
|
28
39
|
}
|
|
29
|
-
//
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
40
|
+
// --- lism-css コアコンポーネントを検索 ---
|
|
41
|
+
if (!pkg || pkg === 'lism-css') {
|
|
42
|
+
const coreMd = loadMarkdown('components-core.md');
|
|
43
|
+
// 見出しにコンポーネント名を含むセクションを検索(Lism, HTML 等)
|
|
44
|
+
const coreLines = coreMd.split('\n');
|
|
45
|
+
const headingLine = coreLines.find((l) => /^#{2,3}\s/.test(l) && l.toLowerCase().includes(`<${nameLower}>`));
|
|
46
|
+
if (headingLine) {
|
|
47
|
+
const headingText = headingLine.replace(/^#+\s*/, '').trim();
|
|
48
|
+
const section = findComponentByHeading(coreMd, headingText);
|
|
49
|
+
if (section)
|
|
50
|
+
return markdownResponse(section);
|
|
51
|
+
}
|
|
52
|
+
// テーブル内の `<ComponentName>` で検索
|
|
53
|
+
const coreSection = findComponentInTables(coreMd, name);
|
|
54
|
+
if (coreSection) {
|
|
55
|
+
return markdownResponse(coreSection);
|
|
56
|
+
}
|
|
33
57
|
}
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
58
|
+
// --- 部分一致で候補を提示 ---
|
|
59
|
+
const uiMd = loadMarkdown('components-ui.md');
|
|
60
|
+
const coreMd = loadMarkdown('components-core.md');
|
|
61
|
+
const uiCandidates = uiMd
|
|
62
|
+
.split('\n')
|
|
63
|
+
.filter((l) => l.startsWith('## ') && l.toLowerCase().includes(nameLower))
|
|
64
|
+
.map((l) => l.slice(3).trim());
|
|
65
|
+
const coreCandidates = [];
|
|
66
|
+
for (const line of coreMd.split('\n')) {
|
|
67
|
+
if (line.startsWith('|') && line.toLowerCase().includes(nameLower)) {
|
|
68
|
+
const match = line.match(/`<([^>]+)>`/);
|
|
69
|
+
if (match)
|
|
70
|
+
coreCandidates.push(match[1]);
|
|
71
|
+
}
|
|
41
72
|
}
|
|
42
|
-
|
|
43
|
-
const suggestions = candidates.filter((c) => c.name.toLowerCase().includes(nameLower)).map((c) => ({ name: c.name, package: c.package }));
|
|
73
|
+
const suggestions = [...new Set([...uiCandidates, ...coreCandidates])];
|
|
44
74
|
if (suggestions.length > 0) {
|
|
45
75
|
return notFound(`Component "${name}" not found. Did you mean one of these? Or try search_docs with a broader query.`, {
|
|
46
76
|
suggestions,
|
|
47
77
|
});
|
|
48
78
|
}
|
|
49
|
-
|
|
50
|
-
|
|
79
|
+
return notFound(`Component "${name}" not found. Try search_docs or get_guide to find related pages.`, {
|
|
80
|
+
tip: 'Use get_guide with topic="components-core" or "components-ui" to see all available components.',
|
|
81
|
+
});
|
|
51
82
|
}
|
|
52
83
|
catch (e) {
|
|
53
|
-
return error(`Failed to load component data: ${e instanceof Error ? e.message : String(e)}. The data files may not be built yet.
|
|
84
|
+
return error(`Failed to load component data: ${e instanceof Error ? e.message : String(e)}. The data files may not be built yet. Run "pnpm build" in packages/mcp first.`);
|
|
54
85
|
}
|
|
55
86
|
});
|
|
56
87
|
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { loadMarkdown } from '../lib/load-markdown.js';
|
|
3
|
+
import { markdownResponse, error, READ_ONLY_ANNOTATIONS } from '../lib/response.js';
|
|
4
|
+
const GUIDE_TOPICS = {
|
|
5
|
+
overview: { file: 'SKILL.md', label: 'Framework overview, packages, implementation rules' },
|
|
6
|
+
tokens: { file: 'tokens.md', label: 'Design tokens (spacing, colors, font sizes, etc.)' },
|
|
7
|
+
'property-class': { file: 'property-class.md', label: 'Property Class system, all props reference table' },
|
|
8
|
+
'components-core': {
|
|
9
|
+
file: 'components-core.md',
|
|
10
|
+
label: 'Core component system (Lism, Box, Flex, Stack, Grid, etc.)',
|
|
11
|
+
},
|
|
12
|
+
'components-ui': {
|
|
13
|
+
file: 'components-ui.md',
|
|
14
|
+
label: 'UI components (Accordion, Modal, Tabs, Button, etc.)',
|
|
15
|
+
},
|
|
16
|
+
'base-styles': { file: 'base-styles.md', label: 'Base styling, reset CSS, HTML element styles' },
|
|
17
|
+
'set-class': { file: 'set-class.md', label: 'Set classes (set--plain, set--shadow, set--hov, etc.)' },
|
|
18
|
+
'module-class': { file: 'module-class.md', label: 'Module class prefixes (is--, l--, a--, c--)' },
|
|
19
|
+
'utility-class': { file: 'utility-class.md', label: 'Utility classes (u--trim, u--cbox, etc.)' },
|
|
20
|
+
'css-rules': { file: 'css-rules.md', label: 'CSS methodology, layer structure, naming conventions' },
|
|
21
|
+
responsive: { file: 'prop-responsive.md', label: 'Responsive design, breakpoints, container queries' },
|
|
22
|
+
};
|
|
23
|
+
const TOPIC_DESCRIPTION = Object.entries(GUIDE_TOPICS)
|
|
24
|
+
.map(([key, { label }]) => `- ${key}: ${label}`)
|
|
25
|
+
.join('\n');
|
|
26
|
+
export function registerGetGuide(server) {
|
|
27
|
+
server.registerTool('get_guide', {
|
|
28
|
+
description: 'Get a detailed guide on a specific lism-css topic. Use this when you need comprehensive documentation on a broad topic rather than a specific component or prop.\n' +
|
|
29
|
+
'For individual component lookup, get_component is more direct. For individual prop lookup, use get_props_system.\n' +
|
|
30
|
+
'The response is the full guide as pre-formatted Markdown. Output it verbatim. Do NOT summarize or omit sections.\n' +
|
|
31
|
+
`\nAvailable topics:\n${TOPIC_DESCRIPTION}`,
|
|
32
|
+
inputSchema: {
|
|
33
|
+
topic: z.enum(Object.keys(GUIDE_TOPICS)).describe('The guide topic to retrieve.'),
|
|
34
|
+
},
|
|
35
|
+
annotations: READ_ONLY_ANNOTATIONS,
|
|
36
|
+
}, ({ topic }) => {
|
|
37
|
+
try {
|
|
38
|
+
const { file } = GUIDE_TOPICS[topic];
|
|
39
|
+
return markdownResponse(loadMarkdown(file));
|
|
40
|
+
}
|
|
41
|
+
catch (e) {
|
|
42
|
+
return error(`Failed to load guide "${topic}": ${e instanceof Error ? e.message : String(e)}. The data files may not be built yet. Run "pnpm build" in packages/mcp first.`);
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
}
|
|
@@ -1,53 +1,40 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { loadMarkdown } from '../lib/load-markdown.js';
|
|
2
|
+
import { extractSection } from '../lib/markdown-utils.js';
|
|
3
3
|
import { markdownResponse, error, READ_ONLY_ANNOTATIONS } from '../lib/response.js';
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
lines.push('');
|
|
19
|
-
lines.push(`## Packages`);
|
|
20
|
-
lines.push('');
|
|
21
|
-
for (const pkg of data.packages) {
|
|
22
|
-
lines.push(`- **${pkg.name}** (\`${pkg.npmName}\` v${pkg.version}): ${pkg.description}`);
|
|
4
|
+
/**
|
|
5
|
+
* SKILL.md を中核に、css-rules.md の Layer 構造セクションと
|
|
6
|
+
* prop-responsive.md のブレークポイントセクションを付加して返す。
|
|
7
|
+
*/
|
|
8
|
+
function buildOverviewMarkdown() {
|
|
9
|
+
const skill = loadMarkdown('SKILL.md');
|
|
10
|
+
const cssRules = loadMarkdown('css-rules.md');
|
|
11
|
+
const responsive = loadMarkdown('prop-responsive.md');
|
|
12
|
+
const layerSection = extractSection(cssRules, 'CSS Layer 構造');
|
|
13
|
+
const bpSection = extractSection(responsive, 'ブレークポイント');
|
|
14
|
+
const parts = [skill];
|
|
15
|
+
if (layerSection) {
|
|
16
|
+
parts.push('\n---\n');
|
|
17
|
+
parts.push(layerSection);
|
|
23
18
|
}
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
for (const [key, value] of Object.entries(data.breakpoints)) {
|
|
28
|
-
lines.push(`- \`${key}\`: ${value}`);
|
|
19
|
+
if (bpSection) {
|
|
20
|
+
parts.push('\n---\n');
|
|
21
|
+
parts.push(bpSection);
|
|
29
22
|
}
|
|
30
|
-
|
|
31
|
-
lines.push(`## CSS Layers`);
|
|
32
|
-
lines.push('');
|
|
33
|
-
lines.push(data.cssLayers);
|
|
34
|
-
lines.push('');
|
|
35
|
-
lines.push(`## Installation`);
|
|
36
|
-
lines.push('');
|
|
37
|
-
lines.push(data.installation);
|
|
38
|
-
return lines.join('\n');
|
|
23
|
+
return parts.join('\n');
|
|
39
24
|
}
|
|
40
25
|
export function registerGetOverview(server) {
|
|
41
26
|
server.registerTool('get_overview', {
|
|
42
|
-
description: 'Get an overview of the lism-css framework: architecture, design philosophy, packages, breakpoints,
|
|
27
|
+
description: 'Get an overview of the lism-css framework: architecture, design philosophy, packages, breakpoints, CSS layers, and implementation rules.\n' +
|
|
28
|
+
'Use this as your FIRST call when starting any lism-css task — it provides the foundational context needed to use other tools effectively.\n' +
|
|
29
|
+
'Do NOT use this to look up specific components (use get_component), individual props (use get_props_system), or design tokens (use get_tokens).\n' +
|
|
30
|
+
'The response is pre-formatted Markdown. Output it verbatim to the user. Do NOT summarize or omit sections.',
|
|
43
31
|
annotations: READ_ONLY_ANNOTATIONS,
|
|
44
32
|
}, () => {
|
|
45
33
|
try {
|
|
46
|
-
|
|
47
|
-
return markdownResponse(toMarkdown(data));
|
|
34
|
+
return markdownResponse(buildOverviewMarkdown());
|
|
48
35
|
}
|
|
49
36
|
catch (e) {
|
|
50
|
-
return error(`Failed to load overview data: ${e instanceof Error ? e.message : String(e)}. The data files may not be built yet.
|
|
37
|
+
return error(`Failed to load overview data: ${e instanceof Error ? e.message : String(e)}. The data files may not be built yet. Run "pnpm build" in packages/mcp first.`);
|
|
51
38
|
}
|
|
52
39
|
});
|
|
53
40
|
}
|
|
@@ -1,61 +1,73 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
2
|
+
import { loadMarkdown } from '../lib/load-markdown.js';
|
|
3
|
+
import { parsePropRows } from '../lib/markdown-utils.js';
|
|
4
4
|
import { parsePropClassName } from '../lib/search.js';
|
|
5
|
-
import {
|
|
6
|
-
/** cssProperty
|
|
5
|
+
import { markdownResponse, error, notFound, READ_ONLY_ANNOTATIONS } from '../lib/response.js';
|
|
6
|
+
/** cssProperty フィールドからコア名を抽出("--hl 変数のみ" → "--hl") */
|
|
7
7
|
function normalizeCssProperty(raw) {
|
|
8
|
-
// "(class: is--container)" → "is--container"
|
|
9
|
-
const classMatch = raw.match(/\(class:\s*(.+?)\)/);
|
|
10
|
-
if (classMatch)
|
|
11
|
-
return classMatch[1].trim().toLowerCase();
|
|
12
|
-
// "--hl (CSS変数)" → "--hl"
|
|
13
8
|
return raw
|
|
14
9
|
.replace(/\s*\(.*\)$/, '')
|
|
10
|
+
.replace(/([^)]*)$/, '')
|
|
15
11
|
.trim()
|
|
16
12
|
.toLowerCase();
|
|
17
13
|
}
|
|
18
14
|
export function registerGetPropsSystem(server) {
|
|
19
15
|
server.registerTool('get_props_system', {
|
|
20
|
-
description: 'Get the lism-css Props system reference: how React/Astro props map to CSS classes and styles. Supports lookup by lism prop name (e.g. "p", "fz") OR by CSS property name (e.g. "padding", "font-size").
|
|
16
|
+
description: 'Get the lism-css Props system reference: how React/Astro props map to CSS classes and styles. Supports lookup by lism prop name (e.g. "p", "fz") OR by CSS property name (e.g. "padding", "font-size"). Omit the prop parameter to get the full reference.\n' +
|
|
17
|
+
'Use this when you need to find a specific prop mapping, understand the Property Class system, or check what CSS property a lism prop corresponds to.\n' +
|
|
18
|
+
'For bulk CSS-to-lism conversion, convert_css is more efficient. For component-specific documentation, use get_component.\n' +
|
|
19
|
+
'The response is pre-formatted Markdown. Output it verbatim. Do NOT summarize the prop tables.',
|
|
21
20
|
inputSchema: {
|
|
22
21
|
prop: z
|
|
23
22
|
.string()
|
|
24
23
|
.optional()
|
|
25
|
-
.describe('Prop name or CSS property name to look up. Accepts lism prop names (e.g. "p", "fz", "bgc") and standard CSS property names (e.g. "padding", "font-size", "background-color"). Omit to get the full
|
|
24
|
+
.describe('Prop name or CSS property name to look up. Accepts lism prop names (e.g. "p", "fz", "bgc") and standard CSS property names (e.g. "padding", "font-size", "background-color"). Omit to get the full reference.'),
|
|
26
25
|
},
|
|
27
26
|
annotations: READ_ONLY_ANNOTATIONS,
|
|
28
27
|
}, ({ prop }) => {
|
|
29
28
|
try {
|
|
30
|
-
const
|
|
29
|
+
const md = loadMarkdown('property-class.md');
|
|
30
|
+
// prop 未指定: 全文を返す
|
|
31
31
|
if (!prop) {
|
|
32
|
-
return
|
|
32
|
+
return markdownResponse(md);
|
|
33
33
|
}
|
|
34
34
|
const queryLower = parsePropClassName(prop) ?? prop.toLowerCase();
|
|
35
|
-
const
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
return false;
|
|
46
|
-
});
|
|
47
|
-
if (found.length > 0) {
|
|
48
|
-
matched.push({ ...cat, props: found });
|
|
49
|
-
}
|
|
50
|
-
}
|
|
35
|
+
const rows = parsePropRows(md);
|
|
36
|
+
// 一致する行を探す
|
|
37
|
+
const matched = rows.filter((row) => {
|
|
38
|
+
if (row.prop.toLowerCase() === queryLower)
|
|
39
|
+
return true;
|
|
40
|
+
const normalizedCss = normalizeCssProperty(row.cssProperty);
|
|
41
|
+
if (normalizedCss === queryLower)
|
|
42
|
+
return true;
|
|
43
|
+
return false;
|
|
44
|
+
});
|
|
51
45
|
if (matched.length === 0) {
|
|
52
|
-
const
|
|
53
|
-
return notFound(`"${prop}" に一致する Prop が見つかりません。Lism prop 名 (例: "p", "fz") または CSS プロパティ名 (例: "padding", "font-size") で検索できます。`, { availableProps
|
|
46
|
+
const availableProps = rows.map((r) => `${r.prop} (${r.cssProperty})`);
|
|
47
|
+
return notFound(`"${prop}" に一致する Prop が見つかりません。Lism prop 名 (例: "p", "fz") または CSS プロパティ名 (例: "padding", "font-size") で検索できます。`, { availableProps });
|
|
48
|
+
}
|
|
49
|
+
// 一致したセクション名のユニーク一覧を取得
|
|
50
|
+
const sections = [...new Set(matched.map((r) => r.sectionName))];
|
|
51
|
+
// property-class.md から対象セクションを抽出して結合
|
|
52
|
+
const lines = md.split('\n');
|
|
53
|
+
const resultParts = [`## 検索結果: "${prop}"\n`];
|
|
54
|
+
for (const sectionName of sections) {
|
|
55
|
+
// セクション内の一致する行だけを含む簡易 Markdown を生成
|
|
56
|
+
const sectionRows = matched.filter((r) => r.sectionName === sectionName);
|
|
57
|
+
const header = `### ${sectionName}\n`;
|
|
58
|
+
const tableHeader = '| Prop | CSS プロパティ | プリセット値クラス | BP クラス |\n|------|--------------|-------------|-----|';
|
|
59
|
+
// 対応する元テーブル行を探して取得
|
|
60
|
+
const tableRows = sectionRows.map((row) => {
|
|
61
|
+
const pattern = new RegExp(`^\\|\\s*\`${row.prop.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&')}\`\\s*\\|`);
|
|
62
|
+
const originalLine = lines.find((l) => pattern.test(l));
|
|
63
|
+
return originalLine ?? `| \`${row.prop}\` | \`${row.cssProperty}\` | — | — |`;
|
|
64
|
+
});
|
|
65
|
+
resultParts.push(header + tableHeader + '\n' + tableRows.join('\n'));
|
|
54
66
|
}
|
|
55
|
-
return
|
|
67
|
+
return markdownResponse(resultParts.join('\n\n'));
|
|
56
68
|
}
|
|
57
69
|
catch (e) {
|
|
58
|
-
return error(`Failed to load props system data: ${e instanceof Error ? e.message : String(e)}. The data files may not be built yet.
|
|
70
|
+
return error(`Failed to load props system data: ${e instanceof Error ? e.message : String(e)}. The data files may not be built yet. Run "pnpm build" in packages/mcp first.`);
|
|
59
71
|
}
|
|
60
72
|
});
|
|
61
73
|
}
|
package/dist/tools/get-tokens.js
CHANGED
|
@@ -1,23 +1,18 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import { TokenCategorySchema } from '../lib/schemas.js';
|
|
4
|
-
import { success, error, READ_ONLY_ANNOTATIONS } from '../lib/response.js';
|
|
5
|
-
const TOKEN_CATEGORIES = ['all', 'color', 'spacing', 'fontSize', 'shadow', 'radius', 'lineHeight', 'letterSpacing', 'fontFamily', 'zIndex'];
|
|
1
|
+
import { loadMarkdown } from '../lib/load-markdown.js';
|
|
2
|
+
import { markdownResponse, error, READ_ONLY_ANNOTATIONS } from '../lib/response.js';
|
|
6
3
|
export function registerGetTokens(server) {
|
|
7
4
|
server.registerTool('get_tokens', {
|
|
8
|
-
description: 'Get design tokens (colors, spacing, font sizes, shadows, etc.) used in lism-css.
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
5
|
+
description: 'Get design tokens (colors, spacing, font sizes, shadows, etc.) used in lism-css. Returns the full token reference including CSS variable names and available values.\n' +
|
|
6
|
+
'Use this when you need to check available token values, variable names, or design scales (e.g. "what spacing values exist?", "what are the font size tokens?").\n' +
|
|
7
|
+
'For prop-to-CSS mappings, get_props_system is more suitable. For CSS conversion, use convert_css. Call get_overview first if you have not yet.\n' +
|
|
8
|
+
'The response is pre-formatted Markdown. Output it verbatim. Do NOT summarize or omit token values.',
|
|
12
9
|
annotations: READ_ONLY_ANNOTATIONS,
|
|
13
|
-
}, (
|
|
10
|
+
}, () => {
|
|
14
11
|
try {
|
|
15
|
-
|
|
16
|
-
const filtered = category === 'all' ? data : data.filter((c) => c.category === category);
|
|
17
|
-
return success({ tokens: filtered });
|
|
12
|
+
return markdownResponse(loadMarkdown('tokens.md'));
|
|
18
13
|
}
|
|
19
14
|
catch (e) {
|
|
20
|
-
return error(`Failed to load tokens data: ${e instanceof Error ? e.message : String(e)}. The data files may not be built yet.
|
|
15
|
+
return error(`Failed to load tokens data: ${e instanceof Error ? e.message : String(e)}. The data files may not be built yet. Run "pnpm build" in packages/mcp first.`);
|
|
21
16
|
}
|
|
22
17
|
});
|
|
23
18
|
}
|