@progress/kendo-react-mcp 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +105 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +84 -0
- package/dist/lib/constants.d.ts +10 -0
- package/dist/lib/constants.js +134 -0
- package/dist/lib/grpc-client.d.ts +5 -0
- package/dist/lib/grpc-client.js +33 -0
- package/dist/lib/logger.d.ts +1 -0
- package/dist/lib/logger.js +26 -0
- package/dist/lib/read-license.d.ts +4 -0
- package/dist/lib/read-license.js +32 -0
- package/package.json +32 -0
- package/proto/service.proto +30 -0
package/README.md
ADDED
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
# KendoReact MCP Server
|
|
2
|
+
|
|
3
|
+
The KendoReact [MCP Server](https://modelcontextprotocol.io/introduction) enables you achieve interation with AI and reach new levels of developer productivity. The MCP server provides proprietary context to AI-powered IDEs, apps and tools. You can use the KendoReact MCP server to ask about Kendo UI for React components, features, or general usage. You can successfully prompt more complex questions and tasks, and generate tailored code that includes Kendo UI for React components and API.
|
|
4
|
+
|
|
5
|
+
## Prerequisites
|
|
6
|
+
|
|
7
|
+
To use the KendoReact MCP server, you need:
|
|
8
|
+
|
|
9
|
+
* A [compatible MCP client (IDE, code editor or app)](https://modelcontextprotocol.io/clients) that supports *MCP tools*.
|
|
10
|
+
* A [Telerik user account](https://www.telerik.com/account/).
|
|
11
|
+
* An active [DevCraft or Kendo UI for React license](https://www.telerik.com/kendo-react-ui/pricing) or a [Kendo UI for React trial](https://www.telerik.com/kendo-react-ui).
|
|
12
|
+
|
|
13
|
+
## Installation
|
|
14
|
+
|
|
15
|
+
Use the documentation of your AI-powered MCP client to add the Kendo React MCP server to a specific workspace or globally. You can see installation tips and examples for some popular MCP clients below.
|
|
16
|
+
|
|
17
|
+
The generic settings of the KendoReact MCP server are:
|
|
18
|
+
|
|
19
|
+
* Server name: `kendo-react-assistant`
|
|
20
|
+
* Type: `stdio` (standard input/output transport)
|
|
21
|
+
* Command: `npx` (the MCP server works through an npm package)
|
|
22
|
+
* Supported arguments: `-y`
|
|
23
|
+
* npm package name: `@progress/kendo-react-mcp`
|
|
24
|
+
* You also need to add your [Telerik licence key](https://www.telerik.com/kendo-react-ui/components/my-license) as an `env` parameter in the `mcp.json` file. There are two options:
|
|
25
|
+
* (recommended) Use a `TELERIK_LICENSE_PATH` argument and point to your Telerik license file location.
|
|
26
|
+
* Use a `TELERIK_LICENSE_KEY` argument and paste your Telerik license key. Make sure to [update the license key](https://www.telerik.com/kendo-react-ui/components/my-license) when necessary.
|
|
27
|
+
|
|
28
|
+
## Authentication
|
|
29
|
+
|
|
30
|
+
The MCP server requires a valid Telerik account with a valid license. In order to get a valid license key check the [KendoReact documentation](https://www.telerik.com/kendo-react-ui/components/my-license).
|
|
31
|
+
|
|
32
|
+
To set up the license key, you need to set it up as an environment variable in the JSON configuration file.
|
|
33
|
+
|
|
34
|
+
## Configuration Examples
|
|
35
|
+
|
|
36
|
+
### VSCode - Copilot
|
|
37
|
+
|
|
38
|
+
Refer to [Use MCP servers in VS Code](https://code.visualstudio.com/docs/copilot/chat/mcp-servers).
|
|
39
|
+
|
|
40
|
+
To enable the Kendo React MCP server in a specific workspace or Blazor app, add a `.vscode` folder with an `mcp.json` file at the root of the workspace:
|
|
41
|
+
|
|
42
|
+
> .vscode/mcp.json at the workspace root
|
|
43
|
+
|
|
44
|
+
```json
|
|
45
|
+
{
|
|
46
|
+
"servers": {
|
|
47
|
+
"kendo-react-assistant": {
|
|
48
|
+
"type": "stdio",
|
|
49
|
+
"command": "npx",
|
|
50
|
+
"args": ["-y", "@progress/kendo-react-mcp@latest"],
|
|
51
|
+
"env": {
|
|
52
|
+
"TELERIK_LICENSE_PATH": "THE_PATH_TO_YOUR_LICENSE_FILE",
|
|
53
|
+
// or
|
|
54
|
+
"TELERIK_LICENSE_KEY": "YOUR_LICENSE_KEY"
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### Cursor
|
|
62
|
+
|
|
63
|
+
Refer to [Model Context Protocol](https://docs.cursor.com/context/model-context-protocol).
|
|
64
|
+
|
|
65
|
+
To [enable the Kendo React MCP server in a specific workspace](https://code.visualstudio.com/docs/copilot/chat/mcp-servers#_add-an-mcp-server-to-your-workspace) or Blazor app, add a `.cursor` folder with an `mcp.json` file at the root of the workspace.
|
|
66
|
+
|
|
67
|
+
> .cursor/mcp.json at the workspace root
|
|
68
|
+
|
|
69
|
+
```json
|
|
70
|
+
{
|
|
71
|
+
"mcpServers": {
|
|
72
|
+
"kendo-react-assistant": {
|
|
73
|
+
"type": "stdio",
|
|
74
|
+
"command": "npx",
|
|
75
|
+
"args": ["-y", "@progress/kendo-react-mcp@latest"],
|
|
76
|
+
"env": {
|
|
77
|
+
"TELERIK_LICENSE_PATH": "THE_PATH_TO_YOUR_LICENSE_FILE",
|
|
78
|
+
// or
|
|
79
|
+
"TELERIK_LICENSE": "YOUR_LICENSE_KEY"
|
|
80
|
+
},
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
## Usage
|
|
87
|
+
|
|
88
|
+
To use the Kendo React MCP server:
|
|
89
|
+
|
|
90
|
+
1. Start your prompt with one of the following:
|
|
91
|
+
* `kendo`
|
|
92
|
+
* `/kendo`
|
|
93
|
+
* `@kendo`
|
|
94
|
+
* `kendoblazor`
|
|
95
|
+
* `/kendoblazor`
|
|
96
|
+
* `@kendoblazor`
|
|
97
|
+
1. Confirm that the Kendo React MCP server is used, because this doesn't happen deterministically. Look for a statement in the output, which is similar to:
|
|
98
|
+
* `Running kendo-react-assistant` (in VS Code)
|
|
99
|
+
* `Calling MCP tool kendo-react-assistant` (in Cursor)
|
|
100
|
+
|
|
101
|
+
If the Kendo React MCP server is not used even though it's installed and enabled, then try rephrasing your prompt and use another trigger syntax from the list in step 1.
|
|
102
|
+
1. Grant the Kendo React MCP sewrver a permission to run for this session, workspace, or always.
|
|
103
|
+
1. If you want to prompt for information or code that are not related to your previous prompts, it is a good practice to start a new session in a new chat window, so that the context is not polluted by irrelevant old information.
|
|
104
|
+
|
|
105
|
+
To increase the probability of the Kendo React MCP Server being used, or to call it without the need to mention "kendo" explicitly, add custom idea instructions to your AI-powered tool. Here are examples for [GitHub Copilot](https://docs.github.com/en/copilot/customizing-copilot/adding-repository-custom-instructions-for-github-copilot#about-repository-custom-instructions-for-github-copilot-chat) and [Cursor](https://docs.cursor.com/context/rules).
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
3
|
+
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
4
|
+
import { z } from 'zod';
|
|
5
|
+
import { ReactComponents, Suite, GrpcErrors, REACT_SUITE_NAME, } from './lib/constants.js';
|
|
6
|
+
import { query as contextApiQuery } from './lib/grpc-client.js';
|
|
7
|
+
import fs from 'fs';
|
|
8
|
+
import path from 'path';
|
|
9
|
+
import { fileURLToPath } from 'url';
|
|
10
|
+
import { log } from './lib/logger.js';
|
|
11
|
+
import { LicenseError } from './lib/read-license.js';
|
|
12
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
13
|
+
const __dirname = path.dirname(__filename);
|
|
14
|
+
const packageJsonPath = path.resolve(__dirname, '../package.json');
|
|
15
|
+
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'));
|
|
16
|
+
const server = new McpServer({
|
|
17
|
+
name: 'kendo-and-kendo-resources',
|
|
18
|
+
version: packageJson.version,
|
|
19
|
+
});
|
|
20
|
+
server.tool('kendo_react_assistant', `Answers questions and retrieves documentation about Kendo UI for React.
|
|
21
|
+
Use this tool when the user asks about Kendo UI for React features, specific components (e.g., Grid, Chart, Editor), implementation details, or general usage.
|
|
22
|
+
Provide the user's detailed question as the 'query'. If the question pertains to a specific Kendo UI for React component,
|
|
23
|
+
specify its name in the 'component' parameter. This tool can be automatically triggered when the following phrases are detected in the user's input/prompt:
|
|
24
|
+
- 'kendo';
|
|
25
|
+
- '/kendo';
|
|
26
|
+
- '/kendoreact'
|
|
27
|
+
- '/ask_kendo';
|
|
28
|
+
- '/help_kendo';
|
|
29
|
+
- '@kendo';
|
|
30
|
+
- '@kendoblazor';
|
|
31
|
+
- '@ask_kendo';
|
|
32
|
+
- '@help_kendo'.`, {
|
|
33
|
+
query: z.string().describe('The query to search for.'),
|
|
34
|
+
component: z
|
|
35
|
+
.enum(ReactComponents)
|
|
36
|
+
.describe('The component to search for. If not specified, you can use General.'),
|
|
37
|
+
}, async ({ query, component }) => {
|
|
38
|
+
log('Calling tool: ', JSON.stringify({ query, component }));
|
|
39
|
+
try {
|
|
40
|
+
const response = await contextApiQuery({
|
|
41
|
+
query,
|
|
42
|
+
component: component.toLocaleLowerCase(),
|
|
43
|
+
lib_name: Suite,
|
|
44
|
+
text_matches_count: 3,
|
|
45
|
+
code_matches_count: 0,
|
|
46
|
+
allowed_types: ['documentation'],
|
|
47
|
+
});
|
|
48
|
+
const content = [
|
|
49
|
+
...response.values.map((item) => ({
|
|
50
|
+
type: 'text',
|
|
51
|
+
text: item,
|
|
52
|
+
})),
|
|
53
|
+
];
|
|
54
|
+
log('Response from tool: ', JSON.stringify(content));
|
|
55
|
+
return {
|
|
56
|
+
content,
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
catch (error) {
|
|
60
|
+
log('Error calling context API: ', JSON.stringify(error));
|
|
61
|
+
if (error?.code === GrpcErrors.UNAUTHENTICATED) {
|
|
62
|
+
throw new Error('Error: You are not authenticated. Please verify the Telerik License Key provided is valid.');
|
|
63
|
+
}
|
|
64
|
+
else if (error?.code === GrpcErrors.RESOURCE_EXHAUSTED) {
|
|
65
|
+
throw new Error(`Error: You have exceeded the extension quota. Message: ${error.message}.`);
|
|
66
|
+
}
|
|
67
|
+
else if (error?.code === GrpcErrors.PERMISSION_DENIED) {
|
|
68
|
+
throw new Error(`Error: You do not have permission to access this resource. Please verify that you have a valid ${REACT_SUITE_NAME} license key.`);
|
|
69
|
+
}
|
|
70
|
+
else if (error instanceof LicenseError) {
|
|
71
|
+
log('License error: ', error.message);
|
|
72
|
+
throw error;
|
|
73
|
+
}
|
|
74
|
+
else {
|
|
75
|
+
throw new Error('Error: An error occurred while calling the service. Please try again later.');
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
});
|
|
79
|
+
async function startServer() {
|
|
80
|
+
const transport = new StdioServerTransport();
|
|
81
|
+
log('Starting server...');
|
|
82
|
+
await server.connect(transport);
|
|
83
|
+
}
|
|
84
|
+
startServer();
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export declare const LICENSE_ENV_VARS: string[];
|
|
2
|
+
export declare const LICENSE_ENV_PATH_VAR = "TELERIK_LICENSE_PATH";
|
|
3
|
+
export declare const REACT_SUITE_NAME = "Kendo UI for React";
|
|
4
|
+
export declare const Suite = "react";
|
|
5
|
+
export declare const ReactComponents: readonly ["ActionSheet", "AIPrompt", "Animation", "AppBar", "ArcGauge", "AutoComplete", "Avatar", "Badge", "Barcode", "BottomNavigation", "Breadcrumb", "Button", "ButtonGroup", "Calendar", "Card", "Chart", "Checkbox", "Chip", "ChipList", "ChunkProgressBar", "CircularGauge", "ColorGradient", "ColorPalette", "ColorPicker", "ComboBox", "ContextMenu", "ConversationalUI", "DataGrid", "DataQuery", "DateInput", "DateMath", "DatePicker", "DateRangePicker", "DateTimePicker", "Dialog", "Drag&Drop", "Drawer", "Drawing", "DropDownButton", "DropDownList", "DropDownTree", "Editor", "Error", "ExcelExport", "ExpansionPanel", "ExternalDropZone", "FileManager", "FileSaver", "Filter", "FlatColorPicker", "FloatingActionButton", "FloatingLabel", "FontIcon", "Form", "Gantt", "Gauge", "General", "GridLayout", "Hint", "Input", "Label", "LinearGauge", "ListBox", "ListView", "Loader", "Map", "MaskedTextBox", "Menu", "MultiColumnComboBox", "MultiSelect", "MultiSelectTree", "MultiViewCalendar", "Notification", "NumericTextBox", "OrgChart", "Pager", "PageTemplates/BuildingBlocks", "PanelBar", "PDFProcessing", "PDFViewer", "PivotGrid", "Popover", "Popup", "ProgressBar", "QRCode", "RadialGauge", "RadioButton", "RadioGroup", "RangeSlider", "Rating", "Ripple", "Sankey", "Scheduler", "ScrollView", "Signature", "Skeleton", "Slider", "Sortable", "Sparkline", "SplitButton", "Splitter", "Spreadsheet", "StackLayout", "Stepper", "StockChart", "SVGIcon", "Switch", "TabStrip", "TaskBoard", "TextArea", "TextBox", "TileLayout", "Timeline", "TimePicker", "Toolbar", "Tooltip", "TreeList", "TreeView", "Typography", "Upload", "VSCodeExtension", "Window"];
|
|
6
|
+
export declare enum GrpcErrors {
|
|
7
|
+
UNAUTHENTICATED = 16,
|
|
8
|
+
RESOURCE_EXHAUSTED = 8,
|
|
9
|
+
PERMISSION_DENIED = 7
|
|
10
|
+
}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
export const LICENSE_ENV_VARS = ['TELERIK_LICENSE', 'KENDO_UI_LICENSE'];
|
|
2
|
+
export const LICENSE_ENV_PATH_VAR = 'TELERIK_LICENSE_PATH';
|
|
3
|
+
export const REACT_SUITE_NAME = 'Kendo UI for React';
|
|
4
|
+
export const Suite = 'react';
|
|
5
|
+
export const ReactComponents = [
|
|
6
|
+
'ActionSheet',
|
|
7
|
+
'AIPrompt',
|
|
8
|
+
'Animation',
|
|
9
|
+
'AppBar',
|
|
10
|
+
'ArcGauge',
|
|
11
|
+
'AutoComplete',
|
|
12
|
+
'Avatar',
|
|
13
|
+
'Badge',
|
|
14
|
+
'Barcode',
|
|
15
|
+
'BottomNavigation',
|
|
16
|
+
'Breadcrumb',
|
|
17
|
+
'Button',
|
|
18
|
+
'ButtonGroup',
|
|
19
|
+
'Calendar',
|
|
20
|
+
'Card',
|
|
21
|
+
'Chart',
|
|
22
|
+
'Checkbox',
|
|
23
|
+
'Chip',
|
|
24
|
+
'ChipList',
|
|
25
|
+
'ChunkProgressBar',
|
|
26
|
+
'CircularGauge',
|
|
27
|
+
'ColorGradient',
|
|
28
|
+
'ColorPalette',
|
|
29
|
+
'ColorPicker',
|
|
30
|
+
'ComboBox',
|
|
31
|
+
'ContextMenu',
|
|
32
|
+
'ConversationalUI',
|
|
33
|
+
'DataGrid',
|
|
34
|
+
'DataQuery',
|
|
35
|
+
'DateInput',
|
|
36
|
+
'DateMath',
|
|
37
|
+
'DatePicker',
|
|
38
|
+
'DateRangePicker',
|
|
39
|
+
'DateTimePicker',
|
|
40
|
+
'Dialog',
|
|
41
|
+
'Drag&Drop',
|
|
42
|
+
'Drawer',
|
|
43
|
+
'Drawing',
|
|
44
|
+
'DropDownButton',
|
|
45
|
+
'DropDownList',
|
|
46
|
+
'DropDownTree',
|
|
47
|
+
'Editor',
|
|
48
|
+
'Error',
|
|
49
|
+
'ExcelExport',
|
|
50
|
+
'ExpansionPanel',
|
|
51
|
+
'ExternalDropZone',
|
|
52
|
+
'FileManager',
|
|
53
|
+
'FileSaver',
|
|
54
|
+
'Filter',
|
|
55
|
+
'FlatColorPicker',
|
|
56
|
+
'FloatingActionButton',
|
|
57
|
+
'FloatingLabel',
|
|
58
|
+
'FontIcon',
|
|
59
|
+
'Form',
|
|
60
|
+
'Gantt',
|
|
61
|
+
'Gauge',
|
|
62
|
+
'General',
|
|
63
|
+
'GridLayout',
|
|
64
|
+
'Hint',
|
|
65
|
+
'Input',
|
|
66
|
+
'Label',
|
|
67
|
+
'LinearGauge',
|
|
68
|
+
'ListBox',
|
|
69
|
+
'ListView',
|
|
70
|
+
'Loader',
|
|
71
|
+
'Map',
|
|
72
|
+
'MaskedTextBox',
|
|
73
|
+
'Menu',
|
|
74
|
+
'MultiColumnComboBox',
|
|
75
|
+
'MultiSelect',
|
|
76
|
+
'MultiSelectTree',
|
|
77
|
+
'MultiViewCalendar',
|
|
78
|
+
'Notification',
|
|
79
|
+
'NumericTextBox',
|
|
80
|
+
'OrgChart',
|
|
81
|
+
'Pager',
|
|
82
|
+
'PageTemplates/BuildingBlocks',
|
|
83
|
+
'PanelBar',
|
|
84
|
+
'PDFProcessing',
|
|
85
|
+
'PDFViewer',
|
|
86
|
+
'PivotGrid',
|
|
87
|
+
'Popover',
|
|
88
|
+
'Popup',
|
|
89
|
+
'ProgressBar',
|
|
90
|
+
'QRCode',
|
|
91
|
+
'RadialGauge',
|
|
92
|
+
'RadioButton',
|
|
93
|
+
'RadioGroup',
|
|
94
|
+
'RangeSlider',
|
|
95
|
+
'Rating',
|
|
96
|
+
'Ripple',
|
|
97
|
+
'Sankey',
|
|
98
|
+
'Scheduler',
|
|
99
|
+
'ScrollView',
|
|
100
|
+
'Signature',
|
|
101
|
+
'Skeleton',
|
|
102
|
+
'Slider',
|
|
103
|
+
'Sortable',
|
|
104
|
+
'Sparkline',
|
|
105
|
+
'SplitButton',
|
|
106
|
+
'Splitter',
|
|
107
|
+
'Spreadsheet',
|
|
108
|
+
'StackLayout',
|
|
109
|
+
'Stepper',
|
|
110
|
+
'StockChart',
|
|
111
|
+
'SVGIcon',
|
|
112
|
+
'Switch',
|
|
113
|
+
'TabStrip',
|
|
114
|
+
'TaskBoard',
|
|
115
|
+
'TextArea',
|
|
116
|
+
'TextBox',
|
|
117
|
+
'TileLayout',
|
|
118
|
+
'Timeline',
|
|
119
|
+
'TimePicker',
|
|
120
|
+
'Toolbar',
|
|
121
|
+
'Tooltip',
|
|
122
|
+
'TreeList',
|
|
123
|
+
'TreeView',
|
|
124
|
+
'Typography',
|
|
125
|
+
'Upload',
|
|
126
|
+
'VSCodeExtension',
|
|
127
|
+
'Window',
|
|
128
|
+
];
|
|
129
|
+
export var GrpcErrors;
|
|
130
|
+
(function (GrpcErrors) {
|
|
131
|
+
GrpcErrors[GrpcErrors["UNAUTHENTICATED"] = 16] = "UNAUTHENTICATED";
|
|
132
|
+
GrpcErrors[GrpcErrors["RESOURCE_EXHAUSTED"] = 8] = "RESOURCE_EXHAUSTED";
|
|
133
|
+
GrpcErrors[GrpcErrors["PERMISSION_DENIED"] = 7] = "PERMISSION_DENIED";
|
|
134
|
+
})(GrpcErrors || (GrpcErrors = {}));
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import protoLoader from '@grpc/proto-loader';
|
|
2
|
+
import grpc from '@grpc/grpc-js';
|
|
3
|
+
import { dirname, join } from 'path';
|
|
4
|
+
import { fileURLToPath } from 'url';
|
|
5
|
+
import { readLicense } from './read-license.js';
|
|
6
|
+
const protoPath = join(dirname(fileURLToPath(import.meta.url)), '../../proto/service.proto');
|
|
7
|
+
const proto = protoLoader.loadSync(protoPath, {
|
|
8
|
+
keepCase: true,
|
|
9
|
+
longs: String,
|
|
10
|
+
enums: String,
|
|
11
|
+
defaults: true,
|
|
12
|
+
oneofs: true,
|
|
13
|
+
});
|
|
14
|
+
const protoDescriptor = grpc.loadPackageDefinition(proto);
|
|
15
|
+
const service = protoDescriptor.ContextQueryService;
|
|
16
|
+
const client = new service('contextapi.telerik.com:443', grpc.credentials.createSsl());
|
|
17
|
+
export async function query(queryParams) {
|
|
18
|
+
const license = readLicense();
|
|
19
|
+
const metadata = new grpc.Metadata();
|
|
20
|
+
if (license && typeof license === 'string') {
|
|
21
|
+
metadata.add('x-license-key', license);
|
|
22
|
+
}
|
|
23
|
+
return new Promise((resolve, reject) => {
|
|
24
|
+
client.query(queryParams, metadata, (err, response) => {
|
|
25
|
+
if (err) {
|
|
26
|
+
reject(err);
|
|
27
|
+
}
|
|
28
|
+
else {
|
|
29
|
+
resolve(response);
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
});
|
|
33
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function log(...args: any[]): void;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
let LOG_ENABLED = false;
|
|
3
|
+
const LOG_FILE = process.env.DEBUG_LOG_FILE;
|
|
4
|
+
const LOG_FILE_DEFINED = !!(LOG_FILE && typeof LOG_FILE === 'string');
|
|
5
|
+
// check if the log file is a valid path
|
|
6
|
+
if (LOG_FILE_DEFINED) {
|
|
7
|
+
const logDir = LOG_FILE.substring(0, LOG_FILE.lastIndexOf('/'));
|
|
8
|
+
if (!fs.existsSync(logDir)) {
|
|
9
|
+
fs.mkdirSync(logDir, { recursive: true });
|
|
10
|
+
fs.writeFileSync(LOG_FILE, '');
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
LOG_ENABLED = LOG_FILE_DEFINED && fs.existsSync(LOG_FILE);
|
|
14
|
+
export function log(...args) {
|
|
15
|
+
if (LOG_ENABLED) {
|
|
16
|
+
// write a new line to the file with the date and time
|
|
17
|
+
const date = new Date();
|
|
18
|
+
const dateString = date.toISOString();
|
|
19
|
+
const logString = `${dateString}: ${args.join(' ')}\n`;
|
|
20
|
+
fs.appendFile(LOG_FILE, logString, (err) => {
|
|
21
|
+
if (err) {
|
|
22
|
+
console.error('Error writing to log file:', err);
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { LICENSE_ENV_VARS, LICENSE_ENV_PATH_VAR } from './constants.js';
|
|
2
|
+
import { readFileSync } from 'node:fs';
|
|
3
|
+
import { log } from './logger.js';
|
|
4
|
+
const LICENSE_PATH = process.env[LICENSE_ENV_PATH_VAR];
|
|
5
|
+
const LICENSE_KEY = process.env[LICENSE_ENV_VARS[0]] || process.env[LICENSE_ENV_VARS[1]];
|
|
6
|
+
// create a custom LicenseError class
|
|
7
|
+
export class LicenseError extends Error {
|
|
8
|
+
constructor(message) {
|
|
9
|
+
super(message);
|
|
10
|
+
this.name = 'LicenseError';
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
export function readLicense() {
|
|
14
|
+
if (!LICENSE_PATH && !LICENSE_KEY) {
|
|
15
|
+
throw new LicenseError(`No license found. Please set one of the following environment variables: ${LICENSE_ENV_VARS.join(', ')} or ${LICENSE_ENV_PATH_VAR}`);
|
|
16
|
+
}
|
|
17
|
+
let licenseKey;
|
|
18
|
+
if (LICENSE_PATH) {
|
|
19
|
+
try {
|
|
20
|
+
licenseKey = readFileSync(LICENSE_PATH, 'utf8');
|
|
21
|
+
log('License key read from file: ', LICENSE_PATH);
|
|
22
|
+
}
|
|
23
|
+
catch (error) {
|
|
24
|
+
throw new LicenseError(`Error reading license file: ${LICENSE_PATH}. Please verify the file exists and is readable.`);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
if (LICENSE_KEY) {
|
|
28
|
+
licenseKey = LICENSE_KEY;
|
|
29
|
+
log('License key read from environment variable.');
|
|
30
|
+
}
|
|
31
|
+
return licenseKey;
|
|
32
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@progress/kendo-react-mcp",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"bin": "./dist/index.js",
|
|
6
|
+
"license": "See LICENSE.md",
|
|
7
|
+
"description": "Model Context Protocol for KendoReact",
|
|
8
|
+
"keywords": [
|
|
9
|
+
"MCP Server",
|
|
10
|
+
"Model Context Protocol",
|
|
11
|
+
"React",
|
|
12
|
+
"Kendo",
|
|
13
|
+
"Progress",
|
|
14
|
+
"AI",
|
|
15
|
+
"Copilot",
|
|
16
|
+
"KendoReact",
|
|
17
|
+
"KendoReact"
|
|
18
|
+
],
|
|
19
|
+
"files": [
|
|
20
|
+
"dist",
|
|
21
|
+
"proto",
|
|
22
|
+
"README.md",
|
|
23
|
+
"LICENSE.md",
|
|
24
|
+
"!**/*.tsbuildinfo"
|
|
25
|
+
],
|
|
26
|
+
"dependencies": {
|
|
27
|
+
"@grpc/grpc-js": "^1.13.3",
|
|
28
|
+
"@grpc/proto-loader": "^0.7.15",
|
|
29
|
+
"@modelcontextprotocol/sdk": "^1.8.0",
|
|
30
|
+
"tslib": "^2.3.0"
|
|
31
|
+
}
|
|
32
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
syntax = "proto3";
|
|
2
|
+
option go_package = "github.com/telerik/ai-builder/contextapi/pkg/api/pb";
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
message ContextQueryRequest {
|
|
6
|
+
string lib_name = 1;
|
|
7
|
+
string query = 2;
|
|
8
|
+
int32 text_matches_count = 3;
|
|
9
|
+
int32 code_matches_count = 4;
|
|
10
|
+
repeated string allowed_types = 5;
|
|
11
|
+
string component = 6;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
message Item {
|
|
15
|
+
map<string, string> metadata = 1;
|
|
16
|
+
string content = 2;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
message ContextQueryResponse {
|
|
20
|
+
repeated Item text = 1;
|
|
21
|
+
repeated Item code = 2;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
message StringArray {
|
|
25
|
+
repeated string values = 1;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
service ContextQueryService {
|
|
29
|
+
rpc Query(ContextQueryRequest) returns (StringArray);
|
|
30
|
+
}
|