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.
Files changed (2) hide show
  1. package/index.js +1087 -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.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.2.3",
4
- "description": "MCP server for MLGym - Auto-validates and fixes docker-compose/Dockerfile for Coolify",
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
  }