@lism-css/mcp 0.22.0 → 0.24.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.
Files changed (74) hide show
  1. package/README.ja.md +15 -15
  2. package/README.md +5 -5
  3. package/dist/data/docs-index.json +232 -96
  4. package/dist/data/guides/SKILL.md +162 -224
  5. package/dist/data/guides/antipatterns-layout.md +268 -0
  6. package/dist/data/guides/antipatterns.md +118 -196
  7. package/dist/data/guides/base-styles.md +14 -12
  8. package/dist/data/guides/components-core.md +26 -8
  9. package/dist/data/guides/components-ui.md +28 -24
  10. package/dist/data/guides/css-rules.md +40 -57
  11. package/dist/data/guides/customize.md +121 -37
  12. package/dist/data/guides/naming.md +23 -42
  13. package/dist/data/guides/primitive-class.md +5 -5
  14. package/dist/data/guides/primitives/a--decorator.md +2 -28
  15. package/dist/data/guides/primitives/a--divider.md +1 -52
  16. package/dist/data/guides/primitives/a--icon.md +2 -76
  17. package/dist/data/guides/primitives/a--spacer.md +1 -49
  18. package/dist/data/guides/primitives/l--autoColumns.md +7 -54
  19. package/dist/data/guides/primitives/l--box.md +1 -21
  20. package/dist/data/guides/primitives/l--center.md +6 -39
  21. package/dist/data/guides/primitives/l--cluster.md +6 -26
  22. package/dist/data/guides/primitives/l--columns.md +7 -56
  23. package/dist/data/guides/primitives/l--flex.md +5 -62
  24. package/dist/data/guides/primitives/l--flow.md +11 -72
  25. package/dist/data/guides/primitives/l--frame.md +7 -78
  26. package/dist/data/guides/primitives/l--grid.md +5 -56
  27. package/dist/data/guides/primitives/l--stack.md +5 -44
  28. package/dist/data/guides/primitives/l--switchColumns.md +8 -53
  29. package/dist/data/guides/primitives/l--tileGrid.md +7 -44
  30. package/dist/data/guides/primitives/l--withSide.md +9 -79
  31. package/dist/data/guides/property-class/all-props.md +244 -0
  32. package/dist/data/guides/property-class/bd.md +5 -70
  33. package/dist/data/guides/property-class/hov.md +14 -73
  34. package/dist/data/guides/property-class/max-sz.md +3 -39
  35. package/dist/data/guides/property-class.md +31 -249
  36. package/dist/data/guides/references/authoring.md +246 -0
  37. package/dist/data/guides/references/page-sections.md +99 -0
  38. package/dist/data/guides/references/verification.md +73 -0
  39. package/dist/data/guides/responsive.md +68 -20
  40. package/dist/data/guides/set-class.md +2 -12
  41. package/dist/data/guides/tokens.md +31 -31
  42. package/dist/data/guides/trait-class/has--gutter.md +3 -31
  43. package/dist/data/guides/trait-class/has--mask.md +3 -36
  44. package/dist/data/guides/trait-class/has--snap.md +3 -34
  45. package/dist/data/guides/trait-class/has--transition.md +3 -41
  46. package/dist/data/guides/trait-class/is--boxLink.md +2 -63
  47. package/dist/data/guides/trait-class/is--container.md +2 -29
  48. package/dist/data/guides/trait-class/is--layer.md +1 -57
  49. package/dist/data/guides/trait-class/is--wrapper.md +11 -60
  50. package/dist/data/guides/trait-class.md +8 -8
  51. package/dist/data/guides/utility-class.md +1 -1
  52. package/dist/data/meta.js +4 -3
  53. package/dist/index.js +4 -1
  54. package/dist/lib/load-markdown.d.ts +4 -0
  55. package/dist/lib/load-markdown.js +10 -0
  56. package/dist/lib/markdown-utils.d.ts +1 -1
  57. package/dist/lib/response.d.ts +5 -0
  58. package/dist/lib/response.js +15 -2
  59. package/dist/lib/schemas.d.ts +35 -0
  60. package/dist/lib/schemas.js +13 -0
  61. package/dist/lib/search.d.ts +2 -0
  62. package/dist/lib/search.js +45 -2
  63. package/dist/lib/types.d.ts +5 -21
  64. package/dist/lib/version.d.ts +2 -0
  65. package/dist/lib/version.js +8 -0
  66. package/dist/tools/convert-css.js +38 -15
  67. package/dist/tools/get-component.js +2 -2
  68. package/dist/tools/get-guide.d.ts +2 -0
  69. package/dist/tools/get-guide.js +40 -17
  70. package/dist/tools/get-overview.js +2 -2
  71. package/dist/tools/get-props-system.js +8 -6
  72. package/dist/tools/get-tokens.js +2 -2
  73. package/dist/tools/search-docs.js +13 -7
  74. package/package.json +17 -2
@@ -1,4 +1,9 @@
1
1
  import { z } from 'zod';
2
+ export const MetaInfoSchema = z.object({
3
+ generatedAt: z.string(),
4
+ sourceCommit: z.string(),
5
+ docsVersion: z.string(),
6
+ });
2
7
  export const DocsEntrySchema = z.object({
3
8
  sourcePath: z.string(),
4
9
  title: z.string(),
@@ -8,3 +13,11 @@ export const DocsEntrySchema = z.object({
8
13
  keywords: z.array(z.string()),
9
14
  snippet: z.string(),
10
15
  });
16
+ export const SearchResultSchema = z.object({
17
+ sourcePath: z.string(),
18
+ url: z.string(),
19
+ heading: z.string(),
20
+ snippet: z.string(),
21
+ score: z.number(),
22
+ nextTool: z.string().nullable(),
23
+ });
@@ -8,5 +8,7 @@ export interface SearchDocsOptions {
8
8
  category?: string;
9
9
  limit?: number;
10
10
  cssPropertyMap?: Map<string, string[]>;
11
+ guideTopics?: ReadonlySet<string>;
11
12
  }
12
13
  export declare function searchDocs(entries: DocsEntry[], query: string, options?: SearchDocsOptions): SearchResult[];
14
+ export declare function sourcePathToUrlSlug(sourcePath: string): string;
@@ -67,7 +67,7 @@ function scoreEntry(entry, queryTokens) {
67
67
  return score;
68
68
  }
69
69
  export function searchDocs(entries, query, options) {
70
- const { category, limit = 10, cssPropertyMap } = options ?? {};
70
+ const { category, limit = 10, cssPropertyMap, guideTopics } = options ?? {};
71
71
  // CSSプロパティ名・Property Class記法をLism prop名に展開してからトークナイズ
72
72
  const expandedQuery = expandQuery(query, cssPropertyMap);
73
73
  const queryTokens = tokenize(expandedQuery);
@@ -92,8 +92,51 @@ export function searchDocs(entries, query, options) {
92
92
  heading: entry.title,
93
93
  snippet: entry.snippet,
94
94
  score,
95
+ nextTool: getNextTool(entry, guideTopics),
95
96
  }));
96
97
  }
98
+ /** `sourcePath`(拡張子なし)の末尾セグメントを返す(例: `primitives/l--flex` → `l--flex`) */
99
+ function getBasename(withoutExt) {
100
+ const parts = withoutExt.split('/');
101
+ return parts[parts.length - 1];
102
+ }
103
+ /**
104
+ * 検索結果のページを詳しく見るための推奨フォローアップツール呼び出しを返す。
105
+ * sourcePath による判定をカテゴリによる判定より優先する。
106
+ */
107
+ function getNextTool(entry, guideTopics) {
108
+ const withoutExt = entry.sourcePath.replace(/\.mdx$/, '');
109
+ const basename = getBasename(withoutExt);
110
+ // sourcePath ベースの判定(category より優先)
111
+ if (withoutExt === 'core-components/lism-props') {
112
+ return 'get_props_system()';
113
+ }
114
+ if (withoutExt.startsWith('primitives/') || withoutExt.startsWith('trait-class/')) {
115
+ return `get_component(name: "${basename}")`;
116
+ }
117
+ if (withoutExt === 'property-class') {
118
+ return 'get_guide(topic: "property-class")';
119
+ }
120
+ if (withoutExt.startsWith('property-class/')) {
121
+ return `get_props_system(prop: "${basename}")`;
122
+ }
123
+ // ui/examples/ は実装例ページ(Card, Hero 等)で get_component では解決できないため誘導しない。
124
+ // 詳細が必要な場合は検索結果の url を参照してもらう。
125
+ if (withoutExt.startsWith('ui/examples/')) {
126
+ return null;
127
+ }
128
+ // category ベースの判定
129
+ switch (entry.category) {
130
+ case 'core-components':
131
+ return `get_component(name: "${basename}")`;
132
+ case 'ui':
133
+ return `get_component(name: "${basename}", package: "@lism-css/ui")`;
134
+ case 'guide':
135
+ return guideTopics?.has(basename) ? `get_guide(topic: "${basename}")` : null;
136
+ default:
137
+ return null;
138
+ }
139
+ }
97
140
  /**
98
141
  * `sourcePath`(実 MDX ファイルの相対パス)を公開 URL のスラッグに変換する。
99
142
  *
@@ -112,7 +155,7 @@ export function searchDocs(entries, query, options) {
112
155
  * `ui/DummyText.mdx` → `ui/dummytext`
113
156
  */
114
157
  const PRESERVE_CASE_PREFIXES = ['primitives/', 'trait-class/'];
115
- function sourcePathToUrlSlug(sourcePath) {
158
+ export function sourcePathToUrlSlug(sourcePath) {
116
159
  const withoutExt = sourcePath.replace(/\.mdx$/, '');
117
160
  return PRESERVE_CASE_PREFIXES.some((prefix) => withoutExt.startsWith(prefix)) ? withoutExt : withoutExt.toLowerCase();
118
161
  }
@@ -1,21 +1,5 @@
1
- export interface MetaInfo {
2
- generatedAt: string;
3
- sourceCommit: string;
4
- docsVersion: string;
5
- }
6
- export interface SearchResult {
7
- sourcePath: string;
8
- url: string;
9
- heading: string;
10
- snippet: string;
11
- score: number;
12
- }
13
- export interface DocsEntry {
14
- sourcePath: string;
15
- title: string;
16
- description: string;
17
- category: string;
18
- headings: string[];
19
- keywords: string[];
20
- snippet: string;
21
- }
1
+ import type { z } from 'zod';
2
+ import type { MetaInfoSchema, DocsEntrySchema, SearchResultSchema } from './schemas.js';
3
+ export type MetaInfo = z.infer<typeof MetaInfoSchema>;
4
+ export type SearchResult = z.infer<typeof SearchResultSchema>;
5
+ export type DocsEntry = z.infer<typeof DocsEntrySchema>;
@@ -0,0 +1,2 @@
1
+ /** package.json の version(サーバーが名乗るバージョンの単一情報源) */
2
+ export declare const packageVersion: string;
@@ -0,0 +1,8 @@
1
+ import { readFileSync } from 'node:fs';
2
+ import { dirname, resolve } from 'node:path';
3
+ import { fileURLToPath } from 'node:url';
4
+ const __dirname = dirname(fileURLToPath(import.meta.url));
5
+ // src/lib/(開発時)と dist/lib/(ビルド後)のどちらから見ても package.json は 2 つ上の階層にある
6
+ const pkgPath = resolve(__dirname, '..', '..', 'package.json');
7
+ /** package.json の version(サーバーが名乗るバージョンの単一情報源) */
8
+ export const packageVersion = JSON.parse(readFileSync(pkgPath, 'utf-8')).version;
@@ -1,7 +1,23 @@
1
1
  import { z } from 'zod';
2
- import { loadMarkdown } from '../lib/load-markdown.js';
2
+ import { loadPropsMarkdown } from '../lib/load-markdown.js';
3
3
  import { parsePropRows } from '../lib/markdown-utils.js';
4
+ import { MetaInfoSchema } from '../lib/schemas.js';
4
5
  import { success, error, READ_ONLY_ANNOTATIONS } from '../lib/response.js';
6
+ /** 変換結果の1行 */
7
+ const ConversionEntrySchema = z.object({
8
+ css: z.string(),
9
+ lismProp: z.string().nullable(),
10
+ suggestedValue: z.string().nullable(),
11
+ availableTokens: z.array(z.string()).nullable(),
12
+ confidence: z.enum(['exact', 'approximate', 'unmapped']),
13
+ note: z.string(),
14
+ });
15
+ /** コンポーネント提案 */
16
+ const ComponentSuggestionSchema = z.object({
17
+ name: z.string(),
18
+ reason: z.string(),
19
+ implicitCss: z.array(z.string()),
20
+ });
5
21
  // ----------------------------------------------------------------
6
22
  // CSS パース
7
23
  // ----------------------------------------------------------------
@@ -9,7 +25,7 @@ import { success, error, READ_ONLY_ANNOTATIONS } from '../lib/response.js';
9
25
  function detectAtRules(cssText) {
10
26
  const atRuleMatch = cssText.match(/^@(\w[\w-]*)/m);
11
27
  if (atRuleMatch) {
12
- return `@${atRuleMatch[1]} ルールは未対応です。CSS 宣言(property: value;)のみを入力してください。`;
28
+ return `@${atRuleMatch[1]} rules are not supported. Provide plain CSS declarations (property: value;) only.`;
13
29
  }
14
30
  return null;
15
31
  }
@@ -56,7 +72,7 @@ function parseCssDeclarations(cssText) {
56
72
  }
57
73
  return declarations;
58
74
  }
59
- /** プリセット値列から値を抽出する(例: "-fz:root, -fz:base" → ["root", "base"]) */
75
+ /** プリセット値列から値を抽出する(例: "-fz:base, -fz:5xl" → ["base", "5xl"]) */
60
76
  function extractPresetValues(presetColumn, propName) {
61
77
  if (!presetColumn || presetColumn === '—' || presetColumn === '-')
62
78
  return [];
@@ -124,7 +140,7 @@ function detectComponent(declarations) {
124
140
  if (display === 'flex' && (flexDirection === 'column' || flexDirection === 'column-reverse')) {
125
141
  return {
126
142
  name: 'Stack',
127
- reason: 'display: flex + flex-direction: column → Stack(縦積み Flex)',
143
+ reason: 'display: flex + flex-direction: column → Stack (vertical flex)',
128
144
  implicitCss: ['display: flex', 'flex-direction: column'],
129
145
  };
130
146
  }
@@ -132,7 +148,7 @@ function detectComponent(declarations) {
132
148
  if (display === 'grid' && placeItems === 'center') {
133
149
  return {
134
150
  name: 'Center',
135
- reason: 'display: grid + place-items: center → Center(中央配置 Grid)',
151
+ reason: 'display: grid + place-items: center → Center (centered grid)',
136
152
  implicitCss: ['display: grid', 'place-items: center'],
137
153
  };
138
154
  }
@@ -140,7 +156,7 @@ function detectComponent(declarations) {
140
156
  if (display === 'flex') {
141
157
  return {
142
158
  name: 'Flex',
143
- reason: 'display: flex → Flex コンポーネント',
159
+ reason: 'display: flex → Flex component',
144
160
  implicitCss: ['display: flex'],
145
161
  };
146
162
  }
@@ -148,7 +164,7 @@ function detectComponent(declarations) {
148
164
  if (display === 'grid') {
149
165
  return {
150
166
  name: 'Grid',
151
- reason: 'display: grid → Grid コンポーネント',
167
+ reason: 'display: grid → Grid component',
152
168
  implicitCss: ['display: grid'],
153
169
  };
154
170
  }
@@ -206,6 +222,13 @@ export function registerConvertCss(server) {
206
222
  .string()
207
223
  .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.'),
208
224
  },
225
+ outputSchema: {
226
+ meta: MetaInfoSchema,
227
+ conversions: z.array(ConversionEntrySchema),
228
+ suggestedComponent: ComponentSuggestionSchema.nullable(),
229
+ example: z.string(),
230
+ tip: z.string(),
231
+ },
209
232
  annotations: READ_ONLY_ANNOTATIONS,
210
233
  }, ({ css }) => {
211
234
  try {
@@ -214,12 +237,12 @@ export function registerConvertCss(server) {
214
237
  if (atRuleError) {
215
238
  return error(atRuleError);
216
239
  }
217
- const md = loadMarkdown('property-class.md');
240
+ const md = loadPropsMarkdown();
218
241
  const mappings = buildMappings(md);
219
242
  const cssPropertyMap = buildCssPropertyMap(mappings);
220
243
  const declarations = parseCssDeclarations(css);
221
244
  if (declarations.length === 0) {
222
- return error('CSS 宣言が見つかりません。"property: value;" 形式の CSS を入力してください。');
245
+ return error('No CSS declarations found. Provide CSS in "property: value;" format.');
223
246
  }
224
247
  // 各宣言を変換
225
248
  const conversions = declarations.map((decl) => {
@@ -231,7 +254,7 @@ export function registerConvertCss(server) {
231
254
  suggestedValue: null,
232
255
  availableTokens: null,
233
256
  confidence: 'unmapped',
234
- note: 'Lism Props に該当なし。style で直接指定してください。',
257
+ note: 'No matching Lism prop. Specify it directly via the style attribute.',
235
258
  };
236
259
  }
237
260
  const suggested = suggestValue(mapping, decl.value);
@@ -243,10 +266,10 @@ export function registerConvertCss(server) {
243
266
  availableTokens: mapping.presetValues.length > 0 ? mapping.presetValues : null,
244
267
  confidence: suggested ? 'exact' : 'approximate',
245
268
  note: suggested
246
- ? `トークン値 '${suggested}' を使用(カテゴリ: ${category})`
269
+ ? `Use token value '${suggested}' (category: ${category})`
247
270
  : mapping.presetValues.length > 0
248
- ? `カスタム値。利用可能なトークン: ${mapping.presetValues.join(', ')}(カテゴリ: ${category})`
249
- : `カスタム値として指定(カテゴリ: ${category})`,
271
+ ? `Custom value. Available tokens: ${mapping.presetValues.join(', ')} (category: ${category})`
272
+ : `Use as a custom value (category: ${category})`,
250
273
  };
251
274
  });
252
275
  // コンポーネント検出
@@ -257,11 +280,11 @@ export function registerConvertCss(server) {
257
280
  conversions,
258
281
  suggestedComponent,
259
282
  example,
260
- tip: 'トークン値にマッチしない値は style 属性で CSS 変数として指定できます(例: style="--p: 1rem")。get_props_system で各 prop の詳細を確認できます。',
283
+ tip: 'Values that do not match a token can be set as CSS variables via the style attribute (e.g. style="--p: 1rem"). Use get_props_system for details on each prop.',
261
284
  });
262
285
  }
263
286
  catch (e) {
264
- return error(`CSS 変換に失敗しました: ${e instanceof Error ? e.message : String(e)}`);
287
+ return error(`CSS conversion failed: ${e instanceof Error ? e.message : String(e)}`);
265
288
  }
266
289
  });
267
290
  }
@@ -1,7 +1,7 @@
1
1
  import { z } from 'zod';
2
2
  import { getGuideFilenames, loadMarkdown } from '../lib/load-markdown.js';
3
3
  import { findComponentByHeading, findComponentInTables } from '../lib/markdown-utils.js';
4
- import { markdownResponse, error, notFound, READ_ONLY_ANNOTATIONS } from '../lib/response.js';
4
+ import { markdownResponse, loadFailureError, notFound, READ_ONLY_ANNOTATIONS } from '../lib/response.js';
5
5
  /** 入力を正規化してエイリアス検索のキーに変換する。
6
6
  * `<Flex>` / `Flex` / `l--flex` / `flex` をすべて `flex` に揃える。 */
7
7
  function normalizeComponentKey(input) {
@@ -171,7 +171,7 @@ export function registerGetComponent(server) {
171
171
  });
172
172
  }
173
173
  catch (e) {
174
- 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.`);
174
+ return loadFailureError('component data', e);
175
175
  }
176
176
  });
177
177
  }
@@ -1,2 +1,4 @@
1
1
  import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ /** get_guide が受理するトピックキーの集合。search.ts の nextTool 判定で利用する。 */
3
+ export declare const GUIDE_TOPIC_KEYS: ReadonlySet<string>;
2
4
  export declare function registerGetGuide(server: McpServer): void;
@@ -1,32 +1,55 @@
1
1
  import { z } from 'zod';
2
2
  import { loadMarkdown } from '../lib/load-markdown.js';
3
- import { markdownResponse, error, READ_ONLY_ANNOTATIONS } from '../lib/response.js';
3
+ import { markdownResponse, loadFailureError, READ_ONLY_ANNOTATIONS } from '../lib/response.js';
4
+ // files に複数指定したトピックは結合して返す。
5
+ // MCP クライアントは Markdown 内の相対リンクを辿れないため、分冊ファイルは本体に結合する。
4
6
  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' },
7
+ overview: { files: ['SKILL.md'], label: 'Framework overview, packages, implementation rules' },
8
+ tokens: { files: ['tokens.md'], label: 'Design tokens (spacing, colors, font sizes, etc.)' },
9
+ 'property-class': {
10
+ files: ['property-class.md', 'property-class/all-props.md', 'property-class/bd.md', 'property-class/hov.md', 'property-class/max-sz.md'],
11
+ label: 'Property Class system, all props reference table, border (bd) / hover (hov) / max-sz details',
12
+ },
8
13
  'components-core': {
9
- file: 'components-core.md',
14
+ files: ['components-core.md'],
10
15
  label: 'Core component system (Lism, Box, Flex, Stack, Grid, etc.)',
11
16
  },
12
17
  'components-ui': {
13
- file: 'components-ui.md',
18
+ files: ['components-ui.md'],
14
19
  label: 'UI components (Accordion, Modal, Tabs, Button, etc.)',
15
20
  },
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--bxsh, set--hov, etc.)' },
21
+ 'base-styles': { files: ['base-styles.md'], label: 'Base styling, reset CSS, HTML element styles' },
22
+ 'set-class': { files: ['set-class.md'], label: 'Set classes (set--plain, set--bxsh, set--hov, etc.)' },
18
23
  'primitive-class': {
19
- file: 'primitive-class.md',
24
+ files: ['primitive-class.md'],
20
25
  label: 'Primitive class prefixes (is--, l--, a--) and Component class (c--), with column-layout primitive selection guide',
21
26
  },
22
- 'utility-class': { file: 'utility-class.md', label: 'Utility classes (u--trim, u--cbox, etc.)' },
23
- 'css-rules': { file: 'css-rules.md', label: 'CSS methodology, layer structure, naming conventions' },
24
- responsive: { file: 'responsive.md', label: 'Responsive design, breakpoints, container queries' },
27
+ 'trait-class': {
28
+ files: ['trait-class.md'],
29
+ label: 'Trait classes (is--, has--): declarative role/feature classes in the lism-trait layer',
30
+ },
31
+ 'utility-class': { files: ['utility-class.md'], label: 'Utility classes (u--trim, u--cbox, etc.)' },
32
+ 'css-rules': { files: ['css-rules.md'], label: 'CSS methodology, layer structure, naming conventions' },
33
+ naming: {
34
+ files: ['naming.md'],
35
+ label: 'Naming conventions: CSS variable / class naming rules, {prop} and {value} abbreviation rules',
36
+ },
37
+ responsive: { files: ['responsive.md'], label: 'Responsive design, breakpoints, container queries' },
38
+ customize: {
39
+ files: ['customize.md'],
40
+ label: 'Customization: @layer opt-out, SCSS settings, lism.config.js, CSS purge',
41
+ },
25
42
  antipatterns: {
26
- file: 'antipatterns.md',
27
- label: 'AI code-generation antipatterns: token typos, prop type mistakes, layout choice errors, responsive omissions',
43
+ files: ['antipatterns.md'],
44
+ label: 'AI code-generation antipatterns (values / style declarations): px hardcoding, token typos, keycolor misuse, prop type mistakes',
45
+ },
46
+ 'antipatterns-layout': {
47
+ files: ['antipatterns-layout.md'],
48
+ label: 'AI code-generation antipatterns (structure / layout / responsive): layout choice errors, responsive omissions, is-- misuse, naming mistakes',
28
49
  },
29
50
  };
51
+ /** get_guide が受理するトピックキーの集合。search.ts の nextTool 判定で利用する。 */
52
+ export const GUIDE_TOPIC_KEYS = new Set(Object.keys(GUIDE_TOPICS));
30
53
  const TOPIC_DESCRIPTION = Object.entries(GUIDE_TOPICS)
31
54
  .map(([key, { label }]) => `- ${key}: ${label}`)
32
55
  .join('\n');
@@ -42,11 +65,11 @@ export function registerGetGuide(server) {
42
65
  annotations: READ_ONLY_ANNOTATIONS,
43
66
  }, ({ topic }) => {
44
67
  try {
45
- const { file } = GUIDE_TOPICS[topic];
46
- return markdownResponse(loadMarkdown(file));
68
+ const { files } = GUIDE_TOPICS[topic];
69
+ return markdownResponse(files.map((file) => loadMarkdown(file)).join('\n\n'));
47
70
  }
48
71
  catch (e) {
49
- 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.`);
72
+ return loadFailureError(`guide "${topic}"`, e);
50
73
  }
51
74
  });
52
75
  }
@@ -1,6 +1,6 @@
1
1
  import { loadMarkdown } from '../lib/load-markdown.js';
2
2
  import { extractSection } from '../lib/markdown-utils.js';
3
- import { markdownResponse, error, READ_ONLY_ANNOTATIONS } from '../lib/response.js';
3
+ import { markdownResponse, loadFailureError, READ_ONLY_ANNOTATIONS } from '../lib/response.js';
4
4
  /**
5
5
  * SKILL.md を中核に、css-rules.md の Layer 構造セクションと
6
6
  * responsive.md のブレイクポイントセクションを付加して返す。
@@ -34,7 +34,7 @@ export function registerGetOverview(server) {
34
34
  return markdownResponse(buildOverviewMarkdown());
35
35
  }
36
36
  catch (e) {
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.`);
37
+ return loadFailureError('overview data', e);
38
38
  }
39
39
  });
40
40
  }
@@ -1,8 +1,8 @@
1
1
  import { z } from 'zod';
2
- import { loadMarkdown } from '../lib/load-markdown.js';
2
+ import { loadPropsMarkdown } from '../lib/load-markdown.js';
3
3
  import { parsePropRows } from '../lib/markdown-utils.js';
4
4
  import { parsePropClassName } from '../lib/search.js';
5
- import { markdownResponse, error, notFound, READ_ONLY_ANNOTATIONS } from '../lib/response.js';
5
+ import { markdownResponse, loadFailureError, notFound, READ_ONLY_ANNOTATIONS } from '../lib/response.js';
6
6
  /** cssProperty フィールドからコア名を抽出("--hl 変数のみ" → "--hl") */
7
7
  function normalizeCssProperty(raw) {
8
8
  return raw
@@ -26,7 +26,7 @@ export function registerGetPropsSystem(server) {
26
26
  annotations: READ_ONLY_ANNOTATIONS,
27
27
  }, ({ prop }) => {
28
28
  try {
29
- const md = loadMarkdown('property-class.md');
29
+ const md = loadPropsMarkdown();
30
30
  // prop 未指定: 全文を返す
31
31
  if (!prop) {
32
32
  return markdownResponse(md);
@@ -44,13 +44,15 @@ export function registerGetPropsSystem(server) {
44
44
  });
45
45
  if (matched.length === 0) {
46
46
  const availableProps = rows.map((r) => `${r.prop} (${r.cssProperty})`);
47
- return notFound(`"${prop}" に一致する Prop が見つかりません。Lism prop (例: "p", "fz") または CSS プロパティ名 (例: "padding", "font-size") で検索できます。`, { availableProps });
47
+ return notFound(`No prop matches "${prop}". Search by Lism prop name (e.g. "p", "fz") or CSS property name (e.g. "padding", "font-size").`, {
48
+ availableProps,
49
+ });
48
50
  }
49
51
  // 一致したセクション名のユニーク一覧を取得
50
52
  const sections = [...new Set(matched.map((r) => r.sectionName))];
51
53
  // property-class.md から対象セクションを抽出して結合
52
54
  const lines = md.split('\n');
53
- const resultParts = [`## 検索結果: "${prop}"\n`];
55
+ const resultParts = [`## Search results: "${prop}"\n`];
54
56
  for (const sectionName of sections) {
55
57
  // セクション内の一致する行だけを含む簡易 Markdown を生成
56
58
  const sectionRows = matched.filter((r) => r.sectionName === sectionName);
@@ -67,7 +69,7 @@ export function registerGetPropsSystem(server) {
67
69
  return markdownResponse(resultParts.join('\n\n'));
68
70
  }
69
71
  catch (e) {
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.`);
72
+ return loadFailureError('props system data', e);
71
73
  }
72
74
  });
73
75
  }
@@ -1,5 +1,5 @@
1
1
  import { loadMarkdown } from '../lib/load-markdown.js';
2
- import { markdownResponse, error, READ_ONLY_ANNOTATIONS } from '../lib/response.js';
2
+ import { markdownResponse, loadFailureError, READ_ONLY_ANNOTATIONS } from '../lib/response.js';
3
3
  export function registerGetTokens(server) {
4
4
  server.registerTool('get_tokens', {
5
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' +
@@ -12,7 +12,7 @@ export function registerGetTokens(server) {
12
12
  return markdownResponse(loadMarkdown('tokens.md'));
13
13
  }
14
14
  catch (e) {
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.`);
15
+ return loadFailureError('tokens data', e);
16
16
  }
17
17
  });
18
18
  }
@@ -1,10 +1,11 @@
1
1
  import { z } from 'zod';
2
2
  import { loadJSON } from '../lib/load-data.js';
3
- import { loadMarkdown } from '../lib/load-markdown.js';
4
- import { DocsEntrySchema } from '../lib/schemas.js';
3
+ import { loadPropsMarkdown } from '../lib/load-markdown.js';
4
+ import { DocsEntrySchema, MetaInfoSchema, SearchResultSchema } from '../lib/schemas.js';
5
5
  import { parsePropRows } from '../lib/markdown-utils.js';
6
6
  import { searchDocs } from '../lib/search.js';
7
- import { success, error, READ_ONLY_ANNOTATIONS } from '../lib/response.js';
7
+ import { success, loadFailureError, READ_ONLY_ANNOTATIONS } from '../lib/response.js';
8
+ import { GUIDE_TOPIC_KEYS } from './get-guide.js';
8
9
  const DOC_CATEGORIES = ['all', 'core-components', 'primitives', 'property-class', 'ui', 'guide'];
9
10
  /**
10
11
  * property-class.md のテーブルから CSSプロパティ名 → Lism prop名 のマップを構築する。
@@ -27,22 +28,27 @@ export function registerSearchDocs(server) {
27
28
  description: "Search lism-css documentation by keyword. Returns matching pages with relevance scores. Supports CSS property names (e.g. 'font-size', 'padding') which are automatically expanded to corresponding lism prop names.\n" +
28
29
  'Use this when you cannot find information with other tools, or to discover available components, features, and guides by keyword.\n' +
29
30
  'Do NOT use this as your first step — prefer get_component for a known component, get_props_system for a known prop, or get_guide for a specific topic. This is a fallback search tool.\n' +
30
- 'Returns JSON with search results including page URLs and relevance scores.',
31
+ 'Returns JSON with search results including page URLs and relevance scores. Each result includes a nextTool field with the recommended follow-up tool call for fetching that page in detail.',
31
32
  inputSchema: {
32
33
  query: z.string().describe('Search query (keywords separated by spaces). CSS property names like "font-size" are also accepted.'),
33
34
  category: z.enum(DOC_CATEGORIES).default('all').describe('Filter by documentation category.'),
34
35
  limit: z.number().int().min(1).max(20).default(10).describe('Maximum number of results to return.'),
35
36
  },
37
+ outputSchema: {
38
+ meta: MetaInfoSchema,
39
+ query: z.string(),
40
+ results: z.array(SearchResultSchema),
41
+ },
36
42
  annotations: READ_ONLY_ANNOTATIONS,
37
43
  }, ({ query, category, limit }) => {
38
44
  try {
39
45
  const entries = loadJSON('docs-index.json', z.array(DocsEntrySchema));
40
- const cssPropertyMap = buildCssPropertyMapFromMarkdown(loadMarkdown('property-class.md'));
41
- const results = searchDocs(entries, query, { category, limit, cssPropertyMap });
46
+ const cssPropertyMap = buildCssPropertyMapFromMarkdown(loadPropsMarkdown());
47
+ const results = searchDocs(entries, query, { category, limit, cssPropertyMap, guideTopics: GUIDE_TOPIC_KEYS });
42
48
  return success({ query, results });
43
49
  }
44
50
  catch (e) {
45
- return error(`Failed to search docs: ${e instanceof Error ? e.message : String(e)}. The data files may not be built yet. Run "pnpm build" in packages/mcp first.`);
51
+ return loadFailureError('docs search index', e);
46
52
  }
47
53
  });
48
54
  }
package/package.json CHANGED
@@ -1,10 +1,22 @@
1
1
  {
2
2
  "name": "@lism-css/mcp",
3
- "version": "0.22.0",
3
+ "version": "0.24.0",
4
4
  "description": "MCP server for lism-css documentation and API reference.",
5
+ "keywords": [
6
+ "mcp",
7
+ "modelcontextprotocol",
8
+ "lism-css",
9
+ "css",
10
+ "documentation"
11
+ ],
12
+ "homepage": "https://lism-css.com/",
13
+ "repository": {
14
+ "type": "git",
15
+ "url": "https://github.com/lism-css/lism-css/tree/main/packages/mcp"
16
+ },
5
17
  "type": "module",
6
18
  "bin": {
7
- "mcp": "./bin/lism-mcp.mjs"
19
+ "lism-mcp": "./bin/lism-mcp.mjs"
8
20
  },
9
21
  "main": "./dist/index.js",
10
22
  "files": [
@@ -21,6 +33,9 @@
21
33
  "vitest": "^4.1.0"
22
34
  },
23
35
  "license": "MIT",
36
+ "engines": {
37
+ "node": ">=18.0.0"
38
+ },
24
39
  "scripts": {
25
40
  "format": "prettier --write . --ignore-path ../../.prettierignore",
26
41
  "build": "tsc -p tsconfig.build.json && cp src/data/docs-index.json dist/data/ && rm -rf dist/data/guides && cp -r ../../skills/lism-css-guide dist/data/guides",