@bike4mind/cli 0.2.5-feat-cli-docker-mcp-support.17095 → 0.2.5-feat-server-side-tool-execution.17096

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/README.md CHANGED
@@ -135,52 +135,46 @@ Check your current API configuration:
135
135
  /api-info
136
136
  ```
137
137
 
138
- ### Tool API Keys
138
+ ### Built-in Tools
139
+
140
+ The CLI provides access to various tools that execute server-side using B4M company API keys:
141
+
142
+ **Local Tools (always available):**
143
+ - ✅ `dice_roll` - Roll dice with custom notation
144
+ - ✅ `math_evaluate` - Evaluate mathematical expressions
145
+ - ✅ `current_datetime` - Get current date and time
146
+ - ✅ `prompt_enhancement` - Improve prompts using LLM
147
+ - ✅ `file_read` - Read files from local filesystem
148
+ - ✅ `create_file` - Create or update files
149
+ - ✅ `glob_files` - Find files by pattern
150
+ - ✅ `grep_search` - Search file contents
151
+ - ✅ `delete_file` - Delete files
152
+
153
+ **B4M Server Tools (require authentication):**
154
+ - 🌐 `weather` - Get weather information worldwide (via OpenWeather)
155
+ - 🔍 `serper` - Search the web using Google (via Serper API)
156
+ - 🔬 `deep_research` - Perform in-depth research using web search
139
157
 
140
- Some built-in tools require API keys to function. Add them to `~/.bike4mind/config.json`:
141
-
142
- ```json
143
- {
144
- "toolApiKeys": {
145
- "openweather": "your-openweather-key",
146
- "serper": "your-serper-key"
147
- }
148
- }
149
- ```
150
-
151
- Or use environment variables (config takes precedence):
152
-
153
- ```bash
154
- export OPENWEATHER_API_KEY="your-key-here"
155
- export SERPER_API_KEY="your-key-here"
156
- ```
157
-
158
- **Available Tools:**
159
- - ✅ `dice_roll` - No API key needed
160
- - ✅ `math_evaluate` - No API key needed
161
- - ✅ `current_datetime` - No API key needed
162
- - ✅ `prompt_enhancement` - No API key needed
163
- - 🔑 `weather_info` - Requires `toolApiKeys.openweather`
164
- - 🔑 `web_search` - Requires `toolApiKeys.serper`
165
- - 🔑 `deep_research` - Requires `toolApiKeys.serper`
166
-
167
- **Get API Keys:**
168
- - OpenWeather API: https://openweathermap.org/api
169
- - Serper API: https://serper.dev/
158
+ **Authentication:**
159
+ B4M server tools execute on the backend using company-managed API keys. Simply authenticate with `/login` to access them. No personal API keys needed!
170
160
 
171
- ### Optional: MCP Servers
161
+ ### MCP Servers
172
162
 
173
- MCP (Model Context Protocol) servers provide additional tools and capabilities. Configure them in `~/.bike4mind/config.json`:
163
+ MCP (Model Context Protocol) servers provide additional tools and capabilities.
174
164
 
175
- #### Internal MCP Servers (Node.js)
165
+ **Built-in B4M Server:**
166
+ The CLI includes a built-in B4M MCP server (`b4m-tools`) that provides server-side tool execution. This server is automatically configured when you authenticate with `/login`.
176
167
 
177
- For MCP servers bundled with the monorepo, you can omit `command` and `args` - the CLI will automatically discover them:
168
+ **Additional MCP Servers:**
169
+ You can add optional MCP servers in `~/.bike4mind/config.json`:
178
170
 
179
171
  ```json
180
172
  {
181
173
  "mcpServers": [
182
174
  {
183
175
  "name": "github",
176
+ "command": "node",
177
+ "args": ["path/to/mcp/server.js"],
184
178
  "env": {
185
179
  "GITHUB_ACCESS_TOKEN": "ghp_..."
186
180
  },
@@ -190,56 +184,12 @@ For MCP servers bundled with the monorepo, you can omit `command` and `args` - t
190
184
  }
191
185
  ```
192
186
 
193
- #### External MCP Servers (Docker)
194
-
195
- You can run MCP servers in Docker containers for isolation and portability:
196
-
197
- ```json
198
- {
199
- "mcpServers": [
200
- {
201
- "name": "github",
202
- "command": "docker",
203
- "args": ["run", "-i", "--rm", "-e", "GITHUB_TOKEN", "ghcr.io/github/github-mcp-server"],
204
- "env": {
205
- "GITHUB_TOKEN": "ghp_..."
206
- },
207
- "enabled": true
208
- }
209
- ]
210
- }
211
- ```
212
-
213
- **Docker Configuration Notes:**
214
- - The `-i` flag is required for stdin communication with the MCP server
215
- - Use `-e VAR_NAME` (without value) to pass environment variables from the host
216
- - The `--rm` flag ensures containers are cleaned up after use
217
- - Requires Docker to be installed and running
218
-
219
- #### External MCP Servers (npx/Custom Commands)
220
-
221
- You can also run any MCP server via npx or custom executables:
222
-
223
- ```json
224
- {
225
- "mcpServers": [
226
- {
227
- "name": "filesystem",
228
- "command": "npx",
229
- "args": ["-y", "@modelcontextprotocol/server-filesystem", "/path/to/allowed/dir"],
230
- "env": {},
231
- "enabled": true
232
- }
233
- ]
234
- }
235
- ```
236
-
237
- **Available Built-in MCP Servers:**
187
+ **Optional MCP Servers:**
238
188
  - 🔧 **GitHub** - Repository management, issues, PRs (requires `GITHUB_ACCESS_TOKEN`)
239
189
  - 🔧 **LinkedIn** - Post management, analytics (requires `LINKEDIN_ACCESS_TOKEN`, `COMPANY_NAME`)
240
190
  - 🔧 **Atlassian** - Jira/Confluence integration (requires `ATLASSIAN_ACCESS_TOKEN`, `ATLASSIAN_CLOUD_ID`, `ATLASSIAN_SITE_URL`)
241
191
 
242
- **Note:** Internal MCP servers must be built and available in the `b4m-core/packages/mcp/dist/src/` directory. The CLI will automatically find them if you're running from the monorepo. For Docker-based servers, ensure Docker is installed and the image is accessible.
192
+ **Note:** The built-in `b4m-tools` MCP server is automatically bundled with the CLI and configured on authentication. Optional MCP servers can be added as needed for additional integrations.
243
193
 
244
194
  ## Session Storage
245
195
 
@@ -0,0 +1,79 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * B4M MCP Proxy Wrapper
4
+ *
5
+ * This wrapper script reads the CLI configuration to get the current JWT token
6
+ * and API URL, then launches the MCP proxy with those credentials.
7
+ *
8
+ * This allows the MCP proxy to authenticate with the B4M backend without
9
+ * requiring users to manually set environment variables.
10
+ *
11
+ * The wrapper reads from ~/.bike4mind/config.json to get:
12
+ * - auth.accessToken (JWT token)
13
+ * - apiConfig.customUrl (custom API URL, if set)
14
+ */
15
+
16
+ import { spawn } from 'child_process';
17
+ import { join, dirname } from 'path';
18
+ import { homedir } from 'os';
19
+ import { readFileSync } from 'fs';
20
+ import { fileURLToPath } from 'url';
21
+
22
+ const __dirname = dirname(fileURLToPath(import.meta.url));
23
+
24
+ // Read config to get current JWT token and API URL
25
+ const configPath = process.env.B4M_CONFIG_PATH || join(homedir(), '.bike4mind', 'config.json');
26
+
27
+ let authToken = '';
28
+ let apiUrl = 'https://api.bike4mind.com';
29
+
30
+ try {
31
+ const configData = readFileSync(configPath, 'utf-8');
32
+ const config = JSON.parse(configData);
33
+
34
+ // Get JWT access token
35
+ authToken = config.auth?.accessToken || '';
36
+
37
+ // Get custom API URL if configured
38
+ apiUrl = config.apiConfig?.customUrl || apiUrl;
39
+
40
+ if (!authToken) {
41
+ console.warn('⚠️ Warning: No authentication token found in config');
42
+ console.warn(' B4M server tools will not be available.');
43
+ console.warn(' Run /login in the CLI to authenticate.');
44
+ }
45
+ } catch (error) {
46
+ // Config doesn't exist or is invalid
47
+ // This is OK - user might not be authenticated yet
48
+ if (error.code !== 'ENOENT') {
49
+ console.error('Warning: Could not read B4M config:', error.message);
50
+ }
51
+ }
52
+
53
+ // Path to the bundled MCP proxy
54
+ const mcpProxyPath = join(__dirname, '../dist/mcp-proxy/index.js');
55
+
56
+ // Launch MCP proxy with dynamic environment variables
57
+ const child = spawn('node', [mcpProxyPath], {
58
+ stdio: 'inherit', // Inherit stdin/stdout/stderr for MCP protocol
59
+ env: {
60
+ ...process.env,
61
+ B4M_AUTH_TOKEN: authToken,
62
+ B4M_API_URL: apiUrl,
63
+ B4M_TIMEOUT: process.env.B4M_TIMEOUT || '30000',
64
+ },
65
+ });
66
+
67
+ // Handle process exit
68
+ child.on('exit', (code) => {
69
+ process.exit(code || 0);
70
+ });
71
+
72
+ // Handle signals
73
+ process.on('SIGINT', () => {
74
+ child.kill('SIGINT');
75
+ });
76
+
77
+ process.on('SIGTERM', () => {
78
+ child.kill('SIGTERM');
79
+ });
package/dist/index.js CHANGED
@@ -1408,21 +1408,21 @@ var AuthTokensSchema = z.object({
1408
1408
  var ApiConfigSchema = z.object({
1409
1409
  customUrl: z.string().url().optional()
1410
1410
  });
1411
+ var LegacyApiConfigSchema = z.object({
1412
+ environment: z.enum(["production", "staging", "preview", "local", "custom"]),
1413
+ customUrl: z.string().url().optional()
1414
+ });
1411
1415
  var CliConfigSchema = z.object({
1412
1416
  version: z.string(),
1413
1417
  userId: z.string(),
1414
1418
  auth: AuthTokensSchema.optional(),
1415
1419
  defaultModel: z.string(),
1416
1420
  apiConfig: ApiConfigSchema.optional(),
1417
- toolApiKeys: z.object({
1418
- openweather: z.string().optional(),
1419
- serper: z.string().optional()
1420
- }).optional(),
1421
1421
  mcpServers: z.array(
1422
1422
  z.object({
1423
1423
  name: z.string(),
1424
- command: z.string().optional(),
1425
- args: z.array(z.string()).optional(),
1424
+ command: z.string(),
1425
+ args: z.array(z.string()),
1426
1426
  env: z.record(z.string()),
1427
1427
  enabled: z.boolean()
1428
1428
  })
@@ -1442,15 +1442,36 @@ var CliConfigSchema = z.object({
1442
1442
  }),
1443
1443
  trustedTools: z.array(z.string()).optional().default([])
1444
1444
  });
1445
+ var getCliPackageRoot = () => {
1446
+ const currentFile = new URL(import.meta.url).pathname;
1447
+ const currentDir = path4.dirname(currentFile);
1448
+ if (currentDir.endsWith("/dist") || currentDir.includes("/dist/")) {
1449
+ return path4.resolve(currentDir, "..");
1450
+ }
1451
+ if (currentDir.includes("/src")) {
1452
+ const srcIndex = currentDir.lastIndexOf("/src");
1453
+ return currentDir.substring(0, srcIndex);
1454
+ }
1455
+ return process.cwd();
1456
+ };
1445
1457
  var DEFAULT_CONFIG = {
1446
1458
  version: "0.1.0",
1447
1459
  userId: uuidv4(),
1448
1460
  defaultModel: "claude-sonnet-4-5-20250929",
1449
- toolApiKeys: {
1450
- openweather: void 0,
1451
- serper: void 0
1452
- },
1453
- mcpServers: [],
1461
+ mcpServers: [
1462
+ // B4M server-side tools (weather, serper) via MCP proxy
1463
+ {
1464
+ name: "b4m-tools",
1465
+ command: "node",
1466
+ args: [path4.join(getCliPackageRoot(), "bin/b4m-mcp-proxy.mjs")],
1467
+ // Absolute path to wrapper script
1468
+ env: {
1469
+ // Token and API URL injected dynamically by wrapper script
1470
+ B4M_TIMEOUT: "30000"
1471
+ },
1472
+ enabled: true
1473
+ }
1474
+ ],
1454
1475
  preferences: {
1455
1476
  maxTokens: 4096,
1456
1477
  temperature: 0.7,
@@ -1505,11 +1526,14 @@ var ConfigStore = class {
1505
1526
  const data = await fs4.readFile(this.configPath, "utf-8");
1506
1527
  const rawConfig = JSON.parse(data);
1507
1528
  if (rawConfig.apiConfig && "environment" in rawConfig.apiConfig) {
1508
- const oldApiConfig = rawConfig.apiConfig;
1509
- if (oldApiConfig.environment === "custom" && oldApiConfig.customUrl) {
1510
- rawConfig.apiConfig = { customUrl: oldApiConfig.customUrl };
1511
- } else {
1512
- rawConfig.apiConfig = {};
1529
+ const legacyConfigResult = LegacyApiConfigSchema.safeParse(rawConfig.apiConfig);
1530
+ if (legacyConfigResult.success) {
1531
+ const oldApiConfig = legacyConfigResult.data;
1532
+ if (oldApiConfig.environment === "custom" && oldApiConfig.customUrl) {
1533
+ rawConfig.apiConfig = { customUrl: oldApiConfig.customUrl };
1534
+ } else {
1535
+ rawConfig.apiConfig = {};
1536
+ }
1513
1537
  }
1514
1538
  }
1515
1539
  const validated = CliConfigSchema.parse(rawConfig);
@@ -1526,10 +1550,6 @@ var ConfigStore = class {
1526
1550
  ...DEFAULT_CONFIG.tools,
1527
1551
  ...validated.tools
1528
1552
  },
1529
- toolApiKeys: {
1530
- ...DEFAULT_CONFIG.toolApiKeys,
1531
- ...validated.toolApiKeys || {}
1532
- },
1533
1553
  trustedTools: validated.trustedTools || []
1534
1554
  };
1535
1555
  return this.config;
@@ -1564,10 +1584,6 @@ var ConfigStore = class {
1564
1584
  tools: {
1565
1585
  ...existingConfig.tools,
1566
1586
  ...config.tools || {}
1567
- },
1568
- toolApiKeys: {
1569
- ...existingConfig.toolApiKeys,
1570
- ...config.toolApiKeys || {}
1571
1587
  }
1572
1588
  };
1573
1589
  }
@@ -1720,6 +1736,26 @@ var ConfigStore = class {
1720
1736
  }
1721
1737
  await this.save(config);
1722
1738
  }
1739
+ /**
1740
+ * Ensure B4M MCP server is configured (auto-add if missing)
1741
+ * This provides server-side tools (weather, serper) to the CLI
1742
+ */
1743
+ async ensureB4mMcpServer() {
1744
+ const config = await this.load();
1745
+ const hasB4mServer = config.mcpServers.some((s) => s.name === "b4m-tools");
1746
+ if (!hasB4mServer) {
1747
+ config.mcpServers.push({
1748
+ name: "b4m-tools",
1749
+ command: "node",
1750
+ args: [path4.join(getCliPackageRoot(), "bin/b4m-mcp-proxy.mjs")],
1751
+ env: {
1752
+ B4M_TIMEOUT: "30000"
1753
+ },
1754
+ enabled: true
1755
+ });
1756
+ await this.save(config);
1757
+ }
1758
+ }
1723
1759
  };
1724
1760
 
1725
1761
  // src/storage/CommandHistoryStore.ts
@@ -4281,8 +4317,8 @@ async function processAndStoreImages(images, context) {
4281
4317
  const buffer = await downloadImage(image);
4282
4318
  const fileType = await fileTypeFromBuffer2(buffer);
4283
4319
  const filename = `${uuidv45()}.${fileType?.ext}`;
4284
- const path13 = await context.imageGenerateStorage.upload(buffer, filename, {});
4285
- return path13;
4320
+ const path14 = await context.imageGenerateStorage.upload(buffer, filename, {});
4321
+ return path14;
4286
4322
  }));
4287
4323
  }
4288
4324
  async function updateQuestAndReturnMarkdown(storedImageUrls, context) {
@@ -5466,8 +5502,8 @@ async function processAndStoreImage(imageUrl, context) {
5466
5502
  const buffer = await downloadImage2(imageUrl);
5467
5503
  const fileType = await fileTypeFromBuffer3(buffer);
5468
5504
  const filename = `${uuidv46()}.${fileType?.ext}`;
5469
- const path13 = await context.imageGenerateStorage.upload(buffer, filename, {});
5470
- return path13;
5505
+ const path14 = await context.imageGenerateStorage.upload(buffer, filename, {});
5506
+ return path14;
5471
5507
  }
5472
5508
  async function updateQuestAndReturnMarkdown2(storedImagePath, context) {
5473
5509
  await context.onFinish?.("edit_image", storedImagePath);
@@ -8186,21 +8222,21 @@ var NoOpStorage = class extends BaseStorage {
8186
8222
  async upload(input, destination, options) {
8187
8223
  return `/tmp/${destination}`;
8188
8224
  }
8189
- async download(path13) {
8225
+ async download(path14) {
8190
8226
  throw new Error("Download not supported in CLI");
8191
8227
  }
8192
- async delete(path13) {
8228
+ async delete(path14) {
8193
8229
  }
8194
- async getSignedUrl(path13) {
8195
- return `/tmp/${path13}`;
8230
+ async getSignedUrl(path14) {
8231
+ return `/tmp/${path14}`;
8196
8232
  }
8197
- getPublicUrl(path13) {
8198
- return `/tmp/${path13}`;
8233
+ getPublicUrl(path14) {
8234
+ return `/tmp/${path14}`;
8199
8235
  }
8200
- async getPreview(path13) {
8201
- return `/tmp/${path13}`;
8236
+ async getPreview(path14) {
8237
+ return `/tmp/${path14}`;
8202
8238
  }
8203
- async getMetadata(path13) {
8239
+ async getMetadata(path14) {
8204
8240
  return { size: 0, contentType: "application/octet-stream" };
8205
8241
  }
8206
8242
  };
@@ -8272,7 +8308,7 @@ function wrapToolWithPermission(tool, permissionManager, showPermissionPrompt, a
8272
8308
  }
8273
8309
  };
8274
8310
  }
8275
- function generateCliTools(userId, llm, model, permissionManager, showPermissionPrompt, agentContext, toolApiKeys) {
8311
+ function generateCliTools(userId, llm, model, permissionManager, showPermissionPrompt, agentContext) {
8276
8312
  const logger2 = new CliLogger();
8277
8313
  const storage = new NoOpStorage();
8278
8314
  const user = {
@@ -8287,50 +8323,27 @@ function generateCliTools(userId, llm, model, permissionManager, showPermissionP
8287
8323
  };
8288
8324
  const onFinish = async (toolName, data) => {
8289
8325
  };
8290
- const mockAdminSettings = {
8291
- findBySettingName: async (settingName) => {
8292
- if (settingName === "OpenWeatherKey") {
8293
- const key = toolApiKeys?.openweather || process.env.OPENWEATHER_API_KEY;
8294
- if (key) {
8295
- return { settingValue: key };
8296
- }
8297
- }
8298
- if (settingName === "SerperKey") {
8299
- const key = toolApiKeys?.serper || process.env.SERPER_API_KEY;
8300
- if (key) {
8301
- return { settingValue: key };
8302
- }
8303
- }
8304
- return null;
8305
- }
8306
- };
8307
8326
  const dbAdapters = {
8308
8327
  db: {
8309
8328
  apiKeys: null,
8310
- adminSettings: mockAdminSettings
8329
+ adminSettings: null
8311
8330
  }
8312
8331
  };
8313
- const hasSerperKey = !!(toolApiKeys?.serper || process.env.SERPER_API_KEY);
8314
- const hasOpenWeatherKey = !!(toolApiKeys?.openweather || process.env.OPENWEATHER_API_KEY);
8315
8332
  const toolConfig = {
8316
- // Enable these tools for CLI (no external API keys needed)
8333
+ // Local-only tools (no external API keys needed)
8317
8334
  dice_roll: {},
8318
8335
  math_evaluate: {},
8319
8336
  current_datetime: {},
8320
8337
  prompt_enhancement: {},
8321
- // File operation tools (CLI-specific)
8338
+ // File operation tools (CLI-specific, local execution)
8322
8339
  file_read: {},
8323
8340
  create_file: {},
8324
8341
  glob_files: {},
8325
8342
  grep_search: {},
8326
- delete_file: {},
8327
- // Tools requiring API keys (only include if API key is available)
8328
- ...hasSerperKey && { web_search: {} },
8329
- // Requires SERPER_API_KEY
8330
- ...hasOpenWeatherKey && { weather_info: {} },
8331
- // Requires OPENWEATHER_API_KEY
8332
- ...hasSerperKey && { deep_research: {} }
8333
- // Requires SERPER_API_KEY
8343
+ delete_file: {}
8344
+ // REMOVED: weather_info, web_search, deep_research
8345
+ // These are now provided by B4M MCP server (b4m-tools)
8346
+ // They execute server-side using B4M company API keys
8334
8347
  // Disable web-only tools
8335
8348
  // blog_publish: undefined,
8336
8349
  // blog_edit: undefined,
@@ -8790,16 +8803,14 @@ var MCPClient = class {
8790
8803
  mcp;
8791
8804
  transport = null;
8792
8805
  envVariables;
8793
- customCommand;
8794
- customArgs;
8806
+ customScriptPath;
8795
8807
  tools = [];
8796
8808
  serverName;
8797
- constructor({ envVariables, name, selectedRepositories, command, args }) {
8809
+ constructor({ envVariables, name, selectedRepositories, customScriptPath }) {
8798
8810
  this.mcp = new Client2({ name: "mcp-client-cli", version: "1.0.0" });
8799
8811
  this.envVariables = [...envVariables];
8800
8812
  this.serverName = name;
8801
- this.customCommand = command;
8802
- this.customArgs = args;
8813
+ this.customScriptPath = customScriptPath;
8803
8814
  if (name === "github" && selectedRepositories) {
8804
8815
  const reposJson = JSON.stringify(selectedRepositories);
8805
8816
  this.envVariables.push({
@@ -8810,16 +8821,12 @@ var MCPClient = class {
8810
8821
  }
8811
8822
  async connectToServer() {
8812
8823
  try {
8813
- const envVarsObject = this.envVariables.reduce((acc, env) => ({
8814
- ...acc,
8815
- [env.key]: env.value
8816
- }), {});
8817
- let command;
8818
- let args;
8819
- if (this.customCommand && this.customCommand.trim() !== "") {
8820
- command = this.customCommand;
8821
- args = this.customArgs ?? [];
8822
- console.log(`[MCP] Using external command for ${this.serverName}: ${command} ${args.join(" ")}`);
8824
+ let serverScriptPath;
8825
+ if (this.customScriptPath) {
8826
+ if (!existsSync6(this.customScriptPath)) {
8827
+ throw new Error(`Custom MCP server script not found at: ${this.customScriptPath}`);
8828
+ }
8829
+ serverScriptPath = this.customScriptPath;
8823
8830
  } else {
8824
8831
  const root = process.env.INIT_CWD || process.cwd();
8825
8832
  const candidatePaths = [
@@ -8835,23 +8842,25 @@ var MCPClient = class {
8835
8842
  path12.join(root, `/b4m-core/packages/mcp/dist/src/${this.serverName}/index.js`),
8836
8843
  path12.join(root, "core", "mcp", "servers", this.serverName, "dist", "index.js")
8837
8844
  ];
8838
- const serverScriptPath = candidatePaths.find((p) => existsSync6(p));
8839
- if (!serverScriptPath) {
8845
+ const foundPath = candidatePaths.find((p) => existsSync6(p));
8846
+ if (!foundPath) {
8840
8847
  const getDirectories = (source) => readdirSync3(source, { withFileTypes: true }).filter((dirent) => dirent.isDirectory()).map((dirent) => dirent.name);
8841
8848
  console.error(`[MCP] Server script not found. Tried paths:`, candidatePaths);
8842
8849
  console.error(`[MCP] Available directories in ${root}:`, getDirectories(root));
8843
8850
  throw new Error(`MCP Server script path not found for ${this.serverName}. Root: ${root}, Available dirs: ${getDirectories(root).join(", ")}`);
8844
8851
  }
8845
- command = process.execPath;
8846
- args = [serverScriptPath];
8847
- console.log(`[MCP] Using server: ${this.serverName} at ${serverScriptPath}`);
8852
+ serverScriptPath = foundPath;
8848
8853
  }
8854
+ console.log(`[MCP] Using server: ${this.serverName} at ${serverScriptPath}`);
8849
8855
  this.transport = new StdioClientTransport({
8850
- command,
8851
- args,
8856
+ command: process.execPath,
8857
+ args: [serverScriptPath],
8852
8858
  env: {
8853
- ...Object.fromEntries(Object.entries(process.env).filter((entry) => entry[1] !== void 0)),
8854
- ...envVarsObject
8859
+ ...process.env,
8860
+ ...this.envVariables.reduce((acc, env) => ({
8861
+ ...acc,
8862
+ [env.key]: env.value
8863
+ }), {})
8855
8864
  }
8856
8865
  });
8857
8866
  this.transport.onerror = (error) => {
@@ -8922,6 +8931,20 @@ var MCPClient = class {
8922
8931
  };
8923
8932
 
8924
8933
  // src/utils/mcpAdapter.ts
8934
+ import path13 from "path";
8935
+ import { fileURLToPath } from "url";
8936
+ var getCliPackageRoot2 = () => {
8937
+ const currentFile = fileURLToPath(import.meta.url);
8938
+ const currentDir = path13.dirname(currentFile);
8939
+ if (currentDir.includes("/dist")) {
8940
+ return path13.resolve(currentDir, "..");
8941
+ }
8942
+ if (currentDir.includes("/src")) {
8943
+ const srcIndex = currentDir.lastIndexOf("/src");
8944
+ return currentDir.substring(0, srcIndex);
8945
+ }
8946
+ return process.cwd();
8947
+ };
8925
8948
  var McpManager = class {
8926
8949
  constructor(config) {
8927
8950
  this.servers = /* @__PURE__ */ new Map();
@@ -8957,11 +8980,16 @@ var McpManager = class {
8957
8980
  key,
8958
8981
  value
8959
8982
  }));
8983
+ let scriptPath = serverConfig.args?.[0];
8984
+ if (scriptPath && !path13.isAbsolute(scriptPath)) {
8985
+ scriptPath = path13.join(getCliPackageRoot2(), scriptPath);
8986
+ logger.debug(`Resolved MCP script path: ${scriptPath}`);
8987
+ }
8960
8988
  const client = new MCPClient({
8961
8989
  envVariables,
8962
8990
  name: serverConfig.name,
8963
- command: serverConfig.command,
8964
- args: serverConfig.args
8991
+ // Pass custom script path for bundled servers like b4m-tools
8992
+ customScriptPath: scriptPath
8965
8993
  });
8966
8994
  await client.connectToServer();
8967
8995
  const mcpData = {
@@ -9419,7 +9447,7 @@ import { isAxiosError as isAxiosError2 } from "axios";
9419
9447
  // package.json
9420
9448
  var package_default = {
9421
9449
  name: "@bike4mind/cli",
9422
- version: "0.2.5-feat-cli-docker-mcp-support.17095+1671ebc5f",
9450
+ version: "0.2.5-feat-server-side-tool-execution.17096+d6b936a82",
9423
9451
  type: "module",
9424
9452
  description: "Interactive CLI tool for Bike4Mind with ReAct agents",
9425
9453
  license: "UNLICENSED",
@@ -9452,6 +9480,7 @@ var package_default = {
9452
9480
  scripts: {
9453
9481
  dev: "tsx src/index.tsx",
9454
9482
  build: "tsup",
9483
+ postbuild: "node scripts/bundle-mcp-proxy.mjs",
9455
9484
  typecheck: "tsc --noEmit",
9456
9485
  test: "vitest run",
9457
9486
  "test:watch": "vitest",
@@ -9521,10 +9550,11 @@ var package_default = {
9521
9550
  },
9522
9551
  devDependencies: {
9523
9552
  "@bike4mind/agents": "0.1.0",
9524
- "@bike4mind/common": "2.37.1-feat-cli-docker-mcp-support.17095+1671ebc5f",
9525
- "@bike4mind/mcp": "1.19.6-feat-cli-docker-mcp-support.17095+1671ebc5f",
9526
- "@bike4mind/services": "2.32.2-feat-cli-docker-mcp-support.17095+1671ebc5f",
9527
- "@bike4mind/utils": "2.1.1-feat-cli-docker-mcp-support.17095+1671ebc5f",
9553
+ "@bike4mind/common": "2.37.1-feat-server-side-tool-execution.17096+d6b936a82",
9554
+ "@bike4mind/mcp": "1.19.6-feat-server-side-tool-execution.17096+d6b936a82",
9555
+ "@bike4mind/mcp-server-b4m-proxy": "1.0.1-feat-server-side-tool-execution.17096+d6b936a82",
9556
+ "@bike4mind/services": "2.32.2-feat-server-side-tool-execution.17096+d6b936a82",
9557
+ "@bike4mind/utils": "2.1.1-feat-server-side-tool-execution.17096+d6b936a82",
9528
9558
  "@types/diff": "^5.0.9",
9529
9559
  "@types/jsonwebtoken": "^9.0.4",
9530
9560
  "@types/node": "^22.9.0",
@@ -9536,7 +9566,7 @@ var package_default = {
9536
9566
  typescript: "^5.9.3",
9537
9567
  vitest: "^3.2.4"
9538
9568
  },
9539
- gitHead: "1671ebc5f5250ee3a79c12b4c549b2c256e71f4d"
9569
+ gitHead: "d6b936a82aed081ee4ef534a0e5260bc9fbe5714"
9540
9570
  };
9541
9571
 
9542
9572
  // src/config/constants.ts
@@ -9643,6 +9673,7 @@ function CliApp() {
9643
9673
  isAuthenticated = true;
9644
9674
  const daysUntilExpiry = Math.floor((expiresAt.getTime() - Date.now()) / (1e3 * 60 * 60 * 24));
9645
9675
  console.log(`\u2705 Authenticated (expires in ${daysUntilExpiry} day${daysUntilExpiry !== 1 ? "s" : ""})`);
9676
+ await state.configStore.ensureB4mMcpServer();
9646
9677
  }
9647
9678
  }
9648
9679
  if (!isAuthenticated) {
@@ -9740,10 +9771,6 @@ function CliApp() {
9740
9771
  setPermissionPrompt(prompt);
9741
9772
  });
9742
9773
  };
9743
- console.log("\u{1F511} Tool API Keys:", {
9744
- openweather: config.toolApiKeys?.openweather ? "\u2713 configured" : "\u2717 missing",
9745
- serper: config.toolApiKeys?.serper ? "\u2713 configured" : "\u2717 missing"
9746
- });
9747
9774
  const agentContext = {
9748
9775
  currentAgent: null,
9749
9776
  observationQueue: []
@@ -9754,18 +9781,35 @@ function CliApp() {
9754
9781
  modelInfo.id,
9755
9782
  permissionManager,
9756
9783
  promptFn,
9757
- agentContext,
9758
- config.toolApiKeys
9784
+ agentContext
9759
9785
  );
9760
- console.log(`\u{1F6E0}\uFE0F Loaded ${b4mTools.length} B4M tool(s)`);
9761
- const mcpManager = new McpManager(config);
9762
- await mcpManager.initialize();
9763
- const mcpTools = mcpManager.getTools();
9764
- if (mcpTools.length > 0) {
9765
- const toolCounts = mcpManager.getToolCount();
9766
- toolCounts.forEach(({ serverName, count }) => {
9767
- console.log(` \u{1F4E1} ${serverName}: ${count} tool(s)`);
9768
- });
9786
+ console.log(`\u{1F6E0}\uFE0F Loaded ${b4mTools.length} local tool(s)`);
9787
+ let mcpManager = null;
9788
+ let mcpTools = [];
9789
+ try {
9790
+ mcpManager = new McpManager(config);
9791
+ await mcpManager.initialize();
9792
+ mcpTools = mcpManager.getTools();
9793
+ if (mcpTools.length > 0) {
9794
+ const toolCounts = mcpManager.getToolCount();
9795
+ toolCounts.forEach(({ serverName, count }) => {
9796
+ console.log(` \u{1F4E1} ${serverName}: ${count} tool(s)`);
9797
+ });
9798
+ }
9799
+ const hasB4mServer = mcpManager.getServerNames().includes("b4m-tools");
9800
+ if (hasB4mServer) {
9801
+ console.log("\u{1F310} B4M server tools available (weather, web search)");
9802
+ } else if (!authTokens) {
9803
+ console.log("\u2139\uFE0F Weather and search tools require authentication");
9804
+ console.log(" Run /login to enable B4M server tools");
9805
+ }
9806
+ } catch (error) {
9807
+ console.log("\u26A0\uFE0F MCP server initialization failed - continuing with local tools only");
9808
+ if (error instanceof Error) {
9809
+ console.log(` Error: ${error.message}`);
9810
+ }
9811
+ mcpManager = null;
9812
+ mcpTools = [];
9769
9813
  }
9770
9814
  const allTools = [...b4mTools, ...mcpTools];
9771
9815
  console.log(`\u2705 Total: ${allTools.length} tool(s) available`);
@@ -10229,9 +10273,10 @@ No usage data available for the last ${USAGE_DAYS} days.`);
10229
10273
  {
10230
10274
  apiUrl: loginApiUrl,
10231
10275
  configStore: state.configStore,
10232
- onSuccess: () => {
10276
+ onSuccess: async () => {
10233
10277
  setState((prev) => ({ ...prev, showLoginFlow: false }));
10234
10278
  console.log("\n\u2705 Login successful! Initializing CLI...\n");
10279
+ await state.configStore.ensureB4mMcpServer();
10235
10280
  init().catch((err) => {
10236
10281
  console.error("\n\u274C Initialization failed:", err.message, "\n");
10237
10282
  exit();
@@ -0,0 +1,47 @@
1
+ /**
2
+ * HTTP client for communicating with B4M backend API
3
+ * Handles authentication, request/response formatting, and error handling
4
+ */
5
+ export interface ToolExecuteRequest {
6
+ tool: string;
7
+ params: Record<string, unknown>;
8
+ }
9
+ export interface ToolExecuteResponse {
10
+ success: boolean;
11
+ data?: unknown;
12
+ error?: {
13
+ code: string;
14
+ message: string;
15
+ details?: Record<string, unknown>;
16
+ };
17
+ metadata?: {
18
+ executionTime: number;
19
+ tool: string;
20
+ };
21
+ }
22
+ export interface ClientConfig {
23
+ /** Base URL for B4M API (e.g., https://api.bike4mind.com or http://localhost:3000) */
24
+ apiUrl: string;
25
+ /** JWT authentication token */
26
+ authToken: string;
27
+ /** Request timeout in milliseconds */
28
+ timeout?: number;
29
+ }
30
+ /**
31
+ * Client for executing tools via B4M backend API
32
+ */
33
+ export declare class B4MApiClient {
34
+ private readonly apiUrl;
35
+ private readonly authToken;
36
+ private readonly timeout;
37
+ constructor(config: ClientConfig);
38
+ /**
39
+ * Execute a tool via the backend API
40
+ */
41
+ executeTool(tool: string, params: Record<string, unknown>): Promise<ToolExecuteResponse>;
42
+ /**
43
+ * Test connection to backend API
44
+ */
45
+ testConnection(): Promise<boolean>;
46
+ }
47
+ //# sourceMappingURL=client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/client.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACjC;AAED,MAAM,WAAW,mBAAmB;IAClC,OAAO,EAAE,OAAO,CAAC;IACjB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,KAAK,CAAC,EAAE;QACN,IAAI,EAAE,MAAM,CAAC;QACb,OAAO,EAAE,MAAM,CAAC;QAChB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;KACnC,CAAC;IACF,QAAQ,CAAC,EAAE;QACT,aAAa,EAAE,MAAM,CAAC;QACtB,IAAI,EAAE,MAAM,CAAC;KACd,CAAC;CACH;AAED,MAAM,WAAW,YAAY;IAC3B,sFAAsF;IACtF,MAAM,EAAE,MAAM,CAAC;IACf,+BAA+B;IAC/B,SAAS,EAAE,MAAM,CAAC;IAClB,sCAAsC;IACtC,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;GAEG;AACH,qBAAa,YAAY;IACvB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;gBAErB,MAAM,EAAE,YAAY;IAMhC;;OAEG;IACG,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,mBAAmB,CAAC;IAgG9F;;OAEG;IACG,cAAc,IAAI,OAAO,CAAC,OAAO,CAAC;CAezC"}
@@ -0,0 +1,124 @@
1
+ /**
2
+ * HTTP client for communicating with B4M backend API
3
+ * Handles authentication, request/response formatting, and error handling
4
+ */
5
+ /**
6
+ * Client for executing tools via B4M backend API
7
+ */
8
+ export class B4MApiClient {
9
+ apiUrl;
10
+ authToken;
11
+ timeout;
12
+ constructor(config) {
13
+ this.apiUrl = config.apiUrl.replace(/\/$/, ''); // Remove trailing slash
14
+ this.authToken = config.authToken;
15
+ this.timeout = config.timeout || 30000; // 30s default
16
+ }
17
+ /**
18
+ * Execute a tool via the backend API
19
+ */
20
+ async executeTool(tool, params) {
21
+ const url = `${this.apiUrl}/api/tools/execute`;
22
+ const requestBody = {
23
+ tool,
24
+ params,
25
+ };
26
+ try {
27
+ // Create abort controller for timeout
28
+ const controller = new AbortController();
29
+ const timeoutId = setTimeout(() => controller.abort(), this.timeout);
30
+ const response = await fetch(url, {
31
+ method: 'POST',
32
+ headers: {
33
+ 'Content-Type': 'application/json',
34
+ Authorization: `Bearer ${this.authToken}`,
35
+ 'User-Agent': 'B4M-MCP-Proxy/1.0',
36
+ },
37
+ body: JSON.stringify(requestBody),
38
+ signal: controller.signal,
39
+ });
40
+ clearTimeout(timeoutId);
41
+ // Parse response
42
+ const data = (await response.json());
43
+ // Check for HTTP errors
44
+ if (!response.ok) {
45
+ // Handle authentication errors with helpful message
46
+ if (response.status === 401) {
47
+ return {
48
+ success: false,
49
+ error: {
50
+ code: 'AUTHENTICATION_REQUIRED',
51
+ message: 'Authentication required. Please run /login in the CLI to authenticate with your B4M account.',
52
+ details: data.error?.details,
53
+ },
54
+ metadata: data.metadata,
55
+ };
56
+ }
57
+ return {
58
+ success: false,
59
+ error: {
60
+ code: data.error?.code || 'HTTP_ERROR',
61
+ message: data.error?.message || `HTTP ${response.status}: ${response.statusText}`,
62
+ details: data.error?.details,
63
+ },
64
+ metadata: data.metadata,
65
+ };
66
+ }
67
+ return data;
68
+ }
69
+ catch (error) {
70
+ // Handle timeout
71
+ if (error instanceof Error && error.name === 'AbortError') {
72
+ return {
73
+ success: false,
74
+ error: {
75
+ code: 'TIMEOUT',
76
+ message: `Request timeout after ${this.timeout}ms`,
77
+ },
78
+ };
79
+ }
80
+ // Handle network errors
81
+ if (error instanceof TypeError) {
82
+ return {
83
+ success: false,
84
+ error: {
85
+ code: 'NETWORK_ERROR',
86
+ message: 'Failed to connect to B4M API',
87
+ details: {
88
+ error: error.message,
89
+ },
90
+ },
91
+ };
92
+ }
93
+ // Unknown error
94
+ return {
95
+ success: false,
96
+ error: {
97
+ code: 'UNKNOWN_ERROR',
98
+ message: error instanceof Error ? error.message : 'An unknown error occurred',
99
+ details: {
100
+ error: String(error),
101
+ },
102
+ },
103
+ };
104
+ }
105
+ }
106
+ /**
107
+ * Test connection to backend API
108
+ */
109
+ async testConnection() {
110
+ try {
111
+ const controller = new AbortController();
112
+ const timeoutId = setTimeout(() => controller.abort(), 5000);
113
+ const response = await fetch(`${this.apiUrl}/api/health`, {
114
+ signal: controller.signal,
115
+ });
116
+ clearTimeout(timeoutId);
117
+ return response.ok;
118
+ }
119
+ catch {
120
+ return false;
121
+ }
122
+ }
123
+ }
124
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/client.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,32 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * B4M MCP Proxy Server
4
+ *
5
+ * This MCP server acts as a proxy between Claude CLI and B4M backend API.
6
+ * It executes tools server-side using B4M company API keys, ensuring
7
+ * keys are never exposed to clients.
8
+ *
9
+ * Environment Variables:
10
+ * - B4M_API_URL: Base URL for B4M API (default: https://api.bike4mind.com)
11
+ * - B4M_AUTH_TOKEN: JWT authentication token (required)
12
+ * - B4M_TIMEOUT: Request timeout in milliseconds (default: 30000)
13
+ *
14
+ * Usage:
15
+ * node dist/index.js
16
+ *
17
+ * MCP Configuration (claude_desktop_config.json):
18
+ * {
19
+ * "mcpServers": {
20
+ * "b4m-tools": {
21
+ * "command": "node",
22
+ * "args": ["/path/to/mcp-server-b4m-proxy/dist/index.js"],
23
+ * "env": {
24
+ * "B4M_API_URL": "https://api.bike4mind.com",
25
+ * "B4M_AUTH_TOKEN": "your-jwt-token"
26
+ * }
27
+ * }
28
+ * }
29
+ * }
30
+ */
31
+ export {};
32
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":";AAEA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG"}
@@ -0,0 +1,187 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * B4M MCP Proxy Server
4
+ *
5
+ * This MCP server acts as a proxy between Claude CLI and B4M backend API.
6
+ * It executes tools server-side using B4M company API keys, ensuring
7
+ * keys are never exposed to clients.
8
+ *
9
+ * Environment Variables:
10
+ * - B4M_API_URL: Base URL for B4M API (default: https://api.bike4mind.com)
11
+ * - B4M_AUTH_TOKEN: JWT authentication token (required)
12
+ * - B4M_TIMEOUT: Request timeout in milliseconds (default: 30000)
13
+ *
14
+ * Usage:
15
+ * node dist/index.js
16
+ *
17
+ * MCP Configuration (claude_desktop_config.json):
18
+ * {
19
+ * "mcpServers": {
20
+ * "b4m-tools": {
21
+ * "command": "node",
22
+ * "args": ["/path/to/mcp-server-b4m-proxy/dist/index.js"],
23
+ * "env": {
24
+ * "B4M_API_URL": "https://api.bike4mind.com",
25
+ * "B4M_AUTH_TOKEN": "your-jwt-token"
26
+ * }
27
+ * }
28
+ * }
29
+ * }
30
+ */
31
+ import { Server } from '@modelcontextprotocol/sdk/server/index.js';
32
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
33
+ import { CallToolRequestSchema, ListToolsRequestSchema, McpError, ErrorCode, } from '@modelcontextprotocol/sdk/types.js';
34
+ import { B4MApiClient } from './client.js';
35
+ // Configuration from environment
36
+ const API_URL = process.env.B4M_API_URL || 'https://api.bike4mind.com';
37
+ const AUTH_TOKEN = process.env.B4M_AUTH_TOKEN || '';
38
+ const TIMEOUT = parseInt(process.env.B4M_TIMEOUT || '30000', 10);
39
+ // Warn if no authentication token (but allow server to start)
40
+ // The wrapper script (b4m-mcp-proxy.mjs) already logs a user-friendly warning
41
+ if (!AUTH_TOKEN) {
42
+ console.error('Warning: B4M_AUTH_TOKEN not set - tools will fail until authenticated');
43
+ }
44
+ // Initialize API client
45
+ const client = new B4MApiClient({
46
+ apiUrl: API_URL,
47
+ authToken: AUTH_TOKEN,
48
+ timeout: TIMEOUT,
49
+ });
50
+ /**
51
+ * Tool definitions for MCP
52
+ * These match the tools available in the backend
53
+ */
54
+ const TOOLS = [
55
+ {
56
+ name: 'weather',
57
+ description: 'Get current weather information for any location worldwide. Returns temperature, conditions, humidity, wind speed, and more.',
58
+ inputSchema: {
59
+ type: 'object',
60
+ properties: {
61
+ location: {
62
+ type: 'string',
63
+ description: 'City name, state, and/or country (e.g., "San Francisco, CA" or "London, UK")',
64
+ },
65
+ units: {
66
+ type: 'string',
67
+ enum: ['metric', 'imperial', 'standard'],
68
+ description: 'Temperature units (metric = Celsius, imperial = Fahrenheit, standard = Kelvin)',
69
+ default: 'metric',
70
+ },
71
+ },
72
+ required: ['location'],
73
+ },
74
+ },
75
+ {
76
+ name: 'serper',
77
+ description: 'Search the web using Google via SerpAPI. Returns organic search results with titles, links, and snippets.',
78
+ inputSchema: {
79
+ type: 'object',
80
+ properties: {
81
+ query: {
82
+ type: 'string',
83
+ description: 'Search query to execute',
84
+ },
85
+ num_results: {
86
+ type: 'number',
87
+ description: 'Number of results to return (1-10, default: 3)',
88
+ minimum: 1,
89
+ maximum: 10,
90
+ default: 3,
91
+ },
92
+ },
93
+ required: ['query'],
94
+ },
95
+ },
96
+ ];
97
+ /**
98
+ * Create and configure MCP server
99
+ */
100
+ const server = new Server({
101
+ name: 'b4m-tools',
102
+ version: '1.0.0',
103
+ }, {
104
+ capabilities: {
105
+ tools: {},
106
+ },
107
+ });
108
+ /**
109
+ * Handle tool list requests
110
+ */
111
+ server.setRequestHandler(ListToolsRequestSchema, async () => {
112
+ return {
113
+ tools: TOOLS,
114
+ };
115
+ });
116
+ /**
117
+ * Handle tool execution requests
118
+ */
119
+ server.setRequestHandler(CallToolRequestSchema, async (request) => {
120
+ const { name: toolName, arguments: toolArgs } = request.params;
121
+ try {
122
+ // Validate tool exists
123
+ const tool = TOOLS.find(t => t.name === toolName);
124
+ if (!tool) {
125
+ throw new McpError(ErrorCode.MethodNotFound, `Unknown tool: ${toolName}`);
126
+ }
127
+ // Execute tool via backend API
128
+ const result = await client.executeTool(toolName, toolArgs || {});
129
+ // Handle backend errors
130
+ if (!result.success) {
131
+ throw new McpError(ErrorCode.InternalError, result.error?.message || 'Tool execution failed', result.error?.details);
132
+ }
133
+ // Return successful result
134
+ return {
135
+ content: [
136
+ {
137
+ type: 'text',
138
+ text: JSON.stringify(result.data, null, 2),
139
+ },
140
+ ],
141
+ };
142
+ }
143
+ catch (error) {
144
+ // Re-throw McpErrors
145
+ if (error instanceof McpError) {
146
+ throw error;
147
+ }
148
+ // Wrap other errors
149
+ throw new McpError(ErrorCode.InternalError, error instanceof Error ? error.message : 'Unknown error occurred', {
150
+ error: String(error),
151
+ });
152
+ }
153
+ });
154
+ /**
155
+ * Start the MCP server
156
+ */
157
+ async function main() {
158
+ // Test connection to backend
159
+ const isConnected = await client.testConnection();
160
+ if (!isConnected) {
161
+ console.error(`Warning: Could not connect to backend API at ${API_URL}`);
162
+ console.error('Server will start but tool execution may fail.');
163
+ }
164
+ else {
165
+ console.error(`Connected to B4M API at ${API_URL}`);
166
+ }
167
+ // Start stdio transport
168
+ const transport = new StdioServerTransport();
169
+ await server.connect(transport);
170
+ console.error('B4M MCP Proxy Server running on stdio');
171
+ console.error(`Available tools: ${TOOLS.map(t => t.name).join(', ')}`);
172
+ }
173
+ // Handle errors
174
+ process.on('uncaughtException', error => {
175
+ console.error('Uncaught exception:', error);
176
+ process.exit(1);
177
+ });
178
+ process.on('unhandledRejection', reason => {
179
+ console.error('Unhandled rejection:', reason);
180
+ process.exit(1);
181
+ });
182
+ // Start server
183
+ main().catch(error => {
184
+ console.error('Failed to start server:', error);
185
+ process.exit(1);
186
+ });
187
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bike4mind/cli",
3
- "version": "0.2.5-feat-cli-docker-mcp-support.17095+1671ebc5f",
3
+ "version": "0.2.5-feat-server-side-tool-execution.17096+d6b936a82",
4
4
  "type": "module",
5
5
  "description": "Interactive CLI tool for Bike4Mind with ReAct agents",
6
6
  "license": "UNLICENSED",
@@ -33,6 +33,7 @@
33
33
  "scripts": {
34
34
  "dev": "tsx src/index.tsx",
35
35
  "build": "tsup",
36
+ "postbuild": "node scripts/bundle-mcp-proxy.mjs",
36
37
  "typecheck": "tsc --noEmit",
37
38
  "test": "vitest run",
38
39
  "test:watch": "vitest",
@@ -102,10 +103,11 @@
102
103
  },
103
104
  "devDependencies": {
104
105
  "@bike4mind/agents": "0.1.0",
105
- "@bike4mind/common": "2.37.1-feat-cli-docker-mcp-support.17095+1671ebc5f",
106
- "@bike4mind/mcp": "1.19.6-feat-cli-docker-mcp-support.17095+1671ebc5f",
107
- "@bike4mind/services": "2.32.2-feat-cli-docker-mcp-support.17095+1671ebc5f",
108
- "@bike4mind/utils": "2.1.1-feat-cli-docker-mcp-support.17095+1671ebc5f",
106
+ "@bike4mind/common": "2.37.1-feat-server-side-tool-execution.17096+d6b936a82",
107
+ "@bike4mind/mcp": "1.19.6-feat-server-side-tool-execution.17096+d6b936a82",
108
+ "@bike4mind/mcp-server-b4m-proxy": "1.0.1-feat-server-side-tool-execution.17096+d6b936a82",
109
+ "@bike4mind/services": "2.32.2-feat-server-side-tool-execution.17096+d6b936a82",
110
+ "@bike4mind/utils": "2.1.1-feat-server-side-tool-execution.17096+d6b936a82",
109
111
  "@types/diff": "^5.0.9",
110
112
  "@types/jsonwebtoken": "^9.0.4",
111
113
  "@types/node": "^22.9.0",
@@ -117,5 +119,5 @@
117
119
  "typescript": "^5.9.3",
118
120
  "vitest": "^3.2.4"
119
121
  },
120
- "gitHead": "1671ebc5f5250ee3a79c12b4c549b2c256e71f4d"
122
+ "gitHead": "d6b936a82aed081ee4ef534a0e5260bc9fbe5714"
121
123
  }