@iress-oss/ids-mcp-server 0.0.1-dev.4 → 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +193 -0
- package/README.md +159 -29
- package/build/componentHandlers.js +205 -0
- package/{dist → build}/config.js +5 -5
- package/build/index.js +51 -0
- package/{dist → build}/iressHandlers.js +46 -52
- package/{dist → build}/resourceHandlers.js +22 -23
- package/{dist → build}/searchHandlers.js +92 -107
- package/{dist → build}/toolHandler.js +13 -13
- package/build/tools.js +165 -0
- package/{dist → build}/utils.js +15 -11
- package/docs/api-reference.md +0 -0
- package/docs/best-practices.md +0 -0
- package/docs/configuration.md +0 -0
- package/docs/examples.md +0 -0
- package/docs/guidelines.md +269 -0
- package/{generated/docs → docs/ids}/components-autocomplete-docs.md +5 -5
- package/{generated/docs → docs/ids}/components-autocomplete-recipes-docs.md +51 -17
- package/{generated/docs → docs/ids}/components-card-recipes-docs.md +1 -1
- package/{generated/docs → docs/ids}/components-checkbox-docs.md +19 -6
- package/{generated/docs → docs/ids}/components-checkboxgroup-docs.md +18 -18
- package/{generated/docs → docs/ids}/components-checkboxgroup-recipes-docs.md +9 -9
- package/{generated/docs → docs/ids}/components-col-docs.md +1 -1
- package/{generated/docs → docs/ids}/components-combobox-docs.md +6 -6
- package/{generated/docs → docs/ids}/components-container-docs.md +42 -8
- package/{generated/docs → docs/ids}/components-filter-docs.md +66 -13
- package/{generated/docs → docs/ids}/components-form-docs.md +368 -342
- package/{generated/docs → docs/ids}/components-form-recipes-docs.md +11 -202
- package/{generated/docs → docs/ids}/components-hide-docs.md +70 -16
- package/{generated/docs → docs/ids}/components-icon-docs.md +4 -4
- package/{generated/docs → docs/ids}/components-input-recipes-docs.md +2 -2
- package/{generated/docs → docs/ids}/components-inputcurrency-recipes-docs.md +40 -6
- package/{generated/docs → docs/ids}/components-modal-docs.md +113 -3
- package/docs/ids/components-popover-docs.md +4 -0
- package/{generated/docs → docs/ids}/components-radiogroup-docs.md +21 -21
- package/{generated/docs → docs/ids}/components-richselect-docs.md +111 -149
- package/{generated/docs → docs/ids}/components-row-docs.md +4 -4
- package/{generated/docs → docs/ids}/components-skeleton-docs.md +3 -3
- package/{generated/docs → docs/ids}/components-skeleton-recipes-docs.md +1 -1
- package/{generated/docs → docs/ids}/components-skiplink-docs.md +1 -1
- package/{generated/docs → docs/ids}/components-slideout-docs.md +113 -3
- package/docs/ids/components-table-ag-grid-docs.md +2694 -0
- package/{generated/docs → docs/ids}/components-table-docs.md +597 -92
- package/{generated/docs → docs/ids}/components-tabset-docs.md +2 -2
- package/{generated/docs → docs/ids}/components-tag-docs.md +1 -1
- package/{generated/docs → docs/ids}/components-toaster-docs.md +5 -56
- package/{generated/docs → docs/ids}/extensions-editor-docs.md +5 -5
- package/{generated/docs → docs/ids}/foundations-colours-docs.md +1 -1
- package/{generated/docs → docs/ids}/foundations-typography-docs.md +2 -7
- package/docs/ids/get-started-develop-docs.md +48 -0
- package/{generated/docs → docs/ids}/introduction-docs.md +4 -4
- package/{generated/docs → docs/ids}/patterns-loading-docs.md +2 -332
- package/docs/ids/resources-migration-guides-from-v4-to-v5-docs.md +639 -0
- package/docs/ids/themes-available-themes-docs.md +74 -0
- package/docs/ids/themes-tokens-docs.md +4580 -0
- package/docs/ids/versions-docs.md +27 -0
- package/docs/tutorials/basic-integration.md +0 -0
- package/package.json +15 -44
- package/LICENSE.txt +0 -201
- package/dist/componentHandlers.js +0 -241
- package/dist/componentHandlers.test.js +0 -380
- package/dist/index.js +0 -53
- package/dist/iressHandlers.test.js +0 -316
- package/dist/resourceHandlers.test.js +0 -352
- package/dist/searchHandlers.test.js +0 -524
- package/dist/toolHandler.test.js +0 -369
- package/dist/tools.js +0 -165
- package/dist/utils.test.js +0 -286
- package/generated/docs/components-popover-docs.md +0 -464
- package/generated/docs/components-provider-docs.md +0 -105
- package/generated/docs/components-table-ag-grid-docs.md +0 -1074
- package/generated/docs/foundations-accessibility-docs.md +0 -62
- package/generated/docs/foundations-consistency-docs.md +0 -52
- package/generated/docs/foundations-content-docs.md +0 -23
- package/generated/docs/foundations-introduction-docs.md +0 -17
- package/generated/docs/foundations-principles-docs.md +0 -70
- package/generated/docs/foundations-user-experience-docs.md +0 -63
- package/generated/docs/foundations-visual-design-docs.md +0 -46
- package/generated/docs/get-started-develop-docs.md +0 -209
- package/generated/docs/guidelines.md +0 -812
- package/generated/docs/resources-migration-guides-from-v4-to-v5-docs.md +0 -437
- package/generated/docs/themes-available-themes-docs.md +0 -66
- package/generated/docs/themes-tokens-docs.md +0 -1200
- package/generated/docs/versions-docs.md +0 -17
- /package/{dist → build}/types.js +0 -0
- /package/{generated/docs → docs/ids}/components-alert-docs.md +0 -0
- /package/{generated/docs → docs/ids}/components-badge-docs.md +0 -0
- /package/{generated/docs → docs/ids}/components-button-docs.md +0 -0
- /package/{generated/docs → docs/ids}/components-button-recipes-docs.md +0 -0
- /package/{generated/docs → docs/ids}/components-buttongroup-docs.md +0 -0
- /package/{generated/docs → docs/ids}/components-card-docs.md +0 -0
- /package/{generated/docs → docs/ids}/components-divider-docs.md +0 -0
- /package/{generated/docs → docs/ids}/components-expander-docs.md +0 -0
- /package/{generated/docs → docs/ids}/components-field-docs.md +0 -0
- /package/{generated/docs → docs/ids}/components-inline-docs.md +0 -0
- /package/{generated/docs → docs/ids}/components-input-docs.md +0 -0
- /package/{generated/docs → docs/ids}/components-inputcurrency-docs.md +0 -0
- /package/{generated/docs → docs/ids}/components-label-docs.md +0 -0
- /package/{generated/docs → docs/ids}/components-menu-docs.md +0 -0
- /package/{generated/docs → docs/ids}/components-menu-menuitem-docs.md +0 -0
- /package/{generated/docs → docs/ids}/components-navbar-docs.md +0 -0
- /package/{generated/docs → docs/ids}/components-navbar-recipes-docs.md +0 -0
- /package/{generated/docs → docs/ids}/components-panel-docs.md +0 -0
- /package/{generated/docs → docs/ids}/components-placeholder-docs.md +0 -0
- /package/{generated/docs → docs/ids}/components-popover-recipes-docs.md +0 -0
- /package/{generated/docs → docs/ids}/components-progress-docs.md +0 -0
- /package/{generated/docs → docs/ids}/components-radio-docs.md +0 -0
- /package/{generated/docs → docs/ids}/components-readonly-docs.md +0 -0
- /package/{generated/docs → docs/ids}/components-select-docs.md +0 -0
- /package/{generated/docs → docs/ids}/components-slider-docs.md +0 -0
- /package/{generated/docs → docs/ids}/components-spinner-docs.md +0 -0
- /package/{generated/docs → docs/ids}/components-stack-docs.md +0 -0
- /package/{generated/docs → docs/ids}/components-tabset-tab-docs.md +0 -0
- /package/{generated/docs → docs/ids}/components-text-docs.md +0 -0
- /package/{generated/docs → docs/ids}/components-toaster-toast-docs.md +0 -0
- /package/{generated/docs → docs/ids}/components-toggle-docs.md +0 -0
- /package/{generated/docs → docs/ids}/components-tooltip-docs.md +0 -0
- /package/{generated/docs → docs/ids}/components-validationmessage-docs.md +0 -0
- /package/{generated/docs → docs/ids}/contact-us-docs.md +0 -0
- /package/{generated/docs → docs/ids}/extensions-editor-recipes-docs.md +0 -0
- /package/{generated/docs → docs/ids}/frequently-asked-questions-docs.md +0 -0
- /package/{generated/docs → docs/ids}/get-started-using-storybook-docs.md +0 -0
- /package/{generated/docs → docs/ids}/resources-changelog-docs.md +0 -0
- /package/{generated/docs → docs/ids}/resources-code-katas-docs.md +0 -0
- /package/{generated/docs → docs/ids}/themes-introduction-docs.md +0 -0
package/build/index.js
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
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, async () => {
|
|
17
|
+
return handleListResources();
|
|
18
|
+
});
|
|
19
|
+
/**
|
|
20
|
+
* Read content from a specific markdown file
|
|
21
|
+
*/
|
|
22
|
+
server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
|
|
23
|
+
return handleReadResource(request);
|
|
24
|
+
});
|
|
25
|
+
/**
|
|
26
|
+
* List available IDS development tools
|
|
27
|
+
*/
|
|
28
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
29
|
+
return {
|
|
30
|
+
tools: toolDefinitions,
|
|
31
|
+
};
|
|
32
|
+
});
|
|
33
|
+
/**
|
|
34
|
+
* Handle IDS component library tool calls
|
|
35
|
+
*/
|
|
36
|
+
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
37
|
+
const result = handleToolCall(request);
|
|
38
|
+
return result;
|
|
39
|
+
});
|
|
40
|
+
/**
|
|
41
|
+
* Start the server
|
|
42
|
+
*/
|
|
43
|
+
async function main() {
|
|
44
|
+
const transport = new StdioServerTransport();
|
|
45
|
+
await server.connect(transport);
|
|
46
|
+
console.error("IDS Component Library MCP Server running on stdio");
|
|
47
|
+
}
|
|
48
|
+
main().catch((error) => {
|
|
49
|
+
console.error("Server error:", error);
|
|
50
|
+
process.exit(1);
|
|
51
|
+
});
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Tool handlers for Iress component-specific operations
|
|
3
3
|
*/
|
|
4
|
-
import { z } from
|
|
5
|
-
import * as path from
|
|
6
|
-
import { mapIressComponentToFile, extractIressComponents, readFileContent, } from
|
|
7
|
-
import { DOCS_DIR } from
|
|
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
8
|
export function handleGetIressComponentInfo(args) {
|
|
9
9
|
const schema = z.object({
|
|
10
10
|
component_name: z.string(),
|
|
@@ -18,7 +18,7 @@ export function handleGetIressComponentInfo(args) {
|
|
|
18
18
|
return {
|
|
19
19
|
content: [
|
|
20
20
|
{
|
|
21
|
-
type:
|
|
21
|
+
type: "text",
|
|
22
22
|
text: `Component "${component_name}" not found. Make sure you're using the correct Iress component name (e.g., IressButton, IressInput, IressTable).`,
|
|
23
23
|
},
|
|
24
24
|
],
|
|
@@ -29,78 +29,47 @@ export function handleGetIressComponentInfo(args) {
|
|
|
29
29
|
const content = readFileContent(filePath);
|
|
30
30
|
let response = `**${component_name} Component Documentation**\n\n`;
|
|
31
31
|
// Extract overview/description
|
|
32
|
-
const lines = content.split(
|
|
33
|
-
const overviewStart = lines.findIndex((line) => line.toLowerCase().includes(
|
|
32
|
+
const lines = content.split("\n");
|
|
33
|
+
const overviewStart = lines.findIndex((line) => line.toLowerCase().includes("overview"));
|
|
34
34
|
if (overviewStart !== -1) {
|
|
35
|
-
const overviewEnd = lines.findIndex((line, index) => index > overviewStart && line.startsWith(
|
|
35
|
+
const overviewEnd = lines.findIndex((line, index) => index > overviewStart && line.startsWith("#"));
|
|
36
36
|
const overviewLines = lines.slice(overviewStart, overviewEnd !== -1 ? overviewEnd : overviewStart + 10);
|
|
37
|
-
response += `${overviewLines.join(
|
|
37
|
+
response += `${overviewLines.join("\n")}\n\n`;
|
|
38
38
|
}
|
|
39
39
|
// Include props if requested
|
|
40
40
|
if (include_props) {
|
|
41
|
-
const propSections = content.match(/(mode|prop|Properties|API)[\s\S]*?(?=\n##|\n\[#|$)/gi)
|
|
41
|
+
const propSections = content.match(/(mode|prop|Properties|API)[\s\S]*?(?=\n##|\n\[#|$)/gi) ||
|
|
42
42
|
[];
|
|
43
43
|
if (propSections.length > 0) {
|
|
44
44
|
response += `**Props & API:**\n${propSections
|
|
45
45
|
.slice(0, 3)
|
|
46
|
-
.join(
|
|
46
|
+
.join("\n\n")}\n\n`;
|
|
47
47
|
}
|
|
48
48
|
}
|
|
49
49
|
// Include examples if requested
|
|
50
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)
|
|
51
|
+
const codeBlocks = content.match(/```[\s\S]*?```/g) || [];
|
|
52
|
+
const jsxBlocks = content.match(/<Iress[A-Z][^>]*>[\s\S]*?<\/Iress[A-Z][^>]*>/g) || [];
|
|
53
53
|
const examples = [...codeBlocks, ...jsxBlocks];
|
|
54
54
|
if (examples.length > 0) {
|
|
55
55
|
response += `**Usage Examples:**\n${examples
|
|
56
56
|
.slice(0, 3)
|
|
57
|
-
.join(
|
|
57
|
+
.join("\n\n")}\n\n`;
|
|
58
58
|
}
|
|
59
59
|
}
|
|
60
60
|
return {
|
|
61
61
|
content: [
|
|
62
62
|
{
|
|
63
|
-
type:
|
|
63
|
+
type: "text",
|
|
64
64
|
text: response,
|
|
65
65
|
},
|
|
66
66
|
],
|
|
67
67
|
};
|
|
68
68
|
}
|
|
69
69
|
catch (error) {
|
|
70
|
-
|
|
71
|
-
throw new Error(`Failed to read component documentation: ${errorMessage}`);
|
|
70
|
+
throw new Error(`Failed to read component documentation: ${error}`);
|
|
72
71
|
}
|
|
73
72
|
}
|
|
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
73
|
export function handleAnalyzeComponentMentions(args) {
|
|
105
74
|
const schema = z.object({
|
|
106
75
|
text: z.string(),
|
|
@@ -112,7 +81,7 @@ export function handleAnalyzeComponentMentions(args) {
|
|
|
112
81
|
return {
|
|
113
82
|
content: [
|
|
114
83
|
{
|
|
115
|
-
type:
|
|
84
|
+
type: "text",
|
|
116
85
|
text: "No Iress component mentions found in the provided text. Components should be mentioned with the 'Iress' prefix (e.g., IressButton, IressInput).",
|
|
117
86
|
},
|
|
118
87
|
],
|
|
@@ -122,21 +91,46 @@ export function handleAnalyzeComponentMentions(args) {
|
|
|
122
91
|
for (const componentName of componentMentions) {
|
|
123
92
|
const componentFile = mapIressComponentToFile(componentName);
|
|
124
93
|
if (detailed && componentFile) {
|
|
125
|
-
|
|
94
|
+
try {
|
|
95
|
+
const filePath = path.join(DOCS_DIR, componentFile);
|
|
96
|
+
const content = readFileContent(filePath);
|
|
97
|
+
// Get brief description
|
|
98
|
+
const lines = content.split("\n");
|
|
99
|
+
const description = lines
|
|
100
|
+
.slice(0, 20)
|
|
101
|
+
.find((line) => line.trim() &&
|
|
102
|
+
!line.startsWith("#") &&
|
|
103
|
+
!line.startsWith("<!--") &&
|
|
104
|
+
!line.includes("Overview") &&
|
|
105
|
+
line.length > 20)
|
|
106
|
+
?.trim() || "IDS component";
|
|
107
|
+
response += `**${componentName}** ✅\n`;
|
|
108
|
+
response += ` - File: ${componentFile}\n`;
|
|
109
|
+
response += ` - Description: ${description}\n\n`;
|
|
110
|
+
}
|
|
111
|
+
catch (error) {
|
|
112
|
+
response += `**${componentName}** ❌\n`;
|
|
113
|
+
response += ` - Error reading documentation\n\n`;
|
|
114
|
+
}
|
|
126
115
|
}
|
|
127
116
|
else {
|
|
128
|
-
|
|
117
|
+
const status = componentFile ? "✅ Available" : "❌ Not found";
|
|
118
|
+
response += `**${componentName}** ${status}\n`;
|
|
119
|
+
if (componentFile) {
|
|
120
|
+
response += ` - Documentation: ${componentFile}\n`;
|
|
121
|
+
}
|
|
122
|
+
response += "\n";
|
|
129
123
|
}
|
|
130
124
|
}
|
|
131
125
|
if (!detailed &&
|
|
132
126
|
componentMentions.some((name) => mapIressComponentToFile(name))) {
|
|
133
127
|
response +=
|
|
134
|
-
|
|
128
|
+
"\n*Use the `get_iress_component_info` tool with a specific component name for detailed information.*";
|
|
135
129
|
}
|
|
136
130
|
return {
|
|
137
131
|
content: [
|
|
138
132
|
{
|
|
139
|
-
type:
|
|
133
|
+
type: "text",
|
|
140
134
|
text: response,
|
|
141
135
|
},
|
|
142
136
|
],
|
|
@@ -1,36 +1,36 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Resource handlers for MCP server
|
|
3
3
|
*/
|
|
4
|
-
import * as path from
|
|
5
|
-
import { getMarkdownFiles, readFileContent } from
|
|
6
|
-
import { DOCS_DIR } from
|
|
4
|
+
import * as path from "path";
|
|
5
|
+
import { getMarkdownFiles, readFileContent } from "./utils.js";
|
|
6
|
+
import { DOCS_DIR } from "./config.js";
|
|
7
7
|
export function handleListResources() {
|
|
8
8
|
const markdownFiles = getMarkdownFiles();
|
|
9
9
|
// Categorize resources for better organization
|
|
10
10
|
const categorizedResources = markdownFiles.map((file) => {
|
|
11
|
-
let category =
|
|
11
|
+
let category = "Other";
|
|
12
12
|
let name = file;
|
|
13
|
-
if (file.startsWith(
|
|
14
|
-
category =
|
|
15
|
-
name = file.replace(
|
|
13
|
+
if (file.startsWith("components-")) {
|
|
14
|
+
category = "Components";
|
|
15
|
+
name = file.replace("components-", "").replace("-docs.md", "");
|
|
16
16
|
}
|
|
17
|
-
else if (file.startsWith(
|
|
18
|
-
category =
|
|
19
|
-
name = file.replace(
|
|
17
|
+
else if (file.startsWith("foundations-")) {
|
|
18
|
+
category = "Foundations";
|
|
19
|
+
name = file.replace("foundations-", "").replace("-docs.md", "");
|
|
20
20
|
}
|
|
21
|
-
else if (file.startsWith(
|
|
22
|
-
category =
|
|
23
|
-
name = file.replace(
|
|
21
|
+
else if (file.startsWith("resources-")) {
|
|
22
|
+
category = "Resources";
|
|
23
|
+
name = file.replace("resources-", "").replace("-docs.md", "");
|
|
24
24
|
}
|
|
25
|
-
else if (file.includes(
|
|
26
|
-
category =
|
|
27
|
-
name =
|
|
25
|
+
else if (file.includes("introduction")) {
|
|
26
|
+
category = "Getting Started";
|
|
27
|
+
name = "Introduction";
|
|
28
28
|
}
|
|
29
29
|
return {
|
|
30
30
|
uri: `file://${path.join(DOCS_DIR, file)}`,
|
|
31
31
|
name: `${category}: ${name}`,
|
|
32
32
|
description: `IDS ${category.toLowerCase()} documentation for ${name}`,
|
|
33
|
-
mimeType:
|
|
33
|
+
mimeType: "text/markdown",
|
|
34
34
|
};
|
|
35
35
|
});
|
|
36
36
|
return {
|
|
@@ -39,14 +39,14 @@ export function handleListResources() {
|
|
|
39
39
|
}
|
|
40
40
|
export function handleReadResource(request) {
|
|
41
41
|
const url = new URL(request.params.uri);
|
|
42
|
-
if (url.protocol !==
|
|
42
|
+
if (url.protocol !== "file:") {
|
|
43
43
|
throw new Error(`Unsupported protocol: ${url.protocol}`);
|
|
44
44
|
}
|
|
45
45
|
const filePath = url.pathname;
|
|
46
46
|
// Ensure the file is within our docs directory
|
|
47
47
|
const relativePath = path.relative(DOCS_DIR, filePath);
|
|
48
|
-
if (relativePath.startsWith(
|
|
49
|
-
throw new Error(
|
|
48
|
+
if (relativePath.startsWith("..") || path.isAbsolute(relativePath)) {
|
|
49
|
+
throw new Error("Access denied: File is outside the docs directory");
|
|
50
50
|
}
|
|
51
51
|
try {
|
|
52
52
|
const content = readFileContent(filePath);
|
|
@@ -54,14 +54,13 @@ export function handleReadResource(request) {
|
|
|
54
54
|
contents: [
|
|
55
55
|
{
|
|
56
56
|
uri: request.params.uri,
|
|
57
|
-
mimeType:
|
|
57
|
+
mimeType: "text/markdown",
|
|
58
58
|
text: content,
|
|
59
59
|
},
|
|
60
60
|
],
|
|
61
61
|
};
|
|
62
62
|
}
|
|
63
63
|
catch (error) {
|
|
64
|
-
|
|
65
|
-
throw new Error(`Failed to read file: ${errorMessage}`);
|
|
64
|
+
throw new Error(`Failed to read file: ${error}`);
|
|
66
65
|
}
|
|
67
66
|
}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Tool handlers for search and documentation operations
|
|
3
3
|
*/
|
|
4
|
-
import { z } from
|
|
5
|
-
import * as path from
|
|
6
|
-
import { getMarkdownFiles, readFileContent } from
|
|
7
|
-
import { DOCS_DIR } from
|
|
4
|
+
import { z } from "zod";
|
|
5
|
+
import * as path from "path";
|
|
6
|
+
import { getMarkdownFiles, readFileContent } from "./utils.js";
|
|
7
|
+
import { DOCS_DIR } from "./config.js";
|
|
8
8
|
export function handleGetUsageExamples(args) {
|
|
9
9
|
const schema = z.object({
|
|
10
10
|
component: z.string(),
|
|
@@ -14,18 +14,18 @@ export function handleGetUsageExamples(args) {
|
|
|
14
14
|
const markdownFiles = getMarkdownFiles();
|
|
15
15
|
// Find component files (main + recipes)
|
|
16
16
|
const componentFiles = markdownFiles.filter((file) => file.includes(`${component.toLowerCase()}`) ||
|
|
17
|
-
(file.includes(
|
|
17
|
+
(file.includes("recipes") &&
|
|
18
18
|
file.toLowerCase().includes(component.toLowerCase())));
|
|
19
19
|
if (componentFiles.length === 0) {
|
|
20
20
|
return {
|
|
21
21
|
content: [
|
|
22
22
|
{
|
|
23
|
-
type:
|
|
23
|
+
type: "text",
|
|
24
24
|
text: `No examples found for "${component}". Try: ${markdownFiles
|
|
25
|
-
.filter((f) => f.startsWith(
|
|
25
|
+
.filter((f) => f.startsWith("components-"))
|
|
26
26
|
.slice(0, 5)
|
|
27
|
-
.map((f) => f.replace(
|
|
28
|
-
.join(
|
|
27
|
+
.map((f) => f.replace("components-", "").replace("-docs.md", ""))
|
|
28
|
+
.join(", ")}`,
|
|
29
29
|
},
|
|
30
30
|
],
|
|
31
31
|
};
|
|
@@ -36,8 +36,8 @@ export function handleGetUsageExamples(args) {
|
|
|
36
36
|
const filePath = path.join(DOCS_DIR, file);
|
|
37
37
|
const content = readFileContent(filePath);
|
|
38
38
|
// Extract code examples
|
|
39
|
-
const codeBlocks = content.match(/```[\s\S]*?```/g)
|
|
40
|
-
const jsxBlocks = content.match(/<[A-Z][^>]*>[\s\S]*?<\/[A-Z][^>]*>/g)
|
|
39
|
+
const codeBlocks = content.match(/```[\s\S]*?```/g) || [];
|
|
40
|
+
const jsxBlocks = content.match(/<[A-Z][^>]*>[\s\S]*?<\/[A-Z][^>]*>/g) || [];
|
|
41
41
|
if (pattern) {
|
|
42
42
|
// Filter examples by pattern
|
|
43
43
|
const patternMatches = [...codeBlocks, ...jsxBlocks].filter((block) => block.toLowerCase().includes(pattern.toLowerCase()));
|
|
@@ -51,18 +51,13 @@ export function handleGetUsageExamples(args) {
|
|
|
51
51
|
console.error(`Error reading file ${file}:`, error);
|
|
52
52
|
}
|
|
53
53
|
}
|
|
54
|
-
const patternSuffix = pattern ? ` (${pattern} pattern)` : '';
|
|
55
|
-
const patternNotFoundSuffix = pattern ? ` with pattern "${pattern}"` : '';
|
|
56
|
-
const examplesText = examples.length > 0
|
|
57
|
-
? `**${component} Usage Examples**${patternSuffix}:\n\n${examples
|
|
58
|
-
.slice(0, 5)
|
|
59
|
-
.join('\n\n---\n\n')}`
|
|
60
|
-
: `No usage examples found for "${component}"${patternNotFoundSuffix}.`;
|
|
61
54
|
return {
|
|
62
55
|
content: [
|
|
63
56
|
{
|
|
64
|
-
type:
|
|
65
|
-
text:
|
|
57
|
+
type: "text",
|
|
58
|
+
text: examples.length > 0
|
|
59
|
+
? `**${component} Usage Examples**${pattern ? ` (${pattern} pattern)` : ""}:\n\n${examples.slice(0, 5).join("\n\n---\n\n")}`
|
|
60
|
+
: `No usage examples found for "${component}"${pattern ? ` with pattern "${pattern}"` : ""}.`,
|
|
66
61
|
},
|
|
67
62
|
],
|
|
68
63
|
};
|
|
@@ -79,7 +74,7 @@ export function handleSearchIdsDocs(args) {
|
|
|
79
74
|
try {
|
|
80
75
|
const filePath = path.join(DOCS_DIR, file);
|
|
81
76
|
const content = readFileContent(filePath);
|
|
82
|
-
const lines = content.split(
|
|
77
|
+
const lines = content.split("\n");
|
|
83
78
|
lines.forEach((line, index) => {
|
|
84
79
|
const searchLine = case_sensitive ? line : line.toLowerCase();
|
|
85
80
|
const searchQuery = case_sensitive ? query : query.toLowerCase();
|
|
@@ -87,9 +82,9 @@ export function handleSearchIdsDocs(args) {
|
|
|
87
82
|
// Get context (surrounding lines)
|
|
88
83
|
const contextStart = Math.max(0, index - 1);
|
|
89
84
|
const contextEnd = Math.min(lines.length - 1, index + 1);
|
|
90
|
-
const context = lines.slice(contextStart, contextEnd + 1).join(
|
|
85
|
+
const context = lines.slice(contextStart, contextEnd + 1).join("\n");
|
|
91
86
|
results.push({
|
|
92
|
-
file: file.replace(
|
|
87
|
+
file: file.replace("-docs.md", "").replace(/^[a-z]+-/, ""),
|
|
93
88
|
line: index + 1,
|
|
94
89
|
content: line.trim(),
|
|
95
90
|
context,
|
|
@@ -104,12 +99,12 @@ export function handleSearchIdsDocs(args) {
|
|
|
104
99
|
return {
|
|
105
100
|
content: [
|
|
106
101
|
{
|
|
107
|
-
type:
|
|
102
|
+
type: "text",
|
|
108
103
|
text: results.length > 0
|
|
109
104
|
? `Found ${results.length} matches in IDS documentation:\n\n${results
|
|
110
105
|
.slice(0, 15) // Limit results
|
|
111
106
|
.map((r) => `**${r.file}:${r.line}**\n\`\`\`\n${r.context}\n\`\`\``)
|
|
112
|
-
.join(
|
|
107
|
+
.join("\n\n")}`
|
|
113
108
|
: `No matches found for "${query}" in IDS documentation.`,
|
|
114
109
|
},
|
|
115
110
|
],
|
|
@@ -118,32 +113,31 @@ export function handleSearchIdsDocs(args) {
|
|
|
118
113
|
export function handleGetDesignTokens(args) {
|
|
119
114
|
const schema = z.object({
|
|
120
115
|
type: z
|
|
121
|
-
.enum([
|
|
122
|
-
.default(
|
|
116
|
+
.enum(["colors", "spacing", "typography", "breakpoints", "all"])
|
|
117
|
+
.default("all"),
|
|
123
118
|
});
|
|
124
119
|
const { type } = schema.parse(args);
|
|
125
120
|
const markdownFiles = getMarkdownFiles();
|
|
126
121
|
// Find foundation files related to design tokens
|
|
127
|
-
const foundationFiles = markdownFiles.filter((file) => file.startsWith(
|
|
128
|
-
(type === 'all' || file.includes(type)));
|
|
122
|
+
const foundationFiles = markdownFiles.filter((file) => file.startsWith("foundations-") && (type === "all" || file.includes(type)));
|
|
129
123
|
const tokenInfo = [];
|
|
130
124
|
for (const file of foundationFiles) {
|
|
131
125
|
try {
|
|
132
126
|
const filePath = path.join(DOCS_DIR, file);
|
|
133
127
|
const content = readFileContent(filePath);
|
|
134
128
|
// Extract CSS custom properties and token information
|
|
135
|
-
const cssVariables = content.match(/--iress-[a-z-]+/g)
|
|
136
|
-
const tokenSections = content.match(
|
|
129
|
+
const cssVariables = content.match(/--iress-[a-z-]+/g) || [];
|
|
130
|
+
const tokenSections = content.match(/###?\s+[^#\n]+[\s\S]*?(?=###|$)/g) || [];
|
|
137
131
|
if (cssVariables.length > 0 || tokenSections.length > 0) {
|
|
138
132
|
let fileInfo = `**${file
|
|
139
|
-
.replace(
|
|
140
|
-
.replace(
|
|
133
|
+
.replace("foundations-", "")
|
|
134
|
+
.replace("-docs.md", "")}**\n`;
|
|
141
135
|
if (cssVariables.length > 0) {
|
|
142
136
|
const uniqueVars = [...new Set(cssVariables)].slice(0, 10);
|
|
143
|
-
fileInfo += `CSS Variables: ${uniqueVars.join(
|
|
137
|
+
fileInfo += `CSS Variables: ${uniqueVars.join(", ")}\n`;
|
|
144
138
|
}
|
|
145
139
|
if (tokenSections.length > 0) {
|
|
146
|
-
fileInfo += `\n${tokenSections.slice(0, 3).join(
|
|
140
|
+
fileInfo += `\n${tokenSections.slice(0, 3).join("\n\n")}`;
|
|
147
141
|
}
|
|
148
142
|
tokenInfo.push(fileInfo);
|
|
149
143
|
}
|
|
@@ -152,61 +146,20 @@ export function handleGetDesignTokens(args) {
|
|
|
152
146
|
console.error(`Error reading file ${file}:`, error);
|
|
153
147
|
}
|
|
154
148
|
}
|
|
155
|
-
const typeLabel = type !== 'all' ? ` (${type})` : '';
|
|
156
|
-
const typeNotFoundLabel = type !== 'all' ? ` for ${type}` : '';
|
|
157
149
|
return {
|
|
158
150
|
content: [
|
|
159
151
|
{
|
|
160
|
-
type:
|
|
152
|
+
type: "text",
|
|
161
153
|
text: tokenInfo.length > 0
|
|
162
|
-
? `**IDS Design Tokens${
|
|
163
|
-
: `No design token information found${
|
|
164
|
-
.filter((f) => f.startsWith(
|
|
165
|
-
.map((f) => f.replace(
|
|
166
|
-
.join(
|
|
154
|
+
? `**IDS Design Tokens${type !== "all" ? ` (${type})` : ""}**\n\n${tokenInfo.join("\n\n---\n\n")}`
|
|
155
|
+
: `No design token information found${type !== "all" ? ` for ${type}` : ""}. Available foundations: ${markdownFiles
|
|
156
|
+
.filter((f) => f.startsWith("foundations-"))
|
|
157
|
+
.map((f) => f.replace("foundations-", "").replace("-docs.md", ""))
|
|
158
|
+
.join(", ")}`,
|
|
167
159
|
},
|
|
168
160
|
],
|
|
169
161
|
};
|
|
170
162
|
}
|
|
171
|
-
function findSectionContent(content, section) {
|
|
172
|
-
const sectionRegex = new RegExp(`(#{1,3}\\s+.*${section}.*?(?=#{1,3}|$))`, 'gis');
|
|
173
|
-
const sectionMatch = sectionRegex.exec(content);
|
|
174
|
-
if (sectionMatch) {
|
|
175
|
-
return sectionMatch[0];
|
|
176
|
-
}
|
|
177
|
-
// Try to find sections that contain the search term
|
|
178
|
-
const sections = content.split(/(?=#{1,3}\s+)/);
|
|
179
|
-
const matchingSections = sections.filter((sectionContent) => sectionContent.toLowerCase().includes(section.toLowerCase()));
|
|
180
|
-
return matchingSections.length > 0 ? matchingSections.join('\n\n') : null;
|
|
181
|
-
}
|
|
182
|
-
function addContextLines(lines, startIndex, endIndex, contextLines) {
|
|
183
|
-
for (let j = startIndex; j < endIndex; j++) {
|
|
184
|
-
if (!contextLines.includes(lines[j])) {
|
|
185
|
-
contextLines.push(lines[j]);
|
|
186
|
-
}
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
|
-
function filterContentByQuery(content, query) {
|
|
190
|
-
const lines = content.split('\n');
|
|
191
|
-
const contextLines = [];
|
|
192
|
-
const queryLower = query.toLowerCase();
|
|
193
|
-
for (let i = 0; i < lines.length; i++) {
|
|
194
|
-
const line = lines[i];
|
|
195
|
-
if (line.toLowerCase().includes(queryLower)) {
|
|
196
|
-
// Add context lines before
|
|
197
|
-
const startContext = Math.max(0, i - 3);
|
|
198
|
-
addContextLines(lines, startContext, i, contextLines);
|
|
199
|
-
// Add the matching line
|
|
200
|
-
if (!contextLines.includes(line)) {
|
|
201
|
-
contextLines.push(line);
|
|
202
|
-
}
|
|
203
|
-
// Add context lines after
|
|
204
|
-
const endContext = Math.min(lines.length, i + 4);
|
|
205
|
-
addContextLines(lines, i + 1, endContext, contextLines);
|
|
206
|
-
}
|
|
207
|
-
}
|
|
208
|
-
return contextLines.length > 0 ? contextLines.join('\n') : null;
|
|
209
|
-
}
|
|
210
163
|
export function handleGetDesignGuidelines(args) {
|
|
211
164
|
const schema = z.object({
|
|
212
165
|
section: z.string().optional(),
|
|
@@ -215,14 +168,14 @@ export function handleGetDesignGuidelines(args) {
|
|
|
215
168
|
const { section, query } = schema.parse(args);
|
|
216
169
|
try {
|
|
217
170
|
// Read the guidelines.md file from the docs directory
|
|
218
|
-
const guidelinesPath = path.join(DOCS_DIR,
|
|
171
|
+
const guidelinesPath = path.join(DOCS_DIR, "..", "guidelines.md");
|
|
219
172
|
const content = readFileContent(guidelinesPath);
|
|
220
173
|
if (!content) {
|
|
221
174
|
return {
|
|
222
175
|
content: [
|
|
223
176
|
{
|
|
224
|
-
type:
|
|
225
|
-
text:
|
|
177
|
+
type: "text",
|
|
178
|
+
text: "Design guidelines file not found. Please ensure guidelines.md exists in the docs directory.",
|
|
226
179
|
},
|
|
227
180
|
],
|
|
228
181
|
};
|
|
@@ -230,56 +183,88 @@ export function handleGetDesignGuidelines(args) {
|
|
|
230
183
|
let filteredContent = content;
|
|
231
184
|
// Filter by section if specified
|
|
232
185
|
if (section) {
|
|
233
|
-
const
|
|
234
|
-
|
|
235
|
-
|
|
186
|
+
const sectionRegex = new RegExp(`(#{1,3}\\s+.*${section}.*?(?=#{1,3}|$))`, "gis");
|
|
187
|
+
const sectionMatch = content.match(sectionRegex);
|
|
188
|
+
if (sectionMatch) {
|
|
189
|
+
filteredContent = sectionMatch[0];
|
|
236
190
|
}
|
|
237
191
|
else {
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
192
|
+
// Try to find sections that contain the search term
|
|
193
|
+
const sections = content.split(/(?=#{1,3}\s+)/);
|
|
194
|
+
const matchingSections = sections.filter((section) => section.toLowerCase().includes(section.toLowerCase()));
|
|
195
|
+
if (matchingSections.length > 0) {
|
|
196
|
+
filteredContent = matchingSections.join("\n\n");
|
|
197
|
+
}
|
|
198
|
+
else {
|
|
199
|
+
return {
|
|
200
|
+
content: [
|
|
201
|
+
{
|
|
202
|
+
type: "text",
|
|
203
|
+
text: `Section "${section}" not found in design guidelines. Available sections include: Core Design Principles, Visual Design Standards, Component Guidelines, Accessibility, Layout Systems, and Best Practices.`,
|
|
204
|
+
},
|
|
205
|
+
],
|
|
206
|
+
};
|
|
207
|
+
}
|
|
246
208
|
}
|
|
247
209
|
}
|
|
248
210
|
// Filter by query if specified
|
|
249
211
|
if (query) {
|
|
250
|
-
const
|
|
251
|
-
|
|
252
|
-
|
|
212
|
+
const lines = filteredContent.split("\n");
|
|
213
|
+
const relevantLines = [];
|
|
214
|
+
let contextLines = [];
|
|
215
|
+
for (let i = 0; i < lines.length; i++) {
|
|
216
|
+
const line = lines[i];
|
|
217
|
+
if (line.toLowerCase().includes(query.toLowerCase())) {
|
|
218
|
+
// Add context lines before
|
|
219
|
+
const startContext = Math.max(0, i - 3);
|
|
220
|
+
for (let j = startContext; j < i; j++) {
|
|
221
|
+
if (!contextLines.includes(lines[j])) {
|
|
222
|
+
contextLines.push(lines[j]);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
// Add the matching line
|
|
226
|
+
if (!contextLines.includes(line)) {
|
|
227
|
+
contextLines.push(line);
|
|
228
|
+
}
|
|
229
|
+
// Add context lines after
|
|
230
|
+
const endContext = Math.min(lines.length, i + 4);
|
|
231
|
+
for (let j = i + 1; j < endContext; j++) {
|
|
232
|
+
if (!contextLines.includes(lines[j])) {
|
|
233
|
+
contextLines.push(lines[j]);
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
if (contextLines.length > 0) {
|
|
239
|
+
filteredContent = contextLines.join("\n");
|
|
253
240
|
}
|
|
254
241
|
else {
|
|
255
242
|
return {
|
|
256
243
|
content: [
|
|
257
244
|
{
|
|
258
|
-
type:
|
|
245
|
+
type: "text",
|
|
259
246
|
text: `No guidelines found matching "${query}". Try searching for terms like: accessibility, typography, colors, spacing, components, principles, or usability.`,
|
|
260
247
|
},
|
|
261
248
|
],
|
|
262
249
|
};
|
|
263
250
|
}
|
|
264
251
|
}
|
|
265
|
-
const sectionSuffix = section ? ` - ${section}` : '';
|
|
266
|
-
const querySuffix = query ? ` (filtered by: ${query})` : '';
|
|
267
252
|
return {
|
|
268
253
|
content: [
|
|
269
254
|
{
|
|
270
|
-
type:
|
|
271
|
-
text: `**IDS Design Guidelines${
|
|
255
|
+
type: "text",
|
|
256
|
+
text: `**IDS Design Guidelines${section ? ` - ${section}` : ""}${query ? ` (filtered by: ${query})` : ""}**\n\n${filteredContent}`,
|
|
272
257
|
},
|
|
273
258
|
],
|
|
274
259
|
};
|
|
275
260
|
}
|
|
276
261
|
catch (error) {
|
|
277
|
-
console.error(
|
|
262
|
+
console.error("Error reading guidelines:", error);
|
|
278
263
|
return {
|
|
279
264
|
content: [
|
|
280
265
|
{
|
|
281
|
-
type:
|
|
282
|
-
text:
|
|
266
|
+
type: "text",
|
|
267
|
+
text: "Error reading design guidelines. Please ensure the guidelines.md file exists and is accessible.",
|
|
283
268
|
},
|
|
284
269
|
],
|
|
285
270
|
};
|