@iress-oss/ids-mcp-server 0.0.1 → 5.14.2

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 (122) hide show
  1. package/LICENSE.txt +201 -0
  2. package/README.md +29 -159
  3. package/dist/componentHandlers.js +241 -0
  4. package/dist/componentHandlers.test.js +380 -0
  5. package/{build → dist}/config.js +5 -5
  6. package/dist/index.js +53 -0
  7. package/{build → dist}/iressHandlers.js +52 -46
  8. package/dist/iressHandlers.test.js +316 -0
  9. package/{build → dist}/resourceHandlers.js +23 -22
  10. package/dist/resourceHandlers.test.js +352 -0
  11. package/{build → dist}/searchHandlers.js +107 -92
  12. package/dist/searchHandlers.test.js +524 -0
  13. package/{build → dist}/toolHandler.js +13 -13
  14. package/dist/toolHandler.test.js +369 -0
  15. package/dist/tools.js +165 -0
  16. package/{build → dist}/utils.js +11 -15
  17. package/dist/utils.test.js +286 -0
  18. package/{docs/ids → generated/docs}/components-autocomplete-docs.md +3 -3
  19. package/{docs/ids → generated/docs}/components-autocomplete-recipes-docs.md +17 -51
  20. package/{docs/ids → generated/docs}/components-card-recipes-docs.md +1 -1
  21. package/{docs/ids → generated/docs}/components-checkbox-docs.md +6 -19
  22. package/{docs/ids → generated/docs}/components-checkboxgroup-docs.md +18 -18
  23. package/{docs/ids → generated/docs}/components-checkboxgroup-recipes-docs.md +9 -9
  24. package/{docs/ids → generated/docs}/components-col-docs.md +1 -1
  25. package/{docs/ids → generated/docs}/components-combobox-docs.md +4 -4
  26. package/{docs/ids → generated/docs}/components-container-docs.md +8 -42
  27. package/{docs/ids → generated/docs}/components-filter-docs.md +14 -67
  28. package/{docs/ids → generated/docs}/components-form-docs.md +341 -335
  29. package/{docs/ids → generated/docs}/components-form-recipes-docs.md +198 -1
  30. package/{docs/ids → generated/docs}/components-hide-docs.md +16 -70
  31. package/{docs/ids → generated/docs}/components-icon-docs.md +4 -4
  32. package/{docs/ids → generated/docs}/components-input-recipes-docs.md +2 -2
  33. package/{docs/ids → generated/docs}/components-inputcurrency-recipes-docs.md +6 -40
  34. package/{docs/ids → generated/docs}/components-modal-docs.md +3 -113
  35. package/generated/docs/components-popover-docs.md +464 -0
  36. package/{docs/ids → generated/docs}/components-radiogroup-docs.md +21 -21
  37. package/{docs/ids → generated/docs}/components-richselect-docs.md +149 -111
  38. package/{docs/ids → generated/docs}/components-row-docs.md +4 -4
  39. package/{docs/ids → generated/docs}/components-skeleton-docs.md +3 -3
  40. package/{docs/ids → generated/docs}/components-skeleton-recipes-docs.md +1 -1
  41. package/{docs/ids → generated/docs}/components-skiplink-docs.md +1 -1
  42. package/{docs/ids → generated/docs}/components-slideout-docs.md +3 -113
  43. package/{docs/ids → generated/docs}/components-table-ag-grid-docs.md +109 -137
  44. package/{docs/ids → generated/docs}/components-table-docs.md +92 -597
  45. package/{docs/ids → generated/docs}/components-tabset-docs.md +2 -2
  46. package/{docs/ids → generated/docs}/components-tag-docs.md +1 -1
  47. package/{docs/ids → generated/docs}/components-toaster-docs.md +5 -5
  48. package/{docs/ids → generated/docs}/extensions-editor-docs.md +4 -4
  49. package/generated/docs/foundations-accessibility-docs.md +62 -0
  50. package/{docs/ids → generated/docs}/foundations-colours-docs.md +1 -1
  51. package/generated/docs/foundations-consistency-docs.md +52 -0
  52. package/generated/docs/foundations-content-docs.md +23 -0
  53. package/generated/docs/foundations-introduction-docs.md +17 -0
  54. package/generated/docs/foundations-principles-docs.md +70 -0
  55. package/{docs/ids → generated/docs}/foundations-typography-docs.md +7 -2
  56. package/generated/docs/foundations-user-experience-docs.md +63 -0
  57. package/generated/docs/foundations-visual-design-docs.md +46 -0
  58. package/{docs/ids → generated/docs}/get-started-develop-docs.md +3 -3
  59. package/generated/docs/guidelines.md +812 -0
  60. package/{docs/ids → generated/docs}/introduction-docs.md +4 -4
  61. package/{docs/ids → generated/docs}/patterns-loading-docs.md +332 -2
  62. package/generated/docs/resources-migration-guides-from-v4-to-v5-docs.md +437 -0
  63. package/generated/docs/themes-available-themes-docs.md +66 -0
  64. package/generated/docs/themes-tokens-docs.md +1200 -0
  65. package/generated/docs/versions-docs.md +17 -0
  66. package/package.json +42 -14
  67. package/LICENSE +0 -193
  68. package/build/componentHandlers.js +0 -205
  69. package/build/index.js +0 -51
  70. package/build/tools.js +0 -165
  71. package/docs/api-reference.md +0 -0
  72. package/docs/best-practices.md +0 -0
  73. package/docs/configuration.md +0 -0
  74. package/docs/examples.md +0 -0
  75. package/docs/guidelines.md +0 -269
  76. package/docs/ids/components-popover-docs.md +0 -4
  77. package/docs/ids/resources-migration-guides-from-v4-to-v5-docs.md +0 -639
  78. package/docs/ids/themes-available-themes-docs.md +0 -74
  79. package/docs/ids/themes-tokens-docs.md +0 -4580
  80. package/docs/ids/versions-docs.md +0 -27
  81. package/docs/tutorials/basic-integration.md +0 -0
  82. /package/{build → dist}/types.js +0 -0
  83. /package/{docs/ids → generated/docs}/components-alert-docs.md +0 -0
  84. /package/{docs/ids → generated/docs}/components-badge-docs.md +0 -0
  85. /package/{docs/ids → generated/docs}/components-button-docs.md +0 -0
  86. /package/{docs/ids → generated/docs}/components-button-recipes-docs.md +0 -0
  87. /package/{docs/ids → generated/docs}/components-buttongroup-docs.md +0 -0
  88. /package/{docs/ids → generated/docs}/components-card-docs.md +0 -0
  89. /package/{docs/ids → generated/docs}/components-divider-docs.md +0 -0
  90. /package/{docs/ids → generated/docs}/components-expander-docs.md +0 -0
  91. /package/{docs/ids → generated/docs}/components-field-docs.md +0 -0
  92. /package/{docs/ids → generated/docs}/components-inline-docs.md +0 -0
  93. /package/{docs/ids → generated/docs}/components-input-docs.md +0 -0
  94. /package/{docs/ids → generated/docs}/components-inputcurrency-docs.md +0 -0
  95. /package/{docs/ids → generated/docs}/components-label-docs.md +0 -0
  96. /package/{docs/ids → generated/docs}/components-menu-docs.md +0 -0
  97. /package/{docs/ids → generated/docs}/components-menu-menuitem-docs.md +0 -0
  98. /package/{docs/ids → generated/docs}/components-navbar-docs.md +0 -0
  99. /package/{docs/ids → generated/docs}/components-navbar-recipes-docs.md +0 -0
  100. /package/{docs/ids → generated/docs}/components-panel-docs.md +0 -0
  101. /package/{docs/ids → generated/docs}/components-placeholder-docs.md +0 -0
  102. /package/{docs/ids → generated/docs}/components-popover-recipes-docs.md +0 -0
  103. /package/{docs/ids → generated/docs}/components-progress-docs.md +0 -0
  104. /package/{docs/ids → generated/docs}/components-radio-docs.md +0 -0
  105. /package/{docs/ids → generated/docs}/components-readonly-docs.md +0 -0
  106. /package/{docs/ids → generated/docs}/components-select-docs.md +0 -0
  107. /package/{docs/ids → generated/docs}/components-slider-docs.md +0 -0
  108. /package/{docs/ids → generated/docs}/components-spinner-docs.md +0 -0
  109. /package/{docs/ids → generated/docs}/components-stack-docs.md +0 -0
  110. /package/{docs/ids → generated/docs}/components-tabset-tab-docs.md +0 -0
  111. /package/{docs/ids → generated/docs}/components-text-docs.md +0 -0
  112. /package/{docs/ids → generated/docs}/components-toaster-toast-docs.md +0 -0
  113. /package/{docs/ids → generated/docs}/components-toggle-docs.md +0 -0
  114. /package/{docs/ids → generated/docs}/components-tooltip-docs.md +0 -0
  115. /package/{docs/ids → generated/docs}/components-validationmessage-docs.md +0 -0
  116. /package/{docs/ids → generated/docs}/contact-us-docs.md +0 -0
  117. /package/{docs/ids → generated/docs}/extensions-editor-recipes-docs.md +0 -0
  118. /package/{docs/ids → generated/docs}/frequently-asked-questions-docs.md +0 -0
  119. /package/{docs/ids → generated/docs}/get-started-using-storybook-docs.md +0 -0
  120. /package/{docs/ids → generated/docs}/resources-changelog-docs.md +0 -0
  121. /package/{docs/ids → generated/docs}/resources-code-katas-docs.md +0 -0
  122. /package/{docs/ids → generated/docs}/themes-introduction-docs.md +0 -0
@@ -0,0 +1,524 @@
1
+ /**
2
+ * Tests for search handlers functionality
3
+ */
4
+ import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
5
+ import { handleGetUsageExamples, handleSearchIdsDocs, handleGetDesignTokens, handleGetDesignGuidelines, } from './searchHandlers.js';
6
+ import * as utils from './utils.js';
7
+ // Mock the utils module
8
+ vi.mock('./utils.js');
9
+ vi.mock('./config.js', () => ({
10
+ DOCS_DIR: '/mock/docs/dir',
11
+ }));
12
+ const mockUtils = vi.mocked(utils);
13
+ describe('searchHandlers', () => {
14
+ beforeEach(() => {
15
+ vi.clearAllMocks();
16
+ });
17
+ afterEach(() => {
18
+ vi.restoreAllMocks();
19
+ });
20
+ describe('handleGetUsageExamples', () => {
21
+ it('should return usage examples for a valid component', () => {
22
+ const mockMarkdownFiles = [
23
+ 'components-button-docs.md',
24
+ 'components-input-docs.md',
25
+ 'recipes-button-variations.md',
26
+ ];
27
+ const mockContent = `
28
+ # Button Component
29
+
30
+ ## Basic Usage
31
+
32
+ \`\`\`jsx
33
+ <Button variant="primary">Click me</Button>
34
+ \`\`\`
35
+
36
+ ## Advanced Example
37
+
38
+ \`\`\`javascript
39
+ const MyButton = () => (
40
+ <Button size="large" onClick={handleClick}>
41
+ Advanced Button
42
+ </Button>
43
+ );
44
+ \`\`\`
45
+
46
+ <Button disabled>Disabled Button</Button>
47
+ `;
48
+ mockUtils.getMarkdownFiles.mockReturnValue(mockMarkdownFiles);
49
+ mockUtils.readFileContent.mockReturnValue(mockContent);
50
+ const result = handleGetUsageExamples({
51
+ component: 'Button',
52
+ });
53
+ expect(result.content).toHaveLength(1);
54
+ expect(result.content[0].type).toBe('text');
55
+ expect(result.content[0].text).toContain('**Button Usage Examples**');
56
+ expect(result.content[0].text).toContain('```jsx');
57
+ expect(result.content[0].text).toContain('<Button variant="primary">');
58
+ expect(result.isError).toBeUndefined();
59
+ });
60
+ it('should find component files including recipes', () => {
61
+ const mockMarkdownFiles = [
62
+ 'components-input-docs.md',
63
+ 'recipes-button-variations.md', // This should match for 'button'
64
+ 'recipes-other-component.md',
65
+ ];
66
+ const mockContent = `
67
+ # Button Recipes
68
+
69
+ \`\`\`jsx
70
+ <Button variant="recipe">Recipe Button</Button>
71
+ \`\`\`
72
+ `;
73
+ mockUtils.getMarkdownFiles.mockReturnValue(mockMarkdownFiles);
74
+ mockUtils.readFileContent.mockReturnValue(mockContent);
75
+ const result = handleGetUsageExamples({
76
+ component: 'Button',
77
+ });
78
+ expect(result.content[0].text).toContain('**Button Usage Examples**');
79
+ expect(result.content[0].text).toContain('Recipe Button');
80
+ // Should have processed the recipes file
81
+ expect(mockUtils.readFileContent).toHaveBeenCalledWith(expect.stringContaining('recipes-button-variations.md'));
82
+ });
83
+ it('should filter examples by pattern when provided', () => {
84
+ const mockMarkdownFiles = ['components-button-docs.md'];
85
+ const mockContent = `
86
+ \`\`\`jsx
87
+ <Button variant="primary">Primary</Button>
88
+ \`\`\`
89
+
90
+ \`\`\`jsx
91
+ <Button variant="secondary">Secondary</Button>
92
+ \`\`\`
93
+
94
+ \`\`\`jsx
95
+ <Button size="large">Large Button</Button>
96
+ \`\`\`
97
+ `;
98
+ mockUtils.getMarkdownFiles.mockReturnValue(mockMarkdownFiles);
99
+ mockUtils.readFileContent.mockReturnValue(mockContent);
100
+ const result = handleGetUsageExamples({
101
+ component: 'Button',
102
+ pattern: 'primary',
103
+ });
104
+ expect(result.content[0].text).toContain('(primary pattern)');
105
+ expect(result.content[0].text).toContain('variant="primary"');
106
+ expect(result.content[0].text).not.toContain('variant="secondary"');
107
+ });
108
+ it('should return appropriate message when no component files found', () => {
109
+ mockUtils.getMarkdownFiles.mockReturnValue([
110
+ 'components-input-docs.md',
111
+ 'components-select-docs.md',
112
+ ]);
113
+ const result = handleGetUsageExamples({
114
+ component: 'NonExistent',
115
+ });
116
+ expect(result.content[0].text).toContain('No examples found for "NonExistent"');
117
+ expect(result.content[0].text).toContain('Try: input, select');
118
+ });
119
+ it('should handle file reading errors gracefully', () => {
120
+ const mockMarkdownFiles = ['components-button-docs.md'];
121
+ mockUtils.getMarkdownFiles.mockReturnValue(mockMarkdownFiles);
122
+ mockUtils.readFileContent.mockImplementation(() => {
123
+ throw new Error('File read error');
124
+ });
125
+ const consoleSpy = vi
126
+ .spyOn(console, 'error')
127
+ .mockImplementation(() => { });
128
+ const result = handleGetUsageExamples({
129
+ component: 'Button',
130
+ });
131
+ expect(consoleSpy).toHaveBeenCalledWith(expect.stringContaining('Error reading file'), expect.any(Error));
132
+ expect(result.content[0].text).toContain('No usage examples found');
133
+ consoleSpy.mockRestore();
134
+ });
135
+ it('should handle multiple file reading errors', () => {
136
+ const mockMarkdownFiles = [
137
+ 'components-button-docs.md',
138
+ 'recipes-button-variations.md',
139
+ ];
140
+ mockUtils.getMarkdownFiles.mockReturnValue(mockMarkdownFiles);
141
+ mockUtils.readFileContent.mockImplementation(() => {
142
+ throw new Error('Multiple file read errors');
143
+ });
144
+ const consoleSpy = vi
145
+ .spyOn(console, 'error')
146
+ .mockImplementation(() => { });
147
+ const result = handleGetUsageExamples({
148
+ component: 'Button',
149
+ });
150
+ expect(consoleSpy).toHaveBeenCalledTimes(2);
151
+ expect(consoleSpy).toHaveBeenCalledWith('Error reading file components-button-docs.md:', expect.any(Error));
152
+ expect(consoleSpy).toHaveBeenCalledWith('Error reading file recipes-button-variations.md:', expect.any(Error));
153
+ expect(result.content[0].text).toContain('No usage examples found');
154
+ consoleSpy.mockRestore();
155
+ });
156
+ it('should validate input parameters using zod schema', () => {
157
+ expect(() => handleGetUsageExamples({})).toThrow();
158
+ expect(() => handleGetUsageExamples({ component: 123 })).toThrow();
159
+ expect(() => handleGetUsageExamples({ component: 'Button', pattern: 123 })).toThrow();
160
+ });
161
+ });
162
+ describe('handleSearchIdsDocs', () => {
163
+ it('should find and return search matches with context', () => {
164
+ const mockMarkdownFiles = [
165
+ 'components-button-docs.md',
166
+ 'foundations-colors-docs.md',
167
+ ];
168
+ const mockButtonContent = `
169
+ # Button Component
170
+ The Button component is used for actions.
171
+ It supports various variants.
172
+ Use primary for main actions.
173
+ `;
174
+ const mockColorsContent = `
175
+ # Colors Foundation
176
+ Primary colors define the brand.
177
+ Secondary colors provide support.
178
+ Use primary sparingly.
179
+ `;
180
+ mockUtils.getMarkdownFiles.mockReturnValue(mockMarkdownFiles);
181
+ mockUtils.readFileContent
182
+ .mockReturnValueOnce(mockButtonContent)
183
+ .mockReturnValueOnce(mockColorsContent);
184
+ const result = handleSearchIdsDocs({
185
+ query: 'primary',
186
+ case_sensitive: false,
187
+ });
188
+ expect(result.content[0].text).toContain('Found 3 matches');
189
+ expect(result.content[0].text).toContain('**button:5**');
190
+ expect(result.content[0].text).toContain('**colors:3**');
191
+ expect(result.content[0].text).toContain('Use primary for main actions');
192
+ expect(result.content[0].text).toContain('Primary colors define the brand');
193
+ });
194
+ it('should respect case sensitivity when specified', () => {
195
+ const mockMarkdownFiles = ['components-button-docs.md'];
196
+ const mockContent = `
197
+ Primary button example
198
+ primary button example
199
+ PRIMARY button example
200
+ `;
201
+ mockUtils.getMarkdownFiles.mockReturnValue(mockMarkdownFiles);
202
+ mockUtils.readFileContent.mockReturnValue(mockContent);
203
+ const caseSensitiveResult = handleSearchIdsDocs({
204
+ query: 'Primary',
205
+ case_sensitive: true,
206
+ });
207
+ const caseInsensitiveResult = handleSearchIdsDocs({
208
+ query: 'Primary',
209
+ case_sensitive: false,
210
+ });
211
+ expect(caseSensitiveResult.content[0].text).toContain('Found 1 matches');
212
+ expect(caseInsensitiveResult.content[0].text).toContain('Found 3 matches');
213
+ });
214
+ it('should return no matches message when query not found', () => {
215
+ const mockMarkdownFiles = ['components-button-docs.md'];
216
+ const mockContent = 'Button component documentation without the search term.';
217
+ mockUtils.getMarkdownFiles.mockReturnValue(mockMarkdownFiles);
218
+ mockUtils.readFileContent.mockReturnValue(mockContent);
219
+ const result = handleSearchIdsDocs({
220
+ query: 'nonexistent',
221
+ case_sensitive: false,
222
+ });
223
+ expect(result.content[0].text).toContain('No matches found for "nonexistent"');
224
+ });
225
+ it('should limit results to 15 matches', () => {
226
+ const mockMarkdownFiles = ['components-button-docs.md'];
227
+ // Create content with 20 lines containing "test"
228
+ const mockContent = Array.from({ length: 20 }, (_, i) => `Line ${i + 1} with test content`).join('\n');
229
+ mockUtils.getMarkdownFiles.mockReturnValue(mockMarkdownFiles);
230
+ mockUtils.readFileContent.mockReturnValue(mockContent);
231
+ const result = handleSearchIdsDocs({
232
+ query: 'test',
233
+ case_sensitive: false,
234
+ });
235
+ // Count the number of **button:** entries in the result
236
+ const matches = (result.content[0].text.match(/\*\*button:\d+\*\*/g) ?? []).length;
237
+ expect(matches).toBe(15);
238
+ });
239
+ it('should use default case_sensitive value when not provided', () => {
240
+ const mockMarkdownFiles = ['components-button-docs.md'];
241
+ const mockContent = 'Primary button and primary action';
242
+ mockUtils.getMarkdownFiles.mockReturnValue(mockMarkdownFiles);
243
+ mockUtils.readFileContent.mockReturnValue(mockContent);
244
+ const result = handleSearchIdsDocs({ query: 'PRIMARY' });
245
+ expect(result.content[0].text).toContain('Found 1 matches');
246
+ });
247
+ it('should handle file reading errors during search', () => {
248
+ const mockMarkdownFiles = [
249
+ 'components-button-docs.md',
250
+ 'foundations-colors-docs.md',
251
+ ];
252
+ mockUtils.getMarkdownFiles.mockReturnValue(mockMarkdownFiles);
253
+ mockUtils.readFileContent.mockImplementation((filePath) => {
254
+ if (filePath.includes('button')) {
255
+ return 'Button content with search term';
256
+ }
257
+ throw new Error('File read error for colors');
258
+ });
259
+ const consoleSpy = vi
260
+ .spyOn(console, 'error')
261
+ .mockImplementation(() => { });
262
+ const result = handleSearchIdsDocs({
263
+ query: 'search',
264
+ case_sensitive: false,
265
+ });
266
+ expect(consoleSpy).toHaveBeenCalledWith('Error reading file foundations-colors-docs.md:', expect.any(Error));
267
+ expect(result.content[0].text).toContain('Found 1 matches');
268
+ expect(result.content[0].text).toContain('Button content with search term');
269
+ consoleSpy.mockRestore();
270
+ });
271
+ });
272
+ describe('handleGetDesignTokens', () => {
273
+ it('should return all design tokens when type is "all"', () => {
274
+ const mockMarkdownFiles = [
275
+ 'foundations-colors-docs.md',
276
+ 'foundations-spacing-docs.md',
277
+ 'foundations-typography-docs.md',
278
+ ];
279
+ const mockColorsContent = `
280
+ # Colors
281
+ ## Primary Colors
282
+ Use these CSS variables: --iress-color-primary, --iress-color-secondary
283
+ `;
284
+ const mockSpacingContent = `
285
+ # Spacing
286
+ ## Base Spacing
287
+ Available variables: --iress-space-sm, --iress-space-md, --iress-space-lg
288
+ `;
289
+ const mockTypographyContent = `
290
+ # Typography
291
+ ## Font Sizes
292
+ Typography tokens: --iress-font-size-sm, --iress-font-size-lg
293
+ `;
294
+ mockUtils.getMarkdownFiles.mockReturnValue(mockMarkdownFiles);
295
+ mockUtils.readFileContent
296
+ .mockReturnValueOnce(mockColorsContent)
297
+ .mockReturnValueOnce(mockSpacingContent)
298
+ .mockReturnValueOnce(mockTypographyContent);
299
+ const result = handleGetDesignTokens({ type: 'all' });
300
+ expect(result.content[0].text).toContain('**IDS Design Tokens**');
301
+ expect(result.content[0].text).toContain('**colors**');
302
+ expect(result.content[0].text).toContain('**spacing**');
303
+ expect(result.content[0].text).toContain('**typography**');
304
+ expect(result.content[0].text).toContain('--iress-color-primary');
305
+ expect(result.content[0].text).toContain('--iress-space-sm');
306
+ expect(result.content[0].text).toContain('--iress-font-size-sm');
307
+ });
308
+ it('should filter tokens by specific type', () => {
309
+ const mockMarkdownFiles = [
310
+ 'foundations-colors-docs.md',
311
+ 'foundations-spacing-docs.md',
312
+ ];
313
+ const mockColorsContent = `
314
+ # Colors
315
+ CSS Variables: --iress-color-primary, --iress-color-secondary
316
+ `;
317
+ mockUtils.getMarkdownFiles.mockReturnValue(mockMarkdownFiles);
318
+ mockUtils.readFileContent.mockReturnValue(mockColorsContent);
319
+ const result = handleGetDesignTokens({ type: 'colors' });
320
+ expect(result.content[0].text).toContain('**IDS Design Tokens (colors)**');
321
+ expect(result.content[0].text).toContain('--iress-color-primary');
322
+ // Should only process the colors file
323
+ expect(mockUtils.readFileContent).toHaveBeenCalledTimes(1);
324
+ });
325
+ it('should handle case when no foundation files match the type', () => {
326
+ mockUtils.getMarkdownFiles.mockReturnValue([
327
+ 'components-button-docs.md',
328
+ 'foundations-other-docs.md',
329
+ ]);
330
+ const result = handleGetDesignTokens({ type: 'colors' });
331
+ expect(result.content[0].text).toContain('No design token information found for colors');
332
+ expect(result.content[0].text).toContain('Available foundations: other');
333
+ });
334
+ it('should use default type "all" when not specified', () => {
335
+ const mockMarkdownFiles = ['foundations-colors-docs.md'];
336
+ const mockContent = 'Colors with --iress-color-primary';
337
+ mockUtils.getMarkdownFiles.mockReturnValue(mockMarkdownFiles);
338
+ mockUtils.readFileContent.mockReturnValue(mockContent);
339
+ const result = handleGetDesignTokens({});
340
+ expect(result.content[0].text).toContain('**IDS Design Tokens**');
341
+ expect(result.content[0].text).not.toContain('(colors)');
342
+ });
343
+ it('should extract token sections and CSS variables correctly', () => {
344
+ const mockMarkdownFiles = ['foundations-tokens-docs.md'];
345
+ const mockContent = `
346
+ # Design Tokens
347
+
348
+ ## Primary Colors
349
+ Main brand colors
350
+
351
+ ### Secondary Colors
352
+ Supporting colors
353
+
354
+ CSS Variables available:
355
+ - --iress-primary-100
356
+ - --iress-primary-200
357
+ - --iress-secondary-100
358
+ `;
359
+ mockUtils.getMarkdownFiles.mockReturnValue(mockMarkdownFiles);
360
+ mockUtils.readFileContent.mockReturnValue(mockContent);
361
+ const result = handleGetDesignTokens({ type: 'all' });
362
+ expect(result.content[0].text).toContain('## Primary Colors');
363
+ expect(result.content[0].text).toContain('### Secondary Colors');
364
+ expect(result.content[0].text).toContain('--iress-primary-');
365
+ expect(result.content[0].text).toContain('--iress-secondary-');
366
+ });
367
+ it('should handle file reading errors during token extraction', () => {
368
+ const mockMarkdownFiles = [
369
+ 'foundations-colors-docs.md',
370
+ 'foundations-spacing-docs.md',
371
+ ];
372
+ mockUtils.getMarkdownFiles.mockReturnValue(mockMarkdownFiles);
373
+ mockUtils.readFileContent.mockImplementation((filePath) => {
374
+ if (filePath.includes('colors')) {
375
+ return 'Colors with --iress-color-primary token';
376
+ }
377
+ throw new Error('File read error for spacing');
378
+ });
379
+ const consoleSpy = vi
380
+ .spyOn(console, 'error')
381
+ .mockImplementation(() => { });
382
+ const result = handleGetDesignTokens({ type: 'all' });
383
+ expect(consoleSpy).toHaveBeenCalledWith('Error reading file foundations-spacing-docs.md:', expect.any(Error));
384
+ expect(result.content[0].text).toContain('**IDS Design Tokens**');
385
+ expect(result.content[0].text).toContain('**colors**');
386
+ expect(result.content[0].text).toContain('--iress-color-primary');
387
+ consoleSpy.mockRestore();
388
+ });
389
+ });
390
+ describe('handleGetDesignGuidelines', () => {
391
+ it('should return full guidelines when no section or query specified', () => {
392
+ const mockGuidelinesContent = `
393
+ # Design Guidelines
394
+
395
+ ## Core Design Principles
396
+ 1. Consistency
397
+ 2. Accessibility
398
+
399
+ ## Visual Design Standards
400
+ Typography and color usage
401
+
402
+ ## Component Guidelines
403
+ How to use components
404
+ `;
405
+ mockUtils.readFileContent.mockReturnValue(mockGuidelinesContent);
406
+ const result = handleGetDesignGuidelines({});
407
+ expect(result.content[0].text).toContain('**IDS Design Guidelines**');
408
+ expect(result.content[0].text).toContain('# Design Guidelines');
409
+ expect(result.content[0].text).toContain('## Core Design Principles');
410
+ expect(result.content[0].text).toContain('## Visual Design Standards');
411
+ });
412
+ it('should filter by section when specified', () => {
413
+ const mockGuidelinesContent = `
414
+ # Design Guidelines
415
+
416
+ ## Core Design Principles
417
+ 1. Consistency is key
418
+ 2. Accessibility first
419
+
420
+ ## Visual Design Standards
421
+ Typography and color usage
422
+
423
+ ## Component Guidelines
424
+ How to use components properly
425
+ `;
426
+ mockUtils.readFileContent.mockReturnValue(mockGuidelinesContent);
427
+ const result = handleGetDesignGuidelines({
428
+ section: 'Core Design Principles',
429
+ });
430
+ expect(result.content[0].text).toContain('**IDS Design Guidelines - Core Design Principles**');
431
+ expect(result.content[0].text).toContain('1. Consistency is key');
432
+ expect(result.content[0].text).toContain('2. Accessibility first');
433
+ expect(result.content[0].text).not.toContain('## Visual Design Standards');
434
+ });
435
+ it('should filter by query when specified', () => {
436
+ const mockGuidelinesContent = `
437
+ # Design Guidelines
438
+
439
+ Accessibility is important for all users.
440
+ Make sure colors meet accessibility standards.
441
+ Typography should be accessible.
442
+
443
+ Design should be consistent across components.
444
+ Use consistent spacing and layout.
445
+ `;
446
+ mockUtils.readFileContent.mockReturnValue(mockGuidelinesContent);
447
+ const result = handleGetDesignGuidelines({
448
+ query: 'accessibility',
449
+ });
450
+ expect(result.content[0].text).toContain('**IDS Design Guidelines (filtered by: accessibility)**');
451
+ expect(result.content[0].text).toContain('Accessibility is important');
452
+ expect(result.content[0].text).toContain('colors meet accessibility standards');
453
+ expect(result.content[0].text).toContain('Typography should be accessible');
454
+ expect(result.content[0].text).not.toContain('consistent spacing');
455
+ });
456
+ it('should handle both section and query filters together', () => {
457
+ const mockGuidelinesContent = `
458
+ # Design Guidelines
459
+
460
+ ## Accessibility Guidelines
461
+ Accessibility is crucial for inclusive design.
462
+ Colors must meet WCAG standards.
463
+ Typography should be legible.
464
+
465
+ ## Design Principles
466
+ Consistency in design elements.
467
+ Accessibility as a core principle.
468
+ `;
469
+ mockUtils.readFileContent.mockReturnValue(mockGuidelinesContent);
470
+ const result = handleGetDesignGuidelines({
471
+ section: 'Accessibility',
472
+ query: 'colors',
473
+ });
474
+ expect(result.content[0].text).toContain('**IDS Design Guidelines - Accessibility (filtered by: colors)**');
475
+ expect(result.content[0].text).toContain('Colors must meet WCAG standards');
476
+ // The filterContentByQuery function includes context lines, so we should expect the typography line to be included
477
+ expect(result.content[0].text).toContain('Typography should be legible');
478
+ });
479
+ it('should return error when section not found', () => {
480
+ const mockGuidelinesContent = `
481
+ # Design Guidelines
482
+
483
+ ## Core Principles
484
+ Basic principles
485
+ `;
486
+ mockUtils.readFileContent.mockReturnValue(mockGuidelinesContent);
487
+ const result = handleGetDesignGuidelines({
488
+ section: 'NonExistent Section',
489
+ });
490
+ expect(result.content[0].text).toContain('Section "NonExistent Section" not found');
491
+ expect(result.content[0].text).toContain('Available sections include:');
492
+ });
493
+ it('should return error when query not found', () => {
494
+ const mockGuidelinesContent = `
495
+ # Design Guidelines
496
+
497
+ Basic design information without the search term.
498
+ `;
499
+ mockUtils.readFileContent.mockReturnValue(mockGuidelinesContent);
500
+ const result = handleGetDesignGuidelines({
501
+ query: 'nonexistent',
502
+ });
503
+ expect(result.content[0].text).toContain('No guidelines found matching "nonexistent"');
504
+ expect(result.content[0].text).toContain('Try searching for terms like:');
505
+ });
506
+ it('should handle file reading errors', () => {
507
+ mockUtils.readFileContent.mockImplementation(() => {
508
+ throw new Error('File not found');
509
+ });
510
+ const consoleSpy = vi
511
+ .spyOn(console, 'error')
512
+ .mockImplementation(() => { });
513
+ const result = handleGetDesignGuidelines({});
514
+ expect(result.content[0].text).toContain('Error reading design guidelines');
515
+ expect(consoleSpy).toHaveBeenCalledWith('Error reading guidelines:', expect.any(Error));
516
+ consoleSpy.mockRestore();
517
+ });
518
+ it('should handle empty file content', () => {
519
+ mockUtils.readFileContent.mockReturnValue('');
520
+ const result = handleGetDesignGuidelines({});
521
+ expect(result.content[0].text).toContain('Design guidelines file not found');
522
+ });
523
+ });
524
+ });
@@ -1,29 +1,29 @@
1
1
  /**
2
2
  * Main tool handler dispatcher
3
3
  */
4
- import { handleFindComponent, handleGetComponentProps, handleListComponents, } from "./componentHandlers.js";
5
- import { handleGetUsageExamples, handleSearchIdsDocs, handleGetDesignTokens, handleGetDesignGuidelines, } from "./searchHandlers.js";
6
- import { handleGetIressComponentInfo, handleAnalyzeComponentMentions, } from "./iressHandlers.js";
4
+ import { handleFindComponent, handleGetComponentProps, handleListComponents, } from './componentHandlers.js';
5
+ import { handleGetUsageExamples, handleSearchIdsDocs, handleGetDesignTokens, handleGetDesignGuidelines, } from './searchHandlers.js';
6
+ import { handleGetIressComponentInfo, handleAnalyzeComponentMentions, } from './iressHandlers.js';
7
7
  export function handleToolCall(request) {
8
- const { name, arguments: args } = request.params;
8
+ const { name, arguments: args = {} } = request.params;
9
9
  switch (name) {
10
- case "find_component":
10
+ case 'find_component':
11
11
  return handleFindComponent(args);
12
- case "get_component_props":
12
+ case 'get_component_props':
13
13
  return handleGetComponentProps(args);
14
- case "get_usage_examples":
14
+ case 'get_usage_examples':
15
15
  return handleGetUsageExamples(args);
16
- case "search_ids_docs":
16
+ case 'search_ids_docs':
17
17
  return handleSearchIdsDocs(args);
18
- case "list_components":
18
+ case 'list_components':
19
19
  return handleListComponents(args);
20
- case "get_design_tokens":
20
+ case 'get_design_tokens':
21
21
  return handleGetDesignTokens(args);
22
- case "get_iress_component_info":
22
+ case 'get_iress_component_info':
23
23
  return handleGetIressComponentInfo(args);
24
- case "analyze_component_mentions":
24
+ case 'analyze_component_mentions':
25
25
  return handleAnalyzeComponentMentions(args);
26
- case "get_design_guidelines":
26
+ case 'get_design_guidelines':
27
27
  return handleGetDesignGuidelines(args);
28
28
  default:
29
29
  throw new Error(`Unknown tool: ${name}`);