@girardmedia/bootspring 2.0.52 → 2.0.53
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/bootspring.js +6 -0
- package/cli/init.js +9 -11
- package/cli/mcp.js +10 -12
- package/core/mcp-config.js +102 -1
- package/package.json +1 -1
package/bin/bootspring.js
CHANGED
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
|
|
8
8
|
const path = require('path');
|
|
9
9
|
const selfUpdate = require('../core/self-update');
|
|
10
|
+
const { ensureProjectMcpConfig } = require('../core/mcp-config');
|
|
10
11
|
|
|
11
12
|
const VERSION = require('../package.json').version;
|
|
12
13
|
|
|
@@ -111,6 +112,11 @@ async function main() {
|
|
|
111
112
|
return;
|
|
112
113
|
}
|
|
113
114
|
|
|
115
|
+
const mcpConfigResult = ensureProjectMcpConfig(process.cwd(), { createIfMissing: false });
|
|
116
|
+
if (mcpConfigResult.status === 'updated') {
|
|
117
|
+
console.error('[bootspring] Updated .mcp.json to use the managed latest-package launcher.');
|
|
118
|
+
}
|
|
119
|
+
|
|
114
120
|
try {
|
|
115
121
|
await runCommand(command, args.slice(1));
|
|
116
122
|
} catch (error) {
|
package/cli/init.js
CHANGED
|
@@ -15,7 +15,7 @@ const { execSync } = require('child_process');
|
|
|
15
15
|
const config = require('../core/config');
|
|
16
16
|
const utils = require('../core/utils');
|
|
17
17
|
const contextLoader = require('../core/context-loader');
|
|
18
|
-
const { getManagedMcpServerConfig } = require('../core/mcp-config');
|
|
18
|
+
const { getManagedMcpServerConfig, ensureProjectMcpConfig } = require('../core/mcp-config');
|
|
19
19
|
const { runQuestionnaire } = require('../generators/questionnaire');
|
|
20
20
|
const claudeTemplate = require('../generators/templates/claude.template');
|
|
21
21
|
const seedTemplate = require('../generators/templates/seed.template');
|
|
@@ -543,7 +543,6 @@ ${utils.COLORS.dim}Development scaffolding with intelligence${utils.COLORS.reset
|
|
|
543
543
|
}
|
|
544
544
|
|
|
545
545
|
// 4. MCP configuration (local or global)
|
|
546
|
-
const mcpPath = path.join(projectRoot, '.mcp.json');
|
|
547
546
|
const globalMcpPath = getGlobalMcpPath();
|
|
548
547
|
const isGlobal = isGloballyInstalled();
|
|
549
548
|
const hasGlobalMcp = isGlobalMcpConfigured();
|
|
@@ -592,18 +591,17 @@ ${utils.COLORS.dim}Development scaffolding with intelligence${utils.COLORS.reset
|
|
|
592
591
|
} catch (error) {
|
|
593
592
|
globalSpinner.fail(`Global MCP config failed: ${error.message}`);
|
|
594
593
|
}
|
|
595
|
-
} else
|
|
596
|
-
// Local .mcp.json
|
|
594
|
+
} else {
|
|
597
595
|
const mcpSpinner = utils.createSpinner('Creating .mcp.json').start();
|
|
598
|
-
const
|
|
599
|
-
|
|
600
|
-
bootspring: getManagedMcpServerConfig()
|
|
601
|
-
}
|
|
602
|
-
};
|
|
603
|
-
if (utils.writeFile(mcpPath, JSON.stringify(mcpConfig, null, 2))) {
|
|
596
|
+
const result = ensureProjectMcpConfig(projectRoot, { createIfMissing: true });
|
|
597
|
+
if (result.status === 'created') {
|
|
604
598
|
mcpSpinner.succeed('Created .mcp.json (MCP server config)');
|
|
599
|
+
} else if (result.status === 'updated' || result.status === 'added') {
|
|
600
|
+
mcpSpinner.succeed('Updated .mcp.json (MCP server config)');
|
|
601
|
+
} else if (result.status === 'unchanged') {
|
|
602
|
+
mcpSpinner.succeed('Verified .mcp.json (MCP server config)');
|
|
605
603
|
} else {
|
|
606
|
-
mcpSpinner.fail(
|
|
604
|
+
mcpSpinner.fail(`Failed to configure .mcp.json${result.error ? `: ${result.error.message}` : ''}`);
|
|
607
605
|
}
|
|
608
606
|
}
|
|
609
607
|
|
package/cli/mcp.js
CHANGED
|
@@ -3,12 +3,11 @@
|
|
|
3
3
|
* Manage the hosted MCP proxy configuration.
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
const path = require('path');
|
|
7
6
|
const config = require('../core/config');
|
|
8
7
|
const utils = require('../core/utils');
|
|
9
8
|
const api = require('../core/api-client');
|
|
10
9
|
const auth = require('../core/auth');
|
|
11
|
-
const {
|
|
10
|
+
const { ensureProjectMcpConfig } = require('../core/mcp-config');
|
|
12
11
|
const { redactErrorMessage } = require('../core/redaction');
|
|
13
12
|
|
|
14
13
|
const C = utils.COLORS;
|
|
@@ -20,18 +19,17 @@ async function startServer() {
|
|
|
20
19
|
|
|
21
20
|
function generateConfig() {
|
|
22
21
|
const cfg = config.load();
|
|
23
|
-
const mcpPath = path.join(cfg._projectRoot, '.mcp.json');
|
|
24
|
-
const mcpConfig = {
|
|
25
|
-
mcpServers: {
|
|
26
|
-
bootspring: getManagedMcpServerConfig()
|
|
27
|
-
}
|
|
28
|
-
};
|
|
29
|
-
|
|
30
22
|
const spinner = utils.createSpinner('Generating .mcp.json').start();
|
|
31
|
-
|
|
32
|
-
|
|
23
|
+
const result = ensureProjectMcpConfig(cfg._projectRoot, { createIfMissing: true });
|
|
24
|
+
|
|
25
|
+
if (result.status === 'created') {
|
|
26
|
+
spinner.succeed(`Created ${result.path}`);
|
|
27
|
+
} else if (result.status === 'updated' || result.status === 'added') {
|
|
28
|
+
spinner.succeed(`Updated ${result.path}`);
|
|
29
|
+
} else if (result.status === 'unchanged') {
|
|
30
|
+
spinner.succeed(`Already configured ${result.path}`);
|
|
33
31
|
} else {
|
|
34
|
-
spinner.fail(
|
|
32
|
+
spinner.fail(`Failed to configure .mcp.json${result.error ? `: ${result.error.message}` : ''}`);
|
|
35
33
|
}
|
|
36
34
|
}
|
|
37
35
|
|
package/core/mcp-config.js
CHANGED
|
@@ -1,4 +1,8 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
|
|
1
4
|
const PACKAGE_NAME = require('../package.json').name || '@girardmedia/bootspring';
|
|
5
|
+
const PROJECT_MCP_FILENAME = '.mcp.json';
|
|
2
6
|
|
|
3
7
|
function getManagedMcpServerConfig() {
|
|
4
8
|
return {
|
|
@@ -8,7 +12,104 @@ function getManagedMcpServerConfig() {
|
|
|
8
12
|
};
|
|
9
13
|
}
|
|
10
14
|
|
|
15
|
+
function isManagedMcpServerConfig(serverConfig) {
|
|
16
|
+
const expected = getManagedMcpServerConfig();
|
|
17
|
+
return (
|
|
18
|
+
serverConfig?.command === expected.command &&
|
|
19
|
+
Array.isArray(serverConfig?.args) &&
|
|
20
|
+
serverConfig.args.join('\u0000') === expected.args.join('\u0000')
|
|
21
|
+
);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function normalizeManagedMcpServerConfig(existingServerConfig) {
|
|
25
|
+
const managedConfig = getManagedMcpServerConfig();
|
|
26
|
+
const env = existingServerConfig?.env && typeof existingServerConfig.env === 'object' && !Array.isArray(existingServerConfig.env)
|
|
27
|
+
? existingServerConfig.env
|
|
28
|
+
: {};
|
|
29
|
+
|
|
30
|
+
return {
|
|
31
|
+
...managedConfig,
|
|
32
|
+
env
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function ensureProjectMcpConfig(projectRoot = process.cwd(), options = {}) {
|
|
37
|
+
const createIfMissing = options.createIfMissing === true;
|
|
38
|
+
const mcpPath = path.join(projectRoot, PROJECT_MCP_FILENAME);
|
|
39
|
+
|
|
40
|
+
if (!fs.existsSync(mcpPath)) {
|
|
41
|
+
if (!createIfMissing) {
|
|
42
|
+
return { status: 'missing', changed: false, path: mcpPath };
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const nextConfig = {
|
|
46
|
+
mcpServers: {
|
|
47
|
+
bootspring: getManagedMcpServerConfig()
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
try {
|
|
52
|
+
fs.writeFileSync(mcpPath, JSON.stringify(nextConfig, null, 2));
|
|
53
|
+
return { status: 'created', changed: true, path: mcpPath, config: nextConfig };
|
|
54
|
+
} catch (error) {
|
|
55
|
+
return { status: 'error', changed: false, path: mcpPath, error };
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
let currentConfig;
|
|
60
|
+
try {
|
|
61
|
+
currentConfig = JSON.parse(fs.readFileSync(mcpPath, 'utf8'));
|
|
62
|
+
} catch (error) {
|
|
63
|
+
return { status: 'invalid', changed: false, path: mcpPath, error };
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
if (!currentConfig || typeof currentConfig !== 'object' || Array.isArray(currentConfig)) {
|
|
67
|
+
return {
|
|
68
|
+
status: 'invalid',
|
|
69
|
+
changed: false,
|
|
70
|
+
path: mcpPath,
|
|
71
|
+
error: new Error('Project MCP config must be a JSON object')
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const currentServers = currentConfig.mcpServers && typeof currentConfig.mcpServers === 'object' && !Array.isArray(currentConfig.mcpServers)
|
|
76
|
+
? currentConfig.mcpServers
|
|
77
|
+
: {};
|
|
78
|
+
const currentBootspring = currentServers.bootspring;
|
|
79
|
+
|
|
80
|
+
if (currentBootspring && isManagedMcpServerConfig(currentBootspring)) {
|
|
81
|
+
return { status: 'unchanged', changed: false, path: mcpPath, config: currentConfig };
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
if (!currentBootspring && !createIfMissing) {
|
|
85
|
+
return { status: 'absent', changed: false, path: mcpPath, config: currentConfig };
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const nextConfig = {
|
|
89
|
+
...currentConfig,
|
|
90
|
+
mcpServers: {
|
|
91
|
+
...currentServers,
|
|
92
|
+
bootspring: normalizeManagedMcpServerConfig(currentBootspring)
|
|
93
|
+
}
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
try {
|
|
97
|
+
fs.writeFileSync(mcpPath, JSON.stringify(nextConfig, null, 2));
|
|
98
|
+
return {
|
|
99
|
+
status: currentBootspring ? 'updated' : 'added',
|
|
100
|
+
changed: true,
|
|
101
|
+
path: mcpPath,
|
|
102
|
+
config: nextConfig
|
|
103
|
+
};
|
|
104
|
+
} catch (error) {
|
|
105
|
+
return { status: 'error', changed: false, path: mcpPath, error };
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
11
109
|
module.exports = {
|
|
12
110
|
PACKAGE_NAME,
|
|
13
|
-
|
|
111
|
+
PROJECT_MCP_FILENAME,
|
|
112
|
+
getManagedMcpServerConfig,
|
|
113
|
+
isManagedMcpServerConfig,
|
|
114
|
+
ensureProjectMcpConfig
|
|
14
115
|
};
|