@iress-oss/ids-mcp-server 0.0.1-dev.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 (107) hide show
  1. package/LICENSE.txt +201 -0
  2. package/README.md +93 -0
  3. package/dist/componentHandlers.js +241 -0
  4. package/dist/componentHandlers.test.js +380 -0
  5. package/dist/config.js +16 -0
  6. package/dist/index.js +53 -0
  7. package/dist/iressHandlers.js +144 -0
  8. package/dist/iressHandlers.test.js +316 -0
  9. package/dist/resourceHandlers.js +67 -0
  10. package/dist/resourceHandlers.test.js +352 -0
  11. package/dist/searchHandlers.js +287 -0
  12. package/dist/searchHandlers.test.js +524 -0
  13. package/dist/toolHandler.js +31 -0
  14. package/dist/toolHandler.test.js +369 -0
  15. package/dist/tools.js +165 -0
  16. package/dist/types.js +4 -0
  17. package/dist/utils.js +59 -0
  18. package/dist/utils.test.js +286 -0
  19. package/generated/docs/components-alert-docs.md +130 -0
  20. package/generated/docs/components-autocomplete-docs.md +754 -0
  21. package/generated/docs/components-autocomplete-recipes-docs.md +104 -0
  22. package/generated/docs/components-badge-docs.md +148 -0
  23. package/generated/docs/components-button-docs.md +362 -0
  24. package/generated/docs/components-button-recipes-docs.md +76 -0
  25. package/generated/docs/components-buttongroup-docs.md +310 -0
  26. package/generated/docs/components-card-docs.md +494 -0
  27. package/generated/docs/components-card-recipes-docs.md +89 -0
  28. package/generated/docs/components-checkbox-docs.md +193 -0
  29. package/generated/docs/components-checkboxgroup-docs.md +692 -0
  30. package/generated/docs/components-checkboxgroup-recipes-docs.md +119 -0
  31. package/generated/docs/components-col-docs.md +466 -0
  32. package/generated/docs/components-combobox-docs.md +1016 -0
  33. package/generated/docs/components-container-docs.md +91 -0
  34. package/generated/docs/components-divider-docs.md +176 -0
  35. package/generated/docs/components-expander-docs.md +215 -0
  36. package/generated/docs/components-field-docs.md +675 -0
  37. package/generated/docs/components-filter-docs.md +1109 -0
  38. package/generated/docs/components-form-docs.md +2442 -0
  39. package/generated/docs/components-form-recipes-docs.md +892 -0
  40. package/generated/docs/components-hide-docs.md +265 -0
  41. package/generated/docs/components-icon-docs.md +553 -0
  42. package/generated/docs/components-inline-docs.md +868 -0
  43. package/generated/docs/components-input-docs.md +335 -0
  44. package/generated/docs/components-input-recipes-docs.md +140 -0
  45. package/generated/docs/components-inputcurrency-docs.md +157 -0
  46. package/generated/docs/components-inputcurrency-recipes-docs.md +116 -0
  47. package/generated/docs/components-label-docs.md +135 -0
  48. package/generated/docs/components-menu-docs.md +704 -0
  49. package/generated/docs/components-menu-menuitem-docs.md +193 -0
  50. package/generated/docs/components-modal-docs.md +587 -0
  51. package/generated/docs/components-navbar-docs.md +291 -0
  52. package/generated/docs/components-navbar-recipes-docs.md +413 -0
  53. package/generated/docs/components-panel-docs.md +380 -0
  54. package/generated/docs/components-placeholder-docs.md +27 -0
  55. package/generated/docs/components-popover-docs.md +464 -0
  56. package/generated/docs/components-popover-recipes-docs.md +245 -0
  57. package/generated/docs/components-progress-docs.md +104 -0
  58. package/generated/docs/components-radio-docs.md +107 -0
  59. package/generated/docs/components-radiogroup-docs.md +683 -0
  60. package/generated/docs/components-readonly-docs.md +89 -0
  61. package/generated/docs/components-richselect-docs.md +2433 -0
  62. package/generated/docs/components-row-docs.md +877 -0
  63. package/generated/docs/components-select-docs.md +456 -0
  64. package/generated/docs/components-skeleton-docs.md +214 -0
  65. package/generated/docs/components-skeleton-recipes-docs.md +76 -0
  66. package/generated/docs/components-skiplink-docs.md +66 -0
  67. package/generated/docs/components-slideout-docs.md +538 -0
  68. package/generated/docs/components-slider-docs.md +346 -0
  69. package/generated/docs/components-spinner-docs.md +59 -0
  70. package/generated/docs/components-stack-docs.md +265 -0
  71. package/generated/docs/components-table-ag-grid-docs.md +2666 -0
  72. package/generated/docs/components-table-docs.md +1305 -0
  73. package/generated/docs/components-tabset-docs.md +341 -0
  74. package/generated/docs/components-tabset-tab-docs.md +86 -0
  75. package/generated/docs/components-tag-docs.md +115 -0
  76. package/generated/docs/components-text-docs.md +394 -0
  77. package/generated/docs/components-toaster-docs.md +294 -0
  78. package/generated/docs/components-toaster-toast-docs.md +157 -0
  79. package/generated/docs/components-toggle-docs.md +158 -0
  80. package/generated/docs/components-tooltip-docs.md +311 -0
  81. package/generated/docs/components-validationmessage-docs.md +241 -0
  82. package/generated/docs/contact-us-docs.md +27 -0
  83. package/generated/docs/extensions-editor-docs.md +288 -0
  84. package/generated/docs/extensions-editor-recipes-docs.md +39 -0
  85. package/generated/docs/foundations-accessibility-docs.md +62 -0
  86. package/generated/docs/foundations-colours-docs.md +257 -0
  87. package/generated/docs/foundations-consistency-docs.md +52 -0
  88. package/generated/docs/foundations-content-docs.md +23 -0
  89. package/generated/docs/foundations-introduction-docs.md +17 -0
  90. package/generated/docs/foundations-principles-docs.md +70 -0
  91. package/generated/docs/foundations-typography-docs.md +191 -0
  92. package/generated/docs/foundations-user-experience-docs.md +63 -0
  93. package/generated/docs/foundations-visual-design-docs.md +46 -0
  94. package/generated/docs/frequently-asked-questions-docs.md +53 -0
  95. package/generated/docs/get-started-develop-docs.md +48 -0
  96. package/generated/docs/get-started-using-storybook-docs.md +68 -0
  97. package/generated/docs/guidelines.md +812 -0
  98. package/generated/docs/introduction-docs.md +43 -0
  99. package/generated/docs/patterns-loading-docs.md +1304 -0
  100. package/generated/docs/resources-changelog-docs.md +6 -0
  101. package/generated/docs/resources-code-katas-docs.md +29 -0
  102. package/generated/docs/resources-migration-guides-from-v4-to-v5-docs.md +437 -0
  103. package/generated/docs/themes-available-themes-docs.md +66 -0
  104. package/generated/docs/themes-introduction-docs.md +121 -0
  105. package/generated/docs/themes-tokens-docs.md +1200 -0
  106. package/generated/docs/versions-docs.md +17 -0
  107. package/package.json +81 -0
@@ -0,0 +1,380 @@
1
+ /**
2
+ * Tests for component handlers
3
+ */
4
+ import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
5
+ import { handleFindComponent, handleGetComponentProps, handleListComponents, } from './componentHandlers.js';
6
+ import * as utils from './utils.js';
7
+ // Mock the utils module
8
+ vi.mock('./utils.js', () => ({
9
+ getMarkdownFiles: vi.fn(),
10
+ mapIressComponentToFile: vi.fn(),
11
+ extractIressComponents: vi.fn(),
12
+ readFileContent: vi.fn(),
13
+ }));
14
+ // Mock the config module
15
+ vi.mock('./config.js', () => ({
16
+ DOCS_DIR: '/mocked/docs/path',
17
+ }));
18
+ const mockUtils = vi.mocked(utils);
19
+ describe('componentHandlers', () => {
20
+ beforeEach(() => {
21
+ vi.clearAllMocks();
22
+ });
23
+ afterEach(() => {
24
+ vi.restoreAllMocks();
25
+ });
26
+ describe('handleFindComponent', () => {
27
+ const mockMarkdownFiles = [
28
+ 'components-button-docs.md',
29
+ 'components-input-docs.md',
30
+ 'components-table-docs.md',
31
+ 'foundations-colors-docs.md',
32
+ 'resources-icons-docs.md',
33
+ ];
34
+ const mockButtonContent = `# Button Component
35
+
36
+ A versatile button component for user interactions.
37
+
38
+ ## Props
39
+ - variant: string - Button style variant
40
+ - size: string - Button size
41
+
42
+ ## Examples
43
+ \`\`\`jsx
44
+ <Button variant="primary">Click me</Button>
45
+ \`\`\`
46
+ `;
47
+ it('should find exact match for Iress component', () => {
48
+ const args = {
49
+ query: 'IressButton',
50
+ };
51
+ mockUtils.mapIressComponentToFile.mockReturnValue('components-button-docs.md');
52
+ const result = handleFindComponent(args);
53
+ expect(mockUtils.mapIressComponentToFile).toHaveBeenCalledWith('IressButton');
54
+ expect(result.content).toHaveLength(1);
55
+ expect(result.content[0].type).toBe('text');
56
+ expect(result.content[0].text).toContain('Found exact match for **IressButton**');
57
+ expect(result.content[0].text).toContain('components-button-docs.md');
58
+ expect(result.content[0].text).toContain('Use `get_iress_component_info` with "IressButton"');
59
+ });
60
+ it('should return null for non-matching Iress component', () => {
61
+ const args = {
62
+ query: 'IressNonExistent',
63
+ };
64
+ mockUtils.mapIressComponentToFile.mockReturnValue(null);
65
+ mockUtils.getMarkdownFiles.mockReturnValue(mockMarkdownFiles);
66
+ mockUtils.readFileContent.mockReturnValue('No relevant content');
67
+ mockUtils.extractIressComponents.mockReturnValue([]);
68
+ const result = handleFindComponent(args);
69
+ expect(result.content[0].text).toContain('No IDS components found matching "IressNonExistent"');
70
+ });
71
+ it('should search and rank components by relevance', () => {
72
+ const args = {
73
+ query: 'button',
74
+ };
75
+ mockUtils.mapIressComponentToFile.mockReturnValue(null);
76
+ mockUtils.getMarkdownFiles.mockReturnValue(mockMarkdownFiles);
77
+ mockUtils.extractIressComponents.mockReturnValue([]);
78
+ // Mock file content with different relevance scores
79
+ mockUtils.readFileContent.mockImplementation((filePath) => {
80
+ if (filePath.includes('button')) {
81
+ return mockButtonContent;
82
+ }
83
+ return 'Some other component content';
84
+ });
85
+ const result = handleFindComponent(args);
86
+ expect(result.content[0].text).toContain('Found');
87
+ expect(result.content[0].text).toContain('relevant IDS components');
88
+ expect(result.content[0].text).toContain('button-docs.md');
89
+ });
90
+ it('should filter by category when specified', () => {
91
+ const args = {
92
+ query: 'component',
93
+ category: 'components',
94
+ };
95
+ mockUtils.mapIressComponentToFile.mockReturnValue(null);
96
+ mockUtils.getMarkdownFiles.mockReturnValue(mockMarkdownFiles);
97
+ mockUtils.extractIressComponents.mockReturnValue([]);
98
+ mockUtils.readFileContent.mockReturnValue('Component content');
99
+ handleFindComponent(args);
100
+ // Should only search components category files
101
+ expect(mockUtils.readFileContent).toHaveBeenCalledWith('/mocked/docs/path/components-button-docs.md');
102
+ expect(mockUtils.readFileContent).toHaveBeenCalledWith('/mocked/docs/path/components-input-docs.md');
103
+ expect(mockUtils.readFileContent).not.toHaveBeenCalledWith('/mocked/docs/path/foundations-colors-docs.md');
104
+ });
105
+ it('should handle Iress component mentions in content', () => {
106
+ const args = {
107
+ query: 'button',
108
+ };
109
+ mockUtils.mapIressComponentToFile.mockReturnValue(null);
110
+ mockUtils.getMarkdownFiles.mockReturnValue(['components-form-docs.md']);
111
+ mockUtils.extractIressComponents.mockReturnValue(['IressButton']);
112
+ mockUtils.readFileContent.mockReturnValue('This form uses IressButton component');
113
+ const result = handleFindComponent(args);
114
+ expect(result.content[0].text).toContain('relevant IDS components');
115
+ });
116
+ it('should validate input parameters using zod schema', () => {
117
+ const invalidArgs = {
118
+ query: 123, // Should be string
119
+ };
120
+ expect(() => handleFindComponent(invalidArgs)).toThrow();
121
+ });
122
+ it('should validate category parameter using zod schema', () => {
123
+ const invalidArgs = {
124
+ query: 'button',
125
+ category: 'invalid-category', // Should be enum value
126
+ };
127
+ expect(() => handleFindComponent(invalidArgs)).toThrow();
128
+ });
129
+ it('should handle file reading errors gracefully', () => {
130
+ const args = {
131
+ query: 'button',
132
+ };
133
+ mockUtils.mapIressComponentToFile.mockReturnValue(null);
134
+ mockUtils.getMarkdownFiles.mockReturnValue(['components-button-docs.md']);
135
+ mockUtils.readFileContent.mockImplementation(() => {
136
+ throw new Error('File read error');
137
+ });
138
+ // Should not throw, but handle error gracefully
139
+ const result = handleFindComponent(args);
140
+ expect(result.content[0].text).toContain('No IDS components found matching "button"');
141
+ });
142
+ it('should limit results to top 10', () => {
143
+ const args = {
144
+ query: 'component',
145
+ };
146
+ const manyFiles = Array.from({ length: 15 }, (_, i) => `components-comp${i}-docs.md`);
147
+ mockUtils.mapIressComponentToFile.mockReturnValue(null);
148
+ mockUtils.getMarkdownFiles.mockReturnValue(manyFiles);
149
+ mockUtils.extractIressComponents.mockReturnValue([]);
150
+ mockUtils.readFileContent.mockReturnValue('component content');
151
+ const result = handleFindComponent(args);
152
+ // Count the number of component entries by splitting on numbered items
153
+ const entries = result.content[0].text.split(/\n\d+\. /).length - 1;
154
+ expect(entries).toBeLessThanOrEqual(10);
155
+ });
156
+ });
157
+ describe('handleGetComponentProps', () => {
158
+ const mockMarkdownFiles = [
159
+ 'components-button-docs.md',
160
+ 'components-input-docs.md',
161
+ ];
162
+ const mockComponentContent = `# Button Component
163
+
164
+ ## Overview
165
+ A button component
166
+
167
+ ## Props
168
+ - variant: string - The button variant
169
+ - size: string - The button size
170
+
171
+ Some text after props
172
+
173
+ ## API
174
+ Additional API methods
175
+
176
+ Some text after API
177
+
178
+ ## Examples
179
+ Usage examples here
180
+
181
+ Some text after examples
182
+ `;
183
+ it('should return component props when component exists', () => {
184
+ const args = {
185
+ component: 'button',
186
+ };
187
+ mockUtils.getMarkdownFiles.mockReturnValue(mockMarkdownFiles);
188
+ mockUtils.readFileContent.mockReturnValue(mockComponentContent);
189
+ const result = handleGetComponentProps(args);
190
+ expect(result.content).toHaveLength(1);
191
+ expect(result.content[0].type).toBe('text');
192
+ expect(result.content[0].text).toContain('**button Component Props & API**');
193
+ expect(result.content[0].text).toContain('## API');
194
+ expect(result.content[0].text).toContain('Additional API methods');
195
+ });
196
+ it('should return error when component not found', () => {
197
+ const args = {
198
+ component: 'nonexistent',
199
+ };
200
+ mockUtils.getMarkdownFiles.mockReturnValue(mockMarkdownFiles);
201
+ const result = handleGetComponentProps(args);
202
+ expect(result.content[0].text).toContain('Component "nonexistent" not found');
203
+ expect(result.content[0].text).toContain('Available components:');
204
+ expect(result.content[0].text).toContain('- button');
205
+ expect(result.content[0].text).toContain('- input');
206
+ });
207
+ it('should handle case-insensitive component matching', () => {
208
+ const args = {
209
+ component: 'BUTTON',
210
+ };
211
+ mockUtils.getMarkdownFiles.mockReturnValue(mockMarkdownFiles);
212
+ mockUtils.readFileContent.mockReturnValue(mockComponentContent);
213
+ const result = handleGetComponentProps(args);
214
+ expect(result.content[0].text).toContain('**BUTTON Component Props & API**');
215
+ });
216
+ it('should extract props from content with no specific props sections', () => {
217
+ const args = {
218
+ component: 'button',
219
+ };
220
+ const contentWithoutProps = `# Button Component
221
+ This is a simple button component without specific props sections.
222
+ Here is some general documentation.`;
223
+ mockUtils.getMarkdownFiles.mockReturnValue(mockMarkdownFiles);
224
+ mockUtils.readFileContent.mockReturnValue(contentWithoutProps);
225
+ const result = handleGetComponentProps(args);
226
+ expect(result.content[0].text).toContain('This is a simple button component');
227
+ });
228
+ it('should throw error when file reading fails', () => {
229
+ const args = {
230
+ component: 'button',
231
+ };
232
+ mockUtils.getMarkdownFiles.mockReturnValue(mockMarkdownFiles);
233
+ mockUtils.readFileContent.mockImplementation(() => {
234
+ throw new Error('File access denied');
235
+ });
236
+ expect(() => handleGetComponentProps(args)).toThrow('Failed to read component file components-button-docs.md: File access denied');
237
+ });
238
+ it('should validate input parameters using zod schema', () => {
239
+ const invalidArgs = {
240
+ component: 123, // Should be string
241
+ };
242
+ expect(() => handleGetComponentProps(invalidArgs)).toThrow();
243
+ });
244
+ it('should handle multiple prop-related sections', () => {
245
+ const contentWithMultipleSections = `# Button Component
246
+
247
+ ## Props
248
+ - variant: string
249
+
250
+ ## API
251
+ Methods available
252
+
253
+ ## Properties
254
+ Additional properties
255
+
256
+ ## mode
257
+ Different modes
258
+ `;
259
+ const args = {
260
+ component: 'button',
261
+ };
262
+ mockUtils.getMarkdownFiles.mockReturnValue(mockMarkdownFiles);
263
+ mockUtils.readFileContent.mockReturnValue(contentWithMultipleSections);
264
+ const result = handleGetComponentProps(args);
265
+ expect(result.content[0].text).toContain('Props');
266
+ expect(result.content[0].text).toContain('API');
267
+ expect(result.content[0].text).toContain('Properties');
268
+ expect(result.content[0].text).toContain('mode');
269
+ });
270
+ });
271
+ describe('handleListComponents', () => {
272
+ const mockMarkdownFiles = [
273
+ 'components-button-docs.md',
274
+ 'components-input-docs.md',
275
+ 'components-table-docs.md',
276
+ 'foundations-colors-docs.md',
277
+ 'foundations-typography-docs.md',
278
+ 'resources-icons-docs.md',
279
+ ];
280
+ it('should list all components by default', () => {
281
+ const args = {};
282
+ mockUtils.getMarkdownFiles.mockReturnValue(mockMarkdownFiles);
283
+ const result = handleListComponents(args);
284
+ expect(result.content).toHaveLength(1);
285
+ expect(result.content[0].type).toBe('text');
286
+ expect(result.content[0].text).toContain('**IDS Component Library**');
287
+ expect(result.content[0].text).toContain('**Components (3)**');
288
+ expect(result.content[0].text).toContain('- button');
289
+ expect(result.content[0].text).toContain('- input');
290
+ expect(result.content[0].text).toContain('- table');
291
+ expect(result.content[0].text).toContain('**Foundations (2)**');
292
+ expect(result.content[0].text).toContain('- colors');
293
+ expect(result.content[0].text).toContain('- typography');
294
+ expect(result.content[0].text).toContain('**Resources (1)**');
295
+ expect(result.content[0].text).toContain('- icons');
296
+ });
297
+ it('should list only components when category is "components"', () => {
298
+ const args = {
299
+ category: 'components',
300
+ };
301
+ mockUtils.getMarkdownFiles.mockReturnValue(mockMarkdownFiles);
302
+ const result = handleListComponents(args);
303
+ expect(result.content[0].text).toContain('**Components (3)**');
304
+ expect(result.content[0].text).toContain('- button');
305
+ expect(result.content[0].text).toContain('- input');
306
+ expect(result.content[0].text).toContain('- table');
307
+ expect(result.content[0].text).not.toContain('**Foundations');
308
+ expect(result.content[0].text).not.toContain('**Resources');
309
+ });
310
+ it('should list only foundations when category is "foundations"', () => {
311
+ const args = {
312
+ category: 'foundations',
313
+ };
314
+ mockUtils.getMarkdownFiles.mockReturnValue(mockMarkdownFiles);
315
+ const result = handleListComponents(args);
316
+ expect(result.content[0].text).toContain('**Foundations (2)**');
317
+ expect(result.content[0].text).toContain('- colors');
318
+ expect(result.content[0].text).toContain('- typography');
319
+ expect(result.content[0].text).not.toContain('**Components');
320
+ expect(result.content[0].text).not.toContain('**Resources');
321
+ });
322
+ it('should list only resources when category is "resources"', () => {
323
+ const args = {
324
+ category: 'resources',
325
+ };
326
+ mockUtils.getMarkdownFiles.mockReturnValue(mockMarkdownFiles);
327
+ const result = handleListComponents(args);
328
+ expect(result.content[0].text).toContain('**Resources (1)**');
329
+ expect(result.content[0].text).toContain('- icons');
330
+ expect(result.content[0].text).not.toContain('**Components');
331
+ expect(result.content[0].text).not.toContain('**Foundations');
332
+ });
333
+ it('should list all categories when category is "all"', () => {
334
+ const args = {
335
+ category: 'all',
336
+ };
337
+ mockUtils.getMarkdownFiles.mockReturnValue(mockMarkdownFiles);
338
+ const result = handleListComponents(args);
339
+ expect(result.content[0].text).toContain('**Components (3)**');
340
+ expect(result.content[0].text).toContain('**Foundations (2)**');
341
+ expect(result.content[0].text).toContain('**Resources (1)**');
342
+ });
343
+ it('should handle empty file lists', () => {
344
+ const args = {
345
+ category: 'components',
346
+ };
347
+ mockUtils.getMarkdownFiles.mockReturnValue([]);
348
+ const result = handleListComponents(args);
349
+ expect(result.content[0].text).toContain('**Components (0)**');
350
+ });
351
+ it('should validate category parameter using zod schema', () => {
352
+ const invalidArgs = {
353
+ category: 'invalid-category', // Should be enum value
354
+ };
355
+ expect(() => handleListComponents(invalidArgs)).toThrow();
356
+ });
357
+ it('should use default category when not specified', () => {
358
+ const args = {};
359
+ mockUtils.getMarkdownFiles.mockReturnValue(mockMarkdownFiles);
360
+ const result = handleListComponents(args);
361
+ // Should default to 'all' and show all categories
362
+ expect(result.content[0].text).toContain('**Components');
363
+ expect(result.content[0].text).toContain('**Foundations');
364
+ expect(result.content[0].text).toContain('**Resources');
365
+ });
366
+ it('should properly format component names by removing prefixes and suffixes', () => {
367
+ const customFiles = [
368
+ 'components-complex-button-component-docs.md',
369
+ 'foundations-design-tokens-docs.md',
370
+ 'resources-icon-library-docs.md',
371
+ ];
372
+ const args = {};
373
+ mockUtils.getMarkdownFiles.mockReturnValue(customFiles);
374
+ const result = handleListComponents(args);
375
+ expect(result.content[0].text).toContain('- complex-button-component');
376
+ expect(result.content[0].text).toContain('- design-tokens');
377
+ expect(result.content[0].text).toContain('- icon-library');
378
+ });
379
+ });
380
+ });
package/dist/config.js ADDED
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Configuration constants for the IDS MCP Server
3
+ */
4
+ import path from 'path';
5
+ import { fileURLToPath } from 'url';
6
+ const __filename = fileURLToPath(import.meta.url);
7
+ const __dirname = path.dirname(__filename);
8
+ export const DOCS_DIR = path.join(__dirname, '..', 'generated', 'docs');
9
+ export const SERVER_CONFIG = {
10
+ name: 'ids-mcp-server',
11
+ version: '1.0.0',
12
+ };
13
+ export const CAPABILITIES = {
14
+ resources: {},
15
+ tools: {},
16
+ };
package/dist/index.js ADDED
@@ -0,0 +1,53 @@
1
+ #!/usr/bin/env node
2
+ import { Server } from '@modelcontextprotocol/sdk/server/index.js';
3
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
4
+ import { CallToolRequestSchema, ListResourcesRequestSchema, ListToolsRequestSchema, ReadResourceRequestSchema, } from '@modelcontextprotocol/sdk/types.js';
5
+ import { SERVER_CONFIG, CAPABILITIES } from './config.js';
6
+ import { toolDefinitions } from './tools.js';
7
+ import { handleToolCall } from './toolHandler.js';
8
+ import { handleListResources, handleReadResource } from './resourceHandlers.js';
9
+ /**
10
+ * Create MCP server for Iress Design System (IDS) component library context
11
+ */
12
+ const server = new Server(SERVER_CONFIG, { capabilities: CAPABILITIES });
13
+ /**
14
+ * List available IDS component documentation resources
15
+ */
16
+ server.setRequestHandler(ListResourcesRequestSchema, () => {
17
+ const result = handleListResources();
18
+ return result;
19
+ });
20
+ /**
21
+ * Read content from a specific markdown file
22
+ */
23
+ server.setRequestHandler(ReadResourceRequestSchema, (request) => {
24
+ const result = handleReadResource(request);
25
+ return result;
26
+ });
27
+ /**
28
+ * List available IDS development tools
29
+ */
30
+ server.setRequestHandler(ListToolsRequestSchema, () => {
31
+ return {
32
+ tools: toolDefinitions,
33
+ };
34
+ });
35
+ /**
36
+ * Handle IDS component library tool calls
37
+ */
38
+ server.setRequestHandler(CallToolRequestSchema, (request) => {
39
+ const result = handleToolCall(request);
40
+ return result;
41
+ });
42
+ /**
43
+ * Start the server
44
+ */
45
+ async function main() {
46
+ const transport = new StdioServerTransport();
47
+ await server.connect(transport);
48
+ console.error('IDS Component Library MCP Server running on stdio');
49
+ }
50
+ main().catch((error) => {
51
+ console.error('Server error:', error);
52
+ process.exit(1);
53
+ });
@@ -0,0 +1,144 @@
1
+ /**
2
+ * Tool handlers for Iress component-specific operations
3
+ */
4
+ import { z } from 'zod';
5
+ import * as path from 'path';
6
+ import { mapIressComponentToFile, extractIressComponents, readFileContent, } from './utils.js';
7
+ import { DOCS_DIR } from './config.js';
8
+ export function handleGetIressComponentInfo(args) {
9
+ const schema = z.object({
10
+ component_name: z.string(),
11
+ include_examples: z.boolean().default(true),
12
+ include_props: z.boolean().default(true),
13
+ });
14
+ const { component_name, include_examples, include_props } = schema.parse(args);
15
+ // Find the corresponding documentation file
16
+ const componentFile = mapIressComponentToFile(component_name);
17
+ if (!componentFile) {
18
+ return {
19
+ content: [
20
+ {
21
+ type: 'text',
22
+ text: `Component "${component_name}" not found. Make sure you're using the correct Iress component name (e.g., IressButton, IressInput, IressTable).`,
23
+ },
24
+ ],
25
+ };
26
+ }
27
+ try {
28
+ const filePath = path.join(DOCS_DIR, componentFile);
29
+ const content = readFileContent(filePath);
30
+ let response = `**${component_name} Component Documentation**\n\n`;
31
+ // Extract overview/description
32
+ const lines = content.split('\n');
33
+ const overviewStart = lines.findIndex((line) => line.toLowerCase().includes('overview'));
34
+ if (overviewStart !== -1) {
35
+ const overviewEnd = lines.findIndex((line, index) => index > overviewStart && line.startsWith('#'));
36
+ const overviewLines = lines.slice(overviewStart, overviewEnd !== -1 ? overviewEnd : overviewStart + 10);
37
+ response += `${overviewLines.join('\n')}\n\n`;
38
+ }
39
+ // Include props if requested
40
+ if (include_props) {
41
+ const propSections = content.match(/(mode|prop|Properties|API)[\s\S]*?(?=\n##|\n\[#|$)/gi) ??
42
+ [];
43
+ if (propSections.length > 0) {
44
+ response += `**Props & API:**\n${propSections
45
+ .slice(0, 3)
46
+ .join('\n\n')}\n\n`;
47
+ }
48
+ }
49
+ // Include examples if requested
50
+ if (include_examples) {
51
+ const codeBlocks = content.match(/```[\s\S]*?```/g) ?? [];
52
+ const jsxBlocks = content.match(/<Iress[A-Z][^>]*>[\s\S]*?<\/Iress[A-Z][^>]*>/g) ?? [];
53
+ const examples = [...codeBlocks, ...jsxBlocks];
54
+ if (examples.length > 0) {
55
+ response += `**Usage Examples:**\n${examples
56
+ .slice(0, 3)
57
+ .join('\n\n')}\n\n`;
58
+ }
59
+ }
60
+ return {
61
+ content: [
62
+ {
63
+ type: 'text',
64
+ text: response,
65
+ },
66
+ ],
67
+ };
68
+ }
69
+ catch (error) {
70
+ const errorMessage = error instanceof Error ? error.message : 'Unknown error';
71
+ throw new Error(`Failed to read component documentation: ${errorMessage}`);
72
+ }
73
+ }
74
+ function formatComponentDetails(componentName, componentFile) {
75
+ if (!componentFile) {
76
+ return `**${componentName}** ❌ Not found\n\n`;
77
+ }
78
+ try {
79
+ const filePath = path.join(DOCS_DIR, componentFile);
80
+ const content = readFileContent(filePath);
81
+ const lines = content.split('\n');
82
+ const description = lines
83
+ .slice(0, 20)
84
+ .find((line) => line.trim() &&
85
+ !line.startsWith('#') &&
86
+ !line.startsWith('<!--') &&
87
+ !line.includes('Overview') &&
88
+ line.length > 20)
89
+ ?.trim() ?? 'IDS component';
90
+ return `**${componentName}** ✅\n - File: ${componentFile}\n - Description: ${description}\n\n`;
91
+ }
92
+ catch {
93
+ return `**${componentName}** ❌\n - Error reading documentation\n\n`;
94
+ }
95
+ }
96
+ function formatComponentSummary(componentName, componentFile) {
97
+ const status = componentFile ? '✅ Available' : '❌ Not found';
98
+ let result = `**${componentName}** ${status}\n`;
99
+ if (componentFile) {
100
+ result += ` - Documentation: ${componentFile}\n`;
101
+ }
102
+ return result + '\n';
103
+ }
104
+ export function handleAnalyzeComponentMentions(args) {
105
+ const schema = z.object({
106
+ text: z.string(),
107
+ detailed: z.boolean().default(false),
108
+ });
109
+ const { text, detailed } = schema.parse(args);
110
+ const componentMentions = extractIressComponents(text);
111
+ if (componentMentions.length === 0) {
112
+ return {
113
+ content: [
114
+ {
115
+ type: 'text',
116
+ text: "No Iress component mentions found in the provided text. Components should be mentioned with the 'Iress' prefix (e.g., IressButton, IressInput).",
117
+ },
118
+ ],
119
+ };
120
+ }
121
+ let response = `**Found ${componentMentions.length} Iress component mention(s):**\n\n`;
122
+ for (const componentName of componentMentions) {
123
+ const componentFile = mapIressComponentToFile(componentName);
124
+ if (detailed && componentFile) {
125
+ response += formatComponentDetails(componentName, componentFile);
126
+ }
127
+ else {
128
+ response += formatComponentSummary(componentName, componentFile);
129
+ }
130
+ }
131
+ if (!detailed &&
132
+ componentMentions.some((name) => mapIressComponentToFile(name))) {
133
+ response +=
134
+ '\n*Use the `get_iress_component_info` tool with a specific component name for detailed information.*';
135
+ }
136
+ return {
137
+ content: [
138
+ {
139
+ type: 'text',
140
+ text: response,
141
+ },
142
+ ],
143
+ };
144
+ }