mobbdev 1.0.85 → 1.0.87

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.
Files changed (3) hide show
  1. package/README.md +84 -0
  2. package/dist/index.mjs +1451 -47
  3. package/package.json +15 -7
package/dist/index.mjs CHANGED
@@ -7,7 +7,7 @@ var __export = (target, all) => {
7
7
  var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
8
8
 
9
9
  // src/index.ts
10
- import Debug20 from "debug";
10
+ import Debug21 from "debug";
11
11
  import { hideBin } from "yargs/helpers";
12
12
 
13
13
  // src/args/commands/convert_to_sarif.ts
@@ -553,16 +553,16 @@ var GetVulnerabilityReportPathsDocument = `
553
553
  }
554
554
  }
555
555
  `;
556
- var GetAnalysisDocument = `
557
- subscription getAnalysis($analysisId: uuid!) {
556
+ var GetAnalysisSubscriptionDocument = `
557
+ subscription getAnalysisSubscription($analysisId: uuid!) {
558
558
  analysis: fixReport_by_pk(id: $analysisId) {
559
559
  id
560
560
  state
561
561
  }
562
562
  }
563
563
  `;
564
- var GetAnalsyisDocument = `
565
- query getAnalsyis($analysisId: uuid!) {
564
+ var GetAnalysisDocument = `
565
+ query getAnalysis($analysisId: uuid!) {
566
566
  analysis: fixReport_by_pk(id: $analysisId) {
567
567
  id
568
568
  state
@@ -913,6 +913,37 @@ var AutoPrAnalysisDocument = `
913
913
  }
914
914
  }
915
915
  `;
916
+ var GetMcpFixesDocument = `
917
+ query GetMCPFixes($fixReportId: uuid!) {
918
+ fix(where: {fixReportId: {_eq: $fixReportId}}) {
919
+ id
920
+ confidence
921
+ safeIssueType
922
+ severityText
923
+ vulnerabilityReportIssues {
924
+ parsedIssueType
925
+ parsedSeverity
926
+ vulnerabilityReportIssueTags {
927
+ vulnerability_report_issue_tag_value
928
+ }
929
+ }
930
+ patchAndQuestions {
931
+ __typename
932
+ ... on FixData {
933
+ patch
934
+ patchOriginalEncodingBase64
935
+ extraContext {
936
+ extraContext {
937
+ key
938
+ value
939
+ }
940
+ fixDescription
941
+ }
942
+ }
943
+ }
944
+ }
945
+ }
946
+ `;
916
947
  var defaultWrapper = (action, _operationName, _operationType, _variables) => action();
917
948
  function getSdk(client, withWrapper = defaultWrapper) {
918
949
  return {
@@ -931,11 +962,11 @@ function getSdk(client, withWrapper = defaultWrapper) {
931
962
  GetVulnerabilityReportPaths(variables, requestHeaders) {
932
963
  return withWrapper((wrappedRequestHeaders) => client.request(GetVulnerabilityReportPathsDocument, variables, { ...requestHeaders, ...wrappedRequestHeaders }), "GetVulnerabilityReportPaths", "query", variables);
933
964
  },
934
- getAnalysis(variables, requestHeaders) {
935
- return withWrapper((wrappedRequestHeaders) => client.request(GetAnalysisDocument, variables, { ...requestHeaders, ...wrappedRequestHeaders }), "getAnalysis", "subscription", variables);
965
+ getAnalysisSubscription(variables, requestHeaders) {
966
+ return withWrapper((wrappedRequestHeaders) => client.request(GetAnalysisSubscriptionDocument, variables, { ...requestHeaders, ...wrappedRequestHeaders }), "getAnalysisSubscription", "subscription", variables);
936
967
  },
937
- getAnalsyis(variables, requestHeaders) {
938
- return withWrapper((wrappedRequestHeaders) => client.request(GetAnalsyisDocument, variables, { ...requestHeaders, ...wrappedRequestHeaders }), "getAnalsyis", "query", variables);
968
+ getAnalysis(variables, requestHeaders) {
969
+ return withWrapper((wrappedRequestHeaders) => client.request(GetAnalysisDocument, variables, { ...requestHeaders, ...wrappedRequestHeaders }), "getAnalysis", "query", variables);
939
970
  },
940
971
  getFixes(variables, requestHeaders) {
941
972
  return withWrapper((wrappedRequestHeaders) => client.request(GetFixesDocument, variables, { ...requestHeaders, ...wrappedRequestHeaders }), "getFixes", "query", variables);
@@ -978,6 +1009,9 @@ function getSdk(client, withWrapper = defaultWrapper) {
978
1009
  },
979
1010
  autoPrAnalysis(variables, requestHeaders) {
980
1011
  return withWrapper((wrappedRequestHeaders) => client.request(AutoPrAnalysisDocument, variables, { ...requestHeaders, ...wrappedRequestHeaders }), "autoPrAnalysis", "mutation", variables);
1012
+ },
1013
+ GetMCPFixes(variables, requestHeaders) {
1014
+ return withWrapper((wrappedRequestHeaders) => client.request(GetMcpFixesDocument, variables, { ...requestHeaders, ...wrappedRequestHeaders }), "GetMCPFixes", "query", variables);
981
1015
  }
982
1016
  };
983
1017
  }
@@ -4545,7 +4579,7 @@ async function getAdoSdk(params) {
4545
4579
  const git = await api2.getGitApi();
4546
4580
  try {
4547
4581
  const branchStatus = await git.getBranch(repo, branch, projectName);
4548
- if (!branchStatus || !branchStatus.commit) {
4582
+ if (!branchStatus?.commit) {
4549
4583
  console.log(`no branch status: ${JSON.stringify(branchStatus)}`);
4550
4584
  throw new InvalidRepoUrlError("no branch status");
4551
4585
  }
@@ -4596,7 +4630,7 @@ async function getAdoSdk(params) {
4596
4630
  const url = new URL(repoUrl);
4597
4631
  const origin2 = url.origin.toLowerCase().endsWith(".visualstudio.com") ? DEFUALT_ADO_ORIGIN : url.origin.toLowerCase();
4598
4632
  const params2 = `path=/&versionDescriptor[versionOptions]=0&versionDescriptor[versionType]=commit&versionDescriptor[version]=${branch}&resolveLfs=true&$format=zip&api-version=5.0&download=true`;
4599
- const path9 = [
4633
+ const path12 = [
4600
4634
  prefixPath,
4601
4635
  owner,
4602
4636
  projectName,
@@ -4607,7 +4641,7 @@ async function getAdoSdk(params) {
4607
4641
  "items",
4608
4642
  "items"
4609
4643
  ].filter(Boolean).join("/");
4610
- return new URL(`${path9}?${params2}`, origin2).toString();
4644
+ return new URL(`${path12}?${params2}`, origin2).toString();
4611
4645
  },
4612
4646
  async getAdoBranchList({ repoUrl }) {
4613
4647
  try {
@@ -4674,7 +4708,7 @@ async function getAdoSdk(params) {
4674
4708
  const results = await Promise.allSettled([
4675
4709
  (async () => {
4676
4710
  const res = await git.getBranch(repo, ref, projectName);
4677
- if (!res.commit || !res.commit.commitId) {
4711
+ if (!res.commit?.commitId) {
4678
4712
  throw new InvalidRepoUrlError("no commit on branch");
4679
4713
  }
4680
4714
  return {
@@ -4694,7 +4728,7 @@ async function getAdoSdk(params) {
4694
4728
  projectName
4695
4729
  );
4696
4730
  const commit = res[0];
4697
- if (!commit || !commit.commitId) {
4731
+ if (!commit?.commitId) {
4698
4732
  throw new Error("no commit");
4699
4733
  }
4700
4734
  return {
@@ -6065,14 +6099,14 @@ function getGithubSdk(params = {}) {
6065
6099
  };
6066
6100
  },
6067
6101
  async getGithubBlameRanges(params2) {
6068
- const { ref, gitHubUrl, path: path9 } = params2;
6102
+ const { ref, gitHubUrl, path: path12 } = params2;
6069
6103
  const { owner, repo } = parseGithubOwnerAndRepo(gitHubUrl);
6070
6104
  const res = await octokit.graphql(
6071
6105
  GET_BLAME_DOCUMENT,
6072
6106
  {
6073
6107
  owner,
6074
6108
  repo,
6075
- path: path9,
6109
+ path: path12,
6076
6110
  ref
6077
6111
  }
6078
6112
  );
@@ -6378,11 +6412,11 @@ var GithubSCMLib = class extends SCMLib {
6378
6412
  markdownComment: comment
6379
6413
  });
6380
6414
  }
6381
- async getRepoBlameRanges(ref, path9) {
6415
+ async getRepoBlameRanges(ref, path12) {
6382
6416
  this._validateUrl();
6383
6417
  return await this.githubSdk.getGithubBlameRanges({
6384
6418
  ref,
6385
- path: path9,
6419
+ path: path12,
6386
6420
  gitHubUrl: this.url
6387
6421
  });
6388
6422
  }
@@ -6784,13 +6818,13 @@ function parseGitlabOwnerAndRepo(gitlabUrl) {
6784
6818
  const { organization, repoName, projectPath } = parsingResult;
6785
6819
  return { owner: organization, repo: repoName, projectPath };
6786
6820
  }
6787
- async function getGitlabBlameRanges({ ref, gitlabUrl, path: path9 }, options) {
6821
+ async function getGitlabBlameRanges({ ref, gitlabUrl, path: path12 }, options) {
6788
6822
  const { projectPath } = parseGitlabOwnerAndRepo(gitlabUrl);
6789
6823
  const api2 = getGitBeaker({
6790
6824
  url: gitlabUrl,
6791
6825
  gitlabAuthToken: options?.gitlabAuthToken
6792
6826
  });
6793
- const resp = await api2.RepositoryFiles.allFileBlames(projectPath, path9, ref);
6827
+ const resp = await api2.RepositoryFiles.allFileBlames(projectPath, path12, ref);
6794
6828
  let lineNumber = 1;
6795
6829
  return resp.filter((range) => range.lines).map((range) => {
6796
6830
  const oldLineNumber = lineNumber;
@@ -6966,10 +7000,10 @@ var GitlabSCMLib = class extends SCMLib {
6966
7000
  markdownComment: comment
6967
7001
  });
6968
7002
  }
6969
- async getRepoBlameRanges(ref, path9) {
7003
+ async getRepoBlameRanges(ref, path12) {
6970
7004
  this._validateUrl();
6971
7005
  return await getGitlabBlameRanges(
6972
- { ref, path: path9, gitlabUrl: this.url },
7006
+ { ref, path: path12, gitlabUrl: this.url },
6973
7007
  {
6974
7008
  url: this.url,
6975
7009
  gitlabAuthToken: this.accessToken
@@ -7352,10 +7386,11 @@ function filterSarifResult(sarifResult, codePathPatterns) {
7352
7386
  const paths = sarifResult.locations.map(
7353
7387
  (l) => l.physicalLocation.artifactLocation.uri
7354
7388
  );
7355
- const matchPaths = multimatch(paths, codePathPatterns, {
7389
+ const uniquePaths = [...new Set(paths)];
7390
+ const matchPaths = multimatch(uniquePaths, codePathPatterns, {
7356
7391
  dot: true
7357
7392
  });
7358
- return matchPaths.length > 0;
7393
+ return matchPaths.length === uniquePaths.length;
7359
7394
  }
7360
7395
  function fortifyVulnerabilityToSarifResult(vulnerability, auditMetadataParser, reportMetadataParser, unifiedNodePoolParser) {
7361
7396
  const suppressed = auditMetadataParser?.getAuditMetadata()[vulnerability.instanceID] || "false";
@@ -7440,7 +7475,7 @@ var SCANNERS = {
7440
7475
  Semgrep: "semgrep",
7441
7476
  Datadog: "datadog"
7442
7477
  };
7443
- var scannerToVulnerability_Report_Vendor_Enum = {
7478
+ var scannerToVulnerabilityReportVendorEnum = {
7444
7479
  [SCANNERS.Checkmarx]: "checkmarx" /* Checkmarx */,
7445
7480
  [SCANNERS.Snyk]: "snyk" /* Snyk */,
7446
7481
  [SCANNERS.Sonarqube]: "sonarqube" /* Sonarqube */,
@@ -7662,7 +7697,8 @@ var mobbCliCommand = {
7662
7697
  scan: "scan",
7663
7698
  analyze: "analyze",
7664
7699
  review: "review",
7665
- convertToSarif: "convert-to-sarif"
7700
+ convertToSarif: "convert-to-sarif",
7701
+ mcp: "mcp"
7666
7702
  };
7667
7703
 
7668
7704
  // src/args/yargs.ts
@@ -7838,7 +7874,7 @@ function buildFixCommentBody({
7838
7874
  }
7839
7875
  const subTitle = validFixParseRes.success ? getCommitDescription({
7840
7876
  issueType: validFixParseRes.data.safeIssueType,
7841
- vendor: scannerToVulnerability_Report_Vendor_Enum[scanner],
7877
+ vendor: scannerToVulnerabilityReportVendorEnum[scanner],
7842
7878
  severity: validFixParseRes.data.severityText,
7843
7879
  guidances: getGuidances({
7844
7880
  questions: validFixParseRes.data.patchAndQuestions.questions.map(toQuestion),
@@ -7884,7 +7920,7 @@ function buildIssueCommentBody({
7884
7920
  const title = `# ${MobbIconMarkdown} Irrelevant issues were spotted - no action required \u{1F9F9}`;
7885
7921
  const subTitle = getCommitIssueDescription({
7886
7922
  issueType,
7887
- vendor: scannerToVulnerability_Report_Vendor_Enum[scanner],
7923
+ vendor: scannerToVulnerabilityReportVendorEnum[scanner],
7888
7924
  irrelevantIssueWithTags,
7889
7925
  fpDescription
7890
7926
  });
@@ -7963,7 +7999,7 @@ async function postIssueComment(params) {
7963
7999
  fpDescription
7964
8000
  } = params;
7965
8001
  const {
7966
- path: path9,
8002
+ path: path12,
7967
8003
  startLine,
7968
8004
  vulnerabilityReportIssue: {
7969
8005
  vulnerabilityReportIssueTags,
@@ -7978,7 +8014,7 @@ async function postIssueComment(params) {
7978
8014
  Refresh the page in order to see the changes.`,
7979
8015
  pull_number: pullRequest,
7980
8016
  commit_id: commitSha,
7981
- path: path9,
8017
+ path: path12,
7982
8018
  line: startLine
7983
8019
  });
7984
8020
  const commentId = commentRes.data.id;
@@ -8012,7 +8048,7 @@ async function postFixComment(params) {
8012
8048
  scanner
8013
8049
  } = params;
8014
8050
  const {
8015
- path: path9,
8051
+ path: path12,
8016
8052
  startLine,
8017
8053
  vulnerabilityReportIssue: { fixId, vulnerabilityReportIssueTags, category },
8018
8054
  vulnerabilityReportIssueId
@@ -8030,7 +8066,7 @@ async function postFixComment(params) {
8030
8066
  Refresh the page in order to see the changes.`,
8031
8067
  pull_number: pullRequest,
8032
8068
  commit_id: commitSha,
8033
- path: path9,
8069
+ path: path12,
8034
8070
  line: startLine
8035
8071
  });
8036
8072
  const commentId = commentRes.data.id;
@@ -8163,7 +8199,7 @@ async function addFixCommentsForPr({
8163
8199
  project: { organizationId }
8164
8200
  }
8165
8201
  } = getAnalysisRes;
8166
- if (!getAnalysisRes.repo || !getAnalysisRes.repo.commitSha || !getAnalysisRes.repo.pullRequest) {
8202
+ if (!getAnalysisRes.repo?.commitSha || !getAnalysisRes.repo.pullRequest) {
8167
8203
  throw new Error("repo not found");
8168
8204
  }
8169
8205
  const { commitSha, pullRequest } = getAnalysisRes.repo;
@@ -8806,7 +8842,7 @@ var GQLClient = class {
8806
8842
  async subscribeToAnalysis(params) {
8807
8843
  const { callbackStates } = params;
8808
8844
  return subscribe(
8809
- GetAnalysisDocument,
8845
+ GetAnalysisSubscriptionDocument,
8810
8846
  params.subscribeToAnalysisParams,
8811
8847
  async (resolve, reject, data) => {
8812
8848
  if (!data.analysis?.state || data.analysis?.state === "Failed" /* Failed */) {
@@ -8830,7 +8866,7 @@ var GQLClient = class {
8830
8866
  );
8831
8867
  }
8832
8868
  async getAnalysis(analysisId) {
8833
- const res = await this._clientSdk.getAnalsyis({
8869
+ const res = await this._clientSdk.getAnalysis({
8834
8870
  analysisId
8835
8871
  });
8836
8872
  if (!res.analysis) {
@@ -8897,7 +8933,7 @@ function endsWithAny(str, suffixes) {
8897
8933
  return str.endsWith(suffix);
8898
8934
  });
8899
8935
  }
8900
- function _get_manifest_files_suffixes() {
8936
+ function getManifestFilesSuffixes() {
8901
8937
  return ["package.json", "pom.xml"];
8902
8938
  }
8903
8939
  async function pack(srcDirPath, vulnFiles, isIncludeAllFiles = false) {
@@ -8938,7 +8974,7 @@ async function pack(srcDirPath, vulnFiles, isIncludeAllFiles = false) {
8938
8974
  for (const filepath of filepaths) {
8939
8975
  const absFilepath = path5.join(srcDirPath, filepath.toString());
8940
8976
  if (!isIncludeAllFiles) {
8941
- vulnFiles = vulnFiles.concat(_get_manifest_files_suffixes());
8977
+ vulnFiles = vulnFiles.concat(getManifestFilesSuffixes());
8942
8978
  if (!endsWithAny(
8943
8979
  absFilepath.toString().replaceAll(path5.win32.sep, path5.posix.sep),
8944
8980
  vulnFiles
@@ -9072,16 +9108,16 @@ function createSpawn({ args, processPath, name, cwd }, options) {
9072
9108
  return createChildProcess({ childProcess: child, name }, options);
9073
9109
  }
9074
9110
  function createChildProcess({ childProcess, name }, options) {
9075
- const debug20 = Debug14(`mobbdev:${name}`);
9111
+ const debug21 = Debug14(`mobbdev:${name}`);
9076
9112
  const { display } = options;
9077
9113
  return new Promise((resolve, reject) => {
9078
9114
  let out = "";
9079
9115
  const onData = (chunk) => {
9080
- debug20(`chunk received from ${name} std ${chunk}`);
9116
+ debug21(`chunk received from ${name} std ${chunk}`);
9081
9117
  out += chunk;
9082
9118
  };
9083
- if (!childProcess || !childProcess?.stdout || !childProcess?.stderr) {
9084
- debug20(`unable to fork ${name}`);
9119
+ if (!childProcess?.stdout || !childProcess?.stderr) {
9120
+ debug21(`unable to fork ${name}`);
9085
9121
  reject(new Error(`unable to fork ${name}`));
9086
9122
  }
9087
9123
  childProcess.stdout?.on("data", onData);
@@ -9091,11 +9127,11 @@ function createChildProcess({ childProcess, name }, options) {
9091
9127
  childProcess.stderr?.pipe(process2.stderr);
9092
9128
  }
9093
9129
  childProcess.on("exit", (code) => {
9094
- debug20(`${name} exit code ${code}`);
9130
+ debug21(`${name} exit code ${code}`);
9095
9131
  resolve({ message: out, code });
9096
9132
  });
9097
9133
  childProcess.on("error", (err) => {
9098
- debug20(`${name} error %o`, err);
9134
+ debug21(`${name} error %o`, err);
9099
9135
  reject(err);
9100
9136
  });
9101
9137
  });
@@ -10339,8 +10375,1371 @@ async function analyzeHandler(args) {
10339
10375
  await analyze(args, { skipPrompts: args.yes });
10340
10376
  }
10341
10377
 
10342
- // src/args/commands/review.ts
10378
+ // src/mcp/core/McpServer.ts
10379
+ import { Server } from "@modelcontextprotocol/sdk/server/index.js";
10380
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
10381
+ import {
10382
+ CallToolRequestSchema,
10383
+ ListToolsRequestSchema
10384
+ } from "@modelcontextprotocol/sdk/types.js";
10385
+
10386
+ // src/mcp/Logger.ts
10387
+ var logglerUrl = "http://localhost:4444/log";
10388
+ var Logger = class {
10389
+ log(message, level = "info", data) {
10390
+ const logMessage = {
10391
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
10392
+ level,
10393
+ message,
10394
+ data
10395
+ };
10396
+ try {
10397
+ fetch(logglerUrl, {
10398
+ method: "POST",
10399
+ headers: { "Content-Type": "application/json" },
10400
+ body: JSON.stringify(logMessage)
10401
+ });
10402
+ } catch (error) {
10403
+ }
10404
+ }
10405
+ };
10406
+ var logger = new Logger();
10407
+ var logInfo = (message, data) => logger.log(message, "info", data);
10408
+ var logError = (message, data) => logger.log(message, "error", data);
10409
+ var logWarn = (message, data) => logger.log(message, "warn", data);
10410
+ var logDebug = (message, data) => logger.log(message, "debug", data);
10411
+ var Logger_default = logger.log;
10412
+
10413
+ // src/mcp/core/ToolRegistry.ts
10414
+ var ToolRegistry = class {
10415
+ constructor() {
10416
+ __publicField(this, "tools", /* @__PURE__ */ new Map());
10417
+ }
10418
+ registerTool(tool) {
10419
+ if (this.tools.has(tool.name)) {
10420
+ logWarn(`Tool ${tool.name} is already registered, overwriting`, {
10421
+ toolName: tool.name
10422
+ });
10423
+ }
10424
+ this.tools.set(tool.name, tool);
10425
+ logDebug(`Tool registered: ${tool.name}`, {
10426
+ toolName: tool.name,
10427
+ description: tool.definition.description
10428
+ });
10429
+ }
10430
+ getTool(name) {
10431
+ return this.tools.get(name);
10432
+ }
10433
+ getAllTools() {
10434
+ return Array.from(this.tools.values()).map((tool) => tool.definition);
10435
+ }
10436
+ getToolNames() {
10437
+ return Array.from(this.tools.keys());
10438
+ }
10439
+ hasTool(name) {
10440
+ return this.tools.has(name);
10441
+ }
10442
+ getToolCount() {
10443
+ return this.tools.size;
10444
+ }
10445
+ };
10446
+
10447
+ // src/mcp/core/McpServer.ts
10448
+ var McpServer = class {
10449
+ constructor(config4) {
10450
+ __publicField(this, "server");
10451
+ __publicField(this, "toolRegistry");
10452
+ __publicField(this, "isEventHandlersSetup", false);
10453
+ this.server = new Server(
10454
+ {
10455
+ name: config4.name,
10456
+ version: config4.version
10457
+ },
10458
+ {
10459
+ capabilities: {
10460
+ tools: {}
10461
+ }
10462
+ }
10463
+ );
10464
+ this.toolRegistry = new ToolRegistry();
10465
+ this.setupHandlers();
10466
+ this.setupProcessEventHandlers();
10467
+ logInfo("MCP server instance created", config4);
10468
+ }
10469
+ setupProcessEventHandlers() {
10470
+ if (this.isEventHandlersSetup) {
10471
+ logDebug("Process event handlers already setup, skipping");
10472
+ return;
10473
+ }
10474
+ const signals = {
10475
+ SIGINT: "MCP server interrupted",
10476
+ SIGTERM: "MCP server terminated",
10477
+ exit: "MCP server exiting",
10478
+ uncaughtException: "Uncaught exception in MCP server",
10479
+ unhandledRejection: "Unhandled promise rejection in MCP server",
10480
+ warning: "Warning in MCP server"
10481
+ };
10482
+ Object.entries(signals).forEach(([signal, message]) => {
10483
+ process.on(
10484
+ signal,
10485
+ (error) => {
10486
+ if (error && signal !== "exit") {
10487
+ logError(`${message}`, { error, signal });
10488
+ } else {
10489
+ logInfo(message, { signal });
10490
+ }
10491
+ if (signal === "SIGINT" || signal === "SIGTERM") {
10492
+ process.exit(0);
10493
+ }
10494
+ if (signal === "uncaughtException") {
10495
+ process.exit(1);
10496
+ }
10497
+ }
10498
+ );
10499
+ });
10500
+ this.isEventHandlersSetup = true;
10501
+ logDebug("Process event handlers registered");
10502
+ }
10503
+ createShutdownPromise() {
10504
+ return new Promise((resolve) => {
10505
+ const cleanup = () => {
10506
+ logInfo("Process shutdown initiated");
10507
+ resolve();
10508
+ };
10509
+ process.once("SIGINT", cleanup);
10510
+ process.once("SIGTERM", cleanup);
10511
+ });
10512
+ }
10513
+ setupHandlers() {
10514
+ this.server.setRequestHandler(
10515
+ ListToolsRequestSchema,
10516
+ async (request) => {
10517
+ logInfo("Received list_tools request", { params: request.params });
10518
+ const tools = this.toolRegistry.getAllTools();
10519
+ const response = { tools };
10520
+ logInfo("Returning list_tools response", {
10521
+ toolCount: tools.length,
10522
+ toolNames: tools.map((t) => t.name),
10523
+ response
10524
+ });
10525
+ return response;
10526
+ }
10527
+ );
10528
+ this.server.setRequestHandler(
10529
+ CallToolRequestSchema,
10530
+ async (request) => {
10531
+ const { name, arguments: args } = request.params;
10532
+ logInfo(`Received call tool request for ${name}`, { name, args });
10533
+ try {
10534
+ const tool = this.toolRegistry.getTool(name);
10535
+ if (!tool) {
10536
+ const errorMsg = `Unknown tool: ${name}`;
10537
+ logWarn(errorMsg, {
10538
+ name,
10539
+ availableTools: this.toolRegistry.getToolNames()
10540
+ });
10541
+ throw new Error(errorMsg);
10542
+ }
10543
+ logDebug(`Executing tool: ${name}`, { args });
10544
+ const response = await tool.execute(args);
10545
+ const serializedResponse = JSON.parse(JSON.stringify(response));
10546
+ logInfo(`Tool ${name} executed successfully`, {
10547
+ responseType: typeof response,
10548
+ hasContent: !!serializedResponse.content
10549
+ });
10550
+ return serializedResponse;
10551
+ } catch (error) {
10552
+ const errorMessage = error instanceof Error ? error.message : String(error);
10553
+ logError(`Error executing tool ${name}: ${errorMessage}`, {
10554
+ error,
10555
+ toolName: name,
10556
+ args
10557
+ });
10558
+ throw error;
10559
+ }
10560
+ }
10561
+ );
10562
+ logDebug("MCP server handlers registered");
10563
+ }
10564
+ registerTool(tool) {
10565
+ this.toolRegistry.registerTool({
10566
+ name: tool.name,
10567
+ definition: tool.definition,
10568
+ execute: tool.execute
10569
+ });
10570
+ logDebug(`Tool registered: ${tool.name}`);
10571
+ }
10572
+ async start() {
10573
+ try {
10574
+ logDebug("Starting MCP server");
10575
+ const transport = new StdioServerTransport();
10576
+ await this.server.connect(transport);
10577
+ logInfo("MCP server is running on stdin/stdout");
10578
+ process.stdin.resume();
10579
+ await this.createShutdownPromise();
10580
+ await this.stop();
10581
+ } catch (error) {
10582
+ logError("Failed to start MCP server", { error });
10583
+ throw error;
10584
+ }
10585
+ }
10586
+ async stop() {
10587
+ logInfo("MCP server shutting down");
10588
+ }
10589
+ };
10590
+
10591
+ // src/mcp/services/GitService.ts
10592
+ import * as path9 from "path";
10593
+ import { simpleGit as simpleGit4 } from "simple-git";
10594
+ var GitService = class {
10595
+ constructor(repositoryPath) {
10596
+ __publicField(this, "git");
10597
+ __publicField(this, "repositoryPath");
10598
+ this.git = simpleGit4(repositoryPath, { binary: "git" });
10599
+ this.repositoryPath = repositoryPath;
10600
+ logDebug("Git service initialized", { repositoryPath });
10601
+ }
10602
+ /**
10603
+ * Validates that the path is a valid git repository
10604
+ */
10605
+ async validateRepository() {
10606
+ logDebug("Validating git repository");
10607
+ try {
10608
+ const isRepo = await this.git.checkIsRepo();
10609
+ if (!isRepo) {
10610
+ const error = "Path is not a valid git repository";
10611
+ logError(error);
10612
+ return { isValid: false, error };
10613
+ }
10614
+ logDebug("Git repository validation successful");
10615
+ return { isValid: true };
10616
+ } catch (error) {
10617
+ const errorMessage = `Failed to verify git repository: ${error.message}`;
10618
+ logError(errorMessage, { error });
10619
+ return { isValid: false, error: errorMessage };
10620
+ }
10621
+ }
10622
+ /**
10623
+ * Gets the current git status and returns changed files
10624
+ */
10625
+ async getChangedFiles() {
10626
+ logDebug("Getting git status");
10627
+ try {
10628
+ const status = await this.git.status();
10629
+ const gitRoot = await this.git.revparse(["--show-toplevel"]);
10630
+ const relativePathFromGitRoot = path9.relative(
10631
+ gitRoot,
10632
+ this.repositoryPath
10633
+ );
10634
+ const files = status.files.map((file) => {
10635
+ const gitRelativePath = file.path;
10636
+ if (relativePathFromGitRoot === "") {
10637
+ return gitRelativePath;
10638
+ }
10639
+ if (gitRelativePath.startsWith(relativePathFromGitRoot + "/")) {
10640
+ return gitRelativePath.substring(relativePathFromGitRoot.length + 1);
10641
+ }
10642
+ return path9.relative(
10643
+ this.repositoryPath,
10644
+ path9.join(gitRoot, gitRelativePath)
10645
+ );
10646
+ });
10647
+ logInfo("Git status retrieved", {
10648
+ fileCount: files.length,
10649
+ files: files.slice(0, 10),
10650
+ // Log first 10 files to avoid spam
10651
+ gitRoot,
10652
+ workingDir: this.repositoryPath,
10653
+ relativePathFromGitRoot
10654
+ });
10655
+ return { files, status };
10656
+ } catch (error) {
10657
+ const errorMessage = `Failed to get git status: ${error.message}`;
10658
+ logError(errorMessage, { error });
10659
+ throw new Error(errorMessage);
10660
+ }
10661
+ }
10662
+ };
10663
+
10664
+ // src/mcp/services/PathValidation.ts
10343
10665
  import fs8 from "node:fs";
10666
+ import path10 from "node:path";
10667
+ var PathValidation = class {
10668
+ /**
10669
+ * Validates a path for MCP usage - combines security and existence checks
10670
+ */
10671
+ async validatePath(inputPath) {
10672
+ logDebug("Validating MCP path", { inputPath });
10673
+ if (inputPath.includes("..")) {
10674
+ const error = `Path contains path traversal patterns: ${inputPath}`;
10675
+ logError(error);
10676
+ return { isValid: false, error };
10677
+ }
10678
+ const normalizedPath = path10.normalize(inputPath);
10679
+ if (normalizedPath.includes("..")) {
10680
+ const error = `Normalized path contains path traversal patterns: ${inputPath}`;
10681
+ logError(error);
10682
+ return { isValid: false, error };
10683
+ }
10684
+ const decodedPath = decodeURIComponent(inputPath);
10685
+ if (decodedPath.includes("..") || decodedPath !== inputPath) {
10686
+ const error = `Path contains encoded traversal attempts: ${inputPath}`;
10687
+ logError(error);
10688
+ return { isValid: false, error };
10689
+ }
10690
+ if (inputPath.includes("\0") || inputPath.includes("\0")) {
10691
+ const error = `Path contains dangerous characters: ${inputPath}`;
10692
+ logError(error);
10693
+ return { isValid: false, error };
10694
+ }
10695
+ logDebug("Path validation successful", { inputPath });
10696
+ logDebug("Checking path existence", { inputPath });
10697
+ try {
10698
+ await fs8.promises.access(inputPath);
10699
+ logDebug("Path exists and is accessible", { inputPath });
10700
+ return { isValid: true };
10701
+ } catch (error) {
10702
+ const errorMessage = `Path does not exist or is not accessible: ${inputPath}`;
10703
+ logError(errorMessage, { error });
10704
+ return { isValid: false, error: errorMessage };
10705
+ }
10706
+ }
10707
+ };
10708
+
10709
+ // src/mcp/services/FilePacking.ts
10710
+ import fs9 from "node:fs";
10711
+ import path11 from "node:path";
10712
+ import AdmZip3 from "adm-zip";
10713
+ import { isBinary as isBinary2 } from "istextorbinary";
10714
+ var MAX_FILE_SIZE2 = 1024 * 1024 * 5;
10715
+ var EXCLUDED_FILE_PATTERNS = [
10716
+ // Configuration files
10717
+ ".json",
10718
+ ".yaml",
10719
+ ".yml",
10720
+ ".toml",
10721
+ ".ini",
10722
+ ".conf",
10723
+ ".config",
10724
+ ".xml",
10725
+ ".env",
10726
+ // Documentation
10727
+ ".md",
10728
+ ".txt",
10729
+ ".rst",
10730
+ ".adoc",
10731
+ // Lock/dependency files
10732
+ ".lock",
10733
+ // Images and media
10734
+ ".png",
10735
+ ".jpg",
10736
+ ".jpeg",
10737
+ ".gif",
10738
+ ".svg",
10739
+ ".ico",
10740
+ ".webp",
10741
+ ".bmp",
10742
+ ".tiff",
10743
+ // Fonts
10744
+ ".ttf",
10745
+ ".otf",
10746
+ ".woff",
10747
+ ".woff2",
10748
+ ".eot",
10749
+ // Archives
10750
+ ".zip",
10751
+ ".tar",
10752
+ ".gz",
10753
+ ".rar",
10754
+ ".7z",
10755
+ // Logs and databases
10756
+ ".log",
10757
+ ".db",
10758
+ ".sqlite",
10759
+ ".sql",
10760
+ // Certificates and keys
10761
+ ".pem",
10762
+ ".crt",
10763
+ ".key",
10764
+ ".p12",
10765
+ ".pfx",
10766
+ // IDE/Editor files
10767
+ ".editorconfig",
10768
+ ".sublime-project",
10769
+ ".sublime-workspace",
10770
+ // System files
10771
+ ".DS_Store",
10772
+ "Thumbs.db",
10773
+ // Coverage reports
10774
+ ".lcov",
10775
+ // Compiled/binary files
10776
+ ".exe",
10777
+ ".dll",
10778
+ ".so",
10779
+ ".dylib",
10780
+ ".class",
10781
+ ".pyc",
10782
+ ".pyo",
10783
+ ".o",
10784
+ ".obj",
10785
+ // Minified files
10786
+ ".min.js",
10787
+ ".min.css",
10788
+ ".min.html",
10789
+ // Test files
10790
+ ".test.js",
10791
+ ".test.ts",
10792
+ ".test.jsx",
10793
+ ".test.tsx",
10794
+ ".spec.js",
10795
+ ".spec.ts",
10796
+ ".spec.jsx",
10797
+ ".spec.tsx",
10798
+ // TypeScript declaration files
10799
+ ".d.ts",
10800
+ // Build/generated files
10801
+ ".bundle.js",
10802
+ ".chunk.js",
10803
+ // Build/CI files (exact filenames)
10804
+ "dockerfile",
10805
+ "jenkinsfile",
10806
+ // Lock files (ones without standard extensions)
10807
+ "go.sum",
10808
+ // Version control
10809
+ ".gitignore",
10810
+ ".gitattributes",
10811
+ ".gitmodules",
10812
+ ".gitkeep",
10813
+ ".keep",
10814
+ ".hgignore",
10815
+ // Node.js specific
10816
+ ".nvmrc",
10817
+ ".node-version",
10818
+ ".npmrc",
10819
+ ".yarnrc",
10820
+ ".pnpmfile.cjs",
10821
+ // Language version files
10822
+ ".ruby-version",
10823
+ ".python-version",
10824
+ ".rvmrc",
10825
+ ".rbenv-version",
10826
+ ".gvmrc",
10827
+ // Build tools and task runners
10828
+ "makefile",
10829
+ "rakefile",
10830
+ "gulpfile.js",
10831
+ "gruntfile.js",
10832
+ "webpack.config.js",
10833
+ "webpack.config.ts",
10834
+ "rollup.config.js",
10835
+ "vite.config.js",
10836
+ "vite.config.ts",
10837
+ "next.config.js",
10838
+ "nuxt.config.js",
10839
+ "tailwind.config.js",
10840
+ "postcss.config.js",
10841
+ // JavaScript/TypeScript config
10842
+ ".babelrc",
10843
+ ".babelrc.js",
10844
+ ".swcrc",
10845
+ ".browserslistrc",
10846
+ // Testing frameworks
10847
+ "jest.config.js",
10848
+ "jest.config.ts",
10849
+ "vitest.config.js",
10850
+ "karma.conf.js",
10851
+ "protractor.conf.js",
10852
+ "cypress.config.js",
10853
+ "playwright.config.js",
10854
+ ".nycrc",
10855
+ ".c8rc",
10856
+ // Linting/formatting configs
10857
+ ".eslintrc",
10858
+ ".eslintrc.js",
10859
+ ".prettierrc",
10860
+ ".prettierrc.js",
10861
+ ".stylelintrc",
10862
+ ".stylelintrc.js",
10863
+ // Package manager configs (ones without standard extensions)
10864
+ "pipfile",
10865
+ "gemfile",
10866
+ "go.mod",
10867
+ "project.clj",
10868
+ // Python specific
10869
+ "setup.py",
10870
+ "setup.cfg",
10871
+ "manifest.in",
10872
+ ".pythonrc",
10873
+ // Documentation files (ones without standard extensions)
10874
+ "readme",
10875
+ "changelog",
10876
+ "authors",
10877
+ "contributors",
10878
+ // License and legal (ones without standard extensions)
10879
+ "license",
10880
+ "notice",
10881
+ "copyright",
10882
+ // Web specific
10883
+ ".htaccess"
10884
+ ];
10885
+ var FilePacking = class {
10886
+ isExcludedFileType(filepath) {
10887
+ const basename = path11.basename(filepath).toLowerCase();
10888
+ if (basename === ".env" || basename.startsWith(".env.")) {
10889
+ return true;
10890
+ }
10891
+ if (EXCLUDED_FILE_PATTERNS.some((pattern) => basename.endsWith(pattern))) {
10892
+ return true;
10893
+ }
10894
+ return false;
10895
+ }
10896
+ async packFiles(sourceDirectoryPath, filesToPack) {
10897
+ logInfo(`FilePacking: packing files from ${sourceDirectoryPath}`);
10898
+ const zip = new AdmZip3();
10899
+ let packedFilesCount = 0;
10900
+ logInfo("FilePacking: compressing files");
10901
+ for (const filepath of filesToPack) {
10902
+ const absoluteFilepath = path11.join(sourceDirectoryPath, filepath);
10903
+ if (this.isExcludedFileType(filepath)) {
10904
+ logInfo(
10905
+ `FilePacking: ignoring ${filepath} because it is an excluded file type`
10906
+ );
10907
+ continue;
10908
+ }
10909
+ if (!fs9.existsSync(absoluteFilepath)) {
10910
+ logInfo(`FilePacking: ignoring ${filepath} because it does not exist`);
10911
+ continue;
10912
+ }
10913
+ if (fs9.lstatSync(absoluteFilepath).size > MAX_FILE_SIZE2) {
10914
+ logInfo(
10915
+ `FilePacking: ignoring ${filepath} because the size is > ${MAX_FILE_SIZE2 / (1024 * 1024)}MB`
10916
+ );
10917
+ continue;
10918
+ }
10919
+ let data;
10920
+ try {
10921
+ data = fs9.readFileSync(absoluteFilepath);
10922
+ } catch (fsError) {
10923
+ logInfo(
10924
+ `FilePacking: failed to read ${filepath} from filesystem: ${fsError}`
10925
+ );
10926
+ continue;
10927
+ }
10928
+ if (isBinary2(null, data)) {
10929
+ logInfo(
10930
+ `FilePacking: ignoring ${filepath} because it seems to be a binary file`
10931
+ );
10932
+ continue;
10933
+ }
10934
+ zip.addFile(filepath, data);
10935
+ packedFilesCount++;
10936
+ }
10937
+ const zipBuffer = zip.toBuffer();
10938
+ logInfo(
10939
+ `FilePacking: read ${packedFilesCount} source files. total size: ${zipBuffer.length} bytes`
10940
+ );
10941
+ logInfo("FilePacking: Files packed successfully");
10942
+ return zipBuffer;
10943
+ }
10944
+ };
10945
+
10946
+ // src/mcp/services/FileUpload.ts
10947
+ import { HttpProxyAgent as HttpProxyAgent2 } from "http-proxy-agent";
10948
+ import { HttpsProxyAgent as HttpsProxyAgent3 } from "https-proxy-agent";
10949
+ var FileUpload = class {
10950
+ getProxyAgent(url) {
10951
+ const HTTPS_PROXY2 = process.env["HTTPS_PROXY"];
10952
+ const HTTP_PROXY2 = process.env["HTTP_PROXY"];
10953
+ try {
10954
+ const parsedUrl = new URL(url);
10955
+ const isHttp = parsedUrl.protocol === "http:";
10956
+ const isHttps = parsedUrl.protocol === "https:";
10957
+ const proxy = isHttps ? HTTPS_PROXY2 : isHttp ? HTTP_PROXY2 : null;
10958
+ if (proxy) {
10959
+ logInfo(`FileUpload: Using proxy ${proxy}`);
10960
+ return isHttps ? new HttpsProxyAgent3(proxy) : new HttpProxyAgent2(proxy);
10961
+ }
10962
+ } catch (err) {
10963
+ logInfo(
10964
+ `FileUpload: Skipping proxy for ${url}. Reason: ${err.message}`
10965
+ );
10966
+ }
10967
+ return void 0;
10968
+ }
10969
+ async uploadFile(options) {
10970
+ const { file, url, uploadKey, uploadFields } = options;
10971
+ logInfo(`FileUpload: upload file start ${url}`);
10972
+ logInfo(`FileUpload: upload fields`, uploadFields);
10973
+ logInfo(`FileUpload: upload key ${uploadKey}`);
10974
+ const {
10975
+ default: fetch5,
10976
+ File: File2,
10977
+ fileFrom: fileFrom2,
10978
+ FormData: FormData2
10979
+ } = await import("node-fetch");
10980
+ const form = new FormData2();
10981
+ Object.entries(uploadFields).forEach(([key, value]) => {
10982
+ form.append(key, value);
10983
+ });
10984
+ if (!form.has("key")) {
10985
+ form.append("key", uploadKey);
10986
+ }
10987
+ if (typeof file === "string") {
10988
+ logInfo(`FileUpload: upload file from path ${file}`);
10989
+ form.append("file", await fileFrom2(file));
10990
+ } else {
10991
+ logInfo(`FileUpload: upload file from buffer`);
10992
+ form.append("file", new File2([file], "file"));
10993
+ }
10994
+ const agent = this.getProxyAgent(url);
10995
+ const response = await fetch5(url, {
10996
+ method: "POST",
10997
+ body: form,
10998
+ agent
10999
+ });
11000
+ if (!response.ok) {
11001
+ logInfo(`FileUpload: error from S3 ${response.body} ${response.status}`);
11002
+ throw new Error(`Failed to upload the file: ${response.status}`);
11003
+ }
11004
+ logInfo(`FileUpload: upload file done`);
11005
+ }
11006
+ };
11007
+
11008
+ // src/mcp/services/McpGQLClient.ts
11009
+ import { GraphQLClient as GraphQLClient2 } from "graphql-request";
11010
+ import { v4 as uuidv42 } from "uuid";
11011
+
11012
+ // src/mcp/constants.ts
11013
+ var DEFAULT_API_URL = "https://api.mobb.ai/v1/graphql";
11014
+ var API_KEY_HEADER_NAME2 = "x-mobb-key";
11015
+
11016
+ // src/mcp/services/Subscribe.ts
11017
+ import Debug20 from "debug";
11018
+ import { createClient as createClient2 } from "graphql-ws";
11019
+ import { HttpsProxyAgent as HttpsProxyAgent4 } from "https-proxy-agent";
11020
+ import WebSocket2 from "ws";
11021
+ var debug19 = Debug20("mobbdev:subscribe");
11022
+ var SUBSCRIPTION_TIMEOUT_MS2 = 30 * 60 * 1e3;
11023
+ function createWSClient2(options) {
11024
+ const proxy = options.url.startsWith("wss://") && process.env["HTTPS_PROXY"] ? new HttpsProxyAgent4(process.env["HTTPS_PROXY"]) : options.url.startsWith("ws://") && process.env["HTTP_PROXY"] ? new HttpsProxyAgent4(process.env["HTTP_PROXY"]) : null;
11025
+ debug19(
11026
+ `Using proxy: ${proxy ? "yes" : "no"} with url: ${options.url} and with proxy: ${process.env["HTTP_PROXY"]} for the websocket connection`
11027
+ );
11028
+ const CustomWebSocket = class extends WebSocket2 {
11029
+ constructor(address, protocols) {
11030
+ super(address, protocols, proxy ? { agent: proxy } : void 0);
11031
+ }
11032
+ };
11033
+ return createClient2({
11034
+ //this is needed to prevent AWS from killing the connection
11035
+ //currently our load balancer has a 29s idle timeout
11036
+ keepAlive: 1e4,
11037
+ url: options.url,
11038
+ webSocketImpl: proxy ? CustomWebSocket : options.websocket || WebSocket2,
11039
+ connectionParams: () => {
11040
+ return {
11041
+ headers: options.type === "apiKey" ? {
11042
+ [API_KEY_HEADER_NAME2]: options.apiKey
11043
+ } : { authorization: `Bearer ${options.token}` }
11044
+ };
11045
+ }
11046
+ });
11047
+ }
11048
+ var Subscribe = class {
11049
+ static subscribe(query, variables, callback, wsClientOptions) {
11050
+ return new Promise((resolve, reject) => {
11051
+ let timer = null;
11052
+ const { timeoutInMs = SUBSCRIPTION_TIMEOUT_MS2 } = wsClientOptions;
11053
+ const API_URL2 = process.env["API_URL"] || DEFAULT_API_URL;
11054
+ const client = createWSClient2({
11055
+ ...wsClientOptions,
11056
+ websocket: WebSocket2,
11057
+ url: API_URL2.replace("http", "ws")
11058
+ });
11059
+ const unsubscribe = client.subscribe(
11060
+ { query, variables },
11061
+ {
11062
+ next: (data) => {
11063
+ function callbackResolve(data2) {
11064
+ unsubscribe();
11065
+ if (timer) {
11066
+ clearTimeout(timer);
11067
+ }
11068
+ resolve(data2);
11069
+ }
11070
+ function callbackReject(data2) {
11071
+ unsubscribe();
11072
+ if (timer) {
11073
+ clearTimeout(timer);
11074
+ }
11075
+ reject(data2);
11076
+ }
11077
+ if (!data.data) {
11078
+ reject(
11079
+ new Error(
11080
+ `Broken data object from graphQL subscribe: ${JSON.stringify(
11081
+ data
11082
+ )} for query: ${query}`
11083
+ )
11084
+ );
11085
+ } else {
11086
+ callback(callbackResolve, callbackReject, data.data);
11087
+ }
11088
+ },
11089
+ error: (error) => {
11090
+ if (timer) {
11091
+ clearTimeout(timer);
11092
+ }
11093
+ reject(error);
11094
+ },
11095
+ complete: () => {
11096
+ return;
11097
+ }
11098
+ }
11099
+ );
11100
+ if (typeof timeoutInMs === "number") {
11101
+ timer = setTimeout(() => {
11102
+ unsubscribe();
11103
+ reject(
11104
+ new Error(
11105
+ `Timeout expired for graphQL subscribe query: ${query} with timeout: ${timeoutInMs}`
11106
+ )
11107
+ );
11108
+ }, timeoutInMs);
11109
+ }
11110
+ });
11111
+ }
11112
+ };
11113
+ var subscribe2 = Subscribe.subscribe;
11114
+
11115
+ // src/mcp/services/McpGQLClient.ts
11116
+ var McpGQLClient = class {
11117
+ constructor(args) {
11118
+ __publicField(this, "client");
11119
+ __publicField(this, "clientSdk");
11120
+ __publicField(this, "apiKey");
11121
+ __publicField(this, "apiUrl");
11122
+ const API_URL2 = process.env["API_URL"] || DEFAULT_API_URL;
11123
+ this.apiKey = args.apiKey;
11124
+ this.apiUrl = API_URL2;
11125
+ this.client = new GraphQLClient2(API_URL2, {
11126
+ headers: { [API_KEY_HEADER_NAME2]: args.apiKey || "" },
11127
+ requestMiddleware: (request) => {
11128
+ const requestId = uuidv42();
11129
+ return {
11130
+ ...request,
11131
+ headers: {
11132
+ ...request.headers,
11133
+ "x-hasura-request-id": requestId
11134
+ }
11135
+ };
11136
+ }
11137
+ });
11138
+ this.clientSdk = getSdk(this.client);
11139
+ }
11140
+ getErrorContext() {
11141
+ return {
11142
+ endpoint: this.apiUrl,
11143
+ headers: {
11144
+ [API_KEY_HEADER_NAME2]: this.apiKey ? "[REDACTED]" : "undefined",
11145
+ "x-hasura-request-id": "[DYNAMIC]"
11146
+ }
11147
+ };
11148
+ }
11149
+ async verifyConnection() {
11150
+ try {
11151
+ logDebug("GraphQL: Calling Me query for connection verification");
11152
+ const result = await this.clientSdk.Me();
11153
+ logInfo("GraphQL: Me query successful", { result });
11154
+ return true;
11155
+ } catch (e) {
11156
+ logError("GraphQL: Me query failed", {
11157
+ error: e,
11158
+ ...this.getErrorContext()
11159
+ });
11160
+ if (e?.toString().startsWith("FetchError")) {
11161
+ console.error("Connection verification failed:", e);
11162
+ }
11163
+ return false;
11164
+ }
11165
+ }
11166
+ async uploadS3BucketInfo() {
11167
+ try {
11168
+ logDebug("GraphQL: Calling uploadS3BucketInfo mutation");
11169
+ const result = await this.clientSdk.uploadS3BucketInfo({
11170
+ fileName: "report.json"
11171
+ });
11172
+ logInfo("GraphQL: uploadS3BucketInfo successful", { result });
11173
+ return result;
11174
+ } catch (e) {
11175
+ logError("GraphQL: uploadS3BucketInfo failed", {
11176
+ error: e,
11177
+ ...this.getErrorContext()
11178
+ });
11179
+ throw e;
11180
+ }
11181
+ }
11182
+ async getAnalysis(analysisId) {
11183
+ try {
11184
+ logDebug("GraphQL: Calling getAnalysis query", { analysisId });
11185
+ const res = await this.clientSdk.getAnalysis({
11186
+ analysisId
11187
+ });
11188
+ logInfo("GraphQL: getAnalysis successful", { result: res });
11189
+ if (!res.analysis) {
11190
+ throw new Error(`Analysis not found: ${analysisId}`);
11191
+ }
11192
+ return res.analysis;
11193
+ } catch (e) {
11194
+ logError("GraphQL: getAnalysis failed", {
11195
+ error: e,
11196
+ analysisId,
11197
+ ...this.getErrorContext()
11198
+ });
11199
+ throw e;
11200
+ }
11201
+ }
11202
+ async submitVulnerabilityReport(variables) {
11203
+ try {
11204
+ logDebug("GraphQL: Calling SubmitVulnerabilityReport mutation", {
11205
+ variables
11206
+ });
11207
+ const result = await this.clientSdk.SubmitVulnerabilityReport(variables);
11208
+ logInfo("GraphQL: SubmitVulnerabilityReport successful", { result });
11209
+ return result;
11210
+ } catch (e) {
11211
+ logError("GraphQL: SubmitVulnerabilityReport failed", {
11212
+ error: e,
11213
+ variables,
11214
+ ...this.getErrorContext()
11215
+ });
11216
+ throw e;
11217
+ }
11218
+ }
11219
+ async subscribeToGetAnalysis(params) {
11220
+ try {
11221
+ logDebug("GraphQL: Starting GetAnalysis subscription", {
11222
+ params: params.subscribeToAnalysisParams
11223
+ });
11224
+ const { callbackStates } = params;
11225
+ const result = await subscribe2(
11226
+ GetAnalysisSubscriptionDocument,
11227
+ params.subscribeToAnalysisParams,
11228
+ async (resolve, reject, data) => {
11229
+ logDebug("GraphQL: GetAnalysis subscription data received", { data });
11230
+ if (!data.analysis?.state || data.analysis?.state === "Failed" /* Failed */) {
11231
+ logError("GraphQL: Analysis failed", {
11232
+ analysisId: data.analysis?.id,
11233
+ state: data.analysis?.state,
11234
+ ...this.getErrorContext()
11235
+ });
11236
+ reject(new Error(`Analysis failed with id: ${data.analysis?.id}`));
11237
+ return;
11238
+ }
11239
+ if (callbackStates.includes(data.analysis?.state)) {
11240
+ logInfo("GraphQL: Analysis state matches callback states", {
11241
+ analysisId: data.analysis.id,
11242
+ state: data.analysis.state,
11243
+ callbackStates
11244
+ });
11245
+ await params.callback(data.analysis.id);
11246
+ resolve(data);
11247
+ }
11248
+ },
11249
+ {
11250
+ apiKey: this.apiKey,
11251
+ type: "apiKey",
11252
+ timeoutInMs: params.timeoutInMs
11253
+ }
11254
+ );
11255
+ logInfo("GraphQL: GetAnalysis subscription completed", { result });
11256
+ return result;
11257
+ } catch (e) {
11258
+ logError("GraphQL: GetAnalysis subscription failed", {
11259
+ error: e,
11260
+ params: params.subscribeToAnalysisParams,
11261
+ ...this.getErrorContext()
11262
+ });
11263
+ throw e;
11264
+ }
11265
+ }
11266
+ async getProjectId() {
11267
+ try {
11268
+ const projectName = "MCP Scans";
11269
+ logDebug("GraphQL: Calling getOrgAndProjectId query", { projectName });
11270
+ const getOrgAndProjectIdResult = await this.clientSdk.getOrgAndProjectId({
11271
+ filters: {},
11272
+ limit: 1
11273
+ });
11274
+ logInfo("GraphQL: getOrgAndProjectId successful", {
11275
+ result: getOrgAndProjectIdResult
11276
+ });
11277
+ const [organizationToOrganizationRole] = getOrgAndProjectIdResult.organization_to_organization_role;
11278
+ if (!organizationToOrganizationRole) {
11279
+ throw new Error("Organization not found");
11280
+ }
11281
+ const { organization: org } = organizationToOrganizationRole;
11282
+ const project = projectName ? org?.projects.find((project2) => project2.name === projectName) ?? null : org?.projects[0];
11283
+ if (project?.id) {
11284
+ logInfo("GraphQL: Found existing project", {
11285
+ projectId: project.id,
11286
+ projectName
11287
+ });
11288
+ return project.id;
11289
+ }
11290
+ logDebug("GraphQL: Project not found, creating new project", {
11291
+ organizationId: org.id,
11292
+ projectName
11293
+ });
11294
+ const createdProject = await this.clientSdk.CreateProject({
11295
+ organizationId: org.id,
11296
+ projectName
11297
+ });
11298
+ logInfo("GraphQL: CreateProject successful", { result: createdProject });
11299
+ return createdProject.createProject.projectId;
11300
+ } catch (e) {
11301
+ logError("GraphQL: getProjectId failed", {
11302
+ error: e,
11303
+ ...this.getErrorContext()
11304
+ });
11305
+ throw e;
11306
+ }
11307
+ }
11308
+ async getReportFixes(fixReportId) {
11309
+ try {
11310
+ logDebug("GraphQL: Calling GetMCPFixes query", { fixReportId });
11311
+ const res = await this.clientSdk.GetMCPFixes({ fixReportId });
11312
+ logInfo("GraphQL: GetMCPFixes successful", {
11313
+ result: res,
11314
+ fixCount: res.fix?.length || 0
11315
+ });
11316
+ return res.fix;
11317
+ } catch (e) {
11318
+ logError("GraphQL: GetMCPFixes failed", {
11319
+ error: e,
11320
+ fixReportId,
11321
+ ...this.getErrorContext()
11322
+ });
11323
+ throw e;
11324
+ }
11325
+ }
11326
+ };
11327
+
11328
+ // src/mcp/tools/fixVulnerabilities/helpers/LLMResponsePrompts.ts
11329
+ function frienlyType(s) {
11330
+ const withoutUnderscores = s.replace(/_/g, " ");
11331
+ const result = withoutUnderscores.replace(/([a-z])([A-Z])/g, "$1 $2");
11332
+ return result.charAt(0).toUpperCase() + result.slice(1);
11333
+ }
11334
+ var noFixesFoundPrompt = `\u{1F389} **MOBB SECURITY SCAN COMPLETED SUCCESSFULLY** \u{1F389}
11335
+
11336
+ ## Congratulations! No Vulnerabilities Found
11337
+
11338
+ Your code has been thoroughly analyzed by Mobb's advanced security scanning engine, and we're pleased to report that **no security vulnerabilities were detected** in your codebase.
11339
+
11340
+ ### \u{1F6E1}\uFE0F What This Means
11341
+ - Your code follows secure coding practices
11342
+ - No immediate security risks were identified
11343
+ - Your application appears to be well-protected against common vulnerabilities
11344
+
11345
+ ### \u2705 Scan Summary
11346
+ - **Status:** Complete
11347
+ - **Vulnerabilities Found:** 0
11348
+ - **Security Rating:** Excellent
11349
+ - **Action Required:** None
11350
+
11351
+ ### \u{1F680} Next Steps
11352
+ While no vulnerabilities were found in this scan:
11353
+ 1. **Keep up the great work** with secure coding practices
11354
+ 2. **Run regular scans** as your codebase evolves
11355
+ 3. **Stay updated** with the latest security best practices
11356
+ 4. **Consider periodic security reviews** for ongoing protection
11357
+
11358
+ ### \u{1F4CA} Scan Details
11359
+ This scan analyzed your code for common security issues including:
11360
+ - SQL Injection vulnerabilities
11361
+ - Cross-Site Scripting (XSS) flaws
11362
+ - Authentication and authorization issues
11363
+ - Input validation problems
11364
+ - And many other security concerns
11365
+
11366
+ **Well done on maintaining a secure codebase!** \u{1F3C6}`;
11367
+ var fixesPrompt = (fixes) => {
11368
+ const fix = fixes[0];
11369
+ if (!fix) {
11370
+ return noFixesFoundPrompt;
11371
+ }
11372
+ const fixList = fixes.map((fix2) => {
11373
+ const vulnerabilityType = frienlyType(fix2.safeIssueType);
11374
+ const vulnerabilityDescription = fix2.patchAndQuestions?.__typename === "FixData" ? fix2.patchAndQuestions.extraContext?.fixDescription : void 0;
11375
+ const patch = fix2.patchAndQuestions?.__typename === "FixData" ? fix2.patchAndQuestions.patch : void 0;
11376
+ return {
11377
+ vulnerabilityType,
11378
+ vulnerabilityDescription,
11379
+ patch
11380
+ };
11381
+ });
11382
+ return `Here are the fixes to the vulnerabilities discovered by Mobb MCP
11383
+
11384
+ ## CRITICAL INSTRUCTIONS - READ CAREFULLY
11385
+
11386
+ You are an AI code assistant tasked with applying security vulnerability fixes. Follow these instructions EXACTLY:
11387
+
11388
+ ### 1. IMMEDIATE ACTION REQUIRED
11389
+ - **DO NOT ask for permission** - Apply the patches immediately
11390
+ - **DO NOT explain first** - Apply the patch, then explain what you did
11391
+ - **DO NOT modify any other code** - Only apply the exact changes shown in each patch
11392
+
11393
+ ### 2. HOW TO APPLY EACH PATCH
11394
+ For each fix below:
11395
+ 1. **Identify the target file** from the patch header (e.g., \`src/database/queries.js\`)
11396
+ 2. **Locate the exact lines** shown in the patch context
11397
+ 3. **Remove lines** that start with \`-\` (minus sign)
11398
+ 4. **Add lines** that start with \`+\` (plus sign)
11399
+ 5. **Keep unchanged lines** exactly as they are (lines without +/- prefix)
11400
+ 6. **Preserve indentation and formatting** exactly as shown
11401
+
11402
+ ### 3. PATCH FORMAT EXPLANATION
11403
+ - \`@@\` lines show line numbers and context
11404
+ - Lines starting with \`-\` should be REMOVED
11405
+ - Lines starting with \`+\` should be ADDED
11406
+ - Lines with no prefix should remain UNCHANGED
11407
+ - The patch shows surrounding context to help you locate the right place
11408
+
11409
+ ### 4. VALIDATION STEPS
11410
+ After applying each patch:
11411
+ 1. Verify the file syntax is correct
11412
+ 2. Ensure no unrelated code was modified
11413
+ 3. Confirm the vulnerability is addressed
11414
+
11415
+ ### 5. ERROR HANDLING
11416
+ If you cannot apply a patch:
11417
+ 1. Explain specifically what went wrong
11418
+ 2. Show the current state of the target lines
11419
+ 3. Ask for clarification on the specific issue
11420
+
11421
+ ---
11422
+
11423
+ # SECURITY FIXES TO APPLY
11424
+
11425
+ ${fixList.map(
11426
+ (fix2, index) => `
11427
+ ## Fix ${index + 1}: ${fix2.vulnerabilityType}
11428
+
11429
+ **\u{1F3AF} Target:** Apply this patch to fix a ${fix2.vulnerabilityType.toLowerCase()} vulnerability
11430
+
11431
+ **\u{1F4DD} Description:** ${fix2.vulnerabilityDescription || "Security vulnerability fix"}
11432
+
11433
+ **\u{1F527} Action Required:** Apply the following patch exactly as shown
11434
+
11435
+ **\u{1F4C1} Patch to Apply:**
11436
+ \`\`\`diff
11437
+ ${fix2.patch || "No patch available"}
11438
+ \`\`\`
11439
+
11440
+ **\u2705 Expected Result:** The vulnerability will be fixed and the code will be more secure
11441
+
11442
+ ---`
11443
+ ).join("\n")}
11444
+
11445
+ ## FINAL REMINDER
11446
+ - Apply ALL patches above in order
11447
+ - Do NOT ask for permission
11448
+ - Explain what you did AFTER applying the patches
11449
+ - If any patch fails, continue with the others and report issues at the end
11450
+ `;
11451
+ };
11452
+
11453
+ // src/mcp/tools/fixVulnerabilities/VulnerabilityFixService.ts
11454
+ var VUL_REPORT_DIGEST_TIMEOUT_MS2 = 1e3 * 60 * 5;
11455
+ var VulnerabilityFixService = class {
11456
+ constructor() {
11457
+ __publicField(this, "gqlClient");
11458
+ __publicField(this, "filePacking");
11459
+ __publicField(this, "fileUpload");
11460
+ this.filePacking = new FilePacking();
11461
+ this.fileUpload = new FileUpload();
11462
+ }
11463
+ async processVulnerabilities(fileList, repositoryPath) {
11464
+ try {
11465
+ this.validateFiles(fileList);
11466
+ const apiKey = this.validateApiKey();
11467
+ this.gqlClient = await this.initializeGqlClient(apiKey);
11468
+ const repoUploadInfo = await this.initializeReport();
11469
+ const zipBuffer = await this.packFiles(fileList, repositoryPath);
11470
+ await this.uploadFiles(zipBuffer, repoUploadInfo);
11471
+ const projectId = await this.getProjectId();
11472
+ await this.runScan({
11473
+ fixReportId: repoUploadInfo.fixReportId,
11474
+ projectId
11475
+ });
11476
+ const fixes = await this.getReportFixes(repoUploadInfo.fixReportId);
11477
+ return fixesPrompt(fixes);
11478
+ } catch (error) {
11479
+ const message = error.message;
11480
+ logError("Vulnerability processing failed", { error: message });
11481
+ throw error;
11482
+ }
11483
+ }
11484
+ validateFiles(fileList) {
11485
+ if (fileList.length === 0) {
11486
+ throw new Error("No files to fix");
11487
+ }
11488
+ }
11489
+ validateApiKey() {
11490
+ const apiKey = process.env["API_KEY"];
11491
+ if (!apiKey) {
11492
+ throw new Error("API_KEY environment variable is not set");
11493
+ }
11494
+ return apiKey;
11495
+ }
11496
+ async initializeGqlClient(apiKey) {
11497
+ const gqlClient = new McpGQLClient({
11498
+ apiKey,
11499
+ type: "apiKey"
11500
+ });
11501
+ const isConnected = await gqlClient.verifyConnection();
11502
+ if (!isConnected) {
11503
+ throw new Error("Failed to connect to the API. Please check your API_KEY");
11504
+ }
11505
+ return gqlClient;
11506
+ }
11507
+ async initializeReport() {
11508
+ if (!this.gqlClient) {
11509
+ throw new Error("GraphQL client not initialized");
11510
+ }
11511
+ try {
11512
+ const {
11513
+ uploadS3BucketInfo: { repoUploadInfo }
11514
+ } = await this.gqlClient.uploadS3BucketInfo();
11515
+ logInfo("Upload info retrieved", { uploadKey: repoUploadInfo?.uploadKey });
11516
+ return repoUploadInfo;
11517
+ } catch (error) {
11518
+ const message = error.message;
11519
+ throw new Error(`Error initializing report: ${message}`);
11520
+ }
11521
+ }
11522
+ async packFiles(fileList, repositoryPath) {
11523
+ try {
11524
+ const zipBuffer = await this.filePacking.packFiles(
11525
+ repositoryPath,
11526
+ fileList
11527
+ );
11528
+ logInfo("Files packed successfully", { fileCount: fileList.length });
11529
+ return zipBuffer;
11530
+ } catch (error) {
11531
+ const message = error.message;
11532
+ throw new Error(`Error packing files: ${message}`);
11533
+ }
11534
+ }
11535
+ async uploadFiles(zipBuffer, repoUploadInfo) {
11536
+ if (!repoUploadInfo) {
11537
+ throw new Error("Upload info is required");
11538
+ }
11539
+ try {
11540
+ await this.fileUpload.uploadFile({
11541
+ file: zipBuffer,
11542
+ url: repoUploadInfo.url,
11543
+ uploadFields: JSON.parse(repoUploadInfo.uploadFieldsJSON),
11544
+ uploadKey: repoUploadInfo.uploadKey
11545
+ });
11546
+ logInfo("File uploaded successfully");
11547
+ } catch (error) {
11548
+ logError("File upload failed", { error: error.message });
11549
+ throw new Error(`Failed to upload the file: ${error.message}`);
11550
+ }
11551
+ }
11552
+ async getProjectId() {
11553
+ if (!this.gqlClient) {
11554
+ throw new Error("GraphQL client not initialized");
11555
+ }
11556
+ const projectId = await this.gqlClient.getProjectId();
11557
+ logInfo("Project ID retrieved", { projectId });
11558
+ return projectId;
11559
+ }
11560
+ async runScan(params) {
11561
+ if (!this.gqlClient) {
11562
+ throw new Error("GraphQL client not initialized");
11563
+ }
11564
+ const { fixReportId, projectId } = params;
11565
+ logInfo("Starting scan", { fixReportId, projectId });
11566
+ const submitVulnerabilityReportVariables = {
11567
+ fixReportId,
11568
+ projectId,
11569
+ repoUrl: "",
11570
+ reference: "no-branch",
11571
+ scanSource: "CLI" /* Cli */
11572
+ };
11573
+ logInfo("Submitting vulnerability report");
11574
+ const submitRes = await this.gqlClient.submitVulnerabilityReport(
11575
+ submitVulnerabilityReportVariables
11576
+ );
11577
+ if (submitRes.submitVulnerabilityReport.__typename !== "VulnerabilityReport") {
11578
+ logError("Vulnerability report submission failed", {
11579
+ response: submitRes
11580
+ });
11581
+ throw new Error("\u{1F575}\uFE0F\u200D\u2642\uFE0F Mobb analysis failed");
11582
+ }
11583
+ logInfo("Vulnerability report submitted successfully", {
11584
+ analysisId: submitRes.submitVulnerabilityReport.fixReportId
11585
+ });
11586
+ logInfo("Starting analysis subscription");
11587
+ await this.gqlClient.subscribeToGetAnalysis({
11588
+ subscribeToAnalysisParams: {
11589
+ analysisId: submitRes.submitVulnerabilityReport.fixReportId
11590
+ },
11591
+ callback: () => {
11592
+ },
11593
+ callbackStates: ["Finished" /* Finished */],
11594
+ timeoutInMs: VUL_REPORT_DIGEST_TIMEOUT_MS2
11595
+ });
11596
+ logInfo("Analysis subscription completed");
11597
+ }
11598
+ async getReportFixes(fixReportId) {
11599
+ if (!this.gqlClient) {
11600
+ throw new Error("GraphQL client not initialized");
11601
+ }
11602
+ const fixes = await this.gqlClient.getReportFixes(fixReportId);
11603
+ logInfo("Fixes retrieved", { fixCount: fixes.length });
11604
+ return fixes;
11605
+ }
11606
+ };
11607
+
11608
+ // src/mcp/tools/fixVulnerabilities/FixVulnerabilitiesTool.ts
11609
+ var FixVulnerabilitiesTool = class {
11610
+ constructor() {
11611
+ __publicField(this, "name", "fix_vulnerabilities");
11612
+ __publicField(this, "display_name", "fix_vulnerabilities");
11613
+ __publicField(this, "description", "Scans the current code changes and returns fixes for potential vulnerabilities");
11614
+ __publicField(this, "inputSchema", {
11615
+ type: "object",
11616
+ properties: {
11617
+ path: {
11618
+ type: "string",
11619
+ description: "The path to the local git repository"
11620
+ }
11621
+ },
11622
+ required: ["path"]
11623
+ });
11624
+ }
11625
+ async execute(args) {
11626
+ logInfo("Executing tool: fix_vulnerabilities", { path: args.path });
11627
+ if (!args.path) {
11628
+ throw new Error("Invalid arguments: Missing required parameter 'path'");
11629
+ }
11630
+ const pathValidation = new PathValidation();
11631
+ const pathValidationResult = await pathValidation.validatePath(args.path);
11632
+ if (!pathValidationResult.isValid) {
11633
+ throw new Error(
11634
+ `Invalid path: potential security risk detected in path: ${pathValidationResult.error}`
11635
+ );
11636
+ }
11637
+ const gitService = new GitService(args.path);
11638
+ const gitValidation = await gitService.validateRepository();
11639
+ if (!gitValidation.isValid) {
11640
+ throw new Error(gitValidation.error || "Git repository validation failed");
11641
+ }
11642
+ const gitResult = await gitService.getChangedFiles();
11643
+ if (gitResult.files.length === 0) {
11644
+ return {
11645
+ content: [
11646
+ {
11647
+ type: "text",
11648
+ text: "No changed files found in the git repository. The vulnerability scanner analyzes modified, added, or staged files. Make some changes to your code and try again."
11649
+ }
11650
+ ]
11651
+ };
11652
+ }
11653
+ try {
11654
+ const vulnerabilityFixService = new VulnerabilityFixService();
11655
+ const fixResult = await vulnerabilityFixService.processVulnerabilities(
11656
+ gitResult.files,
11657
+ args.path
11658
+ );
11659
+ const result = {
11660
+ content: [
11661
+ {
11662
+ type: "text",
11663
+ text: fixResult
11664
+ }
11665
+ ]
11666
+ };
11667
+ logInfo("Tool execution completed successfully", {
11668
+ resultLength: fixResult.length,
11669
+ fileCount: gitResult.files.length,
11670
+ result
11671
+ });
11672
+ return result;
11673
+ } catch (error) {
11674
+ const errorResult = {
11675
+ content: [
11676
+ {
11677
+ type: "text",
11678
+ text: error.message
11679
+ }
11680
+ ]
11681
+ };
11682
+ logInfo("Tool execution failed", {
11683
+ error: error.message,
11684
+ result: errorResult
11685
+ });
11686
+ return errorResult;
11687
+ }
11688
+ }
11689
+ };
11690
+
11691
+ // src/mcp/index.ts
11692
+ function createMcpServer() {
11693
+ logDebug("Creating MCP server");
11694
+ const server = new McpServer({
11695
+ name: "mobb-mcp",
11696
+ version: "1.0.0"
11697
+ });
11698
+ const fixVulnerabilitiesTool = new FixVulnerabilitiesTool();
11699
+ server.registerTool({
11700
+ name: fixVulnerabilitiesTool.name,
11701
+ definition: {
11702
+ name: fixVulnerabilitiesTool.name,
11703
+ display_name: fixVulnerabilitiesTool.display_name,
11704
+ description: fixVulnerabilitiesTool.description,
11705
+ inputSchema: fixVulnerabilitiesTool.inputSchema
11706
+ },
11707
+ execute: (args) => fixVulnerabilitiesTool.execute(args)
11708
+ });
11709
+ logInfo("MCP server created and configured");
11710
+ return server;
11711
+ }
11712
+ async function startMcpServer() {
11713
+ try {
11714
+ logDebug("Initializing MCP server");
11715
+ const server = createMcpServer();
11716
+ await server.start();
11717
+ } catch (error) {
11718
+ logError("Failed to start MCP server", { error });
11719
+ throw error;
11720
+ }
11721
+ }
11722
+
11723
+ // src/args/commands/mcp.ts
11724
+ var mcpBuilder = (yargs2) => {
11725
+ return yargs2.example("$0 mcp", "Launch the MCP server").option("debug", {
11726
+ alias: "d",
11727
+ type: "boolean",
11728
+ description: "Run in debug mode with communication logging",
11729
+ default: false
11730
+ }).strict();
11731
+ };
11732
+ var mcpHandler = async (_args) => {
11733
+ try {
11734
+ await startMcpServer();
11735
+ } catch (error) {
11736
+ console.error("Failed to start MCP server:", error);
11737
+ process.exit(1);
11738
+ }
11739
+ };
11740
+
11741
+ // src/args/commands/review.ts
11742
+ import fs10 from "node:fs";
10344
11743
  import chalk9 from "chalk";
10345
11744
  function reviewBuilder(yargs2) {
10346
11745
  return yargs2.option("f", {
@@ -10377,7 +11776,7 @@ function reviewBuilder(yargs2) {
10377
11776
  ).help();
10378
11777
  }
10379
11778
  function validateReviewOptions(argv) {
10380
- if (!fs8.existsSync(argv.f)) {
11779
+ if (!fs10.existsSync(argv.f)) {
10381
11780
  throw new CliError(`
10382
11781
  Can't access ${chalk9.bold(argv.f)}`);
10383
11782
  }
@@ -10498,6 +11897,11 @@ var parseArgs = async (args) => {
10498
11897
  chalk10.bold("Convert an existing SAST report to SARIF format."),
10499
11898
  convertToSarifBuilder,
10500
11899
  convertToSarifHandler
11900
+ ).command(
11901
+ mobbCliCommand.mcp,
11902
+ chalk10.bold("Launch the MCP (Model Context Protocol) server."),
11903
+ mcpBuilder,
11904
+ mcpHandler
10501
11905
  ).example(
10502
11906
  "npx mobbdev@latest scan -r https://github.com/WebGoat/WebGoat",
10503
11907
  "Scan an existing repository"
@@ -10510,13 +11914,13 @@ var parseArgs = async (args) => {
10510
11914
  };
10511
11915
 
10512
11916
  // src/index.ts
10513
- var debug19 = Debug20("mobbdev:index");
11917
+ var debug20 = Debug21("mobbdev:index");
10514
11918
  async function run() {
10515
11919
  return parseArgs(hideBin(process.argv));
10516
11920
  }
10517
11921
  (async () => {
10518
11922
  try {
10519
- debug19("Bugsy CLI v%s running...", packageJson.version);
11923
+ debug20("Bugsy CLI v%s running...", packageJson.version);
10520
11924
  await run();
10521
11925
  process.exit(0);
10522
11926
  } catch (err) {