vcluster-yaml-mcp-server 1.0.7 → 1.0.8
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 +10 -2
- package/package.json +15 -4
- package/src/cli.js +4 -5
- package/src/schema-validator.js +51 -22
- package/src/server.js +2 -743
- package/src/tool-handlers.js +384 -0
- package/src/tool-registry.js +45 -0
- package/src/validation-rules.js +181 -0
package/README.md
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
# vCluster YAML MCP Server
|
|
2
2
|
|
|
3
|
+
[](https://www.npmjs.com/package/vcluster-yaml-mcp-server)
|
|
4
|
+
[](https://codecov.io/gh/Piotr1215/vcluster-yaml-mcp)
|
|
5
|
+
[](https://nodejs.org/)
|
|
6
|
+
[](https://github.com/Piotr1215/vcluster-yaml-mcp-server#token-optimization)
|
|
7
|
+
[](https://opensource.org/licenses/MIT)
|
|
8
|
+
[](https://hub.docker.com/r/piotrzan/vcluster-yaml-mcp-server)
|
|
9
|
+
[](https://modelcontextprotocol.io)
|
|
10
|
+
|
|
3
11
|
A Model Context Protocol (MCP) server that lets AI assistants query and validate [vCluster](https://github.com/loft-sh/vcluster) YAML configurations directly from GitHub.
|
|
4
12
|
|
|
5
13
|
## What Does It Do?
|
|
@@ -71,11 +79,11 @@ The package also provides a standalone CLI for quick queries and validation with
|
|
|
71
79
|
|
|
72
80
|
```bash
|
|
73
81
|
# Quick start with npx (no installation)
|
|
74
|
-
npx
|
|
82
|
+
npx vcluster-yaml-mcp-server query sync
|
|
75
83
|
|
|
76
84
|
# Or install globally
|
|
77
85
|
npm install -g vcluster-yaml-mcp-server
|
|
78
|
-
vcluster-yaml query sync
|
|
86
|
+
vcluster-yaml query sync
|
|
79
87
|
|
|
80
88
|
# Validate configurations with ease
|
|
81
89
|
vcluster-yaml validate my-config.yaml
|
package/package.json
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vcluster-yaml-mcp-server",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.8",
|
|
4
4
|
"description": "MCP server for querying vcluster YAML configurations using jq",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"bin": {
|
|
8
|
+
"vcluster-yaml-mcp-server": "./src/cli.js",
|
|
8
9
|
"vcluster-yaml-mcp": "./src/index.js",
|
|
9
10
|
"vcluster-yaml": "./src/cli.js"
|
|
10
11
|
},
|
|
@@ -17,11 +18,17 @@
|
|
|
17
18
|
"start:http": "node src/http-server.js",
|
|
18
19
|
"docker:build": "docker build -t piotrzan/vcluster-yaml-mcp-server:latest .",
|
|
19
20
|
"docker:push": "docker push piotrzan/vcluster-yaml-mcp-server:latest",
|
|
20
|
-
"test": "vitest run",
|
|
21
|
-
"test:
|
|
22
|
-
"test:
|
|
21
|
+
"test": "vitest run --exclude tests/integration/**/*.test.js --exclude tests/ci/**/*.test.js --exclude tests/performance.test.js",
|
|
22
|
+
"test:unit": "vitest run --exclude tests/integration/**/*.test.js --exclude tests/ci/**/*.test.js --exclude tests/performance.test.js",
|
|
23
|
+
"test:integration": "vitest run tests/integration/",
|
|
23
24
|
"test:perf": "vitest run tests/performance.test.js",
|
|
24
25
|
"test:ci": "vitest run tests/ci/",
|
|
26
|
+
"test:all": "vitest run",
|
|
27
|
+
"test:watch": "vitest --exclude tests/integration/**/*.test.js",
|
|
28
|
+
"test:coverage": "vitest run --coverage --exclude tests/integration/**/*.test.js",
|
|
29
|
+
"test:mutation": "stryker run",
|
|
30
|
+
"complexity": "eslint --config eslint.config.complexity.js src/**/*.js",
|
|
31
|
+
"complexity:json": "eslint --config eslint.config.complexity.js --format json --output-file reports/complexity.json src/**/*.js || true",
|
|
25
32
|
"lint:workflows": "actionlint .github/workflows/*.yml"
|
|
26
33
|
},
|
|
27
34
|
"dependencies": {
|
|
@@ -42,8 +49,12 @@
|
|
|
42
49
|
"node": ">=18"
|
|
43
50
|
},
|
|
44
51
|
"devDependencies": {
|
|
52
|
+
"@stryker-mutator/core": "^9.2.0",
|
|
53
|
+
"@stryker-mutator/vitest-runner": "^9.2.0",
|
|
45
54
|
"@vitest/coverage-v8": "^3.2.4",
|
|
55
|
+
"eslint": "^9.38.0",
|
|
46
56
|
"execa": "^9.5.2",
|
|
57
|
+
"fast-check": "^4.3.0",
|
|
47
58
|
"json-schema-library": "^10.3.0",
|
|
48
59
|
"jsonschema": "^1.5.0",
|
|
49
60
|
"strip-ansi": "^7.1.0",
|
package/src/cli.js
CHANGED
|
@@ -35,12 +35,12 @@ program
|
|
|
35
35
|
.description('Search for vCluster configuration fields')
|
|
36
36
|
.option('--file <file>', 'Configuration file to search', 'chart/values.yaml')
|
|
37
37
|
.option('-s, --schema-version <version>', 'vCluster version or branch', 'main')
|
|
38
|
-
.option('-f, --format <format>', 'Output format
|
|
38
|
+
.option('-f, --format <format>', 'Output format: json, yaml, table (default: table)', 'table')
|
|
39
39
|
.addHelpText('after', `
|
|
40
40
|
Examples:
|
|
41
41
|
$ vcluster-yaml query sync
|
|
42
42
|
$ vcluster-yaml query sync --schema-version v0.24.0
|
|
43
|
-
$ vcluster-yaml query "controlPlane.replicas"
|
|
43
|
+
$ vcluster-yaml query "controlPlane.replicas"
|
|
44
44
|
`)
|
|
45
45
|
.action(async (query, options) => {
|
|
46
46
|
try {
|
|
@@ -84,7 +84,7 @@ Examples:
|
|
|
84
84
|
program
|
|
85
85
|
.command('list-versions')
|
|
86
86
|
.description('List available vCluster versions')
|
|
87
|
-
.option('-f, --format <format>', 'Output format
|
|
87
|
+
.option('-f, --format <format>', 'Output format: json, yaml, table (default: table)', 'table')
|
|
88
88
|
.action(async (options) => {
|
|
89
89
|
try {
|
|
90
90
|
// Validate format option
|
|
@@ -118,7 +118,7 @@ program
|
|
|
118
118
|
.command('validate [file]')
|
|
119
119
|
.description('Validate vCluster configuration')
|
|
120
120
|
.option('-s, --schema-version <version>', 'vCluster version for schema', 'main')
|
|
121
|
-
.option('-f, --format <format>', 'Output format
|
|
121
|
+
.option('-f, --format <format>', 'Output format: json, yaml, table (default: table)', 'table')
|
|
122
122
|
.addHelpText('after', `
|
|
123
123
|
Arguments:
|
|
124
124
|
file YAML file to validate (use '-' for stdin, omit to read from stdin)
|
|
@@ -128,7 +128,6 @@ Examples:
|
|
|
128
128
|
$ vcluster-yaml validate vcluster.yaml --schema-version v0.24.0
|
|
129
129
|
$ cat vcluster.yaml | vcluster-yaml validate -
|
|
130
130
|
$ vcluster-yaml validate - < vcluster.yaml
|
|
131
|
-
$ vcluster-yaml validate vcluster.yaml --format table
|
|
132
131
|
`)
|
|
133
132
|
.action(async (file, options) => {
|
|
134
133
|
try {
|
package/src/schema-validator.js
CHANGED
|
@@ -153,50 +153,79 @@ function validatePath(path, value, schema) {
|
|
|
153
153
|
}
|
|
154
154
|
|
|
155
155
|
/**
|
|
156
|
-
* Find similar paths
|
|
156
|
+
* Find similar paths using priority-based matching (no magic numbers)
|
|
157
|
+
* Priority 1: Exact match on last segment (e.g., "version" matches "apiVersion")
|
|
158
|
+
* Priority 2: Substring match (e.g., "vers" matches "apiVersion")
|
|
159
|
+
* Priority 3: Shared prefix (e.g., "api.vers" matches "api.version")
|
|
157
160
|
*/
|
|
158
161
|
function findSimilarPaths(targetPath, schema, maxSuggestions = 3) {
|
|
159
162
|
const allPaths = extractSchemaPaths(schema);
|
|
163
|
+
if (allPaths.length === 0) return [];
|
|
164
|
+
|
|
160
165
|
const targetParts = targetPath.toLowerCase().split('.');
|
|
161
166
|
const targetLast = targetParts[targetParts.length - 1];
|
|
162
167
|
|
|
163
|
-
|
|
168
|
+
// Categorize matches by clear priorities (no arbitrary scoring)
|
|
169
|
+
const exactMatches = [];
|
|
170
|
+
const substringMatches = [];
|
|
171
|
+
const prefixMatches = [];
|
|
172
|
+
|
|
173
|
+
for (const schemaPath of allPaths) {
|
|
164
174
|
const parts = schemaPath.toLowerCase().split('.');
|
|
165
175
|
const last = parts[parts.length - 1];
|
|
166
176
|
|
|
167
|
-
|
|
168
|
-
if (last === targetLast)
|
|
169
|
-
|
|
177
|
+
// Priority 1: Exact last segment match
|
|
178
|
+
if (last === targetLast) {
|
|
179
|
+
exactMatches.push(schemaPath);
|
|
180
|
+
continue;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// Priority 2: Substring match in last segment
|
|
184
|
+
if (last.includes(targetLast) || targetLast.includes(last)) {
|
|
185
|
+
substringMatches.push(schemaPath);
|
|
186
|
+
continue;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// Priority 3: Shared path prefix
|
|
190
|
+
const sharedPrefixLength = getSharedPrefix(targetParts, parts);
|
|
191
|
+
if (sharedPrefixLength > 0) {
|
|
192
|
+
prefixMatches.push({ path: schemaPath, prefixLength: sharedPrefixLength });
|
|
193
|
+
}
|
|
194
|
+
}
|
|
170
195
|
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
score += sharedPrefix * 2;
|
|
196
|
+
// Sort prefix matches by shared prefix length (longer is better)
|
|
197
|
+
prefixMatches.sort((a, b) => b.prefixLength - a.prefixLength);
|
|
174
198
|
|
|
175
|
-
|
|
176
|
-
|
|
199
|
+
// Combine in strict priority order
|
|
200
|
+
const suggestions = [
|
|
201
|
+
...exactMatches,
|
|
202
|
+
...substringMatches,
|
|
203
|
+
...prefixMatches.map(m => m.path)
|
|
204
|
+
];
|
|
177
205
|
|
|
178
|
-
return
|
|
179
|
-
.filter(s => s.score > 0)
|
|
180
|
-
.sort((a, b) => b.score - a.score)
|
|
181
|
-
.slice(0, maxSuggestions)
|
|
182
|
-
.map(s => s.path);
|
|
206
|
+
return suggestions.slice(0, maxSuggestions);
|
|
183
207
|
}
|
|
184
208
|
|
|
185
209
|
/**
|
|
186
210
|
* Extract all paths from schema
|
|
211
|
+
* Contract: schema must be an object with optional .properties field
|
|
187
212
|
*/
|
|
188
213
|
function extractSchemaPaths(schema, prefix = '') {
|
|
214
|
+
if (!schema || typeof schema !== 'object') {
|
|
215
|
+
return [];
|
|
216
|
+
}
|
|
217
|
+
|
|
189
218
|
const paths = [];
|
|
219
|
+
// Explicit contract: use .properties if present, otherwise treat schema as properties object
|
|
190
220
|
const props = schema.properties || schema;
|
|
191
221
|
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
paths.push(path);
|
|
222
|
+
for (const [key, value] of Object.entries(props)) {
|
|
223
|
+
const path = prefix ? `${prefix}.${key}` : key;
|
|
224
|
+
paths.push(path);
|
|
196
225
|
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
226
|
+
// Recurse if nested properties exist
|
|
227
|
+
if (value && typeof value === 'object' && value.properties) {
|
|
228
|
+
paths.push(...extractSchemaPaths(value, path));
|
|
200
229
|
}
|
|
201
230
|
}
|
|
202
231
|
|