@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 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 if (!utils.fileExists(mcpPath)) {
596
- // Local .mcp.json
594
+ } else {
597
595
  const mcpSpinner = utils.createSpinner('Creating .mcp.json').start();
598
- const mcpConfig = {
599
- mcpServers: {
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('Failed to create .mcp.json');
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 { getManagedMcpServerConfig } = require('../core/mcp-config');
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
- if (utils.writeFile(mcpPath, JSON.stringify(mcpConfig, null, 2))) {
32
- spinner.succeed(`Created ${mcpPath}`);
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('Failed to create .mcp.json');
32
+ spinner.fail(`Failed to configure .mcp.json${result.error ? `: ${result.error.message}` : ''}`);
35
33
  }
36
34
  }
37
35
 
@@ -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
- getManagedMcpServerConfig
111
+ PROJECT_MCP_FILENAME,
112
+ getManagedMcpServerConfig,
113
+ isManagedMcpServerConfig,
114
+ ensureProjectMcpConfig
14
115
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@girardmedia/bootspring",
3
- "version": "2.0.52",
3
+ "version": "2.0.53",
4
4
  "description": "Thin client for Bootspring cloud MCP, hosted agents, and paywalled workflow intelligence",
5
5
  "keywords": [
6
6
  "ai",