mlgym-deploy 2.4.1 → 2.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/index.js +463 -5
- package/package.json +1 -1
package/index.js
CHANGED
|
@@ -16,8 +16,8 @@ import crypto from 'crypto';
|
|
|
16
16
|
|
|
17
17
|
const execAsync = promisify(exec);
|
|
18
18
|
|
|
19
|
-
// Current version of this MCP server - INCREMENT FOR
|
|
20
|
-
const CURRENT_VERSION = '2.
|
|
19
|
+
// Current version of this MCP server - INCREMENT FOR PROJECT ANALYSIS
|
|
20
|
+
const CURRENT_VERSION = '2.6.0';
|
|
21
21
|
const PACKAGE_NAME = 'mlgym-deploy';
|
|
22
22
|
|
|
23
23
|
// Version check state
|
|
@@ -353,6 +353,379 @@ async function authenticate(args) {
|
|
|
353
353
|
};
|
|
354
354
|
}
|
|
355
355
|
|
|
356
|
+
// Analyze project to detect type, framework, and configuration
|
|
357
|
+
async function analyzeProject(local_path = '.') {
|
|
358
|
+
const absolutePath = path.resolve(local_path);
|
|
359
|
+
const dirName = path.basename(absolutePath);
|
|
360
|
+
|
|
361
|
+
const analysis = {
|
|
362
|
+
project_type: 'unknown',
|
|
363
|
+
detected_files: [],
|
|
364
|
+
suggested_name: dirName.toLowerCase().replace(/[^a-z0-9-]/g, '-'),
|
|
365
|
+
has_dockerfile: false,
|
|
366
|
+
has_git: false,
|
|
367
|
+
framework: null,
|
|
368
|
+
build_command: null,
|
|
369
|
+
start_command: null,
|
|
370
|
+
package_manager: null
|
|
371
|
+
};
|
|
372
|
+
|
|
373
|
+
try {
|
|
374
|
+
// Check for git
|
|
375
|
+
try {
|
|
376
|
+
await execAsync('git status', { cwd: absolutePath });
|
|
377
|
+
analysis.has_git = true;
|
|
378
|
+
} catch {}
|
|
379
|
+
|
|
380
|
+
// Check for Dockerfile
|
|
381
|
+
try {
|
|
382
|
+
await fs.access(path.join(absolutePath, 'Dockerfile'));
|
|
383
|
+
analysis.has_dockerfile = true;
|
|
384
|
+
analysis.detected_files.push('Dockerfile');
|
|
385
|
+
} catch {}
|
|
386
|
+
|
|
387
|
+
// Check for Node.js project
|
|
388
|
+
try {
|
|
389
|
+
const packageJsonPath = path.join(absolutePath, 'package.json');
|
|
390
|
+
await fs.access(packageJsonPath);
|
|
391
|
+
const packageJson = JSON.parse(await fs.readFile(packageJsonPath, 'utf8'));
|
|
392
|
+
|
|
393
|
+
analysis.project_type = 'nodejs';
|
|
394
|
+
analysis.detected_files.push('package.json');
|
|
395
|
+
analysis.suggested_name = packageJson.name || analysis.suggested_name;
|
|
396
|
+
|
|
397
|
+
// Detect package manager
|
|
398
|
+
try {
|
|
399
|
+
await fs.access(path.join(absolutePath, 'package-lock.json'));
|
|
400
|
+
analysis.package_manager = 'npm';
|
|
401
|
+
analysis.detected_files.push('package-lock.json');
|
|
402
|
+
} catch {
|
|
403
|
+
try {
|
|
404
|
+
await fs.access(path.join(absolutePath, 'yarn.lock'));
|
|
405
|
+
analysis.package_manager = 'yarn';
|
|
406
|
+
analysis.detected_files.push('yarn.lock');
|
|
407
|
+
} catch {
|
|
408
|
+
analysis.package_manager = 'npm'; // default
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
// Detect framework
|
|
413
|
+
const deps = { ...packageJson.dependencies, ...packageJson.devDependencies };
|
|
414
|
+
if (deps.next) {
|
|
415
|
+
analysis.framework = 'nextjs';
|
|
416
|
+
analysis.build_command = packageJson.scripts?.build || 'npm run build';
|
|
417
|
+
analysis.start_command = packageJson.scripts?.start || 'npm start';
|
|
418
|
+
} else if (deps.express) {
|
|
419
|
+
analysis.framework = 'express';
|
|
420
|
+
analysis.start_command = packageJson.scripts?.start || 'node index.js';
|
|
421
|
+
} else if (deps.react) {
|
|
422
|
+
analysis.framework = 'react';
|
|
423
|
+
analysis.build_command = packageJson.scripts?.build || 'npm run build';
|
|
424
|
+
} else if (deps.vue) {
|
|
425
|
+
analysis.framework = 'vue';
|
|
426
|
+
analysis.build_command = packageJson.scripts?.build || 'npm run build';
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
// Use package.json scripts as fallback
|
|
430
|
+
if (!analysis.build_command && packageJson.scripts?.build) {
|
|
431
|
+
analysis.build_command = 'npm run build';
|
|
432
|
+
}
|
|
433
|
+
if (!analysis.start_command && packageJson.scripts?.start) {
|
|
434
|
+
analysis.start_command = 'npm start';
|
|
435
|
+
}
|
|
436
|
+
} catch {}
|
|
437
|
+
|
|
438
|
+
// Check for Python project
|
|
439
|
+
if (analysis.project_type === 'unknown') {
|
|
440
|
+
try {
|
|
441
|
+
await fs.access(path.join(absolutePath, 'requirements.txt'));
|
|
442
|
+
analysis.project_type = 'python';
|
|
443
|
+
analysis.detected_files.push('requirements.txt');
|
|
444
|
+
|
|
445
|
+
// Check for specific Python files
|
|
446
|
+
try {
|
|
447
|
+
await fs.access(path.join(absolutePath, 'app.py'));
|
|
448
|
+
analysis.framework = 'flask';
|
|
449
|
+
analysis.start_command = 'python app.py';
|
|
450
|
+
analysis.detected_files.push('app.py');
|
|
451
|
+
} catch {
|
|
452
|
+
try {
|
|
453
|
+
await fs.access(path.join(absolutePath, 'main.py'));
|
|
454
|
+
analysis.framework = 'fastapi';
|
|
455
|
+
analysis.start_command = 'uvicorn main:app --host 0.0.0.0';
|
|
456
|
+
analysis.detected_files.push('main.py');
|
|
457
|
+
} catch {}
|
|
458
|
+
}
|
|
459
|
+
} catch {}
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
// Check for static HTML project
|
|
463
|
+
if (analysis.project_type === 'unknown') {
|
|
464
|
+
try {
|
|
465
|
+
await fs.access(path.join(absolutePath, 'index.html'));
|
|
466
|
+
analysis.project_type = 'static';
|
|
467
|
+
analysis.framework = 'html';
|
|
468
|
+
analysis.detected_files.push('index.html');
|
|
469
|
+
} catch {}
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
// Check for Go project
|
|
473
|
+
if (analysis.project_type === 'unknown') {
|
|
474
|
+
try {
|
|
475
|
+
await fs.access(path.join(absolutePath, 'go.mod'));
|
|
476
|
+
analysis.project_type = 'go';
|
|
477
|
+
analysis.detected_files.push('go.mod');
|
|
478
|
+
analysis.build_command = 'go build -o app';
|
|
479
|
+
analysis.start_command = './app';
|
|
480
|
+
} catch {}
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
} catch (error) {
|
|
484
|
+
console.error('Project analysis error:', error);
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
return analysis;
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
// Check for existing MLGym project in current directory
|
|
491
|
+
async function checkExistingProject(local_path = '.') {
|
|
492
|
+
const absolutePath = path.resolve(local_path);
|
|
493
|
+
|
|
494
|
+
// Check for git repository
|
|
495
|
+
try {
|
|
496
|
+
const { stdout: remotes } = await execAsync('git remote -v', { cwd: absolutePath });
|
|
497
|
+
|
|
498
|
+
// Check for mlgym remote
|
|
499
|
+
if (remotes.includes('mlgym') && remotes.includes('git.mlgym.io')) {
|
|
500
|
+
// Extract project info from remote URL
|
|
501
|
+
const match = remotes.match(/mlgym\s+git@git\.mlgym\.io:([^\/]+)\/([^\.]+)\.git/);
|
|
502
|
+
if (match) {
|
|
503
|
+
const [, namespace, projectName] = match;
|
|
504
|
+
return {
|
|
505
|
+
exists: true,
|
|
506
|
+
name: projectName,
|
|
507
|
+
namespace: namespace,
|
|
508
|
+
configured: true,
|
|
509
|
+
message: `Project '${projectName}' already configured for MLGym deployment`
|
|
510
|
+
};
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
// Git repo exists but no mlgym remote
|
|
515
|
+
return {
|
|
516
|
+
exists: false,
|
|
517
|
+
has_git: true,
|
|
518
|
+
configured: false,
|
|
519
|
+
message: 'Git repository exists but no MLGym remote configured'
|
|
520
|
+
};
|
|
521
|
+
} catch {
|
|
522
|
+
// No git repository
|
|
523
|
+
return {
|
|
524
|
+
exists: false,
|
|
525
|
+
has_git: false,
|
|
526
|
+
configured: false,
|
|
527
|
+
message: 'No git repository found in current directory'
|
|
528
|
+
};
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
// Generate appropriate Dockerfile based on project type
|
|
533
|
+
function generateDockerfile(projectType, framework, packageManager = 'npm') {
|
|
534
|
+
let dockerfile = '';
|
|
535
|
+
|
|
536
|
+
if (projectType === 'nodejs') {
|
|
537
|
+
if (framework === 'nextjs') {
|
|
538
|
+
dockerfile = `# Build stage
|
|
539
|
+
FROM node:18-alpine AS builder
|
|
540
|
+
WORKDIR /app
|
|
541
|
+
COPY package*.json ./
|
|
542
|
+
RUN ${packageManager} ${packageManager === 'npm' ? 'ci' : 'install --frozen-lockfile'}
|
|
543
|
+
COPY . .
|
|
544
|
+
RUN ${packageManager} run build
|
|
545
|
+
|
|
546
|
+
# Production stage
|
|
547
|
+
FROM node:18-alpine
|
|
548
|
+
WORKDIR /app
|
|
549
|
+
COPY --from=builder /app/.next ./.next
|
|
550
|
+
COPY --from=builder /app/node_modules ./node_modules
|
|
551
|
+
COPY --from=builder /app/package.json ./
|
|
552
|
+
COPY --from=builder /app/public ./public
|
|
553
|
+
EXPOSE 3000
|
|
554
|
+
CMD ["${packageManager}", "start"]`;
|
|
555
|
+
} else if (framework === 'express') {
|
|
556
|
+
dockerfile = `FROM node:18-alpine
|
|
557
|
+
WORKDIR /app
|
|
558
|
+
COPY package*.json ./
|
|
559
|
+
RUN ${packageManager} ${packageManager === 'npm' ? 'ci --only=production' : 'install --frozen-lockfile --production'}
|
|
560
|
+
COPY . .
|
|
561
|
+
EXPOSE 3000
|
|
562
|
+
CMD ["node", "index.js"]`;
|
|
563
|
+
} else if (framework === 'react' || framework === 'vue') {
|
|
564
|
+
dockerfile = `# Build stage
|
|
565
|
+
FROM node:18-alpine AS builder
|
|
566
|
+
WORKDIR /app
|
|
567
|
+
COPY package*.json ./
|
|
568
|
+
RUN ${packageManager} ${packageManager === 'npm' ? 'ci' : 'install --frozen-lockfile'}
|
|
569
|
+
COPY . .
|
|
570
|
+
RUN ${packageManager} run build
|
|
571
|
+
|
|
572
|
+
# Production stage
|
|
573
|
+
FROM nginx:alpine
|
|
574
|
+
COPY --from=builder /app/${framework === 'react' ? 'build' : 'dist'} /usr/share/nginx/html
|
|
575
|
+
EXPOSE 80
|
|
576
|
+
CMD ["nginx", "-g", "daemon off;"]`;
|
|
577
|
+
} else {
|
|
578
|
+
// Generic Node.js
|
|
579
|
+
dockerfile = `FROM node:18-alpine
|
|
580
|
+
WORKDIR /app
|
|
581
|
+
COPY package*.json ./
|
|
582
|
+
RUN ${packageManager} ${packageManager === 'npm' ? 'ci --only=production' : 'install --frozen-lockfile --production'}
|
|
583
|
+
COPY . .
|
|
584
|
+
EXPOSE 3000
|
|
585
|
+
CMD ["${packageManager}", "start"]`;
|
|
586
|
+
}
|
|
587
|
+
} else if (projectType === 'python') {
|
|
588
|
+
if (framework === 'flask') {
|
|
589
|
+
dockerfile = `FROM python:3.11-slim
|
|
590
|
+
WORKDIR /app
|
|
591
|
+
COPY requirements.txt .
|
|
592
|
+
RUN pip install --no-cache-dir -r requirements.txt
|
|
593
|
+
COPY . .
|
|
594
|
+
EXPOSE 5000
|
|
595
|
+
CMD ["python", "app.py"]`;
|
|
596
|
+
} else if (framework === 'fastapi') {
|
|
597
|
+
dockerfile = `FROM python:3.11-slim
|
|
598
|
+
WORKDIR /app
|
|
599
|
+
COPY requirements.txt .
|
|
600
|
+
RUN pip install --no-cache-dir -r requirements.txt
|
|
601
|
+
COPY . .
|
|
602
|
+
EXPOSE 8000
|
|
603
|
+
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]`;
|
|
604
|
+
} else {
|
|
605
|
+
// Generic Python
|
|
606
|
+
dockerfile = `FROM python:3.11-slim
|
|
607
|
+
WORKDIR /app
|
|
608
|
+
COPY requirements.txt .
|
|
609
|
+
RUN pip install --no-cache-dir -r requirements.txt
|
|
610
|
+
COPY . .
|
|
611
|
+
EXPOSE 8000
|
|
612
|
+
CMD ["python", "main.py"]`;
|
|
613
|
+
}
|
|
614
|
+
} else if (projectType === 'static') {
|
|
615
|
+
dockerfile = `FROM nginx:alpine
|
|
616
|
+
COPY . /usr/share/nginx/html
|
|
617
|
+
EXPOSE 80
|
|
618
|
+
CMD ["nginx", "-g", "daemon off;"]`;
|
|
619
|
+
} else if (projectType === 'go') {
|
|
620
|
+
dockerfile = `# Build stage
|
|
621
|
+
FROM golang:1.21-alpine AS builder
|
|
622
|
+
WORKDIR /app
|
|
623
|
+
COPY go.mod go.sum ./
|
|
624
|
+
RUN go mod download
|
|
625
|
+
COPY . .
|
|
626
|
+
RUN go build -o app
|
|
627
|
+
|
|
628
|
+
# Production stage
|
|
629
|
+
FROM alpine:latest
|
|
630
|
+
RUN apk --no-cache add ca-certificates
|
|
631
|
+
WORKDIR /root/
|
|
632
|
+
COPY --from=builder /app/app .
|
|
633
|
+
EXPOSE 8080
|
|
634
|
+
CMD ["./app"]`;
|
|
635
|
+
} else {
|
|
636
|
+
// Unknown type - basic Alpine with shell
|
|
637
|
+
dockerfile = `FROM alpine:latest
|
|
638
|
+
WORKDIR /app
|
|
639
|
+
COPY . .
|
|
640
|
+
RUN echo "Unknown project type - please configure manually"
|
|
641
|
+
CMD ["/bin/sh"]`;
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
return dockerfile;
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
// Prepare project for deployment
|
|
648
|
+
async function prepareProject(args) {
|
|
649
|
+
const { local_path = '.', project_type, framework, package_manager } = args;
|
|
650
|
+
const absolutePath = path.resolve(local_path);
|
|
651
|
+
|
|
652
|
+
const actions = [];
|
|
653
|
+
|
|
654
|
+
try {
|
|
655
|
+
// Check if Dockerfile exists
|
|
656
|
+
const dockerfilePath = path.join(absolutePath, 'Dockerfile');
|
|
657
|
+
let dockerfileExists = false;
|
|
658
|
+
|
|
659
|
+
try {
|
|
660
|
+
await fs.access(dockerfilePath);
|
|
661
|
+
dockerfileExists = true;
|
|
662
|
+
actions.push('Dockerfile already exists - skipping generation');
|
|
663
|
+
} catch {}
|
|
664
|
+
|
|
665
|
+
// Generate Dockerfile if missing
|
|
666
|
+
if (!dockerfileExists && project_type !== 'unknown') {
|
|
667
|
+
const dockerfile = generateDockerfile(project_type, framework, package_manager);
|
|
668
|
+
await fs.writeFile(dockerfilePath, dockerfile);
|
|
669
|
+
actions.push(`Generated Dockerfile for ${project_type}/${framework || 'generic'}`);
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
// Check/create .gitignore
|
|
673
|
+
const gitignorePath = path.join(absolutePath, '.gitignore');
|
|
674
|
+
let gitignoreExists = false;
|
|
675
|
+
|
|
676
|
+
try {
|
|
677
|
+
await fs.access(gitignorePath);
|
|
678
|
+
gitignoreExists = true;
|
|
679
|
+
} catch {}
|
|
680
|
+
|
|
681
|
+
if (!gitignoreExists) {
|
|
682
|
+
let gitignoreContent = '';
|
|
683
|
+
|
|
684
|
+
if (project_type === 'nodejs') {
|
|
685
|
+
gitignoreContent = `node_modules/
|
|
686
|
+
.env
|
|
687
|
+
.env.local
|
|
688
|
+
dist/
|
|
689
|
+
build/
|
|
690
|
+
.next/
|
|
691
|
+
*.log`;
|
|
692
|
+
} else if (project_type === 'python') {
|
|
693
|
+
gitignoreContent = `__pycache__/
|
|
694
|
+
*.py[cod]
|
|
695
|
+
*$py.class
|
|
696
|
+
.env
|
|
697
|
+
venv/
|
|
698
|
+
env/
|
|
699
|
+
.venv/`;
|
|
700
|
+
} else {
|
|
701
|
+
gitignoreContent = `.env
|
|
702
|
+
*.log
|
|
703
|
+
.DS_Store`;
|
|
704
|
+
}
|
|
705
|
+
|
|
706
|
+
await fs.writeFile(gitignorePath, gitignoreContent);
|
|
707
|
+
actions.push('Created .gitignore file');
|
|
708
|
+
}
|
|
709
|
+
|
|
710
|
+
return {
|
|
711
|
+
status: 'success',
|
|
712
|
+
message: 'Project prepared for deployment',
|
|
713
|
+
actions: actions,
|
|
714
|
+
dockerfile_created: !dockerfileExists && project_type !== 'unknown',
|
|
715
|
+
project_type: project_type,
|
|
716
|
+
framework: framework
|
|
717
|
+
};
|
|
718
|
+
|
|
719
|
+
} catch (error) {
|
|
720
|
+
return {
|
|
721
|
+
status: 'error',
|
|
722
|
+
message: 'Failed to prepare project',
|
|
723
|
+
error: error.message,
|
|
724
|
+
actions: actions
|
|
725
|
+
};
|
|
726
|
+
}
|
|
727
|
+
}
|
|
728
|
+
|
|
356
729
|
// Check authentication status
|
|
357
730
|
async function checkAuthStatus() {
|
|
358
731
|
const auth = await loadAuth();
|
|
@@ -548,7 +921,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
548
921
|
tools: [
|
|
549
922
|
{
|
|
550
923
|
name: 'mlgym_auth_status',
|
|
551
|
-
description: '
|
|
924
|
+
description: 'ALWAYS CALL THIS FIRST! Check authentication status before any other operation.',
|
|
552
925
|
inputSchema: {
|
|
553
926
|
type: 'object',
|
|
554
927
|
properties: {}
|
|
@@ -556,7 +929,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
556
929
|
},
|
|
557
930
|
{
|
|
558
931
|
name: 'mlgym_authenticate',
|
|
559
|
-
description: '
|
|
932
|
+
description: 'PHASE 1: Authentication ONLY. Get email, password, and existing account status in ONE interaction. Never ask for project details here!',
|
|
560
933
|
inputSchema: {
|
|
561
934
|
type: 'object',
|
|
562
935
|
properties: {
|
|
@@ -589,9 +962,37 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
589
962
|
required: ['email', 'password']
|
|
590
963
|
}
|
|
591
964
|
},
|
|
965
|
+
{
|
|
966
|
+
name: 'mlgym_project_analyze',
|
|
967
|
+
description: 'PHASE 2: Analyze project to detect type, framework, and configuration. Call BEFORE creating project.',
|
|
968
|
+
inputSchema: {
|
|
969
|
+
type: 'object',
|
|
970
|
+
properties: {
|
|
971
|
+
local_path: {
|
|
972
|
+
type: 'string',
|
|
973
|
+
description: 'Local directory path (defaults to current directory)',
|
|
974
|
+
default: '.'
|
|
975
|
+
}
|
|
976
|
+
}
|
|
977
|
+
}
|
|
978
|
+
},
|
|
979
|
+
{
|
|
980
|
+
name: 'mlgym_project_status',
|
|
981
|
+
description: 'PHASE 2: Check if MLGym project exists in current directory.',
|
|
982
|
+
inputSchema: {
|
|
983
|
+
type: 'object',
|
|
984
|
+
properties: {
|
|
985
|
+
local_path: {
|
|
986
|
+
type: 'string',
|
|
987
|
+
description: 'Local directory path (defaults to current directory)',
|
|
988
|
+
default: '.'
|
|
989
|
+
}
|
|
990
|
+
}
|
|
991
|
+
}
|
|
992
|
+
},
|
|
592
993
|
{
|
|
593
994
|
name: 'mlgym_project_init',
|
|
594
|
-
description: '
|
|
995
|
+
description: 'PHASE 2: Create project ONLY after checking project status. Never ask for email/password here - only project details!',
|
|
595
996
|
inputSchema: {
|
|
596
997
|
type: 'object',
|
|
597
998
|
properties: {
|
|
@@ -626,6 +1027,36 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
626
1027
|
},
|
|
627
1028
|
required: ['name', 'description']
|
|
628
1029
|
}
|
|
1030
|
+
},
|
|
1031
|
+
{
|
|
1032
|
+
name: 'mlgym_project_prepare',
|
|
1033
|
+
description: 'PHASE 2: Prepare project for deployment by generating Dockerfile and config files.',
|
|
1034
|
+
inputSchema: {
|
|
1035
|
+
type: 'object',
|
|
1036
|
+
properties: {
|
|
1037
|
+
local_path: {
|
|
1038
|
+
type: 'string',
|
|
1039
|
+
description: 'Local directory path (defaults to current directory)',
|
|
1040
|
+
default: '.'
|
|
1041
|
+
},
|
|
1042
|
+
project_type: {
|
|
1043
|
+
type: 'string',
|
|
1044
|
+
description: 'Project type from analysis',
|
|
1045
|
+
enum: ['nodejs', 'python', 'static', 'go', 'unknown']
|
|
1046
|
+
},
|
|
1047
|
+
framework: {
|
|
1048
|
+
type: 'string',
|
|
1049
|
+
description: 'Framework from analysis',
|
|
1050
|
+
enum: ['nextjs', 'express', 'react', 'vue', 'flask', 'fastapi', 'html', null]
|
|
1051
|
+
},
|
|
1052
|
+
package_manager: {
|
|
1053
|
+
type: 'string',
|
|
1054
|
+
description: 'Package manager for Node.js projects',
|
|
1055
|
+
enum: ['npm', 'yarn'],
|
|
1056
|
+
default: 'npm'
|
|
1057
|
+
}
|
|
1058
|
+
}
|
|
1059
|
+
}
|
|
629
1060
|
}
|
|
630
1061
|
]
|
|
631
1062
|
};
|
|
@@ -645,9 +1076,36 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
645
1076
|
case 'mlgym_authenticate':
|
|
646
1077
|
return await authenticate(args);
|
|
647
1078
|
|
|
1079
|
+
case 'mlgym_project_analyze':
|
|
1080
|
+
const analysis = await analyzeProject(args.local_path);
|
|
1081
|
+
return {
|
|
1082
|
+
content: [{
|
|
1083
|
+
type: 'text',
|
|
1084
|
+
text: JSON.stringify(analysis, null, 2)
|
|
1085
|
+
}]
|
|
1086
|
+
};
|
|
1087
|
+
|
|
1088
|
+
case 'mlgym_project_status':
|
|
1089
|
+
const projectStatus = await checkExistingProject(args.local_path);
|
|
1090
|
+
return {
|
|
1091
|
+
content: [{
|
|
1092
|
+
type: 'text',
|
|
1093
|
+
text: JSON.stringify(projectStatus, null, 2)
|
|
1094
|
+
}]
|
|
1095
|
+
};
|
|
1096
|
+
|
|
648
1097
|
case 'mlgym_project_init':
|
|
649
1098
|
return await initProject(args);
|
|
650
1099
|
|
|
1100
|
+
case 'mlgym_project_prepare':
|
|
1101
|
+
const prepResult = await prepareProject(args);
|
|
1102
|
+
return {
|
|
1103
|
+
content: [{
|
|
1104
|
+
type: 'text',
|
|
1105
|
+
text: JSON.stringify(prepResult, null, 2)
|
|
1106
|
+
}]
|
|
1107
|
+
};
|
|
1108
|
+
|
|
651
1109
|
default:
|
|
652
1110
|
throw new Error(`Unknown tool: ${name}`);
|
|
653
1111
|
}
|