@teambit/cli-mcp-server 0.0.0-07691cc41fb4080a5f5b744e505ccc83cda772a8

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.
@@ -0,0 +1,1250 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.default = exports.CliMcpServerMain = void 0;
7
+ function _cli() {
8
+ const data = require("@teambit/cli");
9
+ _cli = function () {
10
+ return data;
11
+ };
12
+ return data;
13
+ }
14
+ function _child_process() {
15
+ const data = _interopRequireDefault(require("child_process"));
16
+ _child_process = function () {
17
+ return data;
18
+ };
19
+ return data;
20
+ }
21
+ function _stripAnsi() {
22
+ const data = _interopRequireDefault(require("strip-ansi"));
23
+ _stripAnsi = function () {
24
+ return data;
25
+ };
26
+ return data;
27
+ }
28
+ function _fsExtra() {
29
+ const data = _interopRequireDefault(require("fs-extra"));
30
+ _fsExtra = function () {
31
+ return data;
32
+ };
33
+ return data;
34
+ }
35
+ function _commentJson() {
36
+ const data = require("comment-json");
37
+ _commentJson = function () {
38
+ return data;
39
+ };
40
+ return data;
41
+ }
42
+ function _path() {
43
+ const data = _interopRequireDefault(require("path"));
44
+ _path = function () {
45
+ return data;
46
+ };
47
+ return data;
48
+ }
49
+ function _cliMcpServer() {
50
+ const data = require("./cli-mcp-server.aspect");
51
+ _cliMcpServer = function () {
52
+ return data;
53
+ };
54
+ return data;
55
+ }
56
+ function _mcpServer() {
57
+ const data = require("./mcp-server.cmd");
58
+ _mcpServer = function () {
59
+ return data;
60
+ };
61
+ return data;
62
+ }
63
+ function _mcp() {
64
+ const data = require("@modelcontextprotocol/sdk/server/mcp.js");
65
+ _mcp = function () {
66
+ return data;
67
+ };
68
+ return data;
69
+ }
70
+ function _stdio() {
71
+ const data = require("@modelcontextprotocol/sdk/server/stdio.js");
72
+ _stdio = function () {
73
+ return data;
74
+ };
75
+ return data;
76
+ }
77
+ function _zod() {
78
+ const data = require("zod");
79
+ _zod = function () {
80
+ return data;
81
+ };
82
+ return data;
83
+ }
84
+ function _logger() {
85
+ const data = require("@teambit/logger");
86
+ _logger = function () {
87
+ return data;
88
+ };
89
+ return data;
90
+ }
91
+ function _scope() {
92
+ const data = require("@teambit/scope.network");
93
+ _scope = function () {
94
+ return data;
95
+ };
96
+ return data;
97
+ }
98
+ function _legacy() {
99
+ const data = require("@teambit/legacy.constants");
100
+ _legacy = function () {
101
+ return data;
102
+ };
103
+ return data;
104
+ }
105
+ function _nodeFetch() {
106
+ const data = _interopRequireDefault(require("node-fetch"));
107
+ _nodeFetch = function () {
108
+ return data;
109
+ };
110
+ return data;
111
+ }
112
+ function _setupCmd() {
113
+ const data = require("./setup-cmd");
114
+ _setupCmd = function () {
115
+ return data;
116
+ };
117
+ return data;
118
+ }
119
+ function _rulesCmd() {
120
+ const data = require("./rules-cmd");
121
+ _rulesCmd = function () {
122
+ return data;
123
+ };
124
+ return data;
125
+ }
126
+ function _setupUtils() {
127
+ const data = require("./setup-utils");
128
+ _setupUtils = function () {
129
+ return data;
130
+ };
131
+ return data;
132
+ }
133
+ function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
134
+ function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
135
+ function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
136
+ function _defineProperty(e, r, t) { return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: !0, configurable: !0, writable: !0 }) : e[r] = t, e; }
137
+ function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == typeof i ? i : i + ""; }
138
+ function _toPrimitive(t, r) { if ("object" != typeof t || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != typeof i) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); } /* eslint-disable import/extensions */ /* eslint-disable import/no-unresolved */
139
+ class CliMcpServerMain {
140
+ constructor(cli, logger) {
141
+ this.cli = cli;
142
+ this.logger = logger;
143
+ _defineProperty(this, "bitBin", 'bit');
144
+ _defineProperty(this, "_http", void 0);
145
+ _defineProperty(this, "isConsumerProjectMode", false);
146
+ _defineProperty(this, "serverPort", void 0);
147
+ _defineProperty(this, "serverUrl", void 0);
148
+ _defineProperty(this, "serverProcess", null);
149
+ // Whitelist of commands that are considered read-only/query operations
150
+ _defineProperty(this, "readOnlyCommands", new Set(['status', 'list', 'info', 'show', 'schema', 'artifacts', 'diff', 'log', 'graph', 'deps get', 'deps blame', 'why', 'config get', 'envs list', 'envs get', 'remote list', 'templates', 'cat-component', 'cat-lane', 'cat-object', 'cat-scope', 'lane show', 'lane list', 'lane diff', 'lane history', 'lane history-diff', 'test', 'help', 'version']));
151
+ // Validate the default bitBin on construction
152
+ this.bitBin = this.validateBitBin(this.bitBin);
153
+ }
154
+ async getHttp() {
155
+ if (!this._http) {
156
+ this._http = await _scope().Http.connect(_legacy().SYMPHONY_GRAPHQL, _legacy().CENTRAL_BIT_HUB_NAME);
157
+ }
158
+ return this._http;
159
+ }
160
+ async getBitServerPort(cwd, skipValidatePortFlag = false) {
161
+ try {
162
+ const args = ['cli-server-port'];
163
+ if (skipValidatePortFlag) {
164
+ args.push(String(skipValidatePortFlag));
165
+ }
166
+ const result = _child_process().default.spawnSync(this.bitBin, args, {
167
+ cwd,
168
+ env: _objectSpread(_objectSpread({}, process.env), {}, {
169
+ BIT_CLI_SERVER: 'true'
170
+ }),
171
+ encoding: 'utf8'
172
+ });
173
+ if (result.error) {
174
+ throw result.error;
175
+ }
176
+ if (result.status !== 0) {
177
+ throw new Error(`Command failed with status ${result.status}: ${result.stderr}`);
178
+ }
179
+ const existingPort = result.stdout.trim();
180
+ if (!existingPort) return undefined;
181
+ return parseInt(existingPort, 10);
182
+ } catch (err) {
183
+ this.logger.error(`[MCP-DEBUG] error getting existing port from bit server at ${cwd}. err: ${err.message}`);
184
+ return undefined;
185
+ }
186
+ }
187
+
188
+ /**
189
+ * Start a new bit-server process
190
+ */
191
+ async startBitServer(cwd) {
192
+ this.logger.debug('[MCP-DEBUG] Starting new bit-server process');
193
+ return new Promise((resolve, reject) => {
194
+ try {
195
+ const serverProcess = _child_process().default.spawn(this.bitBin, ['server'], {
196
+ cwd,
197
+ stdio: ['pipe', 'pipe', 'pipe'],
198
+ detached: false
199
+ });
200
+ this.serverProcess = serverProcess;
201
+ let serverStarted = false;
202
+ let outputBuffer = '';
203
+ const timeout = setTimeout(() => {
204
+ if (!serverStarted) {
205
+ this.logger.error('[MCP-DEBUG] Timeout waiting for bit-server to start');
206
+ serverProcess.kill();
207
+ resolve(null);
208
+ }
209
+ }, 30000); // 30 second timeout
210
+
211
+ serverProcess.stdout?.on('data', data => {
212
+ const output = data.toString();
213
+ outputBuffer += output;
214
+ this.logger.debug(`[MCP-DEBUG] bit-server stdout: ${output}`);
215
+ if (output.includes('listening on port')) {
216
+ clearTimeout(timeout);
217
+ if (!serverStarted) {
218
+ serverStarted = true;
219
+ // Extract the port from the output
220
+ const portMatch = output.match(/listening on port (\d+)/);
221
+ if (portMatch && portMatch[1]) {
222
+ const port = parseInt(portMatch[1], 10);
223
+ this.logger.debug(`[MCP-DEBUG] bit-server started on port ${port}`);
224
+ this.serverPort = port;
225
+ this.serverUrl = `http://localhost:${port}/api`;
226
+ resolve(port);
227
+ }
228
+ }
229
+ }
230
+ });
231
+ serverProcess.stderr?.on('data', data => {
232
+ const error = data.toString();
233
+ outputBuffer += error;
234
+ this.logger.debug(`[MCP-DEBUG] bit-server stderr: ${error}`);
235
+ });
236
+ serverProcess.on('error', err => {
237
+ clearTimeout(timeout);
238
+ this.logger.error(`[MCP-DEBUG] Failed to start bit-server: ${err.message}`);
239
+ reject(err);
240
+ });
241
+ serverProcess.on('exit', (code, signal) => {
242
+ clearTimeout(timeout);
243
+ if (!serverStarted) {
244
+ this.logger.error(`[MCP-DEBUG] bit-server exited with code ${code}, signal ${signal}`);
245
+ this.logger.debug(`[MCP-DEBUG] bit-server output: ${outputBuffer}`);
246
+ resolve(null);
247
+ }
248
+ });
249
+ const killServerProcess = () => {
250
+ if (this.serverProcess && !this.serverProcess.killed) {
251
+ this.logger.debug('[MCP-DEBUG] Killing bit-server process');
252
+ this.serverProcess.kill();
253
+ }
254
+ };
255
+
256
+ // Handle process cleanup
257
+ process.on('exit', () => {
258
+ killServerProcess();
259
+ });
260
+ process.on('SIGINT', () => {
261
+ killServerProcess();
262
+ process.exit();
263
+ });
264
+ process.on('SIGTERM', () => {
265
+ killServerProcess();
266
+ process.exit();
267
+ });
268
+ } catch (err) {
269
+ this.logger.error(`[MCP-DEBUG] Error spawning bit-server: ${err.message}`);
270
+ reject(err);
271
+ }
272
+ });
273
+ }
274
+
275
+ /**
276
+ * Call bit-server API endpoint using cli-raw route
277
+ */
278
+ async callBitServerAPI(command, args = [], flags = {}, cwd, isReTrying = false) {
279
+ return this.callBitServerAPIWithRoute('cli-raw', command, args, flags, cwd, isReTrying);
280
+ }
281
+
282
+ /**
283
+ * Call bit-server API endpoint using IDE route
284
+ */
285
+ async callBitServerIDEAPI(method, args = [], cwd, isReTrying = false) {
286
+ return this.callBitServerAPIWithRoute('ide', method, args, {}, cwd, isReTrying, true);
287
+ }
288
+
289
+ /**
290
+ * Generic method to call bit-server API with different routes
291
+ */
292
+ async callBitServerAPIWithRoute(route, commandOrMethod, argsOrParams = [], flags = {}, cwd, isReTrying = false, isIDERoute = false) {
293
+ if (!this.serverPort) {
294
+ if (!cwd) throw new Error('CWD is required to call bit-server API');
295
+ this.serverPort = await this.getBitServerPort(cwd);
296
+ if (this.serverPort) {
297
+ this.serverUrl = `http://localhost:${this.serverPort}/api`;
298
+ } else {
299
+ // No server running, try to start one
300
+ this.logger.debug('[MCP-DEBUG] No bit-server found, attempting to start one');
301
+ const startedPort = await this.startBitServer(cwd);
302
+ if (startedPort) {
303
+ this.serverPort = startedPort;
304
+ this.serverUrl = `http://localhost:${this.serverPort}/api`;
305
+ }
306
+ }
307
+ }
308
+ if (!this.serverUrl) {
309
+ throw new Error('Unable to connect to bit-server. Please ensure you are in a valid Bit workspace.');
310
+ }
311
+
312
+ // Resolve the real path to handle symlinks (e.g., /tmp -> /private/tmp on macOS)
313
+ const realCwd = _fsExtra().default.realpathSync(cwd);
314
+ let body;
315
+ let url;
316
+ if (isIDERoute) {
317
+ // For IDE route, use the method name and args directly
318
+ body = {
319
+ args: argsOrParams
320
+ };
321
+ url = `${this.serverUrl}/${route}/${commandOrMethod}`;
322
+ } else {
323
+ // For CLI route, build command array with flags
324
+ const commandArray = [commandOrMethod, ...argsOrParams];
325
+
326
+ // Convert flags to command line arguments
327
+ for (const [key, value] of Object.entries(flags)) {
328
+ if (value === true) {
329
+ commandArray.push(`--${key}`);
330
+ } else if (value !== false && value !== undefined) {
331
+ commandArray.push(`--${key}`, String(value));
332
+ }
333
+ }
334
+ body = {
335
+ command: commandArray,
336
+ pwd: realCwd
337
+ };
338
+ url = `${this.serverUrl}/${route}`;
339
+ }
340
+ try {
341
+ const response = await (0, _nodeFetch().default)(url, {
342
+ method: 'POST',
343
+ headers: {
344
+ 'Content-Type': 'application/json'
345
+ },
346
+ body: JSON.stringify(body)
347
+ });
348
+ if (!response.ok) {
349
+ let errorMessage = `HTTP ${response.status}: ${response.statusText}`;
350
+ try {
351
+ const errorJson = await response.json();
352
+ errorMessage = errorJson.message || errorJson || errorMessage;
353
+ } catch {
354
+ // Ignore JSON parse errors
355
+ }
356
+ throw new Error(errorMessage);
357
+ }
358
+ return await response.json();
359
+ } catch (err) {
360
+ if (err.code === 'ECONNREFUSED' && !isReTrying) {
361
+ // Server is no longer running, reset cached values and try to restart
362
+ this.serverPort = undefined;
363
+ this.serverUrl = undefined;
364
+ this.logger.debug('[MCP-DEBUG] Connection refused, attempting to restart bit-server');
365
+ return this.callBitServerAPIWithRoute(route, commandOrMethod, argsOrParams, flags, cwd, true, isIDERoute);
366
+ }
367
+ throw err;
368
+ }
369
+ }
370
+ async runMcpServer(options) {
371
+ this.logger.debug(`[MCP-DEBUG] Starting MCP server with options: ${JSON.stringify(options)}. CWD: ${process.cwd()}`);
372
+ const commands = this.cli.commands;
373
+
374
+ // Validate and set bitBin with security checks
375
+ if (options.bitBin) {
376
+ this.bitBin = this.validateBitBin(options.bitBin);
377
+ }
378
+ // If no bitBin provided, keep the default 'bit'
379
+
380
+ // Tools to always exclude
381
+ const alwaysExcludeTools = new Set(['login', 'logout', 'completion', 'mcp-server', 'start', 'run-action', 'watch', 'run', 'resume-export', 'server', 'serve-preview']);
382
+
383
+ // Parse command strings from flag options
384
+ let additionalCommandsSet;
385
+ if (options.includeAdditional) {
386
+ additionalCommandsSet = new Set(options.includeAdditional.split(',').map(cmd => cmd.trim()));
387
+ this.logger.debug(`[MCP-DEBUG] Including additional commands: ${Array.from(additionalCommandsSet).join(', ')}`);
388
+ }
389
+ const server = new (_mcp().McpServer)({
390
+ name: 'bit-cli-mcp',
391
+ version: '0.0.1'
392
+ });
393
+
394
+ // Set of tools for consumer projects (non-Bit workspaces)
395
+ const consumerProjectTools = new Set();
396
+ const consumerProject = Boolean(options.consumerProject);
397
+
398
+ // Store consumer project mode globally in the class
399
+ this.isConsumerProjectMode = consumerProject;
400
+
401
+ // Validate flags combination
402
+ if (consumerProject) {
403
+ this.logger.debug(`[MCP-DEBUG] Running MCP server in consumer project mode (for non-Bit workspaces) with tools: bit_remote_search, bit_remote_component_details`);
404
+ if (options.includeAdditional) {
405
+ this.logger.debug(`[MCP-DEBUG] Additional tools enabled in consumer project mode: ${options.includeAdditional}`);
406
+ }
407
+ }
408
+ const filterOptions = {
409
+ additionalCommandsSet,
410
+ alwaysExcludeTools,
411
+ consumerProject,
412
+ consumerProjectTools
413
+ };
414
+ commands.forEach(cmd => {
415
+ const cmdName = (0, _cli().getCommandName)(cmd);
416
+ if (this.shouldIncludeCommand(cmdName, filterOptions)) {
417
+ this.registerToolForCommand(server, cmd);
418
+ }
419
+
420
+ // Process sub-commands
421
+ if (cmd.commands && cmd.commands.length) {
422
+ this.processSubCommands(server, cmd, filterOptions);
423
+ }
424
+ });
425
+
426
+ // Always register remote-search tool
427
+ this.registerRemoteSearchTool(server);
428
+
429
+ // In consumer project mode, only register bit_remote_search and bit_remote_component_details
430
+ // All other tools should not be available in consumer project mode
431
+ if (consumerProject) {
432
+ // Register the new combined remote component details tool
433
+ this.registerRemoteComponentDetailsTool(server);
434
+ } else {
435
+ // Register the bit_workspace_info tool
436
+ this.registerWorkspaceInfoTool(server);
437
+
438
+ // Register the bit_component_details tool
439
+ this.registerComponentDetailsTool(server);
440
+
441
+ // Register command discovery and help tools
442
+ this.registerCommandsListTool(server);
443
+ this.registerCommandHelpTool(server);
444
+ this.registerQueryTool(server);
445
+ this.registerExecuteTool(server);
446
+ }
447
+ await server.connect(new (_stdio().StdioServerTransport)());
448
+ }
449
+ shouldIncludeCommand(cmdName, options) {
450
+ // Always exclude certain commands
451
+ if (options.alwaysExcludeTools.has(cmdName)) return false;
452
+
453
+ // Consumer project mode: only include consumer project tools + any additional specified
454
+ if (options.consumerProject) {
455
+ const shouldInclude = options.consumerProjectTools.has(cmdName) || (options.additionalCommandsSet?.has(cmdName) ?? false);
456
+ if (shouldInclude) {
457
+ this.logger.debug(`[MCP-DEBUG] Including command in consumer project mode: ${cmdName}`);
458
+ }
459
+ return shouldInclude;
460
+ }
461
+
462
+ // Default mode: only include additional specified commands (no default tools anymore)
463
+ return options.additionalCommandsSet?.has(cmdName) ?? false;
464
+ }
465
+ buildZodSchema(config) {
466
+ const schema = {
467
+ // Add cwd parameter as mandatory to all commands
468
+ cwd: _zod().z.string().describe('Path to workspace')
469
+ };
470
+ config.argsData.forEach(arg => {
471
+ const desc = arg.description || `Positional argument: ${arg.nameRaw}`;
472
+ if (arg.isArray) {
473
+ schema[arg.nameCamelCase] = arg.required ? _zod().z.array(_zod().z.string()).describe(desc) : _zod().z.array(_zod().z.string()).optional().describe(desc);
474
+ } else {
475
+ schema[arg.nameCamelCase] = arg.required ? _zod().z.string().describe(desc) : _zod().z.string().optional().describe(desc);
476
+ }
477
+ });
478
+ config.flagsData.forEach(flag => {
479
+ const type = flag.type;
480
+ schema[flag.name] = type === 'string' ? _zod().z.string().optional().describe(flag.description) : _zod().z.boolean().optional().describe(flag.description);
481
+ });
482
+ return schema;
483
+ }
484
+ buildCommandArgs(config, params) {
485
+ // Split the command name on spaces to properly handle subcommands
486
+ const args = config.name.split(' ');
487
+
488
+ // Add positional arguments in order
489
+ config.argsData.forEach(arg => {
490
+ const val = params[arg.nameCamelCase];
491
+ if (val === undefined) return;
492
+ if (arg.isArray && Array.isArray(val)) {
493
+ val.forEach(item => args.push(item));
494
+ } else {
495
+ args.push(val);
496
+ }
497
+ });
498
+
499
+ // Add options as flags
500
+ config.flagsData.forEach(flag => {
501
+ const name = flag.name;
502
+ const type = flag.type;
503
+ const val = params[name];
504
+ if (val === undefined) return;
505
+ if (type === 'boolean' && val) {
506
+ args.push(`--${name}`);
507
+ } else if (type === 'string' && val) {
508
+ // Check if the string value contains spaces and quote it if necessary
509
+ const stringValue = String(val);
510
+ if (stringValue.includes(' ')) {
511
+ args.push(`--${name}`, `"${stringValue}"`);
512
+ } else {
513
+ args.push(`--${name}`, stringValue);
514
+ }
515
+ }
516
+ });
517
+ return args;
518
+ }
519
+ getToolName(name) {
520
+ // replace white spaces (\s) and dashes (-) with underscores (_)
521
+ return `bit_${name}`.replace(/[-\s]/g, '_');
522
+ }
523
+ registerToolForCommand(server, cmd, parentCmd) {
524
+ const cmdName = parentCmd ? `${(0, _cli().getCommandName)(parentCmd)} ${(0, _cli().getCommandName)(cmd)}` : (0, _cli().getCommandName)(cmd);
525
+ const toolName = this.getToolName(cmdName);
526
+
527
+ // Modify description for show and schema commands in consumer project mode
528
+ let description = `${cmd.description}${cmd.extendedDescription ? `.\n(${cmd.extendedDescription})` : ''}`;
529
+ if (this.isConsumerProjectMode && (cmdName === 'show' || cmdName === 'schema')) {
530
+ description += `\n(In consumer project mode, --remote flag is automatically added)`;
531
+ }
532
+ const config = {
533
+ name: cmdName,
534
+ description,
535
+ argsData: (0, _cli().getArgsData)(cmd),
536
+ flagsData: (0, _cli().getFlagsData)(cmd)
537
+ };
538
+ const schema = this.buildZodSchema(config);
539
+ server.tool(toolName, config.description, schema, async params => {
540
+ const argsToRun = this.buildCommandArgs(config, params);
541
+
542
+ // Special handling for consumer projects - auto-add --remote flag for show and schema commands
543
+ if (this.isConsumerProjectMode && (cmdName === 'show' || cmdName === 'schema')) {
544
+ if (!argsToRun.includes('--remote')) {
545
+ this.logger.debug(`[MCP-DEBUG] Auto-adding --remote flag for ${cmdName} in consumer project mode`);
546
+ argsToRun.push('--remote');
547
+ }
548
+ if (cmdName === 'show' && !argsToRun.includes('--legacy')) {
549
+ this.logger.debug(`[MCP-DEBUG] Auto-adding --legacy flag for ${cmdName} in consumer project mode`);
550
+ argsToRun.push('--legacy');
551
+ }
552
+ }
553
+ return this.runBit(argsToRun, params.cwd);
554
+ });
555
+ }
556
+
557
+ /**
558
+ * Read and parse workspace.jsonc file from a given directory
559
+ */
560
+ async readWorkspaceJsonc(workspaceDir) {
561
+ try {
562
+ const workspaceJsoncPath = _path().default.join(workspaceDir, 'workspace.jsonc');
563
+ const fileExists = await _fsExtra().default.pathExists(workspaceJsoncPath);
564
+ if (!fileExists) {
565
+ this.logger.debug(`[MCP-DEBUG] workspace.jsonc not found at ${workspaceJsoncPath}`);
566
+ return null;
567
+ }
568
+ const content = await _fsExtra().default.readFile(workspaceJsoncPath, 'utf-8');
569
+ return (0, _commentJson().parse)(content);
570
+ } catch (error) {
571
+ this.logger.debug(`[MCP-DEBUG] Failed to read workspace.jsonc: ${error}`);
572
+ return null;
573
+ }
574
+ }
575
+
576
+ /**
577
+ * Extract owner from defaultScope in workspace.jsonc
578
+ * If defaultScope contains a dot, split by dot and take the first part
579
+ */
580
+ extractOwnerFromWorkspace(workspaceConfig) {
581
+ try {
582
+ const workspaceSection = workspaceConfig?.['teambit.workspace/workspace'];
583
+ const defaultScope = workspaceSection?.defaultScope;
584
+ if (!defaultScope || typeof defaultScope !== 'string') {
585
+ return null;
586
+ }
587
+
588
+ // If defaultScope contains a dot, split by dot and take the first part (owner)
589
+ if (defaultScope.includes('.')) {
590
+ const parts = defaultScope.split('.');
591
+ return parts[0];
592
+ }
593
+
594
+ // If no dot, the entire defaultScope is treated as the owner
595
+ return defaultScope;
596
+ } catch (error) {
597
+ this.logger.debug(`[MCP-DEBUG] Failed to extract owner from workspace config: ${error}`);
598
+ return null;
599
+ }
600
+ }
601
+ registerRemoteSearchTool(server) {
602
+ const toolName = 'bit_remote_search';
603
+ const description = `Search for components in remote scopes. Use this tool to find existing components before creating new ones. Essential for component reuse and discovery`;
604
+ const schema = {
605
+ queryStr: _zod().z.string().describe(`Search query string - Don't try to search with too many keywords. It will try to find components that match all keywords, which is often too restrictive. Instead, search with a single keyword or a few broad keywords`),
606
+ cwd: _zod().z.string().optional().describe('Path to workspace directory'),
607
+ owners: _zod().z.array(_zod().z.string()).optional().describe('Filter results by specific owners or organizations. AVOID using this parameter - let the system automatically extract the owner from workspace.jsonc defaultScope for better relevance. Only use when you need to override the automatic behavior or search across different owners'),
608
+ skipAutoOwner: _zod().z.boolean().optional().describe('Set to true to disable automatic owner extraction from workspace.jsonc. When false or omitted, the system will try to automatically extract owner from workspace defaultScope')
609
+ };
610
+ server.tool(toolName, description, schema, async params => {
611
+ const http = await this.getHttp();
612
+
613
+ // Determine the owners to use for the search
614
+ let ownersToUse = params.owners && params.owners.length > 0 ? params.owners : undefined;
615
+
616
+ // If owners not explicitly provided and skipAutoOwner is not true, try to extract from workspace.jsonc
617
+ if (!ownersToUse && !params.skipAutoOwner) {
618
+ try {
619
+ // Use provided cwd parameter or fall back to current working directory
620
+ const workspaceDir = params.cwd || process.cwd();
621
+ this.logger.debug(`[MCP-DEBUG] Attempting to auto-extract owner from workspace.jsonc in: ${workspaceDir}`);
622
+ const workspaceConfig = await this.readWorkspaceJsonc(workspaceDir);
623
+ if (workspaceConfig) {
624
+ const extractedOwner = this.extractOwnerFromWorkspace(workspaceConfig);
625
+ if (extractedOwner) {
626
+ ownersToUse = [extractedOwner];
627
+ this.logger.debug(`[MCP-DEBUG] Auto-extracted owner from workspace.jsonc: ${extractedOwner}`);
628
+ }
629
+ }
630
+ } catch (error) {
631
+ this.logger.debug(`[MCP-DEBUG] Failed to auto-extract owner: ${error}`);
632
+ // Continue without auto-extracted owner
633
+ }
634
+ } else {
635
+ this.logger.debug(`[MCP-DEBUG] Using provided owners for search: ${ownersToUse ? ownersToUse.join(', ') : 'none'}`);
636
+ }
637
+ const results = await http.search(params.queryStr, ownersToUse);
638
+ this.logger.debug(`[MCP-DEBUG] Search results: ${JSON.stringify(results)}`);
639
+ if (!results?.components || results.components.length === 0) {
640
+ return {
641
+ content: [{
642
+ type: 'text',
643
+ text: 'No results found'
644
+ }]
645
+ };
646
+ }
647
+ const formattedResults = {
648
+ type: 'text',
649
+ text: results.components.join('\n')
650
+ };
651
+ return {
652
+ content: [formattedResults]
653
+ };
654
+ });
655
+ }
656
+ registerRemoteComponentDetailsTool(server) {
657
+ const toolName = 'bit_remote_component_details';
658
+ const description = 'Get detailed information about a remote component including basic info and its public API schema. Combines the functionality of show and schema commands for remote components.';
659
+ const schema = {
660
+ cwd: _zod().z.string().describe('Path to workspace directory'),
661
+ componentName: _zod().z.string().describe('Component name or component ID to get details for'),
662
+ includeSchema: _zod().z.boolean().optional().describe('Include component public API schema (default: true)')
663
+ };
664
+ server.tool(toolName, description, schema, async params => {
665
+ try {
666
+ const {
667
+ componentName,
668
+ includeSchema = true,
669
+ cwd
670
+ } = params;
671
+
672
+ // Get basic component information using show command via direct execution
673
+ const showArgs = ['show', componentName, '--remote', '--legacy'];
674
+ const showResult = await this.runBit(showArgs, cwd);
675
+ const result = {
676
+ componentInfo: showResult.content[0].text
677
+ };
678
+
679
+ // Get schema information if requested
680
+ if (includeSchema) {
681
+ try {
682
+ const schemaArgs = ['schema', componentName, '--remote'];
683
+ const schemaResult = await this.runBit(schemaArgs, cwd);
684
+ result.schema = schemaResult.content[0].text;
685
+ } catch (schemaError) {
686
+ this.logger.warn(`[MCP-DEBUG] Failed to get schema for ${componentName}: ${schemaError.message}`);
687
+ result.schemaError = `Failed to retrieve schema: ${schemaError.message}`;
688
+ }
689
+ }
690
+ return this.formatAsCallToolResult(result);
691
+ } catch (error) {
692
+ this.logger.error(`[MCP-DEBUG] Error in bit_remote_component_details tool: ${error.message}`);
693
+ return this.formatErrorAsCallToolResult(error, 'getting remote component details');
694
+ }
695
+ });
696
+ }
697
+ registerWorkspaceInfoTool(server) {
698
+ const toolName = 'bit_workspace_info';
699
+ const description = 'Get comprehensive workspace information including status, components list, apps, templates, dependency graph, and workspace dependencies';
700
+ const schema = {
701
+ cwd: _zod().z.string().describe('Path to workspace directory'),
702
+ includeTemplates: _zod().z.boolean().optional().describe('Include templates list (default: false)'),
703
+ includeApps: _zod().z.boolean().optional().describe('Include apps list (default: false)'),
704
+ includeGraph: _zod().z.boolean().optional().describe('Include dependency graph (default: false)'),
705
+ json: _zod().z.boolean().optional().describe('Return output in JSON format - WARNING: This produces verbose output and should be used when structured data is specifically needed (default: false)')
706
+ };
707
+ server.tool(toolName, description, schema, async params => {
708
+ try {
709
+ const includeTemplates = params.includeTemplates === true; // default: false
710
+ const includeApps = params.includeApps === true;
711
+ const includeGraph = params.includeGraph === true;
712
+ const useJson = params.json === true;
713
+ const workspaceInfo = {};
714
+ const flags = useJson ? {
715
+ json: true
716
+ } : {};
717
+ const statusExecution = await this.safeBitCommandExecution('status', [], flags, params.cwd, 'get workspace status', true);
718
+ workspaceInfo.status = statusExecution.result;
719
+ const listExecution = await this.safeBitCommandExecution('list', [], flags, params.cwd, 'get components list', true);
720
+ workspaceInfo.list = listExecution.result;
721
+
722
+ // Get apps list if requested
723
+ if (includeApps) {
724
+ const appsExecution = await this.safeBitCommandExecution('app', ['list'], flags, params.cwd, 'get apps list', true);
725
+ workspaceInfo.apps = appsExecution.result;
726
+ }
727
+
728
+ // Get templates list if requested
729
+ if (includeTemplates) {
730
+ const templatesExecution = await this.safeBitCommandExecution('templates', [], flags, params.cwd, 'get templates list', true);
731
+ workspaceInfo.templates = templatesExecution.result;
732
+ }
733
+
734
+ // Get dependency graph if requested
735
+ if (includeGraph) {
736
+ const graphExecution = await this.safeBitCommandExecution('graph', [], {
737
+ json: true
738
+ }, params.cwd, 'get dependency graph', true);
739
+ workspaceInfo.graph = graphExecution.result;
740
+ }
741
+
742
+ // Get workspace dependencies if requested
743
+ try {
744
+ const workspaceDependencies = await this.callBitServerIDEAPI('getWorkspaceDependencies', [], params.cwd);
745
+ workspaceInfo.workspaceDependencies = workspaceDependencies;
746
+ } catch (error) {
747
+ this.logger.error(`[MCP-DEBUG] Error getting workspace dependencies: ${error.message}`);
748
+ workspaceInfo.workspaceDependencies = {
749
+ error: `Failed to get workspace dependencies: ${error.message}`
750
+ };
751
+ }
752
+
753
+ // Get current lane name with scope
754
+ try {
755
+ const laneId = await this.callBitServerIDEAPI('getCurrentLaneName', [true], params.cwd);
756
+ workspaceInfo.laneId = laneId;
757
+ } catch (error) {
758
+ this.logger.error(`[MCP-DEBUG] Error getting current lane name: ${error.message}`);
759
+ workspaceInfo.laneId = {
760
+ error: `Failed to get current lane name: ${error.message}`
761
+ };
762
+ }
763
+ return this.formatAsCallToolResult(workspaceInfo);
764
+ } catch (error) {
765
+ this.logger.error(`[MCP-DEBUG] Error in bit_workspace_info tool: ${error.message}`);
766
+ return this.formatErrorAsCallToolResult(error, 'getting workspace info');
767
+ }
768
+ });
769
+ }
770
+ registerComponentDetailsTool(server) {
771
+ const toolName = 'bit_component_details';
772
+ const description = 'Get detailed information about a specific component including basic info and optionally its public API schema';
773
+ const schema = {
774
+ cwd: _zod().z.string().describe('Path to workspace directory'),
775
+ componentName: _zod().z.string().describe('Component name or component ID to get details for'),
776
+ includeSchema: _zod().z.boolean().optional().describe('Include component public API schema (default: false)')
777
+ };
778
+ server.tool(toolName, description, schema, async params => {
779
+ try {
780
+ const includeSchema = params.includeSchema === true;
781
+ const componentName = params.componentName;
782
+
783
+ // Get component details using IDE API with includeSchema parameter
784
+ const ideApiResult = await this.callBitServerIDEAPI('getCompDetails', [componentName, includeSchema], params.cwd);
785
+ return this.formatAsCallToolResult(ideApiResult);
786
+ } catch (error) {
787
+ this.logger.error(`[MCP-DEBUG] Error in bit_component_details tool: ${error.message}`);
788
+ return this.formatErrorAsCallToolResult(error, 'getting component details');
789
+ }
790
+ });
791
+ }
792
+ registerCommandsListTool(server) {
793
+ const toolName = 'bit_commands_list';
794
+ const description = 'Get all available Bit commands with descriptions and groups. Use this to discover what commands are available.';
795
+ const schema = {
796
+ extendedDescription: _zod().z.boolean().optional().describe('Include extended descriptions for commands (default: false)'),
797
+ internal: _zod().z.boolean().optional().describe('Include internal/debug commands (default: false)')
798
+ };
799
+ server.tool(toolName, description, schema, async params => {
800
+ try {
801
+ const {
802
+ extendedDescription = false,
803
+ internal = false
804
+ } = params;
805
+ const commandsInfo = [];
806
+ const shouldSkipCommand = cmd => {
807
+ return Boolean(cmd.private && !internal || cmd.description.startsWith('DEPRECATED'));
808
+ };
809
+
810
+ // Build list of all commands
811
+ this.cli.commands.forEach(cmd => {
812
+ if (shouldSkipCommand(cmd)) return;
813
+ const mainCmdName = (0, _cli().getCommandName)(cmd);
814
+ const groupKey = cmd.group;
815
+ const commandInfo = {
816
+ name: mainCmdName,
817
+ description: cmd.description || ''
818
+ };
819
+ if (extendedDescription && cmd.extendedDescription) {
820
+ commandInfo.extendedDescription = cmd.extendedDescription;
821
+ }
822
+ if (groupKey) commandInfo.group = this.cli.groups[groupKey] || groupKey;
823
+
824
+ // Add subcommands summary
825
+ if (cmd.commands && cmd.commands.length > 0) {
826
+ commandInfo.subcommands = cmd.commands.filter(subCmd => !shouldSkipCommand(subCmd)).map(subCmd => ({
827
+ name: (0, _cli().getCommandName)(subCmd),
828
+ description: subCmd.description || ''
829
+ }));
830
+ }
831
+ commandsInfo.push(commandInfo);
832
+ });
833
+ commandsInfo.sort((a, b) => a.name.localeCompare(b.name));
834
+ const result = JSON.stringify({
835
+ total: commandsInfo.length,
836
+ commands: commandsInfo
837
+ }, null, 2);
838
+ this.logger.debug(`[MCP-DEBUG] Successfully retrieved commands list. Total: ${commandsInfo.length}`);
839
+ return this.formatAsCallToolResult(result);
840
+ } catch (error) {
841
+ this.logger.error(`[MCP-DEBUG] Error in bit_commands_list tool: ${error.message}`);
842
+ return this.formatErrorAsCallToolResult(error, 'getting commands list');
843
+ }
844
+ });
845
+ }
846
+ registerCommandHelpTool(server) {
847
+ const toolName = 'bit_command_help';
848
+ const description = 'Get detailed help for a specific Bit command including syntax, arguments, flags, and usage examples. Use this to understand exactly how to use a command.';
849
+ const schema = {
850
+ command: _zod().z.string().describe('The command name to get help for (e.g., "status", "install", "create")'),
851
+ subcommand: _zod().z.string().optional().describe('Optional subcommand name (e.g., for "lane show", use command="lane" and subcommand="show")')
852
+ };
853
+ server.tool(toolName, description, schema, async params => {
854
+ try {
855
+ const {
856
+ command: requestedCommand,
857
+ subcommand: requestedSubcommand
858
+ } = params;
859
+ let commandInfo = null;
860
+ const buildDetailedCommandInfo = (cmd, parentName) => {
861
+ const cmdName = parentName ? `${parentName} ${(0, _cli().getCommandName)(cmd)}` : (0, _cli().getCommandName)(cmd);
862
+ const info = {
863
+ name: cmdName,
864
+ description: cmd.description || '',
865
+ extendedDescription: cmd.extendedDescription || '',
866
+ group: cmd.group ? this.cli.groups[cmd.group] || cmd.group : ''
867
+ };
868
+
869
+ // Add arguments information
870
+ const argsData = (0, _cli().getArgsData)(cmd);
871
+ if (argsData.length > 0) {
872
+ info.arguments = argsData.map(arg => ({
873
+ name: arg.nameRaw,
874
+ description: arg.description || '',
875
+ required: arg.required,
876
+ isArray: arg.isArray
877
+ }));
878
+ }
879
+
880
+ // Add options/flags information
881
+ info.options = (0, _cli().getFlagsData)(cmd);
882
+
883
+ // Add examples if available
884
+ if (cmd.examples) {
885
+ info.examples = cmd.examples;
886
+ }
887
+
888
+ // Add subcommands if available (including private ones for help purposes)
889
+ if (cmd.commands && cmd.commands.length > 0) {
890
+ info.subcommands = cmd.commands.map(subCmd => ({
891
+ name: (0, _cli().getCommandName)(subCmd),
892
+ description: subCmd.description || ''
893
+ }));
894
+ }
895
+ return info;
896
+ };
897
+
898
+ // Search for the requested command
899
+ this.cli.commands.forEach(cmd => {
900
+ const mainCmdName = (0, _cli().getCommandName)(cmd);
901
+ if (requestedSubcommand) {
902
+ // Looking for a subcommand
903
+ if (mainCmdName === requestedCommand && cmd.commands) {
904
+ const subCmd = cmd.commands.find(sub => (0, _cli().getCommandName)(sub) === requestedSubcommand);
905
+ if (subCmd) {
906
+ commandInfo = buildDetailedCommandInfo(subCmd, requestedCommand);
907
+ }
908
+ }
909
+ } else {
910
+ // Looking for a main command
911
+ if (mainCmdName === requestedCommand) {
912
+ commandInfo = buildDetailedCommandInfo(cmd);
913
+ }
914
+ }
915
+ });
916
+ if (!commandInfo) {
917
+ const commandFullName = requestedSubcommand ? `${requestedCommand} ${requestedSubcommand}` : requestedCommand;
918
+ return this.formatAsCallToolResult(`Command not found: ${commandFullName}`);
919
+ }
920
+ const result = JSON.stringify(commandInfo, null, 2);
921
+ this.logger.debug(`[MCP-DEBUG] Successfully retrieved command help for: ${commandInfo.name}`);
922
+ return this.formatAsCallToolResult(result);
923
+ } catch (error) {
924
+ this.logger.error(`[MCP-DEBUG] Error in bit_command_help tool: ${error.message}`);
925
+ return this.formatErrorAsCallToolResult(error, 'getting command help');
926
+ }
927
+ });
928
+ }
929
+ registerQueryTool(server) {
930
+ const toolName = 'bit_query';
931
+ const description = 'Execute read-only Bit commands that safely inspect workspace and component state without making modifications. Only whitelisted query commands are allowed for safety.';
932
+ const schema = {
933
+ cwd: _zod().z.string().describe('Path to workspace directory'),
934
+ command: _zod().z.string().describe('The Bit command to execute (e.g., "status", "show", "list")'),
935
+ args: _zod().z.array(_zod().z.string()).optional().describe('Arguments to pass to the command'),
936
+ flags: _zod().z.record(_zod().z.union([_zod().z.string(), _zod().z.boolean()])).optional().describe('Flags to pass to the command as key-value pairs')
937
+ };
938
+ server.tool(toolName, description, schema, async params => {
939
+ try {
940
+ const {
941
+ command,
942
+ args = [],
943
+ flags = {},
944
+ cwd
945
+ } = params;
946
+
947
+ // Check if command is in the read-only whitelist
948
+ // Support both single commands and subcommands (e.g., "lane show")
949
+ const fullCommand = args.length > 0 ? `${command} ${args[0]}` : command;
950
+ const isAllowed = this.readOnlyCommands.has(command) || this.readOnlyCommands.has(fullCommand);
951
+ if (!isAllowed) {
952
+ const allowedCommands = Array.from(this.readOnlyCommands).sort().join(', ');
953
+ return this.formatAsCallToolResult(`Error: Command "${command}" is not allowed in query mode. Allowed read-only commands: ${allowedCommands}`);
954
+ }
955
+
956
+ // Build command arguments
957
+ const commandArgs = [command, ...args];
958
+
959
+ // Add flags to arguments
960
+ Object.entries(flags).forEach(([key, value]) => {
961
+ if (typeof value === 'boolean' && value) {
962
+ commandArgs.push(`--${key}`);
963
+ } else if (typeof value === 'string' && value) {
964
+ commandArgs.push(`--${key}`);
965
+ commandArgs.push(value);
966
+ }
967
+ });
968
+ this.logger.debug(`[MCP-DEBUG] Executing query command: ${command} with args: ${JSON.stringify(commandArgs)}`);
969
+ const execution = await this.safeBitCommandExecution(command, args, flags, cwd, `execute query command ${command}`);
970
+ return this.formatAsCallToolResult(execution.result);
971
+ } catch (error) {
972
+ this.logger.error(`[MCP-DEBUG] Error in bit_query tool: ${error.message}`);
973
+ return this.formatErrorAsCallToolResult(error, 'executing query command');
974
+ }
975
+ });
976
+ }
977
+ registerExecuteTool(server) {
978
+ const toolName = 'bit_execute';
979
+ const description = 'Execute Bit commands that make changes to workspace or components (not read-only).';
980
+ const schema = {
981
+ cwd: _zod().z.string().describe('Path to workspace directory'),
982
+ command: _zod().z.string().describe('The Bit command to execute (e.g., "import", "tag", "export", "remove")'),
983
+ args: _zod().z.array(_zod().z.string()).optional().describe('Arguments to pass to the command'),
984
+ flags: _zod().z.record(_zod().z.union([_zod().z.string(), _zod().z.boolean()])).optional().describe('Flags to pass to the command as key-value pairs')
985
+ };
986
+ server.tool(toolName, description, schema, async params => {
987
+ try {
988
+ let {
989
+ command,
990
+ args = []
991
+ } = params;
992
+ const {
993
+ flags = {},
994
+ cwd
995
+ } = params;
996
+
997
+ // Handle sub-commands: if command has multiple words, move the second word to args
998
+ const commandParts = command.trim().split(/\s+/);
999
+ if (commandParts.length > 1) {
1000
+ command = commandParts[0];
1001
+ args = [commandParts[1], ...args];
1002
+ }
1003
+ this.logger.debug(`[MCP-DEBUG] Executing command: ${command} with args: ${JSON.stringify(args)} and flags: ${JSON.stringify(flags)}`);
1004
+ const execution = await this.safeBitCommandExecution(command, args, flags, cwd, `execute command ${command}`);
1005
+ return this.formatAsCallToolResult(execution.result);
1006
+ } catch (error) {
1007
+ this.logger.error(`[MCP-DEBUG] Error in bit_execute tool: ${error.message}`);
1008
+ return this.formatErrorAsCallToolResult(error, 'executing command');
1009
+ }
1010
+ });
1011
+ }
1012
+ processSubCommands(server, parentCmd, options) {
1013
+ const parentCmdName = (0, _cli().getCommandName)(parentCmd);
1014
+ parentCmd.commands?.forEach(subCmd => {
1015
+ const subCmdName = (0, _cli().getCommandName)(subCmd);
1016
+ const fullCmdName = `${parentCmdName} ${subCmdName}`;
1017
+ if (this.shouldIncludeCommand(fullCmdName, options)) {
1018
+ this.registerToolForCommand(server, subCmd, parentCmd);
1019
+ }
1020
+ });
1021
+ }
1022
+ async runBit(args, cwd) {
1023
+ this.logger.debug(`[MCP-DEBUG] Running: ${this.bitBin} ${args.join(' ')} in ${cwd}`);
1024
+ try {
1025
+ const result = _child_process().default.spawnSync(this.bitBin, args, {
1026
+ cwd,
1027
+ env: _objectSpread(_objectSpread({}, process.env), {}, {
1028
+ BIT_DISABLE_SPINNER: '1'
1029
+ }),
1030
+ encoding: 'utf8'
1031
+ });
1032
+ if (result.error) {
1033
+ throw result.error;
1034
+ }
1035
+ if (result.status !== 0) {
1036
+ const errorMessage = result.stderr || `Command failed with status ${result.status}`;
1037
+ throw new Error(errorMessage);
1038
+ }
1039
+ this.logger.debug(`[MCP-DEBUG] result. stdout: ${result.stdout}`);
1040
+ return {
1041
+ content: [{
1042
+ type: 'text',
1043
+ text: result.stdout
1044
+ }]
1045
+ };
1046
+ } catch (error) {
1047
+ this.logger.error(`[MCP-DEBUG] Error executing ${this.bitBin} ${args.join(' ')}`, error);
1048
+ return {
1049
+ content: [{
1050
+ type: 'text',
1051
+ text: error.message
1052
+ }]
1053
+ };
1054
+ }
1055
+ }
1056
+
1057
+ /**
1058
+ * Helper method to execute a bit-server API call with standardized error handling
1059
+ */
1060
+ async executeBitServerCommand(command, args = [], flags = {}, cwd, operationName) {
1061
+ try {
1062
+ const result = await this.callBitServerAPI(command, args, flags, cwd);
1063
+ this.logger.debug(`[MCP-DEBUG] Successfully executed ${operationName} via bit-server`);
1064
+ return result;
1065
+ } catch (error) {
1066
+ this.logger.warn(`[MCP-DEBUG] Failed to execute ${operationName} via bit-server: ${error.message}`);
1067
+ throw error;
1068
+ }
1069
+ }
1070
+
1071
+ /**
1072
+ * Helper method to format any result as CallToolResult
1073
+ */
1074
+ formatAsCallToolResult(result) {
1075
+ if (typeof result === 'string') {
1076
+ return {
1077
+ content: [{
1078
+ type: 'text',
1079
+ text: result
1080
+ }]
1081
+ };
1082
+ } else if (typeof result === 'object') {
1083
+ return {
1084
+ content: [{
1085
+ type: 'text',
1086
+ text: JSON.stringify(result, null, 2)
1087
+ }]
1088
+ };
1089
+ } else {
1090
+ return {
1091
+ content: [{
1092
+ type: 'text',
1093
+ text: String(result)
1094
+ }]
1095
+ };
1096
+ }
1097
+ }
1098
+
1099
+ /**
1100
+ * Helper method to format error as CallToolResult
1101
+ */
1102
+ formatErrorAsCallToolResult(error, operation) {
1103
+ return {
1104
+ content: [{
1105
+ type: 'text',
1106
+ text: `Error ${operation}: ${error.message}`
1107
+ }]
1108
+ };
1109
+ }
1110
+
1111
+ /**
1112
+ * Helper method to safely execute a bit command with error handling
1113
+ */
1114
+ async safeBitCommandExecution(command, args = [], flags = {}, cwd, operationName, includeErrorInResult = false) {
1115
+ try {
1116
+ let result = await this.executeBitServerCommand(command, args, flags, cwd, operationName);
1117
+
1118
+ // Unwrap the result from data wrapper if it exists and we have a successful result
1119
+ if (result && typeof result === 'object' && 'data' in result && result.data !== undefined) {
1120
+ result = result.data;
1121
+ }
1122
+
1123
+ // Clean up output by removing ANSI color codes when not using JSON format
1124
+ const useJson = flags && flags.json === true;
1125
+ if (!useJson && typeof result === 'string') {
1126
+ result = (0, _stripAnsi().default)(result);
1127
+ }
1128
+ return {
1129
+ success: true,
1130
+ result
1131
+ };
1132
+ } catch (error) {
1133
+ if (includeErrorInResult) {
1134
+ return {
1135
+ success: false,
1136
+ result: {
1137
+ error: `Failed to ${operationName}: ${error.message}`
1138
+ },
1139
+ error: error.message
1140
+ };
1141
+ } else {
1142
+ throw error;
1143
+ }
1144
+ }
1145
+ }
1146
+ validateBitBin(bitBin) {
1147
+ const trimmed = bitBin?.trim();
1148
+ // Check for shell metacharacters and spaces. Protect against command injection.
1149
+ if (!trimmed || /[;&|`$(){}[\]<>'"\\]/.test(trimmed) || /\s/.test(trimmed)) throw new Error('Invalid bitBin');
1150
+ return trimmed;
1151
+ }
1152
+
1153
+ // Setup command business logic methods
1154
+ getEditorDisplayName(editor) {
1155
+ return _setupUtils().McpSetupUtils.getEditorDisplayName(editor);
1156
+ }
1157
+
1158
+ /**
1159
+ * Get the path to the editor config file based on editor type and scope
1160
+ */
1161
+ getEditorConfigPath(editor, isGlobal, workspaceDir) {
1162
+ const editorLower = editor.toLowerCase();
1163
+ if (editorLower === 'vscode') {
1164
+ // For VS Code, return appropriate config path based on global vs workspace scope
1165
+ return isGlobal ? _setupUtils().McpSetupUtils.getVSCodeSettingsPath(isGlobal, workspaceDir) : _setupUtils().McpSetupUtils.getVSCodeMcpConfigPath(workspaceDir);
1166
+ } else if (editorLower === 'cursor') {
1167
+ return _setupUtils().McpSetupUtils.getCursorSettingsPath(isGlobal, workspaceDir);
1168
+ } else if (editorLower === 'windsurf') {
1169
+ return _setupUtils().McpSetupUtils.getWindsurfSettingsPath(isGlobal, workspaceDir);
1170
+ } else if (editorLower === 'roo') {
1171
+ return _setupUtils().McpSetupUtils.getRooCodeSettingsPath(isGlobal, workspaceDir);
1172
+ } else if (editorLower === 'cline') {
1173
+ return _setupUtils().McpSetupUtils.getClinePromptsPath(isGlobal, workspaceDir);
1174
+ } else if (editorLower === 'claude-code') {
1175
+ return _setupUtils().McpSetupUtils.getClaudeCodeSettingsPath(isGlobal, workspaceDir);
1176
+ }
1177
+ throw new Error(`Editor "${editor}" is not supported yet.`);
1178
+ }
1179
+ async setupEditor(editor, options, workspaceDir) {
1180
+ const supportedEditors = ['vscode', 'cursor', 'windsurf', 'roo', 'cline', 'claude-code'];
1181
+ const editorLower = editor.toLowerCase();
1182
+ if (!supportedEditors.includes(editorLower)) {
1183
+ throw new Error(`Editor "${editor}" is not supported yet. Currently supported: ${supportedEditors.join(', ')}`);
1184
+ }
1185
+
1186
+ // Add workspaceDir to options if provided
1187
+ const setupOptions = _objectSpread({}, options);
1188
+ if (workspaceDir) {
1189
+ setupOptions.workspaceDir = workspaceDir;
1190
+ }
1191
+ if (editorLower === 'vscode') {
1192
+ await _setupUtils().McpSetupUtils.setupVSCode(setupOptions);
1193
+ } else if (editorLower === 'cursor') {
1194
+ await _setupUtils().McpSetupUtils.setupCursor(setupOptions);
1195
+ } else if (editorLower === 'windsurf') {
1196
+ await _setupUtils().McpSetupUtils.setupWindsurf(setupOptions);
1197
+ } else if (editorLower === 'roo') {
1198
+ await _setupUtils().McpSetupUtils.setupRooCode(setupOptions);
1199
+ } else if (editorLower === 'cline') {
1200
+ // Cline doesn't need MCP server setup, only rules files
1201
+ // This is a no-op but we include it for consistency
1202
+ // Users should use the 'rules' command to set up Cline instructions
1203
+ } else if (editorLower === 'claude-code') {
1204
+ await _setupUtils().McpSetupUtils.setupClaudeCode(setupOptions);
1205
+ }
1206
+ }
1207
+ async writeRulesFile(editor, options, workspaceDir) {
1208
+ const supportedEditors = ['vscode', 'cursor', 'roo', 'cline', 'claude-code'];
1209
+ const editorLower = editor.toLowerCase();
1210
+ if (!supportedEditors.includes(editorLower)) {
1211
+ throw new Error(`Editor "${editor}" is not supported yet. Currently supported: ${supportedEditors.join(', ')}`);
1212
+ }
1213
+
1214
+ // Add workspaceDir to options if provided
1215
+ const rulesOptions = _objectSpread({}, options);
1216
+ if (workspaceDir) {
1217
+ rulesOptions.workspaceDir = workspaceDir;
1218
+ }
1219
+ if (editorLower === 'vscode') {
1220
+ await _setupUtils().McpSetupUtils.writeVSCodeRules(rulesOptions);
1221
+ } else if (editorLower === 'cursor') {
1222
+ await _setupUtils().McpSetupUtils.writeCursorRules(rulesOptions);
1223
+ } else if (editorLower === 'roo') {
1224
+ await _setupUtils().McpSetupUtils.writeRooCodeRules(rulesOptions);
1225
+ } else if (editorLower === 'cline') {
1226
+ await _setupUtils().McpSetupUtils.writeClineRules(rulesOptions);
1227
+ } else if (editorLower === 'claude-code') {
1228
+ await _setupUtils().McpSetupUtils.writeClaudeCodeRules(rulesOptions);
1229
+ }
1230
+ }
1231
+ async getRulesContent(consumerProject = false) {
1232
+ return _setupUtils().McpSetupUtils.getDefaultRulesContent(consumerProject);
1233
+ }
1234
+ static async provider([cli, loggerMain]) {
1235
+ const logger = loggerMain.createLogger(_cliMcpServer().CliMcpServerAspect.id);
1236
+ const mcpServer = new CliMcpServerMain(cli, logger);
1237
+ const mcpServerCmd = new (_mcpServer().McpServerCmd)(mcpServer);
1238
+ mcpServerCmd.commands = [new (_mcpServer().McpStartCmd)(mcpServer), new (_setupCmd().McpSetupCmd)(mcpServer), new (_rulesCmd().McpRulesCmd)(mcpServer)];
1239
+ cli.register(mcpServerCmd);
1240
+ return mcpServer;
1241
+ }
1242
+ }
1243
+ exports.CliMcpServerMain = CliMcpServerMain;
1244
+ _defineProperty(CliMcpServerMain, "slots", []);
1245
+ _defineProperty(CliMcpServerMain, "dependencies", [_cli().CLIAspect, _logger().LoggerAspect]);
1246
+ _defineProperty(CliMcpServerMain, "runtime", _cli().MainRuntime);
1247
+ _cliMcpServer().CliMcpServerAspect.addRuntime(CliMcpServerMain);
1248
+ var _default = exports.default = CliMcpServerMain;
1249
+
1250
+ //# sourceMappingURL=cli-mcp-server.main.runtime.js.map