fa-mcp-sdk 0.2.120 ā 0.2.121
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/bin/fa-mcp.js +83 -24
- package/package.json +3 -1
package/bin/fa-mcp.js
CHANGED
|
@@ -6,6 +6,7 @@ import { fileURLToPath } from 'url';
|
|
|
6
6
|
import readline from 'readline';
|
|
7
7
|
import { v4 as uuidv4 } from 'uuid';
|
|
8
8
|
import chalk from 'chalk';
|
|
9
|
+
import yaml from 'js-yaml';
|
|
9
10
|
|
|
10
11
|
const __filename = fileURLToPath(import.meta.url);
|
|
11
12
|
const __dirname = path.dirname(__filename);
|
|
@@ -71,6 +72,34 @@ const getAsk = () => {
|
|
|
71
72
|
};
|
|
72
73
|
};
|
|
73
74
|
|
|
75
|
+
/**
|
|
76
|
+
* Parse configuration file (JSON or YAML)
|
|
77
|
+
* @param {string} filePath - Path to the configuration file
|
|
78
|
+
* @param {string} content - Content of the file
|
|
79
|
+
* @returns {object} Parsed configuration object
|
|
80
|
+
*/
|
|
81
|
+
const parseConfigFile = (filePath, content) => {
|
|
82
|
+
const ext = path.extname(filePath).toLowerCase();
|
|
83
|
+
|
|
84
|
+
try {
|
|
85
|
+
if (ext === '.json') {
|
|
86
|
+
return JSON.parse(content);
|
|
87
|
+
} else if (ext === '.yaml' || ext === '.yml') {
|
|
88
|
+
return yaml.load(content, { schema: yaml.DEFAULT_SCHEMA });
|
|
89
|
+
} else {
|
|
90
|
+
// Try to detect format by content
|
|
91
|
+
const trimmed = content.trim();
|
|
92
|
+
if (trimmed.startsWith('{') || trimmed.startsWith('[')) {
|
|
93
|
+
return JSON.parse(content);
|
|
94
|
+
} else {
|
|
95
|
+
return yaml.load(content, { schema: yaml.DEFAULT_SCHEMA });
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
} catch (error) {
|
|
99
|
+
throw new Error(`Failed to parse configuration file ${filePath}: ${error.message}`);
|
|
100
|
+
}
|
|
101
|
+
};
|
|
102
|
+
|
|
74
103
|
class MCPGenerator {
|
|
75
104
|
constructor () {
|
|
76
105
|
this.templateDir = path.join(__dirname, '..', 'cli-template');
|
|
@@ -166,6 +195,11 @@ class MCPGenerator {
|
|
|
166
195
|
defaultValue: '<envCode.prod>',
|
|
167
196
|
title: 'Production environment code for Consul service ID generation',
|
|
168
197
|
},
|
|
198
|
+
{
|
|
199
|
+
name: 'NODE_CONSUL_ENV',
|
|
200
|
+
defaultValue: '',
|
|
201
|
+
title: 'Affects how the Consul service ID is formed - as a product or development ID. Valid values: "" | "development" | "production"',
|
|
202
|
+
},
|
|
169
203
|
|
|
170
204
|
{
|
|
171
205
|
name: 'mcp.domain',
|
|
@@ -487,6 +521,18 @@ certificate's public and private keys`,
|
|
|
487
521
|
config[name] = String(enabled);
|
|
488
522
|
continue;
|
|
489
523
|
}
|
|
524
|
+
case 'NODE_CONSUL_ENV': {
|
|
525
|
+
if (currentValue === '') {
|
|
526
|
+
continue;
|
|
527
|
+
}
|
|
528
|
+
value = await ask.optional(title, name, defaultValue);
|
|
529
|
+
if (value === '' || value === 'development' || value === 'production') {
|
|
530
|
+
config[name] = value;
|
|
531
|
+
} else {
|
|
532
|
+
config[name] = '';
|
|
533
|
+
}
|
|
534
|
+
continue;
|
|
535
|
+
}
|
|
490
536
|
|
|
491
537
|
default:
|
|
492
538
|
value = await ask.optional(title, name, defaultValue);
|
|
@@ -534,17 +580,17 @@ certificate's public and private keys`,
|
|
|
534
580
|
|
|
535
581
|
async collectConfiguration () {
|
|
536
582
|
const config = {};
|
|
537
|
-
const configFile = process.argv.find((arg) => arg.endsWith('.json')) ||
|
|
583
|
+
const configFile = process.argv.find((arg) => arg.endsWith('.json') || arg.endsWith('.yaml') || arg.endsWith('.yml')) ||
|
|
538
584
|
process.argv.find((arg) => arg.startsWith('--config='))?.split('=')[1];
|
|
539
585
|
|
|
540
586
|
if (configFile) {
|
|
541
587
|
try {
|
|
542
588
|
const configData = await fs.readFile(configFile, 'utf8');
|
|
543
|
-
const parsedConfig =
|
|
589
|
+
const parsedConfig = parseConfigFile(configFile, configData);
|
|
544
590
|
Object.assign(config, parsedConfig);
|
|
545
591
|
console.log(`š Loaded configuration from: ${hly(configFile)}`);
|
|
546
592
|
} catch (error) {
|
|
547
|
-
console.warn(`ā ļø Warning: Could not load config file ${configFile}`);
|
|
593
|
+
console.warn(`ā ļø Warning: Could not load config file ${configFile}: ${error.message}`);
|
|
548
594
|
}
|
|
549
595
|
}
|
|
550
596
|
|
|
@@ -565,7 +611,9 @@ certificate's public and private keys`,
|
|
|
565
611
|
} else if (configProxy.NODE_ENV === 'production') {
|
|
566
612
|
configProxy.isProduction = 'true';
|
|
567
613
|
}
|
|
568
|
-
|
|
614
|
+
if (config['logger.useFileLogger'] !== 'true') {
|
|
615
|
+
config['logger.dir'] = '';
|
|
616
|
+
}
|
|
569
617
|
let confirmed = false;
|
|
570
618
|
let isRetry = false;
|
|
571
619
|
|
|
@@ -592,34 +640,34 @@ certificate's public and private keys`,
|
|
|
592
640
|
async getTargetPath (config = {}) {
|
|
593
641
|
const ask = getAsk();
|
|
594
642
|
|
|
595
|
-
let
|
|
643
|
+
let tp = process.cwd();
|
|
596
644
|
let createInCurrent;
|
|
597
645
|
let pPath = trim(config.projectAbsPath);
|
|
598
646
|
if (pPath) {
|
|
599
|
-
|
|
600
|
-
console.log(`Create project in: ${hl(
|
|
647
|
+
tp = path.resolve(pPath);
|
|
648
|
+
console.log(`Create project in: ${hl(tp)}${FROM_CONFIG}`);
|
|
601
649
|
} else {
|
|
602
|
-
createInCurrent = await ask.yn(`Create project in current directory? (${hl(
|
|
650
|
+
createInCurrent = await ask.yn(`Create project in current directory? (${hl(tp)})`, '', 'n');
|
|
603
651
|
if (!createInCurrent) {
|
|
604
|
-
|
|
605
|
-
|
|
652
|
+
tp = await ask.question('Enter absolute path for project: ');
|
|
653
|
+
tp = path.resolve(tp);
|
|
606
654
|
}
|
|
607
655
|
}
|
|
608
656
|
|
|
609
|
-
config.projectAbsPath =
|
|
657
|
+
config.projectAbsPath = tp;
|
|
610
658
|
// Create directory if it doesn't exist
|
|
611
659
|
try {
|
|
612
|
-
await fs.access(
|
|
660
|
+
await fs.access(tp);
|
|
613
661
|
} catch {
|
|
614
662
|
console.log('Creating directory recursively...');
|
|
615
|
-
await fs.mkdir(
|
|
663
|
+
await fs.mkdir(tp, { recursive: true });
|
|
616
664
|
}
|
|
617
665
|
|
|
618
|
-
const errMsg = `ā Directory ${hl(
|
|
666
|
+
const errMsg = `ā Directory ${hl(tp)} not empty - cannot create project here. Use an empty directory or specify a different path.`;
|
|
619
667
|
|
|
620
668
|
// Check if directory is empty
|
|
621
669
|
try {
|
|
622
|
-
const files = await fs.readdir(
|
|
670
|
+
const files = await fs.readdir(tp);
|
|
623
671
|
const allowedFiles = [
|
|
624
672
|
'.git',
|
|
625
673
|
'.idea',
|
|
@@ -650,7 +698,7 @@ certificate's public and private keys`,
|
|
|
650
698
|
}
|
|
651
699
|
|
|
652
700
|
ask.close();
|
|
653
|
-
return
|
|
701
|
+
return tp;
|
|
654
702
|
}
|
|
655
703
|
|
|
656
704
|
async copyDirectory (source, target) {
|
|
@@ -728,7 +776,16 @@ certificate's public and private keys`,
|
|
|
728
776
|
return files;
|
|
729
777
|
}
|
|
730
778
|
|
|
731
|
-
async
|
|
779
|
+
async transformTargetFile (config, targetRelPath, transformFn) {
|
|
780
|
+
const targetPath = config.projectAbsPath;
|
|
781
|
+
const targetFullPath = path.join(targetPath, targetRelPath);
|
|
782
|
+
const content = await fs.readFile(targetFullPath, 'utf8');
|
|
783
|
+
const transformedContent = transformFn(content, config);
|
|
784
|
+
await fs.writeFile(targetFullPath, transformedContent, 'utf8');
|
|
785
|
+
}
|
|
786
|
+
|
|
787
|
+
async replaceTemplateParameters (config) {
|
|
788
|
+
const targetPath = config.projectAbsPath;
|
|
732
789
|
const files = await this.getAllFiles(targetPath);
|
|
733
790
|
|
|
734
791
|
for (const filePath of files) {
|
|
@@ -754,10 +811,11 @@ certificate's public and private keys`,
|
|
|
754
811
|
await fs.writeFile(filePath, content, 'utf8');
|
|
755
812
|
}
|
|
756
813
|
}
|
|
814
|
+
if (config['NODE_CONSUL_ENV'] === '') {
|
|
815
|
+
await this.transformTargetFile(config, '.env', (c) => c.replace(/^(NODE_CONSUL_ENV)=([^\r\n]*)/m, '#$1=$2'));
|
|
816
|
+
}
|
|
757
817
|
if (config['claude.isBypassPermissions'] === 'true') {
|
|
758
|
-
const
|
|
759
|
-
const content = await fs.readFile(settingsPath, 'utf8')
|
|
760
|
-
.replace('"acceptEdits"', '"bypassPermissions"')
|
|
818
|
+
const transformFn = (c) => c.replace('"acceptEdits"', '"bypassPermissions"')
|
|
761
819
|
.replace(/"allow": \[\s+"Edit",/, `"allow": [
|
|
762
820
|
"Bash(sudo cp:*)",
|
|
763
821
|
"Bash(sudo:*)",
|
|
@@ -791,11 +849,12 @@ certificate's public and private keys`,
|
|
|
791
849
|
"Bash(unset http_proxy)",
|
|
792
850
|
"Bash(wc:*)",
|
|
793
851
|
"Edit",`);
|
|
794
|
-
await
|
|
852
|
+
await this.transformTargetFile(config, '.claude/settings.json', transformFn);
|
|
795
853
|
}
|
|
796
854
|
}
|
|
797
855
|
|
|
798
|
-
async createProject (
|
|
856
|
+
async createProject (config) {
|
|
857
|
+
const targetPath = config.projectAbsPath;
|
|
799
858
|
// Copy template files
|
|
800
859
|
await this.copyDirectory(this.templateDir, targetPath);
|
|
801
860
|
await fs.copyFile(path.join(targetPath, '.env.example'), path.join(targetPath, '.env')); // VVT
|
|
@@ -829,7 +888,7 @@ certificate's public and private keys`,
|
|
|
829
888
|
}
|
|
830
889
|
|
|
831
890
|
// Replace template parameters
|
|
832
|
-
await this.replaceTemplateParameters(
|
|
891
|
+
await this.replaceTemplateParameters(config);
|
|
833
892
|
|
|
834
893
|
// Replace template placeholders with defaultValue from optionalParams and save as _local.yaml
|
|
835
894
|
if (localYamlContent) {
|
|
@@ -878,7 +937,7 @@ certificate's public and private keys`,
|
|
|
878
937
|
const targetPath = await this.getTargetPath(config);
|
|
879
938
|
|
|
880
939
|
console.log(`\nš Creating project in: ${targetPath}`);
|
|
881
|
-
await this.createProject(
|
|
940
|
+
await this.createProject(config);
|
|
882
941
|
|
|
883
942
|
console.log('\nā
MCP Server template created successfully!');
|
|
884
943
|
console.log('\nš Next steps:');
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "fa-mcp-sdk",
|
|
3
3
|
"productName": "FA MCP SDK",
|
|
4
|
-
"version": "0.2.
|
|
4
|
+
"version": "0.2.121",
|
|
5
5
|
"description": "Core infrastructure and templates for building Model Context Protocol (MCP) servers with TypeScript",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"main": "dist/core/index.js",
|
|
@@ -69,6 +69,7 @@
|
|
|
69
69
|
"dotenv": "^17.2.3",
|
|
70
70
|
"express": "^5.2.1",
|
|
71
71
|
"helmet": "^8.1.0",
|
|
72
|
+
"js-yaml": "^4.1.0",
|
|
72
73
|
"node-cache": "^5.1.2",
|
|
73
74
|
"pgvector": "^0.2.1",
|
|
74
75
|
"rate-limiter-flexible": "^9.0.0",
|
|
@@ -83,6 +84,7 @@
|
|
|
83
84
|
"@types/config": "^3.3.5",
|
|
84
85
|
"@types/cors": "^2.8.19",
|
|
85
86
|
"@types/express": "^5.0.6",
|
|
87
|
+
"@types/js-yaml": "^4.0.9",
|
|
86
88
|
"@types/mssql": "^9.1.8",
|
|
87
89
|
"@types/node": "^24.10.1",
|
|
88
90
|
"@types/swagger-jsdoc": "^6.0.4",
|