@aws/ml-container-creator 0.2.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/LICENSE +202 -0
- package/LICENSE-THIRD-PARTY +68620 -0
- package/NOTICE +2 -0
- package/README.md +106 -0
- package/bin/cli.js +365 -0
- package/config/defaults.json +32 -0
- package/config/presets/transformers-djl.json +26 -0
- package/config/presets/transformers-gpu.json +24 -0
- package/config/presets/transformers-lmi.json +27 -0
- package/package.json +129 -0
- package/servers/README.md +419 -0
- package/servers/base-image-picker/catalogs/model-servers.json +1191 -0
- package/servers/base-image-picker/catalogs/python-slim.json +38 -0
- package/servers/base-image-picker/catalogs/triton-backends.json +51 -0
- package/servers/base-image-picker/catalogs/triton.json +38 -0
- package/servers/base-image-picker/index.js +495 -0
- package/servers/base-image-picker/manifest.json +17 -0
- package/servers/base-image-picker/package.json +15 -0
- package/servers/hyperpod-cluster-picker/LICENSE +202 -0
- package/servers/hyperpod-cluster-picker/index.js +424 -0
- package/servers/hyperpod-cluster-picker/manifest.json +14 -0
- package/servers/hyperpod-cluster-picker/package.json +17 -0
- package/servers/instance-recommender/LICENSE +202 -0
- package/servers/instance-recommender/catalogs/instances.json +852 -0
- package/servers/instance-recommender/index.js +284 -0
- package/servers/instance-recommender/manifest.json +16 -0
- package/servers/instance-recommender/package.json +15 -0
- package/servers/lib/LICENSE +202 -0
- package/servers/lib/bedrock-client.js +160 -0
- package/servers/lib/custom-validators.js +46 -0
- package/servers/lib/dynamic-resolver.js +36 -0
- package/servers/lib/package.json +11 -0
- package/servers/lib/schemas/image-catalog.schema.json +185 -0
- package/servers/lib/schemas/instances.schema.json +124 -0
- package/servers/lib/schemas/manifest.schema.json +64 -0
- package/servers/lib/schemas/model-catalog.schema.json +91 -0
- package/servers/lib/schemas/regions.schema.json +26 -0
- package/servers/lib/schemas/triton-backends.schema.json +51 -0
- package/servers/model-picker/catalogs/jumpstart-public.json +66 -0
- package/servers/model-picker/catalogs/popular-diffusors.json +88 -0
- package/servers/model-picker/catalogs/popular-transformers.json +226 -0
- package/servers/model-picker/index.js +1693 -0
- package/servers/model-picker/manifest.json +18 -0
- package/servers/model-picker/package.json +20 -0
- package/servers/region-picker/LICENSE +202 -0
- package/servers/region-picker/catalogs/regions.json +263 -0
- package/servers/region-picker/index.js +230 -0
- package/servers/region-picker/manifest.json +16 -0
- package/servers/region-picker/package.json +15 -0
- package/src/app.js +1007 -0
- package/src/copy-tpl.js +77 -0
- package/src/lib/accelerator-validator.js +39 -0
- package/src/lib/asset-manager.js +385 -0
- package/src/lib/aws-profile-parser.js +181 -0
- package/src/lib/bootstrap-command-handler.js +1647 -0
- package/src/lib/bootstrap-config.js +238 -0
- package/src/lib/ci-register-helpers.js +124 -0
- package/src/lib/ci-report-helpers.js +158 -0
- package/src/lib/ci-stage-helpers.js +268 -0
- package/src/lib/cli-handler.js +529 -0
- package/src/lib/comment-generator.js +544 -0
- package/src/lib/community-reports-validator.js +91 -0
- package/src/lib/config-manager.js +2106 -0
- package/src/lib/configuration-exporter.js +204 -0
- package/src/lib/configuration-manager.js +695 -0
- package/src/lib/configuration-matcher.js +221 -0
- package/src/lib/cpu-validator.js +36 -0
- package/src/lib/cuda-validator.js +57 -0
- package/src/lib/deployment-config-resolver.js +103 -0
- package/src/lib/deployment-entry-schema.js +125 -0
- package/src/lib/deployment-registry.js +598 -0
- package/src/lib/docker-introspection-validator.js +51 -0
- package/src/lib/engine-prefix-resolver.js +60 -0
- package/src/lib/huggingface-client.js +172 -0
- package/src/lib/key-value-parser.js +37 -0
- package/src/lib/known-flags-validator.js +200 -0
- package/src/lib/manifest-cli.js +280 -0
- package/src/lib/mcp-client.js +303 -0
- package/src/lib/mcp-command-handler.js +532 -0
- package/src/lib/neuron-validator.js +80 -0
- package/src/lib/parameter-schema-validator.js +284 -0
- package/src/lib/prompt-runner.js +1349 -0
- package/src/lib/prompts.js +1138 -0
- package/src/lib/registry-command-handler.js +519 -0
- package/src/lib/registry-loader.js +198 -0
- package/src/lib/rocm-validator.js +80 -0
- package/src/lib/schema-validator.js +157 -0
- package/src/lib/sensitive-redactor.js +59 -0
- package/src/lib/template-engine.js +156 -0
- package/src/lib/template-manager.js +341 -0
- package/src/lib/validation-engine.js +314 -0
- package/src/prompt-adapter.js +63 -0
- package/templates/Dockerfile +300 -0
- package/templates/IAM_PERMISSIONS.md +84 -0
- package/templates/MIGRATION.md +488 -0
- package/templates/PROJECT_README.md +439 -0
- package/templates/TEMPLATE_SYSTEM.md +243 -0
- package/templates/buildspec.yml +64 -0
- package/templates/code/chat_template.jinja +1 -0
- package/templates/code/flask/gunicorn_config.py +35 -0
- package/templates/code/flask/wsgi.py +10 -0
- package/templates/code/model_handler.py +387 -0
- package/templates/code/serve +300 -0
- package/templates/code/serve.py +175 -0
- package/templates/code/serving.properties +105 -0
- package/templates/code/start_server.py +39 -0
- package/templates/code/start_server.sh +39 -0
- package/templates/diffusors/Dockerfile +72 -0
- package/templates/diffusors/patch_image_api.py +35 -0
- package/templates/diffusors/serve +115 -0
- package/templates/diffusors/start_server.sh +114 -0
- package/templates/do/.gitkeep +1 -0
- package/templates/do/README.md +541 -0
- package/templates/do/build +83 -0
- package/templates/do/ci +681 -0
- package/templates/do/clean +811 -0
- package/templates/do/config +260 -0
- package/templates/do/deploy +1560 -0
- package/templates/do/export +306 -0
- package/templates/do/logs +319 -0
- package/templates/do/manifest +12 -0
- package/templates/do/push +119 -0
- package/templates/do/register +580 -0
- package/templates/do/run +113 -0
- package/templates/do/submit +417 -0
- package/templates/do/test +1147 -0
- package/templates/hyperpod/configmap.yaml +24 -0
- package/templates/hyperpod/deployment.yaml +71 -0
- package/templates/hyperpod/pvc.yaml +42 -0
- package/templates/hyperpod/service.yaml +17 -0
- package/templates/nginx-diffusors.conf +74 -0
- package/templates/nginx-predictors.conf +47 -0
- package/templates/nginx-tensorrt.conf +74 -0
- package/templates/requirements.txt +61 -0
- package/templates/sample_model/test_inference.py +123 -0
- package/templates/sample_model/train_abalone.py +252 -0
- package/templates/test/test_endpoint.sh +79 -0
- package/templates/test/test_local_image.sh +80 -0
- package/templates/test/test_model_handler.py +180 -0
- package/templates/triton/Dockerfile +128 -0
- package/templates/triton/config.pbtxt +163 -0
- package/templates/triton/model.py +130 -0
- package/templates/triton/requirements.txt +11 -0
|
@@ -0,0 +1,532 @@
|
|
|
1
|
+
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* MCP Command Handler
|
|
6
|
+
*
|
|
7
|
+
* Handles the `mcp` CLI subcommand tree for managing MCP server
|
|
8
|
+
* configurations in config/mcp.json.
|
|
9
|
+
*
|
|
10
|
+
* Subcommands:
|
|
11
|
+
* add <name> -- <command> [args...] Add or update an MCP server
|
|
12
|
+
* list List configured MCP servers
|
|
13
|
+
* get <name> Show full server configuration
|
|
14
|
+
* remove <name> Remove an MCP server
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import fs from 'fs';
|
|
18
|
+
import path from 'path';
|
|
19
|
+
import { execSync } from 'child_process';
|
|
20
|
+
import { fileURLToPath } from 'url';
|
|
21
|
+
|
|
22
|
+
const CONFIG_FILENAME = 'config/mcp.json';
|
|
23
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
24
|
+
const __dirname = path.dirname(__filename);
|
|
25
|
+
const SERVERS_DIR = path.resolve(__dirname, '../../servers');
|
|
26
|
+
|
|
27
|
+
export default class McpCommandHandler {
|
|
28
|
+
constructor(generator) {
|
|
29
|
+
this.generator = generator;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Dispatch mcp subcommands.
|
|
34
|
+
* @param {string[]} args - Remaining positional args after 'mcp'
|
|
35
|
+
* @param {object} options - Parsed CLI options
|
|
36
|
+
*/
|
|
37
|
+
async handle(args, options) {
|
|
38
|
+
if (args.length === 0) {
|
|
39
|
+
this._showMcpHelp();
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const subcommand = args[0].toLowerCase();
|
|
44
|
+
|
|
45
|
+
switch (subcommand) {
|
|
46
|
+
case 'add':
|
|
47
|
+
await this._handleAdd(args.slice(1), options);
|
|
48
|
+
break;
|
|
49
|
+
case 'init':
|
|
50
|
+
await this._handleInit();
|
|
51
|
+
break;
|
|
52
|
+
case 'list':
|
|
53
|
+
this._handleList(options);
|
|
54
|
+
break;
|
|
55
|
+
case 'get':
|
|
56
|
+
this._handleGet(args[1]);
|
|
57
|
+
break;
|
|
58
|
+
case 'remove':
|
|
59
|
+
await this._handleRemove(args[1]);
|
|
60
|
+
break;
|
|
61
|
+
default:
|
|
62
|
+
console.log(`Unknown mcp subcommand: ${subcommand}`);
|
|
63
|
+
this._showMcpHelp();
|
|
64
|
+
break;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* mcp add <name> -- <command> [args...]
|
|
70
|
+
* Supports: -e KEY=VALUE, --tool-name <toolName>, --limit <num>
|
|
71
|
+
* @param {string[]} positionalArgs - Args after 'add'
|
|
72
|
+
* @param {object} options - Parsed CLI options
|
|
73
|
+
*/
|
|
74
|
+
async _handleAdd(positionalArgs, options) {
|
|
75
|
+
if (positionalArgs.length === 0) {
|
|
76
|
+
console.log('Usage: ml-container-creator mcp add <name> -- <command> [args...]');
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const name = positionalArgs[0];
|
|
81
|
+
const isBundled = options.bundled === true || options.bundled === 'true';
|
|
82
|
+
|
|
83
|
+
let command, commandArgs;
|
|
84
|
+
|
|
85
|
+
if (isBundled) {
|
|
86
|
+
// Resolve bundled server
|
|
87
|
+
const resolved = this._resolveBundledServer(name);
|
|
88
|
+
if (!resolved) return;
|
|
89
|
+
|
|
90
|
+
// Lazy dependency installation
|
|
91
|
+
const installed = await this._installBundledDependencies(resolved.serverDir, name);
|
|
92
|
+
if (!installed) return;
|
|
93
|
+
|
|
94
|
+
command = 'node';
|
|
95
|
+
commandArgs = [resolved.entryPoint];
|
|
96
|
+
} else {
|
|
97
|
+
// Find the '--' separator to split name from command
|
|
98
|
+
const separatorIndex = positionalArgs.indexOf('--');
|
|
99
|
+
if (separatorIndex === -1 || separatorIndex + 1 >= positionalArgs.length) {
|
|
100
|
+
console.log('Usage: ml-container-creator mcp add <name> -- <command> [args...]');
|
|
101
|
+
console.log('The "--" separator is required between the server name and the command.');
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const commandParts = positionalArgs.slice(separatorIndex + 1);
|
|
106
|
+
command = commandParts[0];
|
|
107
|
+
commandArgs = commandParts.slice(1);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// Build server config
|
|
111
|
+
const serverConfig = { command };
|
|
112
|
+
if (commandArgs.length > 0) {
|
|
113
|
+
serverConfig.args = commandArgs;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// Parse -e KEY=VALUE env flags
|
|
117
|
+
const env = this._parseEnvFlags(options);
|
|
118
|
+
if (Object.keys(env).length > 0) {
|
|
119
|
+
serverConfig.env = env;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// Parse --tool-name
|
|
123
|
+
const toolName = options['tool-name'] || options.toolName;
|
|
124
|
+
if (toolName) {
|
|
125
|
+
serverConfig.toolName = toolName;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// Parse --limit
|
|
129
|
+
const limit = options.limit;
|
|
130
|
+
if (limit !== undefined) {
|
|
131
|
+
const parsed = parseInt(limit, 10);
|
|
132
|
+
if (isNaN(parsed) || parsed < 1) {
|
|
133
|
+
console.log('Error: --limit must be a positive integer');
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
serverConfig.limit = parsed;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// Read existing config
|
|
140
|
+
const config = this._readConfig();
|
|
141
|
+
|
|
142
|
+
// Check if server already exists
|
|
143
|
+
if (config.mcpServers && config.mcpServers[name]) {
|
|
144
|
+
const answers = await this.generator.prompt([{
|
|
145
|
+
type: 'confirm',
|
|
146
|
+
name: 'overwrite',
|
|
147
|
+
message: `MCP server "${name}" already exists. Overwrite?`,
|
|
148
|
+
default: false
|
|
149
|
+
}]);
|
|
150
|
+
if (!answers.overwrite) {
|
|
151
|
+
console.log('Aborted.');
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// Add server
|
|
157
|
+
if (!config.mcpServers) {
|
|
158
|
+
config.mcpServers = {};
|
|
159
|
+
}
|
|
160
|
+
config.mcpServers[name] = serverConfig;
|
|
161
|
+
|
|
162
|
+
this._writeConfig(config);
|
|
163
|
+
console.log(`✅ MCP server "${name}" added successfully.`);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* mcp init
|
|
168
|
+
* Registers all bundled servers in one shot, creating or updating
|
|
169
|
+
* config/mcp.json. Existing non-MCP keys are preserved.
|
|
170
|
+
*/
|
|
171
|
+
async _handleInit() {
|
|
172
|
+
const bundled = this._getAvailableBundledServers();
|
|
173
|
+
if (bundled.length === 0) {
|
|
174
|
+
console.log('No bundled servers found in servers/ directory.');
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
const config = this._readConfig();
|
|
179
|
+
if (!config.mcpServers) {
|
|
180
|
+
config.mcpServers = {};
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
let added = 0;
|
|
184
|
+
let skipped = 0;
|
|
185
|
+
|
|
186
|
+
for (const server of bundled) {
|
|
187
|
+
if (config.mcpServers[server.name]) {
|
|
188
|
+
skipped++;
|
|
189
|
+
continue;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
const resolved = this._resolveBundledServer(server.name);
|
|
193
|
+
if (!resolved) continue;
|
|
194
|
+
|
|
195
|
+
const installed = await this._installBundledDependencies(resolved.serverDir, server.name);
|
|
196
|
+
if (!installed) continue;
|
|
197
|
+
|
|
198
|
+
config.mcpServers[server.name] = {
|
|
199
|
+
command: 'node',
|
|
200
|
+
args: [resolved.entryPoint]
|
|
201
|
+
};
|
|
202
|
+
added++;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
this._writeConfig(config);
|
|
206
|
+
|
|
207
|
+
if (added > 0) {
|
|
208
|
+
console.log(`\n✅ Initialized ${added} bundled MCP server(s).`);
|
|
209
|
+
}
|
|
210
|
+
if (skipped > 0) {
|
|
211
|
+
console.log(` ${skipped} server(s) already configured (skipped).`);
|
|
212
|
+
}
|
|
213
|
+
if (added === 0 && skipped > 0) {
|
|
214
|
+
console.log('\nAll bundled servers are already configured.');
|
|
215
|
+
}
|
|
216
|
+
console.log(`\nConfig written to ${this._getConfigPath()}`);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* mcp list [--bundled]
|
|
221
|
+
* @param {object} options - Parsed CLI options
|
|
222
|
+
*/
|
|
223
|
+
_handleList(options) {
|
|
224
|
+
const isBundled = options.bundled === true || options.bundled === 'true';
|
|
225
|
+
|
|
226
|
+
if (isBundled) {
|
|
227
|
+
this._listBundledServers();
|
|
228
|
+
return;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
const config = this._readConfig();
|
|
232
|
+
const servers = config.mcpServers;
|
|
233
|
+
|
|
234
|
+
if (!servers || Object.keys(servers).length === 0) {
|
|
235
|
+
console.log('No MCP servers configured.');
|
|
236
|
+
console.log('Use "ml-container-creator mcp add <name> -- <command> [args...]" to add one.');
|
|
237
|
+
return;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
console.log('\nConfigured MCP servers:\n');
|
|
241
|
+
for (const [name, serverConfig] of Object.entries(servers)) {
|
|
242
|
+
const args = serverConfig.args ? serverConfig.args.join(' ') : '';
|
|
243
|
+
console.log(` ${name}: ${serverConfig.command} ${args}`.trimEnd());
|
|
244
|
+
}
|
|
245
|
+
console.log('');
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/**
|
|
249
|
+
* mcp get <name>
|
|
250
|
+
* @param {string} name - Server name
|
|
251
|
+
*/
|
|
252
|
+
_handleGet(name) {
|
|
253
|
+
if (!name) {
|
|
254
|
+
console.log('Usage: ml-container-creator mcp get <name>');
|
|
255
|
+
return;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
const config = this._readConfig();
|
|
259
|
+
const servers = config.mcpServers;
|
|
260
|
+
|
|
261
|
+
if (!servers || !servers[name]) {
|
|
262
|
+
const available = servers ? Object.keys(servers) : [];
|
|
263
|
+
console.log(`Error: MCP server "${name}" not found.`);
|
|
264
|
+
if (available.length > 0) {
|
|
265
|
+
console.log(`Available servers: ${available.join(', ')}`);
|
|
266
|
+
}
|
|
267
|
+
return;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
const serverConfig = servers[name];
|
|
271
|
+
console.log(`\nMCP server: ${name}\n`);
|
|
272
|
+
console.log(` command: ${serverConfig.command}`);
|
|
273
|
+
console.log(` args: ${serverConfig.args ? serverConfig.args.join(' ') : '(none)'}`);
|
|
274
|
+
console.log(` env: ${serverConfig.env ? JSON.stringify(serverConfig.env) : '(none)'}`);
|
|
275
|
+
console.log(` toolName: ${serverConfig.toolName || '(default: get_ml_config)'}`);
|
|
276
|
+
console.log(` limit: ${serverConfig.limit || '(default: 10)'}`);
|
|
277
|
+
console.log('');
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
/**
|
|
281
|
+
* mcp remove <name>
|
|
282
|
+
* @param {string} name - Server name
|
|
283
|
+
*/
|
|
284
|
+
async _handleRemove(name) {
|
|
285
|
+
if (!name) {
|
|
286
|
+
console.log('Usage: ml-container-creator mcp remove <name>');
|
|
287
|
+
return;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
const config = this._readConfig();
|
|
291
|
+
const servers = config.mcpServers;
|
|
292
|
+
|
|
293
|
+
if (!servers || !servers[name]) {
|
|
294
|
+
console.log(`Error: MCP server "${name}" not found.`);
|
|
295
|
+
return;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
delete servers[name];
|
|
299
|
+
|
|
300
|
+
// If last server removed, remove the mcpServers key entirely
|
|
301
|
+
if (Object.keys(servers).length === 0) {
|
|
302
|
+
delete config.mcpServers;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
this._writeConfig(config);
|
|
306
|
+
console.log(`✅ MCP server "${name}" removed.`);
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
/**
|
|
310
|
+
* Show mcp usage help.
|
|
311
|
+
*/
|
|
312
|
+
_showMcpHelp() {
|
|
313
|
+
console.log(`
|
|
314
|
+
MCP Server Management
|
|
315
|
+
|
|
316
|
+
USAGE:
|
|
317
|
+
ml-container-creator mcp <subcommand> [options]
|
|
318
|
+
|
|
319
|
+
SUBCOMMANDS:
|
|
320
|
+
init Add all bundled servers at once
|
|
321
|
+
add <name> -- <command> [args...] Add an MCP server
|
|
322
|
+
add <name> --bundled Add a bundled MCP server
|
|
323
|
+
list List configured MCP servers
|
|
324
|
+
list --bundled List available bundled servers
|
|
325
|
+
get <name> Show full server configuration
|
|
326
|
+
remove <name> Remove an MCP server
|
|
327
|
+
|
|
328
|
+
ADD OPTIONS:
|
|
329
|
+
-e KEY=VALUE Set environment variable (repeatable)
|
|
330
|
+
--tool-name <toolName> MCP tool name (default: get_ml_config)
|
|
331
|
+
--limit <num> Max results per parameter (default: 10)
|
|
332
|
+
--bundled Use a bundled server from servers/
|
|
333
|
+
|
|
334
|
+
EXAMPLES:
|
|
335
|
+
ml-container-creator mcp init
|
|
336
|
+
ml-container-creator mcp add team-config -- node servers/instance-recommender/index.js
|
|
337
|
+
ml-container-creator mcp add instance-recommender --bundled
|
|
338
|
+
ml-container-creator mcp add corp-policy -- npx -y @corp/mcp-policy -e TEAM_ID=ml-platform
|
|
339
|
+
ml-container-creator mcp list
|
|
340
|
+
ml-container-creator mcp list --bundled
|
|
341
|
+
ml-container-creator mcp get team-config
|
|
342
|
+
ml-container-creator mcp remove team-config
|
|
343
|
+
`);
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
/**
|
|
347
|
+
* Resolve a bundled server from the servers/ directory.
|
|
348
|
+
* @param {string} name - Server name (directory name under servers/)
|
|
349
|
+
* @returns {{ serverDir: string, entryPoint: string } | null}
|
|
350
|
+
*/
|
|
351
|
+
_resolveBundledServer(name) {
|
|
352
|
+
const serverDir = path.join(SERVERS_DIR, name);
|
|
353
|
+
const pkgPath = path.join(serverDir, 'package.json');
|
|
354
|
+
|
|
355
|
+
if (!fs.existsSync(pkgPath)) {
|
|
356
|
+
const available = this._getAvailableBundledServers();
|
|
357
|
+
console.log(`Error: Bundled server "${name}" not found.`);
|
|
358
|
+
if (available.length > 0) {
|
|
359
|
+
console.log(`Available bundled servers: ${available.map(s => s.name).join(', ')}`);
|
|
360
|
+
} else {
|
|
361
|
+
console.log('No bundled servers are available.');
|
|
362
|
+
}
|
|
363
|
+
return null;
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
let pkg;
|
|
367
|
+
try {
|
|
368
|
+
pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
|
|
369
|
+
} catch (err) {
|
|
370
|
+
console.log(`Error reading package.json for bundled server "${name}": ${err.message}`);
|
|
371
|
+
return null;
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
const main = pkg.main || 'index.js';
|
|
375
|
+
const entryPoint = path.join(serverDir, main);
|
|
376
|
+
|
|
377
|
+
return { serverDir, entryPoint };
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
/**
|
|
381
|
+
* Install dependencies for a bundled server if not already installed.
|
|
382
|
+
* @param {string} serverDir - Absolute path to the server directory
|
|
383
|
+
* @param {string} name - Server name for display
|
|
384
|
+
* @returns {boolean} true if dependencies are ready, false on failure
|
|
385
|
+
*/
|
|
386
|
+
async _installBundledDependencies(serverDir, name) {
|
|
387
|
+
const nodeModulesDir = path.join(serverDir, 'node_modules');
|
|
388
|
+
|
|
389
|
+
if (fs.existsSync(nodeModulesDir)) {
|
|
390
|
+
return true;
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
console.log(`Installing dependencies for ${name}...`);
|
|
394
|
+
try {
|
|
395
|
+
execSync('npm install --production', {
|
|
396
|
+
cwd: serverDir,
|
|
397
|
+
stdio: 'pipe',
|
|
398
|
+
timeout: 60000
|
|
399
|
+
});
|
|
400
|
+
return true;
|
|
401
|
+
} catch (err) {
|
|
402
|
+
const stderr = err.stderr ? err.stderr.toString() : err.message;
|
|
403
|
+
console.log(`Error: Failed to install dependencies for "${name}".`);
|
|
404
|
+
console.log(stderr);
|
|
405
|
+
return false;
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
/**
|
|
410
|
+
* List available bundled servers from the servers/ directory.
|
|
411
|
+
*/
|
|
412
|
+
_listBundledServers() {
|
|
413
|
+
const servers = this._getAvailableBundledServers();
|
|
414
|
+
|
|
415
|
+
if (servers.length === 0) {
|
|
416
|
+
console.log('No bundled servers available.');
|
|
417
|
+
return;
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
console.log('\nAvailable bundled servers:\n');
|
|
421
|
+
for (const server of servers) {
|
|
422
|
+
console.log(` ${server.name}: ${server.description}`);
|
|
423
|
+
}
|
|
424
|
+
console.log('\nUse "ml-container-creator mcp add <name> --bundled" to add one.');
|
|
425
|
+
console.log('');
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
/**
|
|
429
|
+
* Get list of available bundled servers with metadata.
|
|
430
|
+
* @returns {Array<{ name: string, description: string }>}
|
|
431
|
+
*/
|
|
432
|
+
_getAvailableBundledServers() {
|
|
433
|
+
if (!fs.existsSync(SERVERS_DIR)) {
|
|
434
|
+
return [];
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
const entries = fs.readdirSync(SERVERS_DIR, { withFileTypes: true });
|
|
438
|
+
const servers = [];
|
|
439
|
+
|
|
440
|
+
for (const entry of entries) {
|
|
441
|
+
if (!entry.isDirectory()) continue;
|
|
442
|
+
|
|
443
|
+
const pkgPath = path.join(SERVERS_DIR, entry.name, 'package.json');
|
|
444
|
+
if (!fs.existsSync(pkgPath)) continue;
|
|
445
|
+
|
|
446
|
+
try {
|
|
447
|
+
const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
|
|
448
|
+
|
|
449
|
+
// Skip non-server packages (e.g. shared libraries) —
|
|
450
|
+
// a bundled MCP server must declare @modelcontextprotocol/sdk
|
|
451
|
+
// as a dependency.
|
|
452
|
+
const deps = pkg.dependencies || {};
|
|
453
|
+
if (!deps['@modelcontextprotocol/sdk']) continue;
|
|
454
|
+
|
|
455
|
+
servers.push({
|
|
456
|
+
name: entry.name,
|
|
457
|
+
description: pkg.description || '(no description)'
|
|
458
|
+
});
|
|
459
|
+
} catch (_) {
|
|
460
|
+
servers.push({
|
|
461
|
+
name: entry.name,
|
|
462
|
+
description: '(unable to read package.json)'
|
|
463
|
+
});
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
return servers;
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
/**
|
|
471
|
+
* Parse -e KEY=VALUE flags from options.
|
|
472
|
+
* @param {object} options
|
|
473
|
+
* @returns {object} env key-value pairs
|
|
474
|
+
*/
|
|
475
|
+
_parseEnvFlags(options) {
|
|
476
|
+
const env = {};
|
|
477
|
+
const envFlags = options.e || options.env;
|
|
478
|
+
if (!envFlags) return env;
|
|
479
|
+
|
|
480
|
+
const entries = Array.isArray(envFlags) ? envFlags : [envFlags];
|
|
481
|
+
for (const entry of entries) {
|
|
482
|
+
const eqIndex = entry.indexOf('=');
|
|
483
|
+
if (eqIndex > 0) {
|
|
484
|
+
const key = entry.slice(0, eqIndex);
|
|
485
|
+
const value = entry.slice(eqIndex + 1);
|
|
486
|
+
env[key] = value;
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
return env;
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
/**
|
|
493
|
+
* Read config/mcp.json, preserving all keys.
|
|
494
|
+
* Returns empty object if file doesn't exist.
|
|
495
|
+
* @returns {object}
|
|
496
|
+
*/
|
|
497
|
+
_readConfig() {
|
|
498
|
+
const configPath = this._getConfigPath();
|
|
499
|
+
if (!fs.existsSync(configPath)) {
|
|
500
|
+
return {};
|
|
501
|
+
}
|
|
502
|
+
try {
|
|
503
|
+
return JSON.parse(fs.readFileSync(configPath, 'utf8'));
|
|
504
|
+
} catch (err) {
|
|
505
|
+
console.log(`Error reading ${CONFIG_FILENAME}: ${err.message}`);
|
|
506
|
+
return {};
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
/**
|
|
511
|
+
* Write config to config/mcp.json, preserving non-MCP keys.
|
|
512
|
+
* @param {object} config
|
|
513
|
+
*/
|
|
514
|
+
_writeConfig(config) {
|
|
515
|
+
const configPath = this._getConfigPath();
|
|
516
|
+
const dir = path.dirname(configPath);
|
|
517
|
+
if (!fs.existsSync(dir)) {
|
|
518
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
519
|
+
}
|
|
520
|
+
fs.writeFileSync(configPath, `${JSON.stringify(config, null, 2)}\n`);
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
/**
|
|
524
|
+
* Resolve the config file path.
|
|
525
|
+
* @returns {string}
|
|
526
|
+
*/
|
|
527
|
+
_getConfigPath() {
|
|
528
|
+
return this.generator.destinationPath
|
|
529
|
+
? this.generator.destinationPath(CONFIG_FILENAME)
|
|
530
|
+
: path.resolve(CONFIG_FILENAME);
|
|
531
|
+
}
|
|
532
|
+
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import AcceleratorValidator from './accelerator-validator.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Neuron SDK accelerator validator.
|
|
5
|
+
* Implements Neuron SDK semantic versioning.
|
|
6
|
+
*
|
|
7
|
+
* Requirements: 4.11, 4.12, 4.13, 4.14, 4.22
|
|
8
|
+
*/
|
|
9
|
+
export default class NeuronValidator extends AcceleratorValidator {
|
|
10
|
+
/**
|
|
11
|
+
* Validate Neuron SDK version compatibility.
|
|
12
|
+
* Neuron SDK uses semantic versioning (e.g., 2.15.0, 2.16.0).
|
|
13
|
+
* Major version must match, minor version must be >= required.
|
|
14
|
+
*
|
|
15
|
+
* @param {Object} frameworkConfig - Framework accelerator requirements
|
|
16
|
+
* @param {Object} instanceConfig - Instance accelerator capabilities
|
|
17
|
+
* @returns {Object} ValidationResult
|
|
18
|
+
*/
|
|
19
|
+
validate(frameworkConfig, instanceConfig) {
|
|
20
|
+
const required = frameworkConfig.accelerator;
|
|
21
|
+
const provided = instanceConfig.accelerator;
|
|
22
|
+
|
|
23
|
+
// Parse required Neuron SDK version
|
|
24
|
+
const requiredVersion = this.parseVersion(required.version);
|
|
25
|
+
|
|
26
|
+
// Check if instance supports required Neuron SDK version
|
|
27
|
+
const compatibleVersions = provided.versions.filter(v => {
|
|
28
|
+
const providedVersion = this.parseVersion(v);
|
|
29
|
+
return this.isCompatible(requiredVersion, providedVersion);
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
if (compatibleVersions.length === 0) {
|
|
33
|
+
return {
|
|
34
|
+
compatible: false,
|
|
35
|
+
error: this.getVersionMismatchMessage(required.version, provided.versions)
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return {
|
|
40
|
+
compatible: true,
|
|
41
|
+
info: `Using Neuron SDK ${compatibleVersions[0]} (compatible with required ${required.version})`
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Parse semantic version string into components.
|
|
47
|
+
*
|
|
48
|
+
* @param {string} versionString - Version string (e.g., "2.15.0")
|
|
49
|
+
* @returns {Object} Parsed version with major, minor, patch
|
|
50
|
+
*/
|
|
51
|
+
parseVersion(versionString) {
|
|
52
|
+
const [major, minor, patch] = versionString.split('.').map(Number);
|
|
53
|
+
return { major, minor, patch };
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Check if provided version is compatible with required version.
|
|
58
|
+
* Neuron SDK: major must match, minor must be >= required.
|
|
59
|
+
*
|
|
60
|
+
* @param {Object} required - Required version
|
|
61
|
+
* @param {Object} provided - Provided version
|
|
62
|
+
* @returns {boolean} True if compatible
|
|
63
|
+
*/
|
|
64
|
+
isCompatible(required, provided) {
|
|
65
|
+
return provided.major === required.major &&
|
|
66
|
+
provided.minor >= required.minor;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Get user-friendly error message for Neuron SDK version mismatch.
|
|
71
|
+
*
|
|
72
|
+
* @param {string} required - Required Neuron SDK version
|
|
73
|
+
* @param {Array<string>} provided - Provided Neuron SDK versions
|
|
74
|
+
* @returns {string} User-friendly error message
|
|
75
|
+
*/
|
|
76
|
+
getVersionMismatchMessage(required, provided) {
|
|
77
|
+
return `Framework requires Neuron SDK ${required}, but instance only supports ${provided.join(', ')}. ` +
|
|
78
|
+
'Consider using ml.inf2 instances for Neuron SDK 2.15+ support.';
|
|
79
|
+
}
|
|
80
|
+
}
|