mlgym-deploy 3.3.2 → 3.3.6

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 +43 -32
  2. package/package.json +5 -4
package/index.js CHANGED
@@ -18,7 +18,7 @@ import crypto from 'crypto';
18
18
  const execAsync = promisify(exec);
19
19
 
20
20
  // Current version of this MCP server - INCREMENT FOR WORKFLOW FIXES
21
- const CURRENT_VERSION = '3.3.2'; // Fixed fs.existsSync bug by importing fsSync for synchronous file operations
21
+ const CURRENT_VERSION = '3.3.6'; // Added mlgym_deploy_logs to package.json tools list for proper MCP registration
22
22
  const PACKAGE_NAME = 'mlgym-deploy';
23
23
 
24
24
  // Debug logging configuration - ENABLED BY DEFAULT
@@ -986,8 +986,8 @@ async function detectDeploymentStrategy(projectPath) {
986
986
 
987
987
  // Case 2: Only docker-compose
988
988
  if (!hasDockerfile && hasCompose) {
989
- log.info('MCP >>> [detectDeploymentStrategy] Strategy: docker-compose (compose only)');
990
- return { type: 'docker-compose', reason: 'docker-compose.yml only' };
989
+ log.info('MCP >>> [detectDeploymentStrategy] Strategy: dockercompose (compose only)');
990
+ return { type: 'dockercompose', reason: 'docker-compose.yml only' };
991
991
  }
992
992
 
993
993
  // Case 3: Both exist - analyze docker-compose to decide
@@ -1008,11 +1008,11 @@ async function detectDeploymentStrategy(projectPath) {
1008
1008
 
1009
1009
  // COMPLEX: Multiple services (web + database, etc.)
1010
1010
  // → MUST use docker-compose for orchestration
1011
- log.info('MCP >>> [detectDeploymentStrategy] Strategy: docker-compose (multi-service)');
1012
- return { type: 'docker-compose', reason: 'Application requires multiple services (web + database/cache/etc)' };
1011
+ log.info('MCP >>> [detectDeploymentStrategy] Strategy: dockercompose (multi-service)');
1012
+ return { type: 'dockercompose', reason: 'Application requires multiple services (web + database/cache/etc)' };
1013
1013
  } catch (err) {
1014
- log.error('MCP >>> [detectDeploymentStrategy] Analysis failed, defaulting to docker-compose:', err.message);
1015
- return { type: 'docker-compose', reason: 'Multiple deployment files found' };
1014
+ log.error('MCP >>> [detectDeploymentStrategy] Analysis failed, defaulting to dockercompose:', err.message);
1015
+ return { type: 'dockercompose', reason: 'Multiple deployment files found' };
1016
1016
  }
1017
1017
  }
1018
1018
 
@@ -1296,8 +1296,8 @@ async function initProject(args) {
1296
1296
  projectData.hostname = hostname;
1297
1297
  projectData.local_path = local_path;
1298
1298
 
1299
- // Read docker-compose content if using docker-compose strategy (v3.2.1+)
1300
- if (strategy.type === 'docker-compose') {
1299
+ // Read docker-compose content if using dockercompose strategy (v3.2.1+)
1300
+ if (strategy.type === 'dockercompose') {
1301
1301
  const composeFiles = ['docker-compose.yml', 'docker-compose.yaml'];
1302
1302
  let composeContent = null;
1303
1303
 
@@ -1364,6 +1364,10 @@ async function initProject(args) {
1364
1364
  console.error('⚠️ Warning: Failed to add administrator as member:', memberErr.message);
1365
1365
  }
1366
1366
 
1367
+ // Wait for GitLab project to be fully created and accessible
1368
+ console.error('Waiting for GitLab project creation to complete (20 seconds)...');
1369
+ await new Promise(resolve => setTimeout(resolve, 20000));
1370
+
1367
1371
  // Initialize local git repository if needed
1368
1372
  const absolutePath = path.resolve(local_path);
1369
1373
  try {
@@ -1402,9 +1406,9 @@ async function initProject(args) {
1402
1406
  console.error('Note: ssh-agent not available, relying on SSH config');
1403
1407
  }
1404
1408
 
1405
- // Wait for GitLab SSH key propagation (10 seconds)
1406
- console.error('Waiting for GitLab SSH key propagation (10 seconds)...');
1407
- await new Promise(resolve => setTimeout(resolve, 10000));
1409
+ // Wait for GitLab SSH key propagation (20 seconds)
1410
+ console.error('Waiting for GitLab SSH key propagation (30 seconds)...');
1411
+ await new Promise(resolve => setTimeout(resolve, 30000));
1408
1412
 
1409
1413
  // Test SSH connectivity
1410
1414
  console.error('Testing SSH connection to GitLab...');
@@ -1473,7 +1477,11 @@ async function initProject(args) {
1473
1477
  log.debug('MCP >>> [initProject] Git remote URL:', gitUrl);
1474
1478
  gitSteps.push('Pushing to GitLab to trigger deployment');
1475
1479
  console.error('Executing: git push -u mlgym main');
1476
- await execAsync('git push -u mlgym main', { cwd: absolutePath });
1480
+ const authInfo = await loadAuth();
1481
+ const sanitizedEmail = authInfo.email.replace('@', '_at_').replace(/[^a-zA-Z0-9_-]/g, '_');
1482
+ const sshKeyPath = path.join(os.homedir(), '.ssh', `mlgym_${sanitizedEmail}`);
1483
+ const gitSshCmd = `ssh -i "${sshKeyPath}"`;
1484
+ await execAsync('git push -u mlgym main', { cwd: absolutePath, env: { ...process.env, GIT_SSH_COMMAND: gitSshCmd } });
1477
1485
  gitSteps.push('✅ Successfully pushed to GitLab');
1478
1486
  pushSucceeded = true;
1479
1487
  log.success('MCP >>> [initProject] ✅ Git push completed successfully!');
@@ -1497,7 +1505,7 @@ async function initProject(args) {
1497
1505
  for (let i = 0; i < 12; i++) {
1498
1506
  await new Promise(resolve => setTimeout(resolve, 5000)); // Wait 5 seconds
1499
1507
 
1500
- const statusResult = await apiRequest('GET', `/api/v1/projects/${project.id}/deployment`, null, true);
1508
+ const statusResult = await apiRequest('GET', `/api/v1/projects/${project.id}/deployments`, null, true);
1501
1509
  if (statusResult.success && statusResult.data) {
1502
1510
  const status = statusResult.data.status;
1503
1511
  console.error(`Deployment status: ${status}`);
@@ -2012,7 +2020,7 @@ async function deployProject(args) {
2012
2020
  log.info('MCP >>> STEP 4.5: Validating deployment configuration...');
2013
2021
  const strategy = detectDeploymentStrategy(local_path);
2014
2022
 
2015
- if (strategy.type === 'docker-compose') {
2023
+ if (strategy.type === 'dockercompose') {
2016
2024
  const composePath = path.join(local_path, 'docker-compose.yml');
2017
2025
  const composePathYAML = path.join(local_path, 'docker-compose.yaml');
2018
2026
  const actualPath = fsSync.existsSync(composePath) ? composePath : composePathYAML;
@@ -2224,27 +2232,30 @@ async function getDeploymentLogs(args) {
2224
2232
  }
2225
2233
 
2226
2234
  log.info(`MCP >>> [getDeploymentLogs] Detected project name: ${projectName}`);
2235
+ // Get Coolify app UUID directly from User Agent by name
2236
+ const userAgentURL = 'http://coolify.eu.ezb.net:9000';
2237
+ const appLookupEndpoint = `${userAgentURL}/applications/by-name/${projectName}`;
2238
+
2239
+ log.info(`MCP >>> [getDeploymentLogs] Looking up application by name`);
2227
2240
 
2228
- // Get project details from backend to get Coolify app UUID
2229
- const projectsResult = await apiRequest('GET', '/api/v1/projects', null, true);
2230
- if (!projectsResult.success) {
2231
- throw new Error(`Failed to get projects: ${projectsResult.error}`);
2232
- }
2233
-
2234
- const project = projectsResult.data.projects?.find(p => p.name === projectName);
2235
- if (!project) {
2236
- throw new Error(`Project "${projectName}" not found in your account`);
2237
- }
2238
-
2239
- if (!project.coolify_app_uuid) {
2240
- throw new Error(`Project "${projectName}" does not have deployment enabled`);
2241
+ let appUUID;
2242
+ try {
2243
+ const appResponse = await axios.get(appLookupEndpoint, {
2244
+ headers: {
2245
+ 'Authorization': 'Bearer 1|Jkztb5qPptwRKgtocfbT2TLjp8WGJG1SkPE4DzLt4c5d600f',
2246
+ 'Content-Type': 'application/json'
2247
+ }
2248
+ });
2249
+ appUUID = appResponse.data.uuid;
2250
+ log.info(`MCP >>> [getDeploymentLogs] Found Coolify app UUID: ${appUUID}`);
2251
+ } catch (appError) {
2252
+ if (appError.response?.status === 404) {
2253
+ throw new Error(`Project "${projectName}" not found in Coolify. Make sure deployment is enabled.`);
2254
+ }
2255
+ throw new Error(`Failed to lookup application: ${appError.message}`);
2241
2256
  }
2242
2257
 
2243
- const appUUID = project.coolify_app_uuid;
2244
- log.info(`MCP >>> [getDeploymentLogs] Found Coolify app UUID: ${appUUID}`);
2245
-
2246
2258
  // Call User Agent API to get deployment logs
2247
- const userAgentURL = 'http://coolify.eu.ezb.net:9000';
2248
2259
  const endpoint = `${userAgentURL}/applications/${appUUID}/deployment-logs?depth=${depth}`;
2249
2260
 
2250
2261
  log.info(`MCP >>> [getDeploymentLogs] Calling User Agent: ${endpoint}`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mlgym-deploy",
3
- "version": "3.3.2",
3
+ "version": "3.3.6",
4
4
  "description": "MCP server for MLGym - Complete deployment management: deploy, configure, monitor, and rollback applications",
5
5
  "main": "index.js",
6
6
  "type": "module",
@@ -52,6 +52,10 @@
52
52
  "name": "mlgym_set_env_vars",
53
53
  "description": "Set environment variables for an application (DATABASE_URL, API keys, etc.)"
54
54
  },
55
+ {
56
+ "name": "mlgym_deploy_logs",
57
+ "description": "View deployment history and logs for a project"
58
+ },
55
59
  {
56
60
  "name": "mlgym_set_health_check",
57
61
  "description": "Configure health checks to monitor application availability"
@@ -83,6 +87,3 @@
83
87
  ]
84
88
  }
85
89
  }
86
-
87
-
88
-