frontmcp 0.11.1 → 0.11.2
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/package.json +3 -2
- package/src/args.d.ts +2 -0
- package/src/args.js +2 -0
- package/src/args.js.map +1 -1
- package/src/cli.js +2 -0
- package/src/cli.js.map +1 -1
- package/src/commands/create.d.ts +3 -0
- package/src/commands/create.js +282 -201
- package/src/commands/create.js.map +1 -1
- package/src/commands/install/questionnaire.d.ts +1 -1
- package/src/commands/install/questionnaire.js +73 -96
- package/src/commands/install/questionnaire.js.map +1 -1
- package/src/commands/template.js +81 -67
- package/src/commands/template.js.map +1 -1
- package/src/utils/prompts.d.ts +1 -0
- package/src/utils/prompts.js +17 -0
- package/src/utils/prompts.js.map +1 -0
package/src/commands/create.js
CHANGED
|
@@ -3,41 +3,44 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.runCreate = runCreate;
|
|
4
4
|
const tslib_1 = require("tslib");
|
|
5
5
|
const path = tslib_1.__importStar(require("path"));
|
|
6
|
-
const readline = tslib_1.__importStar(require("readline"));
|
|
7
6
|
const fs_1 = require("fs");
|
|
8
7
|
const colors_1 = require("../colors");
|
|
9
8
|
const utils_1 = require("@frontmcp/utils");
|
|
10
9
|
const tsconfig_1 = require("../tsconfig");
|
|
11
10
|
const version_1 = require("../version");
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
11
|
+
const prompts_1 = require("../utils/prompts");
|
|
12
|
+
const PM_CONFIG = {
|
|
13
|
+
npm: {
|
|
14
|
+
lockfileCopy: 'COPY package*.json package-lock.json* ./',
|
|
15
|
+
installAll: 'RUN npm ci',
|
|
16
|
+
pruneDevDeps: 'RUN npm prune --omit=dev',
|
|
17
|
+
run: 'npm run',
|
|
18
|
+
userInstall: 'npm install',
|
|
19
|
+
ghCache: 'npm',
|
|
20
|
+
ghInstallCmd: 'npm ci',
|
|
21
|
+
engines: { node: '>=22', npm: '>=10' },
|
|
22
|
+
},
|
|
23
|
+
yarn: {
|
|
24
|
+
lockfileCopy: 'COPY package.json yarn.lock* ./',
|
|
25
|
+
installAll: 'RUN yarn install --frozen-lockfile',
|
|
26
|
+
pruneDevDeps: 'RUN yarn install --frozen-lockfile --production',
|
|
27
|
+
run: 'yarn',
|
|
28
|
+
userInstall: 'yarn install',
|
|
29
|
+
ghCache: 'yarn',
|
|
30
|
+
ghInstallCmd: 'yarn install --frozen-lockfile',
|
|
31
|
+
engines: { node: '>=22' },
|
|
32
|
+
},
|
|
33
|
+
pnpm: {
|
|
34
|
+
lockfileCopy: 'COPY package.json pnpm-lock.yaml* ./',
|
|
35
|
+
installAll: 'RUN pnpm install --frozen-lockfile',
|
|
36
|
+
pruneDevDeps: 'RUN pnpm prune --prod',
|
|
37
|
+
run: 'pnpm run',
|
|
38
|
+
userInstall: 'pnpm install',
|
|
39
|
+
ghCache: 'pnpm',
|
|
40
|
+
ghInstallCmd: 'pnpm install --frozen-lockfile',
|
|
41
|
+
engines: { node: '>=22' },
|
|
42
|
+
},
|
|
43
|
+
};
|
|
41
44
|
function isInteractive() {
|
|
42
45
|
return process.stdin.isTTY === true;
|
|
43
46
|
}
|
|
@@ -176,6 +179,29 @@ coverage/
|
|
|
176
179
|
# Test output
|
|
177
180
|
test-output/
|
|
178
181
|
`;
|
|
182
|
+
const TEMPLATE_DOCKERIGNORE = `
|
|
183
|
+
node_modules
|
|
184
|
+
dist
|
|
185
|
+
.git
|
|
186
|
+
coverage
|
|
187
|
+
test-output
|
|
188
|
+
*.tsbuildinfo
|
|
189
|
+
.idea
|
|
190
|
+
.vscode
|
|
191
|
+
.DS_Store
|
|
192
|
+
Thumbs.db
|
|
193
|
+
*.log
|
|
194
|
+
npm-debug.log*
|
|
195
|
+
yarn-debug.log*
|
|
196
|
+
yarn-error.log*
|
|
197
|
+
.env
|
|
198
|
+
.env.local
|
|
199
|
+
.env.*.local
|
|
200
|
+
.frontmcp
|
|
201
|
+
e2e
|
|
202
|
+
*.md
|
|
203
|
+
LICENSE
|
|
204
|
+
`;
|
|
179
205
|
const TEMPLATE_JEST_E2E_CONFIG = `
|
|
180
206
|
/* eslint-disable */
|
|
181
207
|
export default {
|
|
@@ -357,40 +383,43 @@ See the [Redis Setup Guide](https://docs.agentfront.dev/docs/deployment/redis-se
|
|
|
357
383
|
// Deployment Target Templates
|
|
358
384
|
// =============================================================================
|
|
359
385
|
// Docker templates (moved to ci/ folder)
|
|
360
|
-
|
|
386
|
+
function generateDockerfile(pm) {
|
|
387
|
+
const cfg = PM_CONFIG[pm];
|
|
388
|
+
const corepack = pm !== 'npm' ? '\nRUN corepack enable\n' : '';
|
|
389
|
+
return `
|
|
361
390
|
# Build stage
|
|
362
|
-
FROM node:
|
|
391
|
+
FROM node:24-slim AS builder
|
|
363
392
|
|
|
364
393
|
WORKDIR /app
|
|
365
|
-
|
|
394
|
+
${corepack}
|
|
366
395
|
# Install all dependencies (including devDependencies for build)
|
|
367
|
-
|
|
368
|
-
|
|
396
|
+
${cfg.lockfileCopy}
|
|
397
|
+
${cfg.installAll}
|
|
369
398
|
|
|
370
399
|
# Copy source and build
|
|
371
400
|
COPY . .
|
|
372
|
-
RUN
|
|
401
|
+
RUN ${cfg.run} build
|
|
402
|
+
|
|
403
|
+
# Prune devDependencies so only production deps remain
|
|
404
|
+
${cfg.pruneDevDeps}
|
|
373
405
|
|
|
374
406
|
# Production stage
|
|
375
|
-
FROM node:
|
|
407
|
+
FROM node:24-slim AS runner
|
|
376
408
|
|
|
377
409
|
WORKDIR /app
|
|
378
410
|
ENV NODE_ENV=production
|
|
379
411
|
|
|
380
|
-
|
|
381
|
-
COPY package*.json ./
|
|
382
|
-
RUN npm ci --omit=dev
|
|
383
|
-
|
|
384
|
-
# Copy built artifacts from builder
|
|
412
|
+
COPY --from=builder /app/node_modules ./node_modules
|
|
385
413
|
COPY --from=builder /app/dist ./dist
|
|
414
|
+
COPY --from=builder /app/package.json ./
|
|
386
415
|
|
|
387
416
|
EXPOSE 3000
|
|
388
417
|
|
|
389
418
|
CMD ["node", "dist/main.js"]
|
|
390
419
|
`;
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
420
|
+
}
|
|
421
|
+
function generateDockerComposeWithRedis() {
|
|
422
|
+
return `
|
|
394
423
|
services:
|
|
395
424
|
redis:
|
|
396
425
|
image: redis:7-alpine
|
|
@@ -401,7 +430,7 @@ services:
|
|
|
401
430
|
command: redis-server --appendonly yes
|
|
402
431
|
healthcheck:
|
|
403
432
|
test: ['CMD', 'redis-cli', 'ping']
|
|
404
|
-
interval:
|
|
433
|
+
interval: 3s
|
|
405
434
|
timeout: 5s
|
|
406
435
|
retries: 3
|
|
407
436
|
|
|
@@ -413,21 +442,19 @@ services:
|
|
|
413
442
|
- '\${PORT:-3000}:3000'
|
|
414
443
|
environment:
|
|
415
444
|
- NODE_ENV=\${NODE_ENV:-development}
|
|
445
|
+
- PORT=\${PORT:-3000}
|
|
416
446
|
- REDIS_HOST=redis
|
|
417
447
|
- REDIS_PORT=6379
|
|
418
448
|
depends_on:
|
|
419
449
|
redis:
|
|
420
450
|
condition: service_healthy
|
|
421
|
-
volumes:
|
|
422
|
-
- ../src:/app/src
|
|
423
|
-
command: npm run dev
|
|
424
451
|
|
|
425
452
|
volumes:
|
|
426
453
|
redis-data:
|
|
427
454
|
`;
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
455
|
+
}
|
|
456
|
+
function generateDockerComposeNoRedis() {
|
|
457
|
+
return `
|
|
431
458
|
services:
|
|
432
459
|
app:
|
|
433
460
|
build:
|
|
@@ -437,10 +464,9 @@ services:
|
|
|
437
464
|
- '\${PORT:-3000}:3000'
|
|
438
465
|
environment:
|
|
439
466
|
- NODE_ENV=\${NODE_ENV:-development}
|
|
440
|
-
|
|
441
|
-
- ../src:/app/src
|
|
442
|
-
command: npm run dev
|
|
467
|
+
- PORT=\${PORT:-3000}
|
|
443
468
|
`;
|
|
469
|
+
}
|
|
444
470
|
const TEMPLATE_ENV_DOCKER_CI = `
|
|
445
471
|
# Docker-specific environment
|
|
446
472
|
# Use with: docker compose -f ci/docker-compose.yml --env-file ci/.env.docker up
|
|
@@ -525,7 +551,35 @@ NODE_ENV = "production"
|
|
|
525
551
|
// =============================================================================
|
|
526
552
|
// GitHub Actions Templates
|
|
527
553
|
// =============================================================================
|
|
528
|
-
|
|
554
|
+
function generatePmSetupSteps(pm) {
|
|
555
|
+
const cfg = PM_CONFIG[pm];
|
|
556
|
+
if (pm === 'pnpm') {
|
|
557
|
+
return `
|
|
558
|
+
- name: Setup pnpm
|
|
559
|
+
uses: pnpm/action-setup@v4
|
|
560
|
+
|
|
561
|
+
- name: Setup Node.js
|
|
562
|
+
uses: actions/setup-node@v4
|
|
563
|
+
with:
|
|
564
|
+
node-version: '24'
|
|
565
|
+
cache: '${cfg.ghCache}'
|
|
566
|
+
|
|
567
|
+
- name: Install dependencies
|
|
568
|
+
run: ${cfg.ghInstallCmd}`;
|
|
569
|
+
}
|
|
570
|
+
return `
|
|
571
|
+
- name: Setup Node.js
|
|
572
|
+
uses: actions/setup-node@v4
|
|
573
|
+
with:
|
|
574
|
+
node-version: '24'
|
|
575
|
+
cache: '${cfg.ghCache}'
|
|
576
|
+
|
|
577
|
+
- name: Install dependencies
|
|
578
|
+
run: ${cfg.ghInstallCmd}`;
|
|
579
|
+
}
|
|
580
|
+
function generateGhCi(pm) {
|
|
581
|
+
const cfg = PM_CONFIG[pm];
|
|
582
|
+
return `
|
|
529
583
|
name: CI
|
|
530
584
|
|
|
531
585
|
on:
|
|
@@ -540,23 +594,18 @@ jobs:
|
|
|
540
594
|
|
|
541
595
|
steps:
|
|
542
596
|
- uses: actions/checkout@v4
|
|
543
|
-
|
|
544
|
-
- name: Setup Node.js
|
|
545
|
-
uses: actions/setup-node@v4
|
|
546
|
-
with:
|
|
547
|
-
node-version: '22'
|
|
548
|
-
cache: 'npm'
|
|
549
|
-
|
|
550
|
-
- name: Install dependencies
|
|
551
|
-
run: npm ci
|
|
597
|
+
${generatePmSetupSteps(pm)}
|
|
552
598
|
|
|
553
599
|
- name: Type check
|
|
554
600
|
run: npx tsc --noEmit
|
|
555
601
|
|
|
556
602
|
- name: Run tests
|
|
557
|
-
run:
|
|
603
|
+
run: ${cfg.run} test
|
|
558
604
|
`;
|
|
559
|
-
|
|
605
|
+
}
|
|
606
|
+
function generateGhE2e(pm) {
|
|
607
|
+
const cfg = PM_CONFIG[pm];
|
|
608
|
+
return `
|
|
560
609
|
name: E2E Tests
|
|
561
610
|
|
|
562
611
|
on:
|
|
@@ -571,22 +620,15 @@ jobs:
|
|
|
571
620
|
|
|
572
621
|
steps:
|
|
573
622
|
- uses: actions/checkout@v4
|
|
574
|
-
|
|
575
|
-
- name: Setup Node.js
|
|
576
|
-
uses: actions/setup-node@v4
|
|
577
|
-
with:
|
|
578
|
-
node-version: '22'
|
|
579
|
-
cache: 'npm'
|
|
580
|
-
|
|
581
|
-
- name: Install dependencies
|
|
582
|
-
run: npm ci
|
|
623
|
+
${generatePmSetupSteps(pm)}
|
|
583
624
|
|
|
584
625
|
- name: Build
|
|
585
|
-
run:
|
|
626
|
+
run: ${cfg.run} build
|
|
586
627
|
|
|
587
628
|
- name: Run E2E tests
|
|
588
|
-
run:
|
|
629
|
+
run: ${cfg.run} test:e2e
|
|
589
630
|
`;
|
|
631
|
+
}
|
|
590
632
|
const TEMPLATE_GH_DEPLOY_DOCKER = `
|
|
591
633
|
name: Build and Push Docker Image
|
|
592
634
|
|
|
@@ -631,7 +673,9 @@ jobs:
|
|
|
631
673
|
tags: \${{ steps.meta.outputs.tags }}
|
|
632
674
|
labels: \${{ steps.meta.outputs.labels }}
|
|
633
675
|
`;
|
|
634
|
-
|
|
676
|
+
function generateGhDeployVercel(pm) {
|
|
677
|
+
const cfg = PM_CONFIG[pm];
|
|
678
|
+
return `
|
|
635
679
|
name: Deploy to Vercel
|
|
636
680
|
|
|
637
681
|
on:
|
|
@@ -644,18 +688,10 @@ jobs:
|
|
|
644
688
|
|
|
645
689
|
steps:
|
|
646
690
|
- uses: actions/checkout@v4
|
|
647
|
-
|
|
648
|
-
- name: Setup Node.js
|
|
649
|
-
uses: actions/setup-node@v4
|
|
650
|
-
with:
|
|
651
|
-
node-version: '22'
|
|
652
|
-
cache: 'npm'
|
|
653
|
-
|
|
654
|
-
- name: Install dependencies
|
|
655
|
-
run: npm ci
|
|
691
|
+
${generatePmSetupSteps(pm)}
|
|
656
692
|
|
|
657
693
|
- name: Build
|
|
658
|
-
run:
|
|
694
|
+
run: ${cfg.run} build
|
|
659
695
|
|
|
660
696
|
- name: Deploy to Vercel
|
|
661
697
|
uses: amondnet/vercel-action@v25
|
|
@@ -665,7 +701,10 @@ jobs:
|
|
|
665
701
|
vercel-project-id: \${{ secrets.VERCEL_PROJECT_ID }}
|
|
666
702
|
vercel-args: '--prod'
|
|
667
703
|
`;
|
|
668
|
-
|
|
704
|
+
}
|
|
705
|
+
function generateGhDeployLambda(pm) {
|
|
706
|
+
const cfg = PM_CONFIG[pm];
|
|
707
|
+
return `
|
|
669
708
|
name: Deploy to AWS Lambda
|
|
670
709
|
|
|
671
710
|
on:
|
|
@@ -678,18 +717,10 @@ jobs:
|
|
|
678
717
|
|
|
679
718
|
steps:
|
|
680
719
|
- uses: actions/checkout@v4
|
|
681
|
-
|
|
682
|
-
- name: Setup Node.js
|
|
683
|
-
uses: actions/setup-node@v4
|
|
684
|
-
with:
|
|
685
|
-
node-version: '22'
|
|
686
|
-
cache: 'npm'
|
|
687
|
-
|
|
688
|
-
- name: Install dependencies
|
|
689
|
-
run: npm ci
|
|
720
|
+
${generatePmSetupSteps(pm)}
|
|
690
721
|
|
|
691
722
|
- name: Build
|
|
692
|
-
run:
|
|
723
|
+
run: ${cfg.run} build
|
|
693
724
|
|
|
694
725
|
- name: Configure AWS credentials
|
|
695
726
|
uses: aws-actions/configure-aws-credentials@v4
|
|
@@ -707,7 +738,10 @@ jobs:
|
|
|
707
738
|
sam build
|
|
708
739
|
sam deploy --no-confirm-changeset --no-fail-on-empty-changeset
|
|
709
740
|
`;
|
|
710
|
-
|
|
741
|
+
}
|
|
742
|
+
function generateGhDeployCloudflare(pm) {
|
|
743
|
+
const cfg = PM_CONFIG[pm];
|
|
744
|
+
return `
|
|
711
745
|
name: Deploy to Cloudflare Workers
|
|
712
746
|
|
|
713
747
|
on:
|
|
@@ -720,29 +754,23 @@ jobs:
|
|
|
720
754
|
|
|
721
755
|
steps:
|
|
722
756
|
- uses: actions/checkout@v4
|
|
723
|
-
|
|
724
|
-
- name: Setup Node.js
|
|
725
|
-
uses: actions/setup-node@v4
|
|
726
|
-
with:
|
|
727
|
-
node-version: '22'
|
|
728
|
-
cache: 'npm'
|
|
729
|
-
|
|
730
|
-
- name: Install dependencies
|
|
731
|
-
run: npm ci
|
|
757
|
+
${generatePmSetupSteps(pm)}
|
|
732
758
|
|
|
733
759
|
- name: Build
|
|
734
|
-
run:
|
|
760
|
+
run: ${cfg.run} build
|
|
735
761
|
|
|
736
762
|
- name: Deploy to Cloudflare
|
|
737
763
|
uses: cloudflare/wrangler-action@v3
|
|
738
764
|
with:
|
|
739
765
|
apiToken: \${{ secrets.CLOUDFLARE_API_TOKEN }}
|
|
740
766
|
`;
|
|
767
|
+
}
|
|
741
768
|
// =============================================================================
|
|
742
769
|
// Dynamic README Templates
|
|
743
770
|
// =============================================================================
|
|
744
771
|
function generateReadme(options) {
|
|
745
|
-
const { projectName, deploymentTarget, redisSetup, enableGitHubActions } = options;
|
|
772
|
+
const { projectName, deploymentTarget, redisSetup, enableGitHubActions, packageManager } = options;
|
|
773
|
+
const cfg = PM_CONFIG[packageManager];
|
|
746
774
|
let readme = `# ${projectName}
|
|
747
775
|
|
|
748
776
|
A TypeScript MCP server built with [FrontMCP](https://github.com/agentfront/frontmcp).
|
|
@@ -758,13 +786,13 @@ A TypeScript MCP server built with [FrontMCP](https://github.com/agentfront/fron
|
|
|
758
786
|
|
|
759
787
|
\`\`\`bash
|
|
760
788
|
# Install dependencies
|
|
761
|
-
|
|
789
|
+
${cfg.userInstall}
|
|
762
790
|
|
|
763
791
|
# Start development server
|
|
764
|
-
|
|
792
|
+
${cfg.run} dev
|
|
765
793
|
|
|
766
794
|
# Run MCP Inspector
|
|
767
|
-
|
|
795
|
+
${cfg.run} inspect
|
|
768
796
|
\`\`\`
|
|
769
797
|
`;
|
|
770
798
|
// Deployment-specific sections
|
|
@@ -774,13 +802,13 @@ npm run inspect
|
|
|
774
802
|
|
|
775
803
|
\`\`\`bash
|
|
776
804
|
# Start all services${redisSetup === 'docker' ? ' (includes Redis)' : ''}
|
|
777
|
-
|
|
805
|
+
${cfg.run} docker:up
|
|
778
806
|
|
|
779
807
|
# Stop all services
|
|
780
|
-
|
|
808
|
+
${cfg.run} docker:down
|
|
781
809
|
|
|
782
810
|
# Rebuild Docker image
|
|
783
|
-
|
|
811
|
+
${cfg.run} docker:build
|
|
784
812
|
\`\`\`
|
|
785
813
|
`;
|
|
786
814
|
if (redisSetup === 'docker') {
|
|
@@ -812,7 +840,7 @@ docker push your-registry/${projectName}:latest
|
|
|
812
840
|
|
|
813
841
|
\`\`\`bash
|
|
814
842
|
# Build for production
|
|
815
|
-
|
|
843
|
+
${cfg.run} build
|
|
816
844
|
|
|
817
845
|
# Deploy using Vercel CLI
|
|
818
846
|
npx vercel --prod
|
|
@@ -827,10 +855,10 @@ Or connect your repository to Vercel for automatic deployments.
|
|
|
827
855
|
|
|
828
856
|
\`\`\`bash
|
|
829
857
|
# Build the project
|
|
830
|
-
|
|
858
|
+
${cfg.run} build
|
|
831
859
|
|
|
832
860
|
# Deploy using AWS SAM
|
|
833
|
-
|
|
861
|
+
${cfg.run} deploy
|
|
834
862
|
\`\`\`
|
|
835
863
|
|
|
836
864
|
### Prerequisites
|
|
@@ -845,10 +873,10 @@ npm run deploy
|
|
|
845
873
|
|
|
846
874
|
\`\`\`bash
|
|
847
875
|
# Build the project
|
|
848
|
-
|
|
876
|
+
${cfg.run} build
|
|
849
877
|
|
|
850
878
|
# Deploy using Wrangler
|
|
851
|
-
|
|
879
|
+
${cfg.run} deploy
|
|
852
880
|
\`\`\`
|
|
853
881
|
|
|
854
882
|
### Prerequisites
|
|
@@ -921,21 +949,21 @@ No additional secrets required - uses \`GITHUB_TOKEN\` for GHCR.
|
|
|
921
949
|
|
|
922
950
|
| Script | Description |
|
|
923
951
|
|--------|-------------|
|
|
924
|
-
|
|
|
925
|
-
|
|
|
926
|
-
|
|
|
927
|
-
|
|
|
928
|
-
|
|
|
929
|
-
|
|
|
952
|
+
| \`${cfg.run} dev\` | Start development server with hot reload |
|
|
953
|
+
| \`${cfg.run} build\` | Build for production |
|
|
954
|
+
| \`${cfg.run} inspect\` | Launch MCP Inspector |
|
|
955
|
+
| \`${cfg.run} doctor\` | Check project configuration |
|
|
956
|
+
| \`${cfg.run} test\` | Run unit tests |
|
|
957
|
+
| \`${cfg.run} test:e2e\` | Run E2E tests |
|
|
930
958
|
`;
|
|
931
959
|
if (deploymentTarget === 'node') {
|
|
932
|
-
readme += `|
|
|
933
|
-
|
|
|
934
|
-
|
|
|
960
|
+
readme += `| \`${cfg.run} docker:up\` | Start Docker services |
|
|
961
|
+
| \`${cfg.run} docker:down\` | Stop Docker services |
|
|
962
|
+
| \`${cfg.run} docker:build\` | Rebuild Docker image |
|
|
935
963
|
`;
|
|
936
964
|
}
|
|
937
965
|
if (deploymentTarget === 'lambda' || deploymentTarget === 'cloudflare') {
|
|
938
|
-
readme += `|
|
|
966
|
+
readme += `| \`${cfg.run} deploy\` | Deploy to ${deploymentTarget === 'lambda' ? 'AWS Lambda' : 'Cloudflare Workers'} |
|
|
939
967
|
`;
|
|
940
968
|
}
|
|
941
969
|
readme += `
|
|
@@ -948,7 +976,8 @@ No additional secrets required - uses \`GITHUB_TOKEN\` for GHCR.
|
|
|
948
976
|
├── .gitignore # Git ignore rules
|
|
949
977
|
`;
|
|
950
978
|
if (deploymentTarget === 'node') {
|
|
951
|
-
readme += `├──
|
|
979
|
+
readme += `├── .dockerignore # Docker build context exclusions
|
|
980
|
+
├── ci/
|
|
952
981
|
│ ├── Dockerfile # Container build config
|
|
953
982
|
│ ├── docker-compose.yml # Docker services config
|
|
954
983
|
│ └── .env.docker # Docker-specific env vars
|
|
@@ -1002,46 +1031,107 @@ function getDefaults(projectArg) {
|
|
|
1002
1031
|
deploymentTarget: 'node',
|
|
1003
1032
|
redisSetup: 'docker',
|
|
1004
1033
|
enableGitHubActions: true,
|
|
1034
|
+
packageManager: 'npm',
|
|
1005
1035
|
};
|
|
1006
1036
|
}
|
|
1007
|
-
async function collectOptions(
|
|
1037
|
+
async function collectOptions(projectArg, flags) {
|
|
1038
|
+
const p = await (0, prompts_1.clack)();
|
|
1008
1039
|
// Project name
|
|
1009
1040
|
let projectName = projectArg;
|
|
1010
1041
|
if (!projectName) {
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1042
|
+
const result = await p.text({
|
|
1043
|
+
message: 'Project name',
|
|
1044
|
+
validate: (val) => {
|
|
1045
|
+
if (!val.trim())
|
|
1046
|
+
return 'Project name is required';
|
|
1047
|
+
return undefined;
|
|
1048
|
+
},
|
|
1049
|
+
});
|
|
1050
|
+
if (p.isCancel(result)) {
|
|
1051
|
+
p.cancel('Cancelled.');
|
|
1052
|
+
process.exit(0);
|
|
1014
1053
|
}
|
|
1015
|
-
|
|
1016
|
-
else {
|
|
1017
|
-
console.log(`${(0, colors_1.c)('cyan', '?')} Project name: ${(0, colors_1.c)('bold', projectName)}`);
|
|
1054
|
+
projectName = result;
|
|
1018
1055
|
}
|
|
1019
1056
|
// Deployment target
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1057
|
+
let deploymentTarget = flags?.target;
|
|
1058
|
+
if (!deploymentTarget) {
|
|
1059
|
+
const result = await p.select({
|
|
1060
|
+
message: 'Select deployment target',
|
|
1061
|
+
options: [
|
|
1062
|
+
{ label: 'Node.js (Docker) - Recommended for production', value: 'node' },
|
|
1063
|
+
{ label: 'Vercel (Serverless)', value: 'vercel' },
|
|
1064
|
+
{ label: 'AWS Lambda', value: 'lambda' },
|
|
1065
|
+
{ label: 'Cloudflare Workers', value: 'cloudflare' },
|
|
1066
|
+
],
|
|
1067
|
+
initialValue: 'node',
|
|
1068
|
+
});
|
|
1069
|
+
if (p.isCancel(result)) {
|
|
1070
|
+
p.cancel('Cancelled.');
|
|
1071
|
+
process.exit(0);
|
|
1072
|
+
}
|
|
1073
|
+
deploymentTarget = result;
|
|
1074
|
+
}
|
|
1027
1075
|
// Redis setup (only for Node.js/Docker)
|
|
1028
1076
|
let redisSetup = 'none';
|
|
1029
1077
|
if (deploymentTarget === 'node') {
|
|
1030
|
-
|
|
1031
|
-
flags
|
|
1032
|
-
|
|
1078
|
+
if (flags?.redis) {
|
|
1079
|
+
redisSetup = flags.redis;
|
|
1080
|
+
}
|
|
1081
|
+
else {
|
|
1082
|
+
const result = await p.select({
|
|
1083
|
+
message: 'Redis setup',
|
|
1084
|
+
options: [
|
|
1033
1085
|
{ label: 'Docker Compose (recommended for development)', value: 'docker' },
|
|
1034
1086
|
{ label: 'Existing Redis (I have my own Redis)', value: 'existing' },
|
|
1035
1087
|
{ label: 'None (skip Redis)', value: 'none' },
|
|
1036
|
-
]
|
|
1088
|
+
],
|
|
1089
|
+
initialValue: 'docker',
|
|
1090
|
+
});
|
|
1091
|
+
if (p.isCancel(result)) {
|
|
1092
|
+
p.cancel('Cancelled.');
|
|
1093
|
+
process.exit(0);
|
|
1094
|
+
}
|
|
1095
|
+
redisSetup = result;
|
|
1096
|
+
}
|
|
1097
|
+
}
|
|
1098
|
+
// Package manager
|
|
1099
|
+
let packageManager = flags?.pm;
|
|
1100
|
+
if (!packageManager) {
|
|
1101
|
+
const result = await p.select({
|
|
1102
|
+
message: 'Package manager',
|
|
1103
|
+
options: [
|
|
1104
|
+
{ label: 'npm (default)', value: 'npm' },
|
|
1105
|
+
{ label: 'yarn', value: 'yarn' },
|
|
1106
|
+
{ label: 'pnpm', value: 'pnpm' },
|
|
1107
|
+
],
|
|
1108
|
+
initialValue: 'npm',
|
|
1109
|
+
});
|
|
1110
|
+
if (p.isCancel(result)) {
|
|
1111
|
+
p.cancel('Cancelled.');
|
|
1112
|
+
process.exit(0);
|
|
1113
|
+
}
|
|
1114
|
+
packageManager = result;
|
|
1037
1115
|
}
|
|
1038
1116
|
// GitHub Actions
|
|
1039
|
-
|
|
1117
|
+
let enableGitHubActions = flags?.cicd;
|
|
1118
|
+
if (enableGitHubActions === undefined) {
|
|
1119
|
+
const result = await p.confirm({
|
|
1120
|
+
message: 'Set up GitHub Actions CI/CD?',
|
|
1121
|
+
initialValue: true,
|
|
1122
|
+
});
|
|
1123
|
+
if (p.isCancel(result)) {
|
|
1124
|
+
p.cancel('Cancelled.');
|
|
1125
|
+
process.exit(0);
|
|
1126
|
+
}
|
|
1127
|
+
enableGitHubActions = result;
|
|
1128
|
+
}
|
|
1040
1129
|
return {
|
|
1041
1130
|
projectName,
|
|
1042
1131
|
deploymentTarget,
|
|
1043
1132
|
redisSetup,
|
|
1044
1133
|
enableGitHubActions,
|
|
1134
|
+
packageManager,
|
|
1045
1135
|
};
|
|
1046
1136
|
}
|
|
1047
1137
|
async function scaffoldDeploymentFiles(targetDir, options) {
|
|
@@ -1050,10 +1140,11 @@ async function scaffoldDeploymentFiles(targetDir, options) {
|
|
|
1050
1140
|
case 'node': {
|
|
1051
1141
|
const ciDir = path.join(targetDir, 'ci');
|
|
1052
1142
|
await (0, utils_1.ensureDir)(ciDir);
|
|
1053
|
-
await scaffoldFileIfMissing(targetDir, path.join(ciDir, 'Dockerfile'),
|
|
1054
|
-
const dockerCompose = redisSetup === 'docker' ?
|
|
1143
|
+
await scaffoldFileIfMissing(targetDir, path.join(ciDir, 'Dockerfile'), generateDockerfile(options.packageManager));
|
|
1144
|
+
const dockerCompose = redisSetup === 'docker' ? generateDockerComposeWithRedis() : generateDockerComposeNoRedis();
|
|
1055
1145
|
await scaffoldFileIfMissing(targetDir, path.join(ciDir, 'docker-compose.yml'), dockerCompose);
|
|
1056
1146
|
await scaffoldFileIfMissing(targetDir, path.join(ciDir, '.env.docker'), TEMPLATE_ENV_DOCKER_CI);
|
|
1147
|
+
await scaffoldFileIfMissing(targetDir, path.join(targetDir, '.dockerignore'), TEMPLATE_DOCKERIGNORE);
|
|
1057
1148
|
break;
|
|
1058
1149
|
}
|
|
1059
1150
|
case 'vercel':
|
|
@@ -1079,30 +1170,30 @@ const TEMPLATE_ENV_EXAMPLE_BASIC = `
|
|
|
1079
1170
|
PORT=3000
|
|
1080
1171
|
NODE_ENV=development
|
|
1081
1172
|
`;
|
|
1082
|
-
async function scaffoldGitHubActions(targetDir, deploymentTarget) {
|
|
1173
|
+
async function scaffoldGitHubActions(targetDir, deploymentTarget, pm) {
|
|
1083
1174
|
const workflowDir = path.join(targetDir, '.github', 'workflows');
|
|
1084
1175
|
await (0, utils_1.ensureDir)(workflowDir);
|
|
1085
1176
|
// Always create CI and E2E workflows
|
|
1086
|
-
await scaffoldFileIfMissing(targetDir, path.join(workflowDir, 'ci.yml'),
|
|
1087
|
-
await scaffoldFileIfMissing(targetDir, path.join(workflowDir, 'e2e.yml'),
|
|
1177
|
+
await scaffoldFileIfMissing(targetDir, path.join(workflowDir, 'ci.yml'), generateGhCi(pm));
|
|
1178
|
+
await scaffoldFileIfMissing(targetDir, path.join(workflowDir, 'e2e.yml'), generateGhE2e(pm));
|
|
1088
1179
|
// Create deployment workflow based on target
|
|
1089
|
-
const deployTemplate = getDeployWorkflowTemplate(deploymentTarget);
|
|
1180
|
+
const deployTemplate = getDeployWorkflowTemplate(deploymentTarget, pm);
|
|
1090
1181
|
await scaffoldFileIfMissing(targetDir, path.join(workflowDir, 'deploy.yml'), deployTemplate);
|
|
1091
1182
|
}
|
|
1092
|
-
function getDeployWorkflowTemplate(target) {
|
|
1183
|
+
function getDeployWorkflowTemplate(target, pm) {
|
|
1093
1184
|
switch (target) {
|
|
1094
1185
|
case 'node':
|
|
1095
1186
|
return TEMPLATE_GH_DEPLOY_DOCKER;
|
|
1096
1187
|
case 'vercel':
|
|
1097
|
-
return
|
|
1188
|
+
return generateGhDeployVercel(pm);
|
|
1098
1189
|
case 'lambda':
|
|
1099
|
-
return
|
|
1190
|
+
return generateGhDeployLambda(pm);
|
|
1100
1191
|
case 'cloudflare':
|
|
1101
|
-
return
|
|
1192
|
+
return generateGhDeployCloudflare(pm);
|
|
1102
1193
|
}
|
|
1103
1194
|
}
|
|
1104
1195
|
async function scaffoldProject(options) {
|
|
1105
|
-
const { projectName, deploymentTarget, redisSetup, enableGitHubActions } = options;
|
|
1196
|
+
const { projectName, deploymentTarget, redisSetup, enableGitHubActions, packageManager } = options;
|
|
1106
1197
|
const folder = sanitizeForFolder(projectName);
|
|
1107
1198
|
const pkgName = sanitizeForNpm(projectName);
|
|
1108
1199
|
const targetDir = path.resolve(process.cwd(), folder);
|
|
@@ -1133,6 +1224,7 @@ async function scaffoldProject(options) {
|
|
|
1133
1224
|
if (deploymentTarget === 'node') {
|
|
1134
1225
|
console.log((0, colors_1.c)('gray', ` Redis: ${redisSetup}`));
|
|
1135
1226
|
}
|
|
1227
|
+
console.log((0, colors_1.c)('gray', ` Package manager: ${packageManager}`));
|
|
1136
1228
|
console.log((0, colors_1.c)('gray', ` GitHub Actions: ${enableGitHubActions ? 'Yes' : 'No'}`));
|
|
1137
1229
|
console.log('');
|
|
1138
1230
|
process.chdir(targetDir);
|
|
@@ -1140,7 +1232,7 @@ async function scaffoldProject(options) {
|
|
|
1140
1232
|
await (0, tsconfig_1.runInit)(targetDir);
|
|
1141
1233
|
// Create package.json with deployment-specific scripts
|
|
1142
1234
|
const selfVersion = (0, version_1.getSelfVersion)();
|
|
1143
|
-
await upsertPackageJsonWithTarget(targetDir, pkgName, selfVersion, deploymentTarget);
|
|
1235
|
+
await upsertPackageJsonWithTarget(targetDir, pkgName, selfVersion, deploymentTarget, packageManager);
|
|
1144
1236
|
// Scaffold base files
|
|
1145
1237
|
await scaffoldFileIfMissing(targetDir, path.join(targetDir, 'src', 'main.ts'), TEMPLATE_MAIN_TS);
|
|
1146
1238
|
await scaffoldFileIfMissing(targetDir, path.join(targetDir, 'src', 'calc.app.ts'), TEMPLATE_CALC_APP_TS);
|
|
@@ -1155,26 +1247,27 @@ async function scaffoldProject(options) {
|
|
|
1155
1247
|
await scaffoldDeploymentFiles(targetDir, options);
|
|
1156
1248
|
// GitHub Actions
|
|
1157
1249
|
if (enableGitHubActions) {
|
|
1158
|
-
await scaffoldGitHubActions(targetDir, deploymentTarget);
|
|
1250
|
+
await scaffoldGitHubActions(targetDir, deploymentTarget, packageManager);
|
|
1159
1251
|
}
|
|
1160
1252
|
// Dynamic README
|
|
1161
1253
|
await scaffoldFileIfMissing(targetDir, path.join(targetDir, 'README.md'), generateReadme(options));
|
|
1162
1254
|
// Print next steps
|
|
1163
|
-
printNextSteps(folder, deploymentTarget, redisSetup, enableGitHubActions);
|
|
1255
|
+
printNextSteps(folder, deploymentTarget, redisSetup, enableGitHubActions, packageManager);
|
|
1164
1256
|
}
|
|
1165
|
-
function printNextSteps(folder, deploymentTarget, redisSetup, enableGitHubActions) {
|
|
1257
|
+
function printNextSteps(folder, deploymentTarget, redisSetup, enableGitHubActions, pm) {
|
|
1258
|
+
const cfg = PM_CONFIG[pm];
|
|
1166
1259
|
console.log('\nNext steps:');
|
|
1167
1260
|
console.log(` 1) cd ${folder}`);
|
|
1168
|
-
console.log(
|
|
1169
|
-
console.log(
|
|
1170
|
-
console.log(
|
|
1171
|
-
console.log(
|
|
1172
|
-
console.log(
|
|
1261
|
+
console.log(` 2) ${cfg.userInstall}`);
|
|
1262
|
+
console.log(` 3) ${cfg.run} dev `, (0, colors_1.c)('gray', '# tsx watcher + async tsc type-check'));
|
|
1263
|
+
console.log(` 4) ${cfg.run} inspect `, (0, colors_1.c)('gray', '# launch MCP Inspector'));
|
|
1264
|
+
console.log(` 5) ${cfg.run} build `, (0, colors_1.c)('gray', '# compile with tsc via frontmcp build'));
|
|
1265
|
+
console.log(` 6) ${cfg.run} test:e2e `, (0, colors_1.c)('gray', '# run E2E tests'));
|
|
1173
1266
|
if (deploymentTarget === 'node') {
|
|
1174
1267
|
console.log('');
|
|
1175
1268
|
console.log((0, colors_1.c)('cyan', 'Docker:'));
|
|
1176
|
-
console.log(
|
|
1177
|
-
console.log(
|
|
1269
|
+
console.log(` ${cfg.run} docker:up `, (0, colors_1.c)('gray', `# start${redisSetup === 'docker' ? ' Redis +' : ''} app in Docker`));
|
|
1270
|
+
console.log(` ${cfg.run} docker:down `, (0, colors_1.c)('gray', '# stop Docker services'));
|
|
1178
1271
|
}
|
|
1179
1272
|
if (deploymentTarget === 'vercel') {
|
|
1180
1273
|
console.log('');
|
|
@@ -1184,12 +1277,12 @@ function printNextSteps(folder, deploymentTarget, redisSetup, enableGitHubAction
|
|
|
1184
1277
|
if (deploymentTarget === 'lambda') {
|
|
1185
1278
|
console.log('');
|
|
1186
1279
|
console.log((0, colors_1.c)('cyan', 'Deploy to AWS Lambda:'));
|
|
1187
|
-
console.log(
|
|
1280
|
+
console.log(` ${cfg.run} deploy `, (0, colors_1.c)('gray', '# deploy with SAM'));
|
|
1188
1281
|
}
|
|
1189
1282
|
if (deploymentTarget === 'cloudflare') {
|
|
1190
1283
|
console.log('');
|
|
1191
1284
|
console.log((0, colors_1.c)('cyan', 'Deploy to Cloudflare:'));
|
|
1192
|
-
console.log(
|
|
1285
|
+
console.log(` ${cfg.run} deploy `, (0, colors_1.c)('gray', '# deploy with Wrangler'));
|
|
1193
1286
|
}
|
|
1194
1287
|
if (enableGitHubActions) {
|
|
1195
1288
|
console.log('');
|
|
@@ -1200,7 +1293,7 @@ function printNextSteps(folder, deploymentTarget, redisSetup, enableGitHubAction
|
|
|
1200
1293
|
// =============================================================================
|
|
1201
1294
|
// Package.json with Target-Specific Scripts
|
|
1202
1295
|
// =============================================================================
|
|
1203
|
-
async function upsertPackageJsonWithTarget(cwd, nameOverride, selfVersion, deploymentTarget) {
|
|
1296
|
+
async function upsertPackageJsonWithTarget(cwd, nameOverride, selfVersion, deploymentTarget, pm = 'npm') {
|
|
1204
1297
|
const pkgPath = path.join(cwd, 'package.json');
|
|
1205
1298
|
const existing = await (0, utils_1.readJSON)(pkgPath);
|
|
1206
1299
|
const frontmcpLibRange = `~${selfVersion}`;
|
|
@@ -1231,19 +1324,17 @@ async function upsertPackageJsonWithTarget(cwd, nameOverride, selfVersion, deplo
|
|
|
1231
1324
|
type: 'commonjs',
|
|
1232
1325
|
main: 'src/main.ts',
|
|
1233
1326
|
scripts: baseScripts,
|
|
1234
|
-
engines:
|
|
1235
|
-
node: '>=22',
|
|
1236
|
-
npm: '>=10',
|
|
1237
|
-
},
|
|
1327
|
+
engines: PM_CONFIG[pm].engines,
|
|
1238
1328
|
dependencies: {
|
|
1239
1329
|
'@frontmcp/sdk': frontmcpLibRange,
|
|
1240
1330
|
'@frontmcp/plugins': frontmcpLibRange,
|
|
1241
1331
|
'@frontmcp/adapters': frontmcpLibRange,
|
|
1332
|
+
frontmcp: selfVersion,
|
|
1333
|
+
tslib: '^2.5.0',
|
|
1242
1334
|
zod: '^4.0.0',
|
|
1243
1335
|
'reflect-metadata': '^0.2.2',
|
|
1244
1336
|
},
|
|
1245
1337
|
devDependencies: {
|
|
1246
|
-
frontmcp: selfVersion,
|
|
1247
1338
|
'@frontmcp/testing': frontmcpLibRange,
|
|
1248
1339
|
'@swc/core': '^1.11.29',
|
|
1249
1340
|
'@swc/jest': '^0.2.37',
|
|
@@ -1270,8 +1361,7 @@ async function upsertPackageJsonWithTarget(cwd, nameOverride, selfVersion, deplo
|
|
|
1270
1361
|
};
|
|
1271
1362
|
merged.engines = {
|
|
1272
1363
|
...(existing.engines || {}),
|
|
1273
|
-
|
|
1274
|
-
npm: existing.engines?.npm || base.engines.npm,
|
|
1364
|
+
...base.engines,
|
|
1275
1365
|
};
|
|
1276
1366
|
merged.dependencies = {
|
|
1277
1367
|
...(existing.dependencies || {}),
|
|
@@ -1298,6 +1388,8 @@ async function runCreate(projectArg, flags) {
|
|
|
1298
1388
|
options.redisSetup = flags.redis;
|
|
1299
1389
|
if (flags?.cicd !== undefined)
|
|
1300
1390
|
options.enableGitHubActions = flags.cicd;
|
|
1391
|
+
if (flags?.pm)
|
|
1392
|
+
options.packageManager = flags.pm;
|
|
1301
1393
|
if (projectArg)
|
|
1302
1394
|
options.projectName = projectArg;
|
|
1303
1395
|
if (!options.projectName) {
|
|
@@ -1309,21 +1401,10 @@ async function runCreate(projectArg, flags) {
|
|
|
1309
1401
|
return;
|
|
1310
1402
|
}
|
|
1311
1403
|
// Interactive mode
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
}
|
|
1318
|
-
catch (err) {
|
|
1319
|
-
if (err instanceof Error && err.message === 'Project name is required') {
|
|
1320
|
-
console.error((0, colors_1.c)('red', '\nError: Project name is required.'));
|
|
1321
|
-
process.exit(1);
|
|
1322
|
-
}
|
|
1323
|
-
throw err;
|
|
1324
|
-
}
|
|
1325
|
-
finally {
|
|
1326
|
-
prompt.close();
|
|
1327
|
-
}
|
|
1404
|
+
const p = await (0, prompts_1.clack)();
|
|
1405
|
+
p.intro('Create a new FrontMCP project');
|
|
1406
|
+
const options = await collectOptions(projectArg, flags);
|
|
1407
|
+
await scaffoldProject(options);
|
|
1408
|
+
p.outro('Done!');
|
|
1328
1409
|
}
|
|
1329
1410
|
//# sourceMappingURL=create.js.map
|