mlgym-deploy 3.2.3 → 3.3.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/index.js +1087 -2
- package/package.json +34 -2
package/index.js
CHANGED
|
@@ -17,7 +17,7 @@ import crypto from 'crypto';
|
|
|
17
17
|
const execAsync = promisify(exec);
|
|
18
18
|
|
|
19
19
|
// Current version of this MCP server - INCREMENT FOR WORKFLOW FIXES
|
|
20
|
-
const CURRENT_VERSION = '3.
|
|
20
|
+
const CURRENT_VERSION = '3.3.1'; // Updated help with plain English examples showing what users say vs tool calls
|
|
21
21
|
const PACKAGE_NAME = 'mlgym-deploy';
|
|
22
22
|
|
|
23
23
|
// Debug logging configuration - ENABLED BY DEFAULT
|
|
@@ -2311,6 +2311,826 @@ async function getDeploymentLogs(args) {
|
|
|
2311
2311
|
}
|
|
2312
2312
|
}
|
|
2313
2313
|
|
|
2314
|
+
// Set environment variables for an application
|
|
2315
|
+
async function setEnvironmentVariables(args) {
|
|
2316
|
+
try {
|
|
2317
|
+
const {project_name, variables, local_path = '.'} = args;
|
|
2318
|
+
|
|
2319
|
+
log.info(`MCP >>> [setEnvironmentVariables] Setting environment variables for ${project_name || 'project'}`);
|
|
2320
|
+
log.debug(`Variables to set:`, JSON.stringify(Object.keys(variables), null, 2));
|
|
2321
|
+
|
|
2322
|
+
// Ensure authentication
|
|
2323
|
+
let auth = await loadAuth();
|
|
2324
|
+
if (!auth || !auth.jwt_token) {
|
|
2325
|
+
throw new Error('Not authenticated. Please run mlgym_deploy or mlgym_auth_login first');
|
|
2326
|
+
}
|
|
2327
|
+
|
|
2328
|
+
log.debug(`Using JWT token: ${auth.jwt_token.substring(0, 20)}...`);
|
|
2329
|
+
|
|
2330
|
+
// Determine project name
|
|
2331
|
+
let finalProjectName = project_name;
|
|
2332
|
+
if (!finalProjectName) {
|
|
2333
|
+
// Try to get project name from git remote
|
|
2334
|
+
try {
|
|
2335
|
+
const {stdout} = await execAsync('git remote get-url origin', {cwd: local_path});
|
|
2336
|
+
const match = stdout.match(/\/([^\/]+)\.git/);
|
|
2337
|
+
if (match) {
|
|
2338
|
+
finalProjectName = match[1];
|
|
2339
|
+
log.debug(`Detected project name from git remote: ${finalProjectName}`);
|
|
2340
|
+
}
|
|
2341
|
+
} catch (error) {
|
|
2342
|
+
throw new Error('project_name is required when not in a git repository with remote configured');
|
|
2343
|
+
}
|
|
2344
|
+
}
|
|
2345
|
+
|
|
2346
|
+
log.info(`Setting environment variables for project: ${finalProjectName}`);
|
|
2347
|
+
|
|
2348
|
+
// Get application UUID from backend
|
|
2349
|
+
const backendUrl = `${CONFIG.backend_url}/api/v1/projects/${finalProjectName}`;
|
|
2350
|
+
log.debug(`Fetching project details from: ${backendUrl}`);
|
|
2351
|
+
|
|
2352
|
+
const projectResponse = await axios.get(backendUrl, {
|
|
2353
|
+
headers: {
|
|
2354
|
+
'Authorization': `Bearer ${auth.jwt_token}`,
|
|
2355
|
+
'Content-Type': 'application/json'
|
|
2356
|
+
}
|
|
2357
|
+
});
|
|
2358
|
+
|
|
2359
|
+
if (!projectResponse.data || !projectResponse.data.application_uuid) {
|
|
2360
|
+
throw new Error(`Project ${finalProjectName} not found or has no application`);
|
|
2361
|
+
}
|
|
2362
|
+
|
|
2363
|
+
const appUUID = projectResponse.data.application_uuid;
|
|
2364
|
+
log.debug(`Application UUID: ${appUUID}`);
|
|
2365
|
+
|
|
2366
|
+
// Call User Agent to set environment variables
|
|
2367
|
+
const userAgentUrl = 'http://coolify.eu.ezb.net:9000/applications/' + appUUID + '/environment-variables';
|
|
2368
|
+
log.debug(`Calling User Agent: ${userAgentUrl}`);
|
|
2369
|
+
|
|
2370
|
+
const envResponse = await axios.post(userAgentUrl, {
|
|
2371
|
+
variables: variables
|
|
2372
|
+
}, {
|
|
2373
|
+
headers: {
|
|
2374
|
+
'Authorization': 'Bearer 1|Jkztb5qPptwRKgtocfbT2TLjp8WGJG1SkPE4DzLt4c5d600f',
|
|
2375
|
+
'Content-Type': 'application/json'
|
|
2376
|
+
}
|
|
2377
|
+
});
|
|
2378
|
+
|
|
2379
|
+
log.success(`Environment variables set successfully`);
|
|
2380
|
+
log.debug(`Response:`, JSON.stringify(envResponse.data, null, 2));
|
|
2381
|
+
|
|
2382
|
+
return {
|
|
2383
|
+
content: [{
|
|
2384
|
+
type: 'text',
|
|
2385
|
+
text: JSON.stringify({
|
|
2386
|
+
status: 'success',
|
|
2387
|
+
project_name: finalProjectName,
|
|
2388
|
+
application_uuid: appUUID,
|
|
2389
|
+
variables_set: envResponse.data.variables_set || Object.keys(variables),
|
|
2390
|
+
count: envResponse.data.count || Object.keys(variables).length,
|
|
2391
|
+
message: `Successfully set ${Object.keys(variables).length} environment variable(s) for ${finalProjectName}`,
|
|
2392
|
+
next_steps: [
|
|
2393
|
+
'Environment variables are now set for both build-time and runtime',
|
|
2394
|
+
'If you need to trigger a rebuild with new variables, use mlgym_deploy',
|
|
2395
|
+
'Variables will be available in your application as process.env.VARIABLE_NAME'
|
|
2396
|
+
]
|
|
2397
|
+
}, null, 2)
|
|
2398
|
+
}]
|
|
2399
|
+
};
|
|
2400
|
+
} catch (error) {
|
|
2401
|
+
log.error(`MCP >>> [setEnvironmentVariables] Error: ${error.message}`);
|
|
2402
|
+
if (error.response) {
|
|
2403
|
+
log.error(`Response status: ${error.response.status}`);
|
|
2404
|
+
log.error(`Response data:`, JSON.stringify(error.response.data, null, 2));
|
|
2405
|
+
}
|
|
2406
|
+
return {
|
|
2407
|
+
content: [{
|
|
2408
|
+
type: 'text',
|
|
2409
|
+
text: JSON.stringify({
|
|
2410
|
+
status: 'error',
|
|
2411
|
+
error: error.message,
|
|
2412
|
+
hint: error.response?.data?.error || 'Make sure the project exists and you are authenticated'
|
|
2413
|
+
}, null, 2)
|
|
2414
|
+
}]
|
|
2415
|
+
};
|
|
2416
|
+
}
|
|
2417
|
+
}
|
|
2418
|
+
|
|
2419
|
+
// Helper function to get application UUID from project name
|
|
2420
|
+
async function getApplicationUUID(projectName, auth) {
|
|
2421
|
+
const backendUrl = `${CONFIG.backend_url}/api/v1/projects/${projectName}`;
|
|
2422
|
+
log.debug(`Fetching project details from: ${backendUrl}`);
|
|
2423
|
+
|
|
2424
|
+
const projectResponse = await axios.get(backendUrl, {
|
|
2425
|
+
headers: {
|
|
2426
|
+
'Authorization': `Bearer ${auth.jwt_token}`,
|
|
2427
|
+
'Content-Type': 'application/json'
|
|
2428
|
+
}
|
|
2429
|
+
});
|
|
2430
|
+
|
|
2431
|
+
if (!projectResponse.data || !projectResponse.data.application_uuid) {
|
|
2432
|
+
throw new Error(`Project ${projectName} not found or has no application`);
|
|
2433
|
+
}
|
|
2434
|
+
|
|
2435
|
+
return projectResponse.data.application_uuid;
|
|
2436
|
+
}
|
|
2437
|
+
|
|
2438
|
+
// Set health check configuration
|
|
2439
|
+
async function setHealthCheck(args) {
|
|
2440
|
+
try {
|
|
2441
|
+
const {project_name, local_path = '.', ...healthConfig} = args;
|
|
2442
|
+
|
|
2443
|
+
log.info(`MCP >>> [setHealthCheck] Configuring health check for ${project_name || 'project'}`);
|
|
2444
|
+
|
|
2445
|
+
// Ensure authentication
|
|
2446
|
+
let auth = await loadAuth();
|
|
2447
|
+
if (!auth || !auth.jwt_token) {
|
|
2448
|
+
throw new Error('Not authenticated. Please run mlgym_deploy or mlgym_auth_login first');
|
|
2449
|
+
}
|
|
2450
|
+
|
|
2451
|
+
// Determine project name
|
|
2452
|
+
let finalProjectName = project_name;
|
|
2453
|
+
if (!finalProjectName) {
|
|
2454
|
+
try {
|
|
2455
|
+
const {stdout} = await execAsync('git remote get-url origin', {cwd: local_path});
|
|
2456
|
+
const match = stdout.match(/\/([^\/]+)\.git/);
|
|
2457
|
+
if (match) finalProjectName = match[1];
|
|
2458
|
+
} catch (error) {
|
|
2459
|
+
throw new Error('project_name is required when not in a git repository');
|
|
2460
|
+
}
|
|
2461
|
+
}
|
|
2462
|
+
|
|
2463
|
+
const appUUID = await getApplicationUUID(finalProjectName, auth);
|
|
2464
|
+
log.debug(`Application UUID: ${appUUID}`);
|
|
2465
|
+
|
|
2466
|
+
// Call User Agent to set health check
|
|
2467
|
+
const userAgentUrl = `http://coolify.eu.ezb.net:9000/applications/${appUUID}/health-check`;
|
|
2468
|
+
const response = await axios.post(userAgentUrl, healthConfig, {
|
|
2469
|
+
headers: {
|
|
2470
|
+
'Authorization': 'Bearer 1|Jkztb5qPptwRKgtocfbT2TLjp8WGJG1SkPE4DzLt4c5d600f',
|
|
2471
|
+
'Content-Type': 'application/json'
|
|
2472
|
+
}
|
|
2473
|
+
});
|
|
2474
|
+
|
|
2475
|
+
log.success(`Health check configured successfully`);
|
|
2476
|
+
|
|
2477
|
+
return {
|
|
2478
|
+
content: [{
|
|
2479
|
+
type: 'text',
|
|
2480
|
+
text: JSON.stringify({
|
|
2481
|
+
status: 'success',
|
|
2482
|
+
project_name: finalProjectName,
|
|
2483
|
+
application_uuid: appUUID,
|
|
2484
|
+
health_check: healthConfig,
|
|
2485
|
+
message: `Health check ${healthConfig.enabled ? 'enabled' : 'disabled'} for ${finalProjectName}`
|
|
2486
|
+
}, null, 2)
|
|
2487
|
+
}]
|
|
2488
|
+
};
|
|
2489
|
+
} catch (error) {
|
|
2490
|
+
log.error(`MCP >>> [setHealthCheck] Error: ${error.message}`);
|
|
2491
|
+
return {
|
|
2492
|
+
content: [{
|
|
2493
|
+
type: 'text',
|
|
2494
|
+
text: JSON.stringify({
|
|
2495
|
+
status: 'error',
|
|
2496
|
+
error: error.message,
|
|
2497
|
+
hint: error.response?.data?.error || 'Make sure the project exists and you are authenticated'
|
|
2498
|
+
}, null, 2)
|
|
2499
|
+
}]
|
|
2500
|
+
};
|
|
2501
|
+
}
|
|
2502
|
+
}
|
|
2503
|
+
|
|
2504
|
+
// Set custom domain
|
|
2505
|
+
async function setDomain(args) {
|
|
2506
|
+
try {
|
|
2507
|
+
const {project_name, domain, local_path = '.'} = args;
|
|
2508
|
+
|
|
2509
|
+
log.info(`MCP >>> [setDomain] Setting domain for ${project_name || 'project'}`);
|
|
2510
|
+
|
|
2511
|
+
let auth = await loadAuth();
|
|
2512
|
+
if (!auth || !auth.jwt_token) {
|
|
2513
|
+
throw new Error('Not authenticated. Please run mlgym_deploy or mlgym_auth_login first');
|
|
2514
|
+
}
|
|
2515
|
+
|
|
2516
|
+
let finalProjectName = project_name;
|
|
2517
|
+
if (!finalProjectName) {
|
|
2518
|
+
try {
|
|
2519
|
+
const {stdout} = await execAsync('git remote get-url origin', {cwd: local_path});
|
|
2520
|
+
const match = stdout.match(/\/([^\/]+)\.git/);
|
|
2521
|
+
if (match) finalProjectName = match[1];
|
|
2522
|
+
} catch (error) {
|
|
2523
|
+
throw new Error('project_name is required when not in a git repository');
|
|
2524
|
+
}
|
|
2525
|
+
}
|
|
2526
|
+
|
|
2527
|
+
const appUUID = await getApplicationUUID(finalProjectName, auth);
|
|
2528
|
+
|
|
2529
|
+
const userAgentUrl = `http://coolify.eu.ezb.net:9000/applications/${appUUID}/domain`;
|
|
2530
|
+
const response = await axios.post(userAgentUrl, {domain}, {
|
|
2531
|
+
headers: {
|
|
2532
|
+
'Authorization': 'Bearer 1|Jkztb5qPptwRKgtocfbT2TLjp8WGJG1SkPE4DzLt4c5d600f',
|
|
2533
|
+
'Content-Type': 'application/json'
|
|
2534
|
+
}
|
|
2535
|
+
});
|
|
2536
|
+
|
|
2537
|
+
log.success(`Domain set successfully: ${domain}`);
|
|
2538
|
+
|
|
2539
|
+
return {
|
|
2540
|
+
content: [{
|
|
2541
|
+
type: 'text',
|
|
2542
|
+
text: JSON.stringify({
|
|
2543
|
+
status: 'success',
|
|
2544
|
+
project_name: finalProjectName,
|
|
2545
|
+
application_uuid: appUUID,
|
|
2546
|
+
domain: response.data.domain,
|
|
2547
|
+
message: `Domain updated to ${response.data.domain}`
|
|
2548
|
+
}, null, 2)
|
|
2549
|
+
}]
|
|
2550
|
+
};
|
|
2551
|
+
} catch (error) {
|
|
2552
|
+
log.error(`MCP >>> [setDomain] Error: ${error.message}`);
|
|
2553
|
+
return {
|
|
2554
|
+
content: [{
|
|
2555
|
+
type: 'text',
|
|
2556
|
+
text: JSON.stringify({
|
|
2557
|
+
status: 'error',
|
|
2558
|
+
error: error.message,
|
|
2559
|
+
hint: error.response?.data?.error || 'Make sure the project exists and you are authenticated'
|
|
2560
|
+
}, null, 2)
|
|
2561
|
+
}]
|
|
2562
|
+
};
|
|
2563
|
+
}
|
|
2564
|
+
}
|
|
2565
|
+
|
|
2566
|
+
// Set deployment commands
|
|
2567
|
+
async function setDeploymentCommands(args) {
|
|
2568
|
+
try {
|
|
2569
|
+
const {project_name, local_path = '.', ...commands} = args;
|
|
2570
|
+
|
|
2571
|
+
log.info(`MCP >>> [setDeploymentCommands] Setting deployment commands for ${project_name || 'project'}`);
|
|
2572
|
+
|
|
2573
|
+
let auth = await loadAuth();
|
|
2574
|
+
if (!auth || !auth.jwt_token) {
|
|
2575
|
+
throw new Error('Not authenticated. Please run mlgym_deploy or mlgym_auth_login first');
|
|
2576
|
+
}
|
|
2577
|
+
|
|
2578
|
+
let finalProjectName = project_name;
|
|
2579
|
+
if (!finalProjectName) {
|
|
2580
|
+
try {
|
|
2581
|
+
const {stdout} = await execAsync('git remote get-url origin', {cwd: local_path});
|
|
2582
|
+
const match = stdout.match(/\/([^\/]+)\.git/);
|
|
2583
|
+
if (match) finalProjectName = match[1];
|
|
2584
|
+
} catch (error) {
|
|
2585
|
+
throw new Error('project_name is required when not in a git repository');
|
|
2586
|
+
}
|
|
2587
|
+
}
|
|
2588
|
+
|
|
2589
|
+
const appUUID = await getApplicationUUID(finalProjectName, auth);
|
|
2590
|
+
|
|
2591
|
+
const userAgentUrl = `http://coolify.eu.ezb.net:9000/applications/${appUUID}/deployment-commands`;
|
|
2592
|
+
const response = await axios.post(userAgentUrl, commands, {
|
|
2593
|
+
headers: {
|
|
2594
|
+
'Authorization': 'Bearer 1|Jkztb5qPptwRKgtocfbT2TLjp8WGJG1SkPE4DzLt4c5d600f',
|
|
2595
|
+
'Content-Type': 'application/json'
|
|
2596
|
+
}
|
|
2597
|
+
});
|
|
2598
|
+
|
|
2599
|
+
log.success(`Deployment commands set successfully`);
|
|
2600
|
+
|
|
2601
|
+
return {
|
|
2602
|
+
content: [{
|
|
2603
|
+
type: 'text',
|
|
2604
|
+
text: JSON.stringify({
|
|
2605
|
+
status: 'success',
|
|
2606
|
+
project_name: finalProjectName,
|
|
2607
|
+
application_uuid: appUUID,
|
|
2608
|
+
commands: commands,
|
|
2609
|
+
message: `Deployment commands updated for ${finalProjectName}`
|
|
2610
|
+
}, null, 2)
|
|
2611
|
+
}]
|
|
2612
|
+
};
|
|
2613
|
+
} catch (error) {
|
|
2614
|
+
log.error(`MCP >>> [setDeploymentCommands] Error: ${error.message}`);
|
|
2615
|
+
return {
|
|
2616
|
+
content: [{
|
|
2617
|
+
type: 'text',
|
|
2618
|
+
text: JSON.stringify({
|
|
2619
|
+
status: 'error',
|
|
2620
|
+
error: error.message,
|
|
2621
|
+
hint: error.response?.data?.error || 'Make sure the project exists and you are authenticated'
|
|
2622
|
+
}, null, 2)
|
|
2623
|
+
}]
|
|
2624
|
+
};
|
|
2625
|
+
}
|
|
2626
|
+
}
|
|
2627
|
+
|
|
2628
|
+
// Manually trigger deployment
|
|
2629
|
+
async function manualDeploy(args) {
|
|
2630
|
+
try {
|
|
2631
|
+
const {project_name, local_path = '.'} = args;
|
|
2632
|
+
|
|
2633
|
+
log.info(`MCP >>> [manualDeploy] Triggering manual deployment for ${project_name || 'project'}`);
|
|
2634
|
+
|
|
2635
|
+
let auth = await loadAuth();
|
|
2636
|
+
if (!auth || !auth.jwt_token) {
|
|
2637
|
+
throw new Error('Not authenticated. Please run mlgym_deploy or mlgym_auth_login first');
|
|
2638
|
+
}
|
|
2639
|
+
|
|
2640
|
+
let finalProjectName = project_name;
|
|
2641
|
+
if (!finalProjectName) {
|
|
2642
|
+
try {
|
|
2643
|
+
const {stdout} = await execAsync('git remote get-url origin', {cwd: local_path});
|
|
2644
|
+
const match = stdout.match(/\/([^\/]+)\.git/);
|
|
2645
|
+
if (match) finalProjectName = match[1];
|
|
2646
|
+
} catch (error) {
|
|
2647
|
+
throw new Error('project_name is required when not in a git repository');
|
|
2648
|
+
}
|
|
2649
|
+
}
|
|
2650
|
+
|
|
2651
|
+
const appUUID = await getApplicationUUID(finalProjectName, auth);
|
|
2652
|
+
|
|
2653
|
+
const userAgentUrl = `http://coolify.eu.ezb.net:9000/applications/${appUUID}/deploy-manual`;
|
|
2654
|
+
const response = await axios.post(userAgentUrl, {}, {
|
|
2655
|
+
headers: {
|
|
2656
|
+
'Authorization': 'Bearer 1|Jkztb5qPptwRKgtocfbT2TLjp8WGJG1SkPE4DzLt4c5d600f',
|
|
2657
|
+
'Content-Type': 'application/json'
|
|
2658
|
+
}
|
|
2659
|
+
});
|
|
2660
|
+
|
|
2661
|
+
log.success(`Manual deployment triggered successfully`);
|
|
2662
|
+
|
|
2663
|
+
return {
|
|
2664
|
+
content: [{
|
|
2665
|
+
type: 'text',
|
|
2666
|
+
text: JSON.stringify({
|
|
2667
|
+
status: 'success',
|
|
2668
|
+
project_name: finalProjectName,
|
|
2669
|
+
application_uuid: appUUID,
|
|
2670
|
+
deployment_uuid: response.data.deployment_uuid,
|
|
2671
|
+
deployment_status: response.data.status,
|
|
2672
|
+
message: `Manual deployment triggered for ${finalProjectName}. Use mlgym_deploy_logs to monitor progress.`,
|
|
2673
|
+
next_steps: [
|
|
2674
|
+
'Deployment is queued and will start shortly',
|
|
2675
|
+
'Use mlgym_deploy_logs to view deployment progress',
|
|
2676
|
+
'Check application URL once deployment completes'
|
|
2677
|
+
]
|
|
2678
|
+
}, null, 2)
|
|
2679
|
+
}]
|
|
2680
|
+
};
|
|
2681
|
+
} catch (error) {
|
|
2682
|
+
log.error(`MCP >>> [manualDeploy] Error: ${error.message}`);
|
|
2683
|
+
return {
|
|
2684
|
+
content: [{
|
|
2685
|
+
type: 'text',
|
|
2686
|
+
text: JSON.stringify({
|
|
2687
|
+
status: 'error',
|
|
2688
|
+
error: error.message,
|
|
2689
|
+
hint: error.response?.data?.error || 'Make sure the project exists and you are authenticated'
|
|
2690
|
+
}, null, 2)
|
|
2691
|
+
}]
|
|
2692
|
+
};
|
|
2693
|
+
}
|
|
2694
|
+
}
|
|
2695
|
+
|
|
2696
|
+
// Set application options
|
|
2697
|
+
async function setOptions(args) {
|
|
2698
|
+
try {
|
|
2699
|
+
const {project_name, local_path = '.', ...options} = args;
|
|
2700
|
+
|
|
2701
|
+
log.info(`MCP >>> [setOptions] Setting options for ${project_name || 'project'}`);
|
|
2702
|
+
|
|
2703
|
+
let auth = await loadAuth();
|
|
2704
|
+
if (!auth || !auth.jwt_token) {
|
|
2705
|
+
throw new Error('Not authenticated. Please run mlgym_deploy or mlgym_auth_login first');
|
|
2706
|
+
}
|
|
2707
|
+
|
|
2708
|
+
let finalProjectName = project_name;
|
|
2709
|
+
if (!finalProjectName) {
|
|
2710
|
+
try {
|
|
2711
|
+
const {stdout} = await execAsync('git remote get-url origin', {cwd: local_path});
|
|
2712
|
+
const match = stdout.match(/\/([^\/]+)\.git/);
|
|
2713
|
+
if (match) finalProjectName = match[1];
|
|
2714
|
+
} catch (error) {
|
|
2715
|
+
throw new Error('project_name is required when not in a git repository');
|
|
2716
|
+
}
|
|
2717
|
+
}
|
|
2718
|
+
|
|
2719
|
+
const appUUID = await getApplicationUUID(finalProjectName, auth);
|
|
2720
|
+
|
|
2721
|
+
const userAgentUrl = `http://coolify.eu.ezb.net:9000/applications/${appUUID}/options`;
|
|
2722
|
+
const response = await axios.post(userAgentUrl, options, {
|
|
2723
|
+
headers: {
|
|
2724
|
+
'Authorization': 'Bearer 1|Jkztb5qPptwRKgtocfbT2TLjp8WGJG1SkPE4DzLt4c5d600f',
|
|
2725
|
+
'Content-Type': 'application/json'
|
|
2726
|
+
}
|
|
2727
|
+
});
|
|
2728
|
+
|
|
2729
|
+
log.success(`Options set successfully`);
|
|
2730
|
+
|
|
2731
|
+
return {
|
|
2732
|
+
content: [{
|
|
2733
|
+
type: 'text',
|
|
2734
|
+
text: JSON.stringify({
|
|
2735
|
+
status: 'success',
|
|
2736
|
+
project_name: finalProjectName,
|
|
2737
|
+
application_uuid: appUUID,
|
|
2738
|
+
options: options,
|
|
2739
|
+
message: `Application options updated for ${finalProjectName}`
|
|
2740
|
+
}, null, 2)
|
|
2741
|
+
}]
|
|
2742
|
+
};
|
|
2743
|
+
} catch (error) {
|
|
2744
|
+
log.error(`MCP >>> [setOptions] Error: ${error.message}`);
|
|
2745
|
+
return {
|
|
2746
|
+
content: [{
|
|
2747
|
+
type: 'text',
|
|
2748
|
+
text: JSON.stringify({
|
|
2749
|
+
status: 'error',
|
|
2750
|
+
error: error.message,
|
|
2751
|
+
hint: error.response?.data?.error || 'Make sure the project exists and you are authenticated'
|
|
2752
|
+
}, null, 2)
|
|
2753
|
+
}]
|
|
2754
|
+
};
|
|
2755
|
+
}
|
|
2756
|
+
}
|
|
2757
|
+
|
|
2758
|
+
// Rollback to previous deployment
|
|
2759
|
+
async function rollback(args) {
|
|
2760
|
+
try {
|
|
2761
|
+
const {project_name, deployment_id, local_path = '.'} = args;
|
|
2762
|
+
|
|
2763
|
+
log.info(`MCP >>> [rollback] Rolling back ${project_name || 'project'}`);
|
|
2764
|
+
|
|
2765
|
+
let auth = await loadAuth();
|
|
2766
|
+
if (!auth || !auth.jwt_token) {
|
|
2767
|
+
throw new Error('Not authenticated. Please run mlgym_deploy or mlgym_auth_login first');
|
|
2768
|
+
}
|
|
2769
|
+
|
|
2770
|
+
let finalProjectName = project_name;
|
|
2771
|
+
if (!finalProjectName) {
|
|
2772
|
+
try {
|
|
2773
|
+
const {stdout} = await execAsync('git remote get-url origin', {cwd: local_path});
|
|
2774
|
+
const match = stdout.match(/\/([^\/]+)\.git/);
|
|
2775
|
+
if (match) finalProjectName = match[1];
|
|
2776
|
+
} catch (error) {
|
|
2777
|
+
throw new Error('project_name is required when not in a git repository');
|
|
2778
|
+
}
|
|
2779
|
+
}
|
|
2780
|
+
|
|
2781
|
+
const appUUID = await getApplicationUUID(finalProjectName, auth);
|
|
2782
|
+
|
|
2783
|
+
const userAgentUrl = `http://coolify.eu.ezb.net:9000/applications/${appUUID}/rollback`;
|
|
2784
|
+
const requestBody = deployment_id ? {deployment_id} : {};
|
|
2785
|
+
const response = await axios.post(userAgentUrl, requestBody, {
|
|
2786
|
+
headers: {
|
|
2787
|
+
'Authorization': 'Bearer 1|Jkztb5qPptwRKgtocfbT2TLjp8WGJG1SkPE4DzLt4c5d600f',
|
|
2788
|
+
'Content-Type': 'application/json'
|
|
2789
|
+
}
|
|
2790
|
+
});
|
|
2791
|
+
|
|
2792
|
+
log.success(`Rollback triggered successfully`);
|
|
2793
|
+
|
|
2794
|
+
return {
|
|
2795
|
+
content: [{
|
|
2796
|
+
type: 'text',
|
|
2797
|
+
text: JSON.stringify({
|
|
2798
|
+
status: 'success',
|
|
2799
|
+
project_name: finalProjectName,
|
|
2800
|
+
application_uuid: appUUID,
|
|
2801
|
+
rollback_to_deployment: response.data.rollback_to_deployment,
|
|
2802
|
+
commit: response.data.commit,
|
|
2803
|
+
deployment_uuid: response.data.deployment_uuid,
|
|
2804
|
+
message: `Rollback deployment queued for ${finalProjectName}. Use mlgym_deploy_logs to monitor progress.`,
|
|
2805
|
+
next_steps: [
|
|
2806
|
+
'Rollback deployment is queued',
|
|
2807
|
+
'Use mlgym_deploy_logs to view deployment progress',
|
|
2808
|
+
'Application will revert to previous version once deployed'
|
|
2809
|
+
]
|
|
2810
|
+
}, null, 2)
|
|
2811
|
+
}]
|
|
2812
|
+
};
|
|
2813
|
+
} catch (error) {
|
|
2814
|
+
log.error(`MCP >>> [rollback] Error: ${error.message}`);
|
|
2815
|
+
return {
|
|
2816
|
+
content: [{
|
|
2817
|
+
type: 'text',
|
|
2818
|
+
text: JSON.stringify({
|
|
2819
|
+
status: 'error',
|
|
2820
|
+
error: error.message,
|
|
2821
|
+
hint: error.response?.data?.error || 'Make sure the project exists and has previous deployments'
|
|
2822
|
+
}, null, 2)
|
|
2823
|
+
}]
|
|
2824
|
+
};
|
|
2825
|
+
}
|
|
2826
|
+
}
|
|
2827
|
+
|
|
2828
|
+
// Display help with all available tools
|
|
2829
|
+
async function showHelp(args) {
|
|
2830
|
+
const helpText = `
|
|
2831
|
+
╔═══════════════════════════════════════════════════════════════════════╗
|
|
2832
|
+
║ MLGym MCP Tools v${CURRENT_VERSION} ║
|
|
2833
|
+
║ Complete Deployment Platform for GitLab + Coolify ║
|
|
2834
|
+
╚═══════════════════════════════════════════════════════════════════════╝
|
|
2835
|
+
|
|
2836
|
+
HOW TO USE THIS GUIDE:
|
|
2837
|
+
Users speak in plain English. When they say "deploy to mlgym" or "show me
|
|
2838
|
+
the status", the AI translates this to the appropriate tool call.
|
|
2839
|
+
|
|
2840
|
+
Examples show both what users say AND the tool call that gets executed.
|
|
2841
|
+
|
|
2842
|
+
📦 DEPLOYMENT TOOLS
|
|
2843
|
+
───────────────────────────────────────────────────────────────────────
|
|
2844
|
+
|
|
2845
|
+
🚀 mlgym_deploy - Deploy your application
|
|
2846
|
+
What users say:
|
|
2847
|
+
• "Deploy to mlgym"
|
|
2848
|
+
• "Deploy this project to mlgym"
|
|
2849
|
+
• "I want to deploy my app"
|
|
2850
|
+
• "Push this to production"
|
|
2851
|
+
|
|
2852
|
+
Tool call:
|
|
2853
|
+
mlgym_deploy({
|
|
2854
|
+
project_name: "my-app",
|
|
2855
|
+
project_description: "My application",
|
|
2856
|
+
email: "user@example.com", // optional if already logged in
|
|
2857
|
+
password: "password123" // optional if already logged in
|
|
2858
|
+
})
|
|
2859
|
+
|
|
2860
|
+
📊 mlgym_status - Check deployment status
|
|
2861
|
+
What users say:
|
|
2862
|
+
• "Show me the status"
|
|
2863
|
+
• "What's deployed?"
|
|
2864
|
+
• "Am I logged in?"
|
|
2865
|
+
• "Check if my project is configured"
|
|
2866
|
+
|
|
2867
|
+
Tool call:
|
|
2868
|
+
mlgym_status({
|
|
2869
|
+
local_path: "." // optional, defaults to current directory
|
|
2870
|
+
})
|
|
2871
|
+
|
|
2872
|
+
📝 mlgym_deploy_logs - View deployment logs
|
|
2873
|
+
What users say:
|
|
2874
|
+
• "Show me the deployment logs"
|
|
2875
|
+
• "What happened in the last deployment?"
|
|
2876
|
+
• "Why did my deployment fail?"
|
|
2877
|
+
• "Show the build logs"
|
|
2878
|
+
|
|
2879
|
+
Tool call:
|
|
2880
|
+
mlgym_deploy_logs({
|
|
2881
|
+
project_name: "my-app",
|
|
2882
|
+
depth: 3 // optional, number of recent deployments
|
|
2883
|
+
})
|
|
2884
|
+
|
|
2885
|
+
⚙️ CONFIGURATION TOOLS
|
|
2886
|
+
───────────────────────────────────────────────────────────────────────
|
|
2887
|
+
|
|
2888
|
+
🔧 mlgym_set_env_vars - Set environment variables
|
|
2889
|
+
What users say:
|
|
2890
|
+
• "Set the database URL"
|
|
2891
|
+
• "Add these environment variables"
|
|
2892
|
+
• "Configure my API keys"
|
|
2893
|
+
• "I need to set DATABASE_URL and STRIPE_KEY"
|
|
2894
|
+
|
|
2895
|
+
Tool call:
|
|
2896
|
+
mlgym_set_env_vars({
|
|
2897
|
+
project_name: "my-app",
|
|
2898
|
+
variables: {
|
|
2899
|
+
DATABASE_URL: "postgresql://user:pass@host:5432/db",
|
|
2900
|
+
STRIPE_KEY: "sk_live_123...",
|
|
2901
|
+
NODE_ENV: "production"
|
|
2902
|
+
}
|
|
2903
|
+
})
|
|
2904
|
+
|
|
2905
|
+
❤️ mlgym_set_health_check - Configure health monitoring
|
|
2906
|
+
What users say:
|
|
2907
|
+
• "Set up a health check"
|
|
2908
|
+
• "Monitor my /health endpoint"
|
|
2909
|
+
• "Check if my app is alive every 30 seconds"
|
|
2910
|
+
• "Enable health checks on /api/health"
|
|
2911
|
+
|
|
2912
|
+
Tool call:
|
|
2913
|
+
mlgym_set_health_check({
|
|
2914
|
+
project_name: "my-app",
|
|
2915
|
+
enabled: true,
|
|
2916
|
+
path: "/health", // endpoint to check
|
|
2917
|
+
method: "GET", // HTTP method
|
|
2918
|
+
return_code: 200, // expected status code
|
|
2919
|
+
interval: 30 // check every 30 seconds
|
|
2920
|
+
})
|
|
2921
|
+
|
|
2922
|
+
🌐 mlgym_set_domain - Set custom domain
|
|
2923
|
+
What users say:
|
|
2924
|
+
• "Use my custom domain"
|
|
2925
|
+
• "Point myapp.com to this deployment"
|
|
2926
|
+
• "Set the domain to app.example.com"
|
|
2927
|
+
• "Change the URL to www.mysite.com"
|
|
2928
|
+
|
|
2929
|
+
Tool call:
|
|
2930
|
+
mlgym_set_domain({
|
|
2931
|
+
project_name: "my-app",
|
|
2932
|
+
domain: "myapp.example.com" // your custom domain
|
|
2933
|
+
})
|
|
2934
|
+
|
|
2935
|
+
📜 mlgym_set_deployment_commands - Configure build commands
|
|
2936
|
+
What users say:
|
|
2937
|
+
• "Run migrations before deploying"
|
|
2938
|
+
• "Seed the database after deployment"
|
|
2939
|
+
• "Execute npm run build before starting"
|
|
2940
|
+
• "Run these commands during deployment"
|
|
2941
|
+
|
|
2942
|
+
Tool call:
|
|
2943
|
+
mlgym_set_deployment_commands({
|
|
2944
|
+
project_name: "my-app",
|
|
2945
|
+
pre_command: "npm run migrate", // runs before deployment
|
|
2946
|
+
post_command: "npm run seed" // runs after deployment
|
|
2947
|
+
})
|
|
2948
|
+
|
|
2949
|
+
⚙️ mlgym_set_options - Configure deployment options
|
|
2950
|
+
What users say:
|
|
2951
|
+
• "Disable build cache"
|
|
2952
|
+
• "Turn off the build cache"
|
|
2953
|
+
• "Don't use cached builds"
|
|
2954
|
+
• "Force a fresh build"
|
|
2955
|
+
|
|
2956
|
+
Tool call:
|
|
2957
|
+
mlgym_set_options({
|
|
2958
|
+
project_name: "my-app",
|
|
2959
|
+
disable_build_cache: true // force fresh builds
|
|
2960
|
+
})
|
|
2961
|
+
|
|
2962
|
+
🎬 DEPLOYMENT MANAGEMENT
|
|
2963
|
+
───────────────────────────────────────────────────────────────────────
|
|
2964
|
+
|
|
2965
|
+
▶️ mlgym_deploy_manual - Trigger deployment manually
|
|
2966
|
+
What users say:
|
|
2967
|
+
• "Deploy now"
|
|
2968
|
+
• "Trigger a deployment"
|
|
2969
|
+
• "Redeploy my application"
|
|
2970
|
+
• "Start a new deployment"
|
|
2971
|
+
|
|
2972
|
+
Tool call:
|
|
2973
|
+
mlgym_deploy_manual({
|
|
2974
|
+
project_name: "my-app"
|
|
2975
|
+
})
|
|
2976
|
+
|
|
2977
|
+
⏪ mlgym_rollback - Rollback to previous version
|
|
2978
|
+
What users say:
|
|
2979
|
+
• "Rollback my deployment"
|
|
2980
|
+
• "Go back to the previous version"
|
|
2981
|
+
• "Undo the last deployment"
|
|
2982
|
+
• "Revert to deployment #123"
|
|
2983
|
+
|
|
2984
|
+
Tool call:
|
|
2985
|
+
mlgym_rollback({
|
|
2986
|
+
project_name: "my-app",
|
|
2987
|
+
deployment_id: 123 // optional, uses previous if omitted
|
|
2988
|
+
})
|
|
2989
|
+
|
|
2990
|
+
👤 USER MANAGEMENT
|
|
2991
|
+
───────────────────────────────────────────────────────────────────────
|
|
2992
|
+
|
|
2993
|
+
➕ mlgym_user_create - Create new account
|
|
2994
|
+
What users say:
|
|
2995
|
+
• "Create a new account"
|
|
2996
|
+
• "Sign me up"
|
|
2997
|
+
• "Register a new user"
|
|
2998
|
+
• "I need an account"
|
|
2999
|
+
|
|
3000
|
+
Tool call:
|
|
3001
|
+
mlgym_user_create({
|
|
3002
|
+
email: "user@example.com",
|
|
3003
|
+
name: "John Doe",
|
|
3004
|
+
password: "SecurePass123!",
|
|
3005
|
+
accept_terms: true // must be true
|
|
3006
|
+
})
|
|
3007
|
+
|
|
3008
|
+
🔐 mlgym_auth_login - Login to existing account
|
|
3009
|
+
What users say:
|
|
3010
|
+
• "Login"
|
|
3011
|
+
• "Log me in"
|
|
3012
|
+
• "Authenticate with my credentials"
|
|
3013
|
+
• "Sign in to my account"
|
|
3014
|
+
|
|
3015
|
+
Tool call:
|
|
3016
|
+
mlgym_auth_login({
|
|
3017
|
+
email: "user@example.com",
|
|
3018
|
+
password: "SecurePass123!"
|
|
3019
|
+
})
|
|
3020
|
+
|
|
3021
|
+
❓ HELP
|
|
3022
|
+
───────────────────────────────────────────────────────────────────────
|
|
3023
|
+
|
|
3024
|
+
📖 mlgym_help - Display this help
|
|
3025
|
+
What users say:
|
|
3026
|
+
• "Help"
|
|
3027
|
+
• "Show me what I can do"
|
|
3028
|
+
• "What commands are available?"
|
|
3029
|
+
• "How do I use mlgym?"
|
|
3030
|
+
|
|
3031
|
+
Tool call:
|
|
3032
|
+
mlgym_help()
|
|
3033
|
+
|
|
3034
|
+
╔═══════════════════════════════════════════════════════════════════════╗
|
|
3035
|
+
║ COMMON WORKFLOW EXAMPLES ║
|
|
3036
|
+
╚═══════════════════════════════════════════════════════════════════════╝
|
|
3037
|
+
|
|
3038
|
+
1️⃣ First Time User - Complete Deployment:
|
|
3039
|
+
|
|
3040
|
+
User: "Deploy to mlgym"
|
|
3041
|
+
|
|
3042
|
+
AI executes:
|
|
3043
|
+
mlgym_deploy({
|
|
3044
|
+
project_name: "my-app",
|
|
3045
|
+
project_description: "My new app",
|
|
3046
|
+
email: "me@example.com",
|
|
3047
|
+
password: "pass123"
|
|
3048
|
+
})
|
|
3049
|
+
|
|
3050
|
+
2️⃣ Set Environment Variables:
|
|
3051
|
+
|
|
3052
|
+
User: "Set the database URL for my app"
|
|
3053
|
+
|
|
3054
|
+
AI executes:
|
|
3055
|
+
mlgym_set_env_vars({
|
|
3056
|
+
project_name: "my-app",
|
|
3057
|
+
variables: {
|
|
3058
|
+
DATABASE_URL: "postgresql://user:pass@host:5432/db"
|
|
3059
|
+
}
|
|
3060
|
+
})
|
|
3061
|
+
|
|
3062
|
+
3️⃣ Configure Health Check:
|
|
3063
|
+
|
|
3064
|
+
User: "Monitor my /health endpoint every 30 seconds"
|
|
3065
|
+
|
|
3066
|
+
AI executes:
|
|
3067
|
+
mlgym_set_health_check({
|
|
3068
|
+
project_name: "my-app",
|
|
3069
|
+
enabled: true,
|
|
3070
|
+
path: "/health",
|
|
3071
|
+
interval: 30
|
|
3072
|
+
})
|
|
3073
|
+
|
|
3074
|
+
4️⃣ Set Custom Domain:
|
|
3075
|
+
|
|
3076
|
+
User: "Point myapp.com to this deployment"
|
|
3077
|
+
|
|
3078
|
+
AI executes:
|
|
3079
|
+
mlgym_set_domain({
|
|
3080
|
+
project_name: "my-app",
|
|
3081
|
+
domain: "myapp.com"
|
|
3082
|
+
})
|
|
3083
|
+
|
|
3084
|
+
5️⃣ Manually Trigger Deployment:
|
|
3085
|
+
|
|
3086
|
+
User: "Deploy now"
|
|
3087
|
+
|
|
3088
|
+
AI executes:
|
|
3089
|
+
mlgym_deploy_manual({
|
|
3090
|
+
project_name: "my-app"
|
|
3091
|
+
})
|
|
3092
|
+
|
|
3093
|
+
6️⃣ Check Deployment Logs:
|
|
3094
|
+
|
|
3095
|
+
User: "Show me the last 5 deployments"
|
|
3096
|
+
|
|
3097
|
+
AI executes:
|
|
3098
|
+
mlgym_deploy_logs({
|
|
3099
|
+
project_name: "my-app",
|
|
3100
|
+
depth: 5
|
|
3101
|
+
})
|
|
3102
|
+
|
|
3103
|
+
7️⃣ Rollback if Something Breaks:
|
|
3104
|
+
|
|
3105
|
+
User: "Rollback to the previous version"
|
|
3106
|
+
|
|
3107
|
+
AI executes:
|
|
3108
|
+
mlgym_rollback({
|
|
3109
|
+
project_name: "my-app"
|
|
3110
|
+
})
|
|
3111
|
+
|
|
3112
|
+
╔═══════════════════════════════════════════════════════════════════════╗
|
|
3113
|
+
║ QUICK TIPS ║
|
|
3114
|
+
╚═══════════════════════════════════════════════════════════════════════╝
|
|
3115
|
+
|
|
3116
|
+
💡 Users speak naturally - AI translates to tool calls
|
|
3117
|
+
💡 Authentication is cached - login once, deploy many times
|
|
3118
|
+
💡 All tools work with project_name to identify the application
|
|
3119
|
+
💡 Most parameters are optional with sensible defaults
|
|
3120
|
+
|
|
3121
|
+
📚 Documentation: https://mlgym.info
|
|
3122
|
+
🌐 Backend: https://backend.eu.ezb.net
|
|
3123
|
+
📊 Dashboard: https://platform.mlgym.info
|
|
3124
|
+
`;
|
|
3125
|
+
|
|
3126
|
+
return {
|
|
3127
|
+
content: [{
|
|
3128
|
+
type: 'text',
|
|
3129
|
+
text: helpText
|
|
3130
|
+
}]
|
|
3131
|
+
};
|
|
3132
|
+
}
|
|
3133
|
+
|
|
2314
3134
|
// Create the MCP server
|
|
2315
3135
|
const server = new Server(
|
|
2316
3136
|
{
|
|
@@ -2429,6 +3249,231 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
2429
3249
|
}
|
|
2430
3250
|
}
|
|
2431
3251
|
},
|
|
3252
|
+
{
|
|
3253
|
+
name: 'mlgym_set_env_vars',
|
|
3254
|
+
description: 'Set environment variables for a Coolify application. Environment variables are available during both build time and runtime. Useful for setting DATABASE_URL, API keys, and other configuration.',
|
|
3255
|
+
inputSchema: {
|
|
3256
|
+
type: 'object',
|
|
3257
|
+
properties: {
|
|
3258
|
+
project_name: {
|
|
3259
|
+
type: 'string',
|
|
3260
|
+
description: 'Project/application name',
|
|
3261
|
+
pattern: '^[a-z0-9][a-z0-9-]*[a-z0-9]$',
|
|
3262
|
+
minLength: 3
|
|
3263
|
+
},
|
|
3264
|
+
variables: {
|
|
3265
|
+
type: 'object',
|
|
3266
|
+
description: 'Environment variables as key-value pairs (e.g., {"DATABASE_URL": "postgresql://...", "STRIPE_SECRET_KEY": "sk_..."})',
|
|
3267
|
+
additionalProperties: {
|
|
3268
|
+
type: 'string'
|
|
3269
|
+
}
|
|
3270
|
+
},
|
|
3271
|
+
local_path: {
|
|
3272
|
+
type: 'string',
|
|
3273
|
+
description: 'Local project directory path (optional, defaults to current directory)',
|
|
3274
|
+
default: '.'
|
|
3275
|
+
}
|
|
3276
|
+
},
|
|
3277
|
+
required: ['variables']
|
|
3278
|
+
}
|
|
3279
|
+
},
|
|
3280
|
+
{
|
|
3281
|
+
name: 'mlgym_set_health_check',
|
|
3282
|
+
description: 'Configure health check for application. Health checks monitor application availability and restart containers if unhealthy.',
|
|
3283
|
+
inputSchema: {
|
|
3284
|
+
type: 'object',
|
|
3285
|
+
properties: {
|
|
3286
|
+
project_name: {
|
|
3287
|
+
type: 'string',
|
|
3288
|
+
description: 'Project/application name',
|
|
3289
|
+
pattern: '^[a-z0-9][a-z0-9-]*[a-z0-9]$',
|
|
3290
|
+
minLength: 3
|
|
3291
|
+
},
|
|
3292
|
+
enabled: {
|
|
3293
|
+
type: 'boolean',
|
|
3294
|
+
description: 'Enable or disable health checks'
|
|
3295
|
+
},
|
|
3296
|
+
path: {
|
|
3297
|
+
type: 'string',
|
|
3298
|
+
description: 'Health check endpoint path (e.g., "/health", "/api/health")',
|
|
3299
|
+
default: '/'
|
|
3300
|
+
},
|
|
3301
|
+
port: {
|
|
3302
|
+
type: 'string',
|
|
3303
|
+
description: 'Port to check (optional, uses exposed port if not specified)'
|
|
3304
|
+
},
|
|
3305
|
+
method: {
|
|
3306
|
+
type: 'string',
|
|
3307
|
+
description: 'HTTP method for health check',
|
|
3308
|
+
enum: ['GET', 'POST', 'HEAD'],
|
|
3309
|
+
default: 'GET'
|
|
3310
|
+
},
|
|
3311
|
+
return_code: {
|
|
3312
|
+
type: 'number',
|
|
3313
|
+
description: 'Expected HTTP return code for healthy status',
|
|
3314
|
+
default: 200
|
|
3315
|
+
},
|
|
3316
|
+
interval: {
|
|
3317
|
+
type: 'number',
|
|
3318
|
+
description: 'Seconds between health checks',
|
|
3319
|
+
default: 5
|
|
3320
|
+
},
|
|
3321
|
+
timeout: {
|
|
3322
|
+
type: 'number',
|
|
3323
|
+
description: 'Seconds before health check times out',
|
|
3324
|
+
default: 5
|
|
3325
|
+
},
|
|
3326
|
+
retries: {
|
|
3327
|
+
type: 'number',
|
|
3328
|
+
description: 'Number of retries before marking unhealthy',
|
|
3329
|
+
default: 10
|
|
3330
|
+
},
|
|
3331
|
+
local_path: {
|
|
3332
|
+
type: 'string',
|
|
3333
|
+
description: 'Local project directory path (optional)',
|
|
3334
|
+
default: '.'
|
|
3335
|
+
}
|
|
3336
|
+
},
|
|
3337
|
+
required: ['enabled']
|
|
3338
|
+
}
|
|
3339
|
+
},
|
|
3340
|
+
{
|
|
3341
|
+
name: 'mlgym_set_domain',
|
|
3342
|
+
description: 'Set custom domain/FQDN for application. Updates the application URL where it will be accessible.',
|
|
3343
|
+
inputSchema: {
|
|
3344
|
+
type: 'object',
|
|
3345
|
+
properties: {
|
|
3346
|
+
project_name: {
|
|
3347
|
+
type: 'string',
|
|
3348
|
+
description: 'Project/application name',
|
|
3349
|
+
pattern: '^[a-z0-9][a-z0-9-]*[a-z0-9]$',
|
|
3350
|
+
minLength: 3
|
|
3351
|
+
},
|
|
3352
|
+
domain: {
|
|
3353
|
+
type: 'string',
|
|
3354
|
+
description: 'Domain/FQDN (e.g., "myapp.example.com" or "https://myapp.example.com")'
|
|
3355
|
+
},
|
|
3356
|
+
local_path: {
|
|
3357
|
+
type: 'string',
|
|
3358
|
+
description: 'Local project directory path (optional)',
|
|
3359
|
+
default: '.'
|
|
3360
|
+
}
|
|
3361
|
+
},
|
|
3362
|
+
required: ['domain']
|
|
3363
|
+
}
|
|
3364
|
+
},
|
|
3365
|
+
{
|
|
3366
|
+
name: 'mlgym_set_deployment_commands',
|
|
3367
|
+
description: 'Configure pre-deployment and post-deployment commands. Pre-deployment runs before build, post-deployment runs after deployment.',
|
|
3368
|
+
inputSchema: {
|
|
3369
|
+
type: 'object',
|
|
3370
|
+
properties: {
|
|
3371
|
+
project_name: {
|
|
3372
|
+
type: 'string',
|
|
3373
|
+
description: 'Project/application name',
|
|
3374
|
+
pattern: '^[a-z0-9][a-z0-9-]*[a-z0-9]$',
|
|
3375
|
+
minLength: 3
|
|
3376
|
+
},
|
|
3377
|
+
pre_command: {
|
|
3378
|
+
type: 'string',
|
|
3379
|
+
description: 'Command to run before deployment (e.g., "npm run migrate")'
|
|
3380
|
+
},
|
|
3381
|
+
pre_command_container: {
|
|
3382
|
+
type: 'string',
|
|
3383
|
+
description: 'Container to run pre-command in (for docker-compose apps)'
|
|
3384
|
+
},
|
|
3385
|
+
post_command: {
|
|
3386
|
+
type: 'string',
|
|
3387
|
+
description: 'Command to run after deployment (e.g., "npm run seed")'
|
|
3388
|
+
},
|
|
3389
|
+
post_command_container: {
|
|
3390
|
+
type: 'string',
|
|
3391
|
+
description: 'Container to run post-command in (for docker-compose apps)'
|
|
3392
|
+
},
|
|
3393
|
+
local_path: {
|
|
3394
|
+
type: 'string',
|
|
3395
|
+
description: 'Local project directory path (optional)',
|
|
3396
|
+
default: '.'
|
|
3397
|
+
}
|
|
3398
|
+
}
|
|
3399
|
+
}
|
|
3400
|
+
},
|
|
3401
|
+
{
|
|
3402
|
+
name: 'mlgym_deploy_manual',
|
|
3403
|
+
description: 'Manually trigger a new deployment. Forces rebuild and deploys current code from GitLab repository.',
|
|
3404
|
+
inputSchema: {
|
|
3405
|
+
type: 'object',
|
|
3406
|
+
properties: {
|
|
3407
|
+
project_name: {
|
|
3408
|
+
type: 'string',
|
|
3409
|
+
description: 'Project/application name',
|
|
3410
|
+
pattern: '^[a-z0-9][a-z0-9-]*[a-z0-9]$',
|
|
3411
|
+
minLength: 3
|
|
3412
|
+
},
|
|
3413
|
+
local_path: {
|
|
3414
|
+
type: 'string',
|
|
3415
|
+
description: 'Local project directory path (optional)',
|
|
3416
|
+
default: '.'
|
|
3417
|
+
}
|
|
3418
|
+
}
|
|
3419
|
+
}
|
|
3420
|
+
},
|
|
3421
|
+
{
|
|
3422
|
+
name: 'mlgym_set_options',
|
|
3423
|
+
description: 'Configure application options like build cache. More options may be added in future versions.',
|
|
3424
|
+
inputSchema: {
|
|
3425
|
+
type: 'object',
|
|
3426
|
+
properties: {
|
|
3427
|
+
project_name: {
|
|
3428
|
+
type: 'string',
|
|
3429
|
+
description: 'Project/application name',
|
|
3430
|
+
pattern: '^[a-z0-9][a-z0-9-]*[a-z0-9]$',
|
|
3431
|
+
minLength: 3
|
|
3432
|
+
},
|
|
3433
|
+
disable_build_cache: {
|
|
3434
|
+
type: 'boolean',
|
|
3435
|
+
description: 'Disable Docker build cache (forces clean builds)'
|
|
3436
|
+
},
|
|
3437
|
+
local_path: {
|
|
3438
|
+
type: 'string',
|
|
3439
|
+
description: 'Local project directory path (optional)',
|
|
3440
|
+
default: '.'
|
|
3441
|
+
}
|
|
3442
|
+
}
|
|
3443
|
+
}
|
|
3444
|
+
},
|
|
3445
|
+
{
|
|
3446
|
+
name: 'mlgym_rollback',
|
|
3447
|
+
description: 'Rollback to a previous deployment. If deployment_id not specified, rolls back to the last successful deployment.',
|
|
3448
|
+
inputSchema: {
|
|
3449
|
+
type: 'object',
|
|
3450
|
+
properties: {
|
|
3451
|
+
project_name: {
|
|
3452
|
+
type: 'string',
|
|
3453
|
+
description: 'Project/application name',
|
|
3454
|
+
pattern: '^[a-z0-9][a-z0-9-]*[a-z0-9]$',
|
|
3455
|
+
minLength: 3
|
|
3456
|
+
},
|
|
3457
|
+
deployment_id: {
|
|
3458
|
+
type: 'number',
|
|
3459
|
+
description: 'Specific deployment ID to rollback to (optional, uses previous if not specified)'
|
|
3460
|
+
},
|
|
3461
|
+
local_path: {
|
|
3462
|
+
type: 'string',
|
|
3463
|
+
description: 'Local project directory path (optional)',
|
|
3464
|
+
default: '.'
|
|
3465
|
+
}
|
|
3466
|
+
}
|
|
3467
|
+
}
|
|
3468
|
+
},
|
|
3469
|
+
{
|
|
3470
|
+
name: 'mlgym_help',
|
|
3471
|
+
description: 'Display all available MLGym tools with descriptions and usage examples. Use this to discover what operations you can perform.',
|
|
3472
|
+
inputSchema: {
|
|
3473
|
+
type: 'object',
|
|
3474
|
+
properties: {}
|
|
3475
|
+
}
|
|
3476
|
+
},
|
|
2432
3477
|
// ========== LEGACY TOOLS (Re-enabled for specific use cases) ==========
|
|
2433
3478
|
{
|
|
2434
3479
|
name: 'mlgym_user_create',
|
|
@@ -2521,8 +3566,48 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
2521
3566
|
result = await loginUser(args);
|
|
2522
3567
|
break;
|
|
2523
3568
|
|
|
3569
|
+
case 'mlgym_set_env_vars':
|
|
3570
|
+
log.info(`Setting environment variables...`);
|
|
3571
|
+
result = await setEnvironmentVariables(args);
|
|
3572
|
+
break;
|
|
3573
|
+
|
|
3574
|
+
case 'mlgym_set_health_check':
|
|
3575
|
+
log.info(`Configuring health check...`);
|
|
3576
|
+
result = await setHealthCheck(args);
|
|
3577
|
+
break;
|
|
3578
|
+
|
|
3579
|
+
case 'mlgym_set_domain':
|
|
3580
|
+
log.info(`Setting custom domain...`);
|
|
3581
|
+
result = await setDomain(args);
|
|
3582
|
+
break;
|
|
3583
|
+
|
|
3584
|
+
case 'mlgym_set_deployment_commands':
|
|
3585
|
+
log.info(`Setting deployment commands...`);
|
|
3586
|
+
result = await setDeploymentCommands(args);
|
|
3587
|
+
break;
|
|
3588
|
+
|
|
3589
|
+
case 'mlgym_deploy_manual':
|
|
3590
|
+
log.info(`Triggering manual deployment...`);
|
|
3591
|
+
result = await manualDeploy(args);
|
|
3592
|
+
break;
|
|
3593
|
+
|
|
3594
|
+
case 'mlgym_set_options':
|
|
3595
|
+
log.info(`Setting application options...`);
|
|
3596
|
+
result = await setOptions(args);
|
|
3597
|
+
break;
|
|
3598
|
+
|
|
3599
|
+
case 'mlgym_rollback':
|
|
3600
|
+
log.info(`Rolling back deployment...`);
|
|
3601
|
+
result = await rollback(args);
|
|
3602
|
+
break;
|
|
3603
|
+
|
|
3604
|
+
case 'mlgym_help':
|
|
3605
|
+
log.info(`Showing help...`);
|
|
3606
|
+
result = await showHelp(args);
|
|
3607
|
+
break;
|
|
3608
|
+
|
|
2524
3609
|
default:
|
|
2525
|
-
throw new Error(`Unknown tool: ${name}. Available tools: mlgym_deploy, mlgym_status, mlgym_deploy_logs, mlgym_user_create, mlgym_auth_login`);
|
|
3610
|
+
throw new Error(`Unknown tool: ${name}. Available tools: mlgym_deploy, mlgym_status, mlgym_deploy_logs, mlgym_user_create, mlgym_auth_login, mlgym_set_env_vars, mlgym_set_health_check, mlgym_set_domain, mlgym_set_deployment_commands, mlgym_deploy_manual, mlgym_set_options, mlgym_rollback`);
|
|
2526
3611
|
}
|
|
2527
3612
|
|
|
2528
3613
|
const duration = Date.now() - startTime;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mlgym-deploy",
|
|
3
|
-
"version": "3.
|
|
4
|
-
"description": "MCP server for MLGym -
|
|
3
|
+
"version": "3.3.1",
|
|
4
|
+
"description": "MCP server for MLGym - Complete deployment management: deploy, configure, monitor, and rollback applications",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"bin": {
|
|
@@ -47,6 +47,38 @@
|
|
|
47
47
|
{
|
|
48
48
|
"name": "mlgym_auth_login",
|
|
49
49
|
"description": "Login with existing credentials"
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
"name": "mlgym_set_env_vars",
|
|
53
|
+
"description": "Set environment variables for an application (DATABASE_URL, API keys, etc.)"
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
"name": "mlgym_set_health_check",
|
|
57
|
+
"description": "Configure health checks to monitor application availability"
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
"name": "mlgym_set_domain",
|
|
61
|
+
"description": "Set custom domain/FQDN for application"
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
"name": "mlgym_set_deployment_commands",
|
|
65
|
+
"description": "Configure pre-deployment and post-deployment commands"
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
"name": "mlgym_deploy_manual",
|
|
69
|
+
"description": "Manually trigger a new deployment"
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
"name": "mlgym_set_options",
|
|
73
|
+
"description": "Configure application options (build cache, etc.)"
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
"name": "mlgym_rollback",
|
|
77
|
+
"description": "Rollback to previous deployment"
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
"name": "mlgym_help",
|
|
81
|
+
"description": "Display all available tools with descriptions and usage examples"
|
|
50
82
|
}
|
|
51
83
|
]
|
|
52
84
|
}
|