mlgym-deploy 3.2.3 → 3.3.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.
Files changed (2) hide show
  1. package/index.js +963 -2
  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.2.2'; // Added automatic validation and fixing for docker-compose and Dockerfile
20
+ const CURRENT_VERSION = '3.3.0'; // Added 6 new tools: health check, domain, deployment commands, manual deploy, options, rollback
21
21
  const PACKAGE_NAME = 'mlgym-deploy';
22
22
 
23
23
  // Debug logging configuration - ENABLED BY DEFAULT
@@ -2311,6 +2311,702 @@ 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
+ 📦 DEPLOYMENT TOOLS
2837
+ ───────────────────────────────────────────────────────────────────────
2838
+
2839
+ 🚀 mlgym_deploy
2840
+ Complete deployment workflow - auth, analyze, create repo, and deploy in one call.
2841
+
2842
+ Usage:
2843
+ mlgym_deploy({
2844
+ project_name: "my-app",
2845
+ project_description: "My application",
2846
+ email: "user@example.com", // optional if already authenticated
2847
+ password: "password123" // optional if already authenticated
2848
+ })
2849
+
2850
+ 📊 mlgym_status
2851
+ Check authentication status and project configuration.
2852
+
2853
+ Usage:
2854
+ mlgym_status({local_path: "."})
2855
+
2856
+ 📝 mlgym_deploy_logs
2857
+ View deployment history and build logs.
2858
+
2859
+ Usage:
2860
+ mlgym_deploy_logs({
2861
+ project_name: "my-app",
2862
+ depth: 3 // number of deployments to retrieve
2863
+ })
2864
+
2865
+ ⚙️ CONFIGURATION TOOLS
2866
+ ───────────────────────────────────────────────────────────────────────
2867
+
2868
+ 🔧 mlgym_set_env_vars
2869
+ Set environment variables (DATABASE_URL, API keys, etc.)
2870
+
2871
+ Usage:
2872
+ mlgym_set_env_vars({
2873
+ project_name: "my-app",
2874
+ variables: {
2875
+ DATABASE_URL: "postgresql://...",
2876
+ API_KEY: "secret123"
2877
+ }
2878
+ })
2879
+
2880
+ ❤️ mlgym_set_health_check
2881
+ Configure health checks to monitor application availability.
2882
+
2883
+ Usage:
2884
+ mlgym_set_health_check({
2885
+ project_name: "my-app",
2886
+ enabled: true,
2887
+ path: "/health",
2888
+ method: "GET",
2889
+ return_code: 200,
2890
+ interval: 5
2891
+ })
2892
+
2893
+ 🌐 mlgym_set_domain
2894
+ Set custom domain/FQDN for your application.
2895
+
2896
+ Usage:
2897
+ mlgym_set_domain({
2898
+ project_name: "my-app",
2899
+ domain: "myapp.example.com"
2900
+ })
2901
+
2902
+ 📜 mlgym_set_deployment_commands
2903
+ Configure pre-deployment and post-deployment commands.
2904
+
2905
+ Usage:
2906
+ mlgym_set_deployment_commands({
2907
+ project_name: "my-app",
2908
+ pre_command: "npm run migrate",
2909
+ post_command: "npm run seed"
2910
+ })
2911
+
2912
+ ⚙️ mlgym_set_options
2913
+ Configure application options (build cache, etc.)
2914
+
2915
+ Usage:
2916
+ mlgym_set_options({
2917
+ project_name: "my-app",
2918
+ disable_build_cache: true
2919
+ })
2920
+
2921
+ 🎬 DEPLOYMENT MANAGEMENT
2922
+ ───────────────────────────────────────────────────────────────────────
2923
+
2924
+ ▶️ mlgym_deploy_manual
2925
+ Manually trigger a new deployment.
2926
+
2927
+ Usage:
2928
+ mlgym_deploy_manual({project_name: "my-app"})
2929
+
2930
+ ⏪ mlgym_rollback
2931
+ Rollback to previous deployment.
2932
+
2933
+ Usage:
2934
+ mlgym_rollback({
2935
+ project_name: "my-app",
2936
+ deployment_id: 123 // optional, uses previous if not specified
2937
+ })
2938
+
2939
+ 👤 USER MANAGEMENT
2940
+ ───────────────────────────────────────────────────────────────────────
2941
+
2942
+ ➕ mlgym_user_create
2943
+ Create a new MLGym user account.
2944
+
2945
+ Usage:
2946
+ mlgym_user_create({
2947
+ email: "user@example.com",
2948
+ name: "John Doe",
2949
+ password: "SecurePass123!",
2950
+ accept_terms: true
2951
+ })
2952
+
2953
+ 🔐 mlgym_auth_login
2954
+ Login with existing credentials.
2955
+
2956
+ Usage:
2957
+ mlgym_auth_login({
2958
+ email: "user@example.com",
2959
+ password: "SecurePass123!"
2960
+ })
2961
+
2962
+ ❓ HELP
2963
+ ───────────────────────────────────────────────────────────────────────
2964
+
2965
+ 📖 mlgym_help
2966
+ Display this help message.
2967
+
2968
+ Usage:
2969
+ mlgym_help()
2970
+
2971
+ ╔═══════════════════════════════════════════════════════════════════════╗
2972
+ ║ WORKFLOW EXAMPLES ║
2973
+ ╚═══════════════════════════════════════════════════════════════════════╝
2974
+
2975
+ 1️⃣ Complete New Deployment:
2976
+ mlgym_deploy({
2977
+ project_name: "my-app",
2978
+ project_description: "My new app",
2979
+ email: "me@example.com",
2980
+ password: "pass123"
2981
+ })
2982
+
2983
+ 2️⃣ Set Environment Variables:
2984
+ mlgym_set_env_vars({
2985
+ project_name: "my-app",
2986
+ variables: {DATABASE_URL: "..."}
2987
+ })
2988
+
2989
+ 3️⃣ Configure & Deploy:
2990
+ mlgym_set_health_check({project_name: "my-app", enabled: true})
2991
+ mlgym_set_domain({project_name: "my-app", domain: "app.example.com"})
2992
+ mlgym_deploy_manual({project_name: "my-app"})
2993
+
2994
+ 4️⃣ Monitor & Rollback if needed:
2995
+ mlgym_deploy_logs({project_name: "my-app", depth: 5})
2996
+ mlgym_rollback({project_name: "my-app"})
2997
+
2998
+ 📚 More info: https://backend.eu.ezb.net
2999
+ 🐛 Issues: Report via user dashboard
3000
+ `;
3001
+
3002
+ return {
3003
+ content: [{
3004
+ type: 'text',
3005
+ text: helpText
3006
+ }]
3007
+ };
3008
+ }
3009
+
2314
3010
  // Create the MCP server
2315
3011
  const server = new Server(
2316
3012
  {
@@ -2429,6 +3125,231 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
2429
3125
  }
2430
3126
  }
2431
3127
  },
3128
+ {
3129
+ name: 'mlgym_set_env_vars',
3130
+ 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.',
3131
+ inputSchema: {
3132
+ type: 'object',
3133
+ properties: {
3134
+ project_name: {
3135
+ type: 'string',
3136
+ description: 'Project/application name',
3137
+ pattern: '^[a-z0-9][a-z0-9-]*[a-z0-9]$',
3138
+ minLength: 3
3139
+ },
3140
+ variables: {
3141
+ type: 'object',
3142
+ description: 'Environment variables as key-value pairs (e.g., {"DATABASE_URL": "postgresql://...", "STRIPE_SECRET_KEY": "sk_..."})',
3143
+ additionalProperties: {
3144
+ type: 'string'
3145
+ }
3146
+ },
3147
+ local_path: {
3148
+ type: 'string',
3149
+ description: 'Local project directory path (optional, defaults to current directory)',
3150
+ default: '.'
3151
+ }
3152
+ },
3153
+ required: ['variables']
3154
+ }
3155
+ },
3156
+ {
3157
+ name: 'mlgym_set_health_check',
3158
+ description: 'Configure health check for application. Health checks monitor application availability and restart containers if unhealthy.',
3159
+ inputSchema: {
3160
+ type: 'object',
3161
+ properties: {
3162
+ project_name: {
3163
+ type: 'string',
3164
+ description: 'Project/application name',
3165
+ pattern: '^[a-z0-9][a-z0-9-]*[a-z0-9]$',
3166
+ minLength: 3
3167
+ },
3168
+ enabled: {
3169
+ type: 'boolean',
3170
+ description: 'Enable or disable health checks'
3171
+ },
3172
+ path: {
3173
+ type: 'string',
3174
+ description: 'Health check endpoint path (e.g., "/health", "/api/health")',
3175
+ default: '/'
3176
+ },
3177
+ port: {
3178
+ type: 'string',
3179
+ description: 'Port to check (optional, uses exposed port if not specified)'
3180
+ },
3181
+ method: {
3182
+ type: 'string',
3183
+ description: 'HTTP method for health check',
3184
+ enum: ['GET', 'POST', 'HEAD'],
3185
+ default: 'GET'
3186
+ },
3187
+ return_code: {
3188
+ type: 'number',
3189
+ description: 'Expected HTTP return code for healthy status',
3190
+ default: 200
3191
+ },
3192
+ interval: {
3193
+ type: 'number',
3194
+ description: 'Seconds between health checks',
3195
+ default: 5
3196
+ },
3197
+ timeout: {
3198
+ type: 'number',
3199
+ description: 'Seconds before health check times out',
3200
+ default: 5
3201
+ },
3202
+ retries: {
3203
+ type: 'number',
3204
+ description: 'Number of retries before marking unhealthy',
3205
+ default: 10
3206
+ },
3207
+ local_path: {
3208
+ type: 'string',
3209
+ description: 'Local project directory path (optional)',
3210
+ default: '.'
3211
+ }
3212
+ },
3213
+ required: ['enabled']
3214
+ }
3215
+ },
3216
+ {
3217
+ name: 'mlgym_set_domain',
3218
+ description: 'Set custom domain/FQDN for application. Updates the application URL where it will be accessible.',
3219
+ inputSchema: {
3220
+ type: 'object',
3221
+ properties: {
3222
+ project_name: {
3223
+ type: 'string',
3224
+ description: 'Project/application name',
3225
+ pattern: '^[a-z0-9][a-z0-9-]*[a-z0-9]$',
3226
+ minLength: 3
3227
+ },
3228
+ domain: {
3229
+ type: 'string',
3230
+ description: 'Domain/FQDN (e.g., "myapp.example.com" or "https://myapp.example.com")'
3231
+ },
3232
+ local_path: {
3233
+ type: 'string',
3234
+ description: 'Local project directory path (optional)',
3235
+ default: '.'
3236
+ }
3237
+ },
3238
+ required: ['domain']
3239
+ }
3240
+ },
3241
+ {
3242
+ name: 'mlgym_set_deployment_commands',
3243
+ description: 'Configure pre-deployment and post-deployment commands. Pre-deployment runs before build, post-deployment runs after deployment.',
3244
+ inputSchema: {
3245
+ type: 'object',
3246
+ properties: {
3247
+ project_name: {
3248
+ type: 'string',
3249
+ description: 'Project/application name',
3250
+ pattern: '^[a-z0-9][a-z0-9-]*[a-z0-9]$',
3251
+ minLength: 3
3252
+ },
3253
+ pre_command: {
3254
+ type: 'string',
3255
+ description: 'Command to run before deployment (e.g., "npm run migrate")'
3256
+ },
3257
+ pre_command_container: {
3258
+ type: 'string',
3259
+ description: 'Container to run pre-command in (for docker-compose apps)'
3260
+ },
3261
+ post_command: {
3262
+ type: 'string',
3263
+ description: 'Command to run after deployment (e.g., "npm run seed")'
3264
+ },
3265
+ post_command_container: {
3266
+ type: 'string',
3267
+ description: 'Container to run post-command in (for docker-compose apps)'
3268
+ },
3269
+ local_path: {
3270
+ type: 'string',
3271
+ description: 'Local project directory path (optional)',
3272
+ default: '.'
3273
+ }
3274
+ }
3275
+ }
3276
+ },
3277
+ {
3278
+ name: 'mlgym_deploy_manual',
3279
+ description: 'Manually trigger a new deployment. Forces rebuild and deploys current code from GitLab repository.',
3280
+ inputSchema: {
3281
+ type: 'object',
3282
+ properties: {
3283
+ project_name: {
3284
+ type: 'string',
3285
+ description: 'Project/application name',
3286
+ pattern: '^[a-z0-9][a-z0-9-]*[a-z0-9]$',
3287
+ minLength: 3
3288
+ },
3289
+ local_path: {
3290
+ type: 'string',
3291
+ description: 'Local project directory path (optional)',
3292
+ default: '.'
3293
+ }
3294
+ }
3295
+ }
3296
+ },
3297
+ {
3298
+ name: 'mlgym_set_options',
3299
+ description: 'Configure application options like build cache. More options may be added in future versions.',
3300
+ inputSchema: {
3301
+ type: 'object',
3302
+ properties: {
3303
+ project_name: {
3304
+ type: 'string',
3305
+ description: 'Project/application name',
3306
+ pattern: '^[a-z0-9][a-z0-9-]*[a-z0-9]$',
3307
+ minLength: 3
3308
+ },
3309
+ disable_build_cache: {
3310
+ type: 'boolean',
3311
+ description: 'Disable Docker build cache (forces clean builds)'
3312
+ },
3313
+ local_path: {
3314
+ type: 'string',
3315
+ description: 'Local project directory path (optional)',
3316
+ default: '.'
3317
+ }
3318
+ }
3319
+ }
3320
+ },
3321
+ {
3322
+ name: 'mlgym_rollback',
3323
+ description: 'Rollback to a previous deployment. If deployment_id not specified, rolls back to the last successful deployment.',
3324
+ inputSchema: {
3325
+ type: 'object',
3326
+ properties: {
3327
+ project_name: {
3328
+ type: 'string',
3329
+ description: 'Project/application name',
3330
+ pattern: '^[a-z0-9][a-z0-9-]*[a-z0-9]$',
3331
+ minLength: 3
3332
+ },
3333
+ deployment_id: {
3334
+ type: 'number',
3335
+ description: 'Specific deployment ID to rollback to (optional, uses previous if not specified)'
3336
+ },
3337
+ local_path: {
3338
+ type: 'string',
3339
+ description: 'Local project directory path (optional)',
3340
+ default: '.'
3341
+ }
3342
+ }
3343
+ }
3344
+ },
3345
+ {
3346
+ name: 'mlgym_help',
3347
+ description: 'Display all available MLGym tools with descriptions and usage examples. Use this to discover what operations you can perform.',
3348
+ inputSchema: {
3349
+ type: 'object',
3350
+ properties: {}
3351
+ }
3352
+ },
2432
3353
  // ========== LEGACY TOOLS (Re-enabled for specific use cases) ==========
2433
3354
  {
2434
3355
  name: 'mlgym_user_create',
@@ -2521,8 +3442,48 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
2521
3442
  result = await loginUser(args);
2522
3443
  break;
2523
3444
 
3445
+ case 'mlgym_set_env_vars':
3446
+ log.info(`Setting environment variables...`);
3447
+ result = await setEnvironmentVariables(args);
3448
+ break;
3449
+
3450
+ case 'mlgym_set_health_check':
3451
+ log.info(`Configuring health check...`);
3452
+ result = await setHealthCheck(args);
3453
+ break;
3454
+
3455
+ case 'mlgym_set_domain':
3456
+ log.info(`Setting custom domain...`);
3457
+ result = await setDomain(args);
3458
+ break;
3459
+
3460
+ case 'mlgym_set_deployment_commands':
3461
+ log.info(`Setting deployment commands...`);
3462
+ result = await setDeploymentCommands(args);
3463
+ break;
3464
+
3465
+ case 'mlgym_deploy_manual':
3466
+ log.info(`Triggering manual deployment...`);
3467
+ result = await manualDeploy(args);
3468
+ break;
3469
+
3470
+ case 'mlgym_set_options':
3471
+ log.info(`Setting application options...`);
3472
+ result = await setOptions(args);
3473
+ break;
3474
+
3475
+ case 'mlgym_rollback':
3476
+ log.info(`Rolling back deployment...`);
3477
+ result = await rollback(args);
3478
+ break;
3479
+
3480
+ case 'mlgym_help':
3481
+ log.info(`Showing help...`);
3482
+ result = await showHelp(args);
3483
+ break;
3484
+
2524
3485
  default:
2525
- throw new Error(`Unknown tool: ${name}. Available tools: mlgym_deploy, mlgym_status, mlgym_deploy_logs, mlgym_user_create, mlgym_auth_login`);
3486
+ 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
3487
  }
2527
3488
 
2528
3489
  const duration = Date.now() - startTime;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "mlgym-deploy",
3
- "version": "3.2.3",
4
- "description": "MCP server for MLGym - Auto-validates and fixes docker-compose/Dockerfile for Coolify",
3
+ "version": "3.3.0",
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
  }