vcluster-yaml-mcp-server 1.0.1 → 1.0.3

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 CHANGED
@@ -76,9 +76,18 @@ npx -p vcluster-yaml-mcp-server vcluster-yaml query sync --format table
76
76
  # Or install globally
77
77
  npm install -g vcluster-yaml-mcp-server
78
78
  vcluster-yaml query sync --format table
79
+
80
+ # Validate configurations with ease
81
+ vcluster-yaml validate my-config.yaml
82
+ cat my-config.yaml | vcluster-yaml validate -
83
+ vcluster-yaml validate my-config.yaml --schema-version v0.24.0
84
+
85
+ # Shell completion (bash/zsh)
86
+ vcluster-yaml completion bash 2>/dev/null > ~/.vcluster-yaml-completion.bash
87
+ vcluster-yaml completion zsh 2>/dev/null > ~/.zsh/completion/_vcluster-yaml
79
88
  ```
80
89
 
81
- 📖 **[Full CLI Documentation →](docs/CLI.md)**
90
+ 📖 **[Full CLI Documentation →](docs/CLI.md)**
82
91
 
83
92
  ## Available Tools
84
93
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vcluster-yaml-mcp-server",
3
- "version": "1.0.1",
3
+ "version": "1.0.3",
4
4
  "description": "MCP server for querying vcluster YAML configurations using jq",
5
5
  "main": "src/index.js",
6
6
  "type": "module",
@@ -0,0 +1,62 @@
1
+ /**
2
+ * CLI utility functions
3
+ * Provides helpers for stdin reading and file operations
4
+ */
5
+
6
+ import { readFile } from 'fs/promises';
7
+ import { stdin } from 'process';
8
+
9
+ /**
10
+ * Read content from stdin
11
+ * Used for piping content to CLI commands
12
+ * @returns {Promise<string>} The content read from stdin
13
+ */
14
+ export async function readStdin() {
15
+ return new Promise((resolve, reject) => {
16
+ let data = '';
17
+
18
+ stdin.setEncoding('utf8');
19
+ stdin.on('data', chunk => data += chunk);
20
+ stdin.on('end', () => resolve(data));
21
+ stdin.on('error', reject);
22
+ });
23
+ }
24
+
25
+ /**
26
+ * Read content from a file or stdin based on the file argument
27
+ * @param {string|undefined} file - File path, '-' for stdin, or undefined for stdin
28
+ * @returns {Promise<{content: string, source: string}>} The content and its source
29
+ */
30
+ export async function readContentSource(file) {
31
+ // Read from stdin if no file provided or file is '-'
32
+ if (!file || file === '-') {
33
+ // If no file argument provided (not even '-'), check if we're in interactive mode
34
+ // In interactive mode (TTY), we should not wait for stdin
35
+ if (!file) {
36
+ // stdin.isTTY is true when running in interactive terminal
37
+ // stdin.isTTY is false/undefined when piping or redirecting
38
+ const isInteractive = stdin.isTTY === true;
39
+
40
+ if (isInteractive) {
41
+ throw new Error(
42
+ 'No input provided. Please specify a file or pipe content via stdin.\n' +
43
+ 'Examples:\n' +
44
+ ' vcluster-yaml validate my-config.yaml\n' +
45
+ ' cat my-config.yaml | vcluster-yaml validate -\n' +
46
+ ' vcluster-yaml validate --help'
47
+ );
48
+ }
49
+ }
50
+
51
+ const content = await readStdin();
52
+ return { content, source: 'stdin' };
53
+ }
54
+
55
+ // Read from file
56
+ try {
57
+ const content = await readFile(file, 'utf-8');
58
+ return { content, source: file };
59
+ } catch (error) {
60
+ throw new Error(`Cannot read file '${file}': ${error.message}`);
61
+ }
62
+ }
package/src/cli.js CHANGED
@@ -7,23 +7,41 @@
7
7
  */
8
8
 
9
9
  import { Command } from 'commander';
10
+ import { readFile } from 'fs/promises';
11
+ import { fileURLToPath } from 'url';
12
+ import { dirname, join } from 'path';
10
13
  import { handleQuery, handleListVersions, handleValidate } from './cli-handlers.js';
11
14
  import { formatOutput } from './formatters.js';
15
+ import { readContentSource } from './cli-utils.js';
16
+ import { generateBashCompletion, generateZshCompletion, getInstallInstructions } from './completions.js';
17
+
18
+ // Get package.json version
19
+ const __filename = fileURLToPath(import.meta.url);
20
+ const __dirname = dirname(__filename);
21
+ const packageJson = JSON.parse(
22
+ await readFile(join(__dirname, '../package.json'), 'utf-8')
23
+ );
12
24
 
13
25
  const program = new Command();
14
26
 
15
27
  program
16
28
  .name('vcluster-yaml')
17
29
  .description('vCluster YAML configuration CLI')
18
- .version('0.1.0');
30
+ .version(packageJson.version);
19
31
 
20
32
  // Query command
21
33
  program
22
34
  .command('query <query>')
23
35
  .description('Search for vCluster configuration fields')
24
36
  .option('--file <file>', 'Configuration file to search', 'chart/values.yaml')
25
- .option('--version <version>', 'vCluster version or branch', 'main')
37
+ .option('-s, --schema-version <version>', 'vCluster version or branch', 'main')
26
38
  .option('-f, --format <format>', 'Output format (json, yaml, table)', 'json')
39
+ .addHelpText('after', `
40
+ Examples:
41
+ $ vcluster-yaml query sync
42
+ $ vcluster-yaml query sync --schema-version v0.24.0
43
+ $ vcluster-yaml query "controlPlane.replicas" --format table
44
+ `)
27
45
  .action(async (query, options) => {
28
46
  try {
29
47
  // Validate format option
@@ -32,9 +50,15 @@ program
32
50
  process.exit(1);
33
51
  }
34
52
 
53
+ // Add validation for empty query
54
+ if (!query || query.trim() === '') {
55
+ console.error(`Error: Query cannot be empty. Try 'vcluster-yaml query sync' or see examples with --help`);
56
+ process.exit(1);
57
+ }
58
+
35
59
  const result = await handleQuery(query, {
36
60
  file: options.file,
37
- version: options.version
61
+ version: options.schemaVersion
38
62
  });
39
63
 
40
64
  if (!result.success) {
@@ -91,11 +115,22 @@ program
91
115
 
92
116
  // Validate command
93
117
  program
94
- .command('validate <content>')
118
+ .command('validate [file]')
95
119
  .description('Validate vCluster configuration')
96
- .option('--version <version>', 'vCluster version for schema', 'main')
120
+ .option('-s, --schema-version <version>', 'vCluster version for schema', 'main')
97
121
  .option('-f, --format <format>', 'Output format (json, yaml, table)', 'json')
98
- .action(async (content, options) => {
122
+ .addHelpText('after', `
123
+ Arguments:
124
+ file YAML file to validate (use '-' for stdin, omit to read from stdin)
125
+
126
+ Examples:
127
+ $ vcluster-yaml validate vcluster.yaml
128
+ $ vcluster-yaml validate vcluster.yaml --schema-version v0.24.0
129
+ $ cat vcluster.yaml | vcluster-yaml validate -
130
+ $ vcluster-yaml validate - < vcluster.yaml
131
+ $ vcluster-yaml validate vcluster.yaml --format table
132
+ `)
133
+ .action(async (file, options) => {
99
134
  try {
100
135
  // Validate format option
101
136
  if (!['json', 'yaml', 'table'].includes(options.format)) {
@@ -103,8 +138,24 @@ program
103
138
  process.exit(1);
104
139
  }
105
140
 
141
+ // Read content from file or stdin
142
+ let content;
143
+ try {
144
+ const result = await readContentSource(file);
145
+ content = result.content;
146
+ } catch (error) {
147
+ console.error(`Error: ${error.message}`);
148
+ process.exit(1);
149
+ }
150
+
151
+ // Check for empty content
152
+ if (!content || content.trim() === '') {
153
+ console.error(`Error: No content to validate. Please provide a file path or pipe content via stdin.`);
154
+ process.exit(1);
155
+ }
156
+
106
157
  const result = await handleValidate(content, {
107
- version: options.version
158
+ version: options.schemaVersion
108
159
  });
109
160
 
110
161
  // For validation, always output the result (even if not successful)
@@ -124,5 +175,46 @@ program
124
175
  }
125
176
  });
126
177
 
178
+ // Completion command
179
+ program
180
+ .command('completion <shell>')
181
+ .description('Generate shell completion script')
182
+ .addHelpText('after', `
183
+ Supported shells:
184
+ bash Bash completion script
185
+ zsh Zsh completion script
186
+
187
+ Examples:
188
+ $ vcluster-yaml completion bash > ~/.vcluster-yaml-completion.bash
189
+ $ vcluster-yaml completion zsh > ~/.zsh/completion/_vcluster-yaml
190
+ $ vcluster-yaml completion bash --help
191
+ `)
192
+ .action((shell) => {
193
+ const validShells = ['bash', 'zsh'];
194
+
195
+ if (!validShells.includes(shell)) {
196
+ console.error(`Error: Unsupported shell "${shell}". Supported shells: ${validShells.join(', ')}`);
197
+ console.error('');
198
+ console.error('Examples:');
199
+ console.error(' vcluster-yaml completion bash > ~/.vcluster-yaml-completion.bash');
200
+ console.error(' vcluster-yaml completion zsh > ~/.zsh/completion/_vcluster-yaml');
201
+ process.exit(1);
202
+ }
203
+
204
+ let script;
205
+ if (shell === 'bash') {
206
+ script = generateBashCompletion();
207
+ } else if (shell === 'zsh') {
208
+ script = generateZshCompletion();
209
+ }
210
+
211
+ // Output the script
212
+ console.log(script);
213
+
214
+ // Add installation instructions to stderr so they don't end up in the script
215
+ console.error('');
216
+ console.error(getInstallInstructions(shell));
217
+ });
218
+
127
219
  // Parse arguments
128
220
  program.parse();
@@ -0,0 +1,234 @@
1
+ /**
2
+ * Shell completion generators
3
+ * Generates bash and zsh completion scripts for vcluster-yaml CLI
4
+ */
5
+
6
+ /**
7
+ * Generate bash completion script
8
+ * @returns {string} Bash completion script
9
+ */
10
+ export function generateBashCompletion() {
11
+ return `# vcluster-yaml bash completion
12
+
13
+ _vcluster_yaml_completions() {
14
+ local cur prev words cword
15
+ _init_completion || return
16
+
17
+ # Available commands
18
+ local commands="query list-versions validate help"
19
+
20
+ # Available format options
21
+ local formats="json yaml table"
22
+
23
+ case "\${cword}" in
24
+ 1)
25
+ # First argument: complete commands
26
+ COMPREPLY=($(compgen -W "\${commands}" -- "\${cur}"))
27
+ return 0
28
+ ;;
29
+ *)
30
+ # Handle options based on previous word
31
+ case "\${prev}" in
32
+ -f|--format)
33
+ # Complete format options
34
+ COMPREPLY=($(compgen -W "\${formats}" -- "\${cur}"))
35
+ return 0
36
+ ;;
37
+ -s|--schema-version)
38
+ # Complete common versions (user can type others)
39
+ COMPREPLY=($(compgen -W "main v0.28.0 v0.29.0 v0.30.0" -- "\${cur}"))
40
+ return 0
41
+ ;;
42
+ --file)
43
+ # Complete YAML files
44
+ COMPREPLY=($(compgen -f -X '!*.yaml' -- "\${cur}"))
45
+ COMPREPLY+=($(compgen -f -X '!*.yml' -- "\${cur}"))
46
+ return 0
47
+ ;;
48
+ validate)
49
+ # After validate command, complete YAML files or options
50
+ if [[ "\${cur}" == -* ]]; then
51
+ COMPREPLY=($(compgen -W "-s --schema-version -f --format -h --help" -- "\${cur}"))
52
+ else
53
+ COMPREPLY=($(compgen -f -X '!*.yaml' -- "\${cur}"))
54
+ COMPREPLY+=($(compgen -f -X '!*.yml' -- "\${cur}"))
55
+ [[ "\${cur}" == "" || "\${cur}" == "-" ]] && COMPREPLY+=("-")
56
+ fi
57
+ return 0
58
+ ;;
59
+ query)
60
+ # After query command, complete options
61
+ if [[ "\${cur}" == -* ]]; then
62
+ COMPREPLY=($(compgen -W "--file -s --schema-version -f --format -h --help" -- "\${cur}"))
63
+ fi
64
+ return 0
65
+ ;;
66
+ list-versions)
67
+ # After list-versions command, complete options
68
+ COMPREPLY=($(compgen -W "-f --format -h --help" -- "\${cur}"))
69
+ return 0
70
+ ;;
71
+ esac
72
+ ;;
73
+ esac
74
+
75
+ # Default: suggest options if starting with -
76
+ if [[ "\${cur}" == -* ]]; then
77
+ COMPREPLY=($(compgen -W "-V --version -h --help" -- "\${cur}"))
78
+ return 0
79
+ fi
80
+ }
81
+
82
+ complete -F _vcluster_yaml_completions vcluster-yaml
83
+ `;
84
+ }
85
+
86
+ /**
87
+ * Generate zsh completion script
88
+ * @returns {string} Zsh completion script
89
+ */
90
+ export function generateZshCompletion() {
91
+ return `#compdef vcluster-yaml
92
+
93
+ # vcluster-yaml zsh completion
94
+
95
+ _vcluster_yaml() {
96
+ local -a commands
97
+ commands=(
98
+ 'query:Search for vCluster configuration fields'
99
+ 'list-versions:List available vCluster versions'
100
+ 'validate:Validate vCluster configuration'
101
+ 'help:Display help for command'
102
+ )
103
+
104
+ local -a formats
105
+ formats=(
106
+ 'json:JSON output format'
107
+ 'yaml:YAML output format'
108
+ 'table:Table output format'
109
+ )
110
+
111
+ local -a versions
112
+ versions=(
113
+ 'main:Latest development version'
114
+ 'v0.28.0:Version 0.28.0'
115
+ 'v0.29.0:Version 0.29.0'
116
+ 'v0.30.0:Version 0.30.0'
117
+ )
118
+
119
+ _arguments -C \\
120
+ '(-V --version)'{-V,--version}'[output the version number]' \\
121
+ '(-h --help)'{-h,--help}'[display help for command]' \\
122
+ '1: :->command' \\
123
+ '*:: :->args'
124
+
125
+ case $state in
126
+ command)
127
+ _describe 'vcluster-yaml commands' commands
128
+ ;;
129
+ args)
130
+ case $words[1] in
131
+ query)
132
+ _arguments \\
133
+ '--file[Configuration file to search]:file:_files -g "*.yaml *.yml"' \\
134
+ '(-s --schema-version)'{-s,--schema-version}'[vCluster version or branch]:version:->versions' \\
135
+ '(-f --format)'{-f,--format}'[Output format]:format:->formats' \\
136
+ '(-h --help)'{-h,--help}'[display help]' \\
137
+ '1:query string:'
138
+
139
+ case $state in
140
+ formats)
141
+ _describe 'output formats' formats
142
+ ;;
143
+ versions)
144
+ _describe 'vCluster versions' versions
145
+ ;;
146
+ esac
147
+ ;;
148
+ list-versions)
149
+ _arguments \\
150
+ '(-f --format)'{-f,--format}'[Output format]:format:->formats' \\
151
+ '(-h --help)'{-h,--help}'[display help]'
152
+
153
+ case $state in
154
+ formats)
155
+ _describe 'output formats' formats
156
+ ;;
157
+ esac
158
+ ;;
159
+ validate)
160
+ _arguments \\
161
+ '(-s --schema-version)'{-s,--schema-version}'[vCluster version for schema]:version:->versions' \\
162
+ '(-f --format)'{-f,--format}'[Output format]:format:->formats' \\
163
+ '(-h --help)'{-h,--help}'[display help]' \\
164
+ '1:file:_files -g "*.yaml *.yml"'
165
+
166
+ case $state in
167
+ formats)
168
+ _describe 'output formats' formats
169
+ ;;
170
+ versions)
171
+ _describe 'vCluster versions' versions
172
+ ;;
173
+ esac
174
+ ;;
175
+ help)
176
+ _describe 'vcluster-yaml commands' commands
177
+ ;;
178
+ esac
179
+ ;;
180
+ esac
181
+ }
182
+
183
+ _vcluster_yaml "$@"
184
+ `;
185
+ }
186
+
187
+ /**
188
+ * Display installation instructions for a shell
189
+ * @param {string} shell - Shell type (bash or zsh)
190
+ * @returns {string} Installation instructions
191
+ */
192
+ export function getInstallInstructions(shell) {
193
+ if (shell === 'bash') {
194
+ return `
195
+ Bash Completion Installation:
196
+
197
+ 1. Save the completion script:
198
+ $ vcluster-yaml completion bash > ~/.vcluster-yaml-completion.bash
199
+
200
+ 2. Source it in your ~/.bashrc:
201
+ $ echo 'source ~/.vcluster-yaml-completion.bash' >> ~/.bashrc
202
+
203
+ 3. Reload your shell:
204
+ $ source ~/.bashrc
205
+
206
+ Or install system-wide (requires sudo):
207
+ $ sudo vcluster-yaml completion bash > /etc/bash_completion.d/vcluster-yaml
208
+ `;
209
+ }
210
+
211
+ if (shell === 'zsh') {
212
+ return `
213
+ Zsh Completion Installation:
214
+
215
+ 1. Create completion directory if needed:
216
+ $ mkdir -p ~/.zsh/completion
217
+
218
+ 2. Save the completion script:
219
+ $ vcluster-yaml completion zsh > ~/.zsh/completion/_vcluster-yaml
220
+
221
+ 3. Add to fpath in your ~/.zshrc (before compinit):
222
+ fpath=(~/.zsh/completion $fpath)
223
+ autoload -Uz compinit && compinit
224
+
225
+ 4. Reload your shell:
226
+ $ exec zsh
227
+
228
+ Or install system-wide (requires sudo):
229
+ $ sudo vcluster-yaml completion zsh > /usr/local/share/zsh/site-functions/_vcluster-yaml
230
+ `;
231
+ }
232
+
233
+ return 'Unsupported shell. Available: bash, zsh';
234
+ }