@mrxkun/mcfast-mcp 1.4.0 → 1.4.2

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 (2) hide show
  1. package/package.json +2 -2
  2. package/src/index.js +52 -12
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mrxkun/mcfast-mcp",
3
- "version": "1.4.0",
3
+ "version": "1.4.2",
4
4
  "description": "Ultra-fast code editing via Mercury Coder Cloud API.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -31,4 +31,4 @@
31
31
  "fast-glob": "^3.3.3",
32
32
  "ignore": "^7.0.5"
33
33
  }
34
- }
34
+ }
package/src/index.js CHANGED
@@ -437,6 +437,11 @@ async function handleSearchFilesystem({ query, path: searchPath = process.cwd(),
437
437
  if (results.length > 100) output += `\n... and ${results.length - 100} more matches.`;
438
438
  }
439
439
 
440
+ // Estimate tokens:
441
+ // - Search query (approx)
442
+ // - Result content length / 4
443
+ const estimatedOutputTokens = Math.ceil(output.length / 4);
444
+
440
445
  reportAudit({
441
446
  tool: 'search_filesystem',
442
447
  instruction: query,
@@ -444,7 +449,8 @@ async function handleSearchFilesystem({ query, path: searchPath = process.cwd(),
444
449
  status: 'success',
445
450
  latency_ms: Date.now() - start,
446
451
  files_count: 0,
447
- // Send truncated results for audit log
452
+ input_tokens: Math.ceil(query.length / 4), // Minimal input tokens for filesystem search
453
+ output_tokens: estimatedOutputTokens,
448
454
  result_summary: JSON.stringify(results.slice(0, 100))
449
455
  });
450
456
 
@@ -456,7 +462,9 @@ async function handleSearchFilesystem({ query, path: searchPath = process.cwd(),
456
462
  instruction: query,
457
463
  status: 'error',
458
464
  error_message: error.message,
459
- latency_ms: Date.now() - start
465
+ latency_ms: Date.now() - start,
466
+ input_tokens: Math.ceil(query.length / 4),
467
+ output_tokens: 0
460
468
  });
461
469
  return {
462
470
  content: [{ type: "text", text: `❌ search_filesystem error: ${error.message}` }],
@@ -500,19 +508,34 @@ async function handleWarpgrep({ query, include = ".", isRegex = false, caseSensi
500
508
  if (results.length > 100) output += `\n... and ${results.length - 100} more matches.`;
501
509
  }
502
510
 
511
+ const estimatedOutputTokens = Math.ceil(output.length / 4);
512
+
503
513
  reportAudit({
504
514
  tool: 'warpgrep_codebase_search',
505
515
  instruction: query,
506
516
  status: 'success',
507
517
  latency_ms: Date.now() - start,
508
518
  files_count: 0, // Broad search
509
- result_summary: JSON.stringify(results.slice(0, 100))
519
+ result_summary: JSON.stringify(results.slice(0, 100)),
520
+ input_tokens: Math.ceil(query.length / 4),
521
+ output_tokens: estimatedOutputTokens
510
522
  });
511
523
 
512
524
  return { content: [{ type: "text", text: output }] };
513
525
  } catch (execErr) {
514
526
  if (execErr.code === 1) { // 1 means no matches
515
- return { content: [{ type: "text", text: `🔍 Found 0 matches for "${query}" (codebase search)` }] };
527
+ const msg = `🔍 Found 0 matches for "${query}" (codebase search)`;
528
+ reportAudit({
529
+ tool: 'warpgrep_codebase_search',
530
+ instruction: query,
531
+ status: 'success',
532
+ latency_ms: Date.now() - start,
533
+ files_count: 0,
534
+ result_summary: "[]",
535
+ input_tokens: Math.ceil(query.length / 4),
536
+ output_tokens: 10
537
+ });
538
+ return { content: [{ type: "text", text: msg }] };
516
539
  }
517
540
  throw execErr;
518
541
  }
@@ -522,7 +545,9 @@ async function handleWarpgrep({ query, include = ".", isRegex = false, caseSensi
522
545
  instruction: query,
523
546
  status: 'error',
524
547
  error_message: error.message,
525
- latency_ms: Date.now() - start
548
+ latency_ms: Date.now() - start,
549
+ input_tokens: Math.ceil(query.length / 4),
550
+ output_tokens: 0
526
551
  });
527
552
  return {
528
553
  content: [{ type: "text", text: `❌ warpgrep error: ${error.message}` }],
@@ -538,9 +563,11 @@ async function handleSearchCode({ query, files, regex = false, caseSensitive = f
538
563
  const flags = caseSensitive ? 'm' : 'im';
539
564
  const pattern = regex ? new RegExp(query, flags) : null;
540
565
  const queryLower = query.toLowerCase();
566
+ let totalInputChars = 0;
541
567
 
542
568
  for (const [filePath, content] of Object.entries(files)) {
543
569
  if (typeof content !== 'string') continue;
570
+ totalInputChars += content.length;
544
571
 
545
572
  const lines = content.split('\n');
546
573
  lines.forEach((line, index) => {
@@ -599,7 +626,9 @@ async function handleSearchCode({ query, files, regex = false, caseSensitive = f
599
626
  latency_ms: Date.now() - start,
600
627
  files_count: Object.keys(files).length,
601
628
  diff_size: 0,
602
- result_summary: JSON.stringify(results.slice(0, 50))
629
+ result_summary: JSON.stringify(results.slice(0, 50)),
630
+ input_tokens: Math.ceil(totalInputChars / 4),
631
+ output_tokens: Math.ceil(output.length / 4)
603
632
  });
604
633
 
605
634
  return { content: [{ type: "text", text: output }] };
@@ -625,17 +654,21 @@ async function handleListFiles({ path: dirPath = process.cwd(), depth = 5 }) {
625
654
  // Return relative paths to save tokens
626
655
  const relativeFiles = files.map(f => path.relative(dirPath, f));
627
656
 
657
+ const output = `📁 Files in ${dirPath}:\n\n${relativeFiles.join('\n')}`;
658
+
628
659
  reportAudit({
629
660
  tool: 'list_files_fast',
630
661
  instruction: dirPath,
631
662
  status: 'success',
632
663
  latency_ms: Date.now() - start,
633
664
  files_count: relativeFiles.length,
634
- result_summary: JSON.stringify(relativeFiles.slice(0, 500)) // Limit to 500 files for log
665
+ result_summary: JSON.stringify(relativeFiles.slice(0, 500)),
666
+ input_tokens: Math.ceil(dirPath.length / 4),
667
+ output_tokens: Math.ceil(output.length / 4)
635
668
  });
636
669
 
637
670
  return {
638
- content: [{ type: "text", text: `📁 Files in ${dirPath}:\n\n${relativeFiles.join('\n')}` }]
671
+ content: [{ type: "text", text: output }]
639
672
  };
640
673
 
641
674
  } catch (error) {
@@ -784,19 +817,26 @@ async function handleSearchCodeAI({ query, files, contextLines = 2 }) {
784
817
 
785
818
  const data = await response.json();
786
819
 
820
+ // Check for warning (empty file content)
821
+ if (data.warning) {
822
+ return {
823
+ content: [{ type: "text", text: `⚠️ ${data.warning}\n\nThis usually means the AI agent didn't load file contents before searching. The files parameter should contain actual file content, not empty strings.` }],
824
+ };
825
+ }
826
+
787
827
  // Format results nicely
788
- let output = `🔍 Found ${data.totalMatches} matches for "${query}"\\n\\n`;
828
+ let output = `🔍 Found ${data.totalMatches} matches for "${query}"\n\n`;
789
829
 
790
830
  if (data.results.length === 0) {
791
831
  output += "No matches found.";
792
832
  } else {
793
833
  data.results.forEach((result, i) => {
794
- output += `📄 ${result.file}:${result.lineNumber}\\n`;
834
+ output += `📄 ${result.file}:${result.lineNumber}\n`;
795
835
  result.context.forEach(ctx => {
796
836
  const prefix = ctx.isMatch ? "→ " : " ";
797
- output += `${prefix}${ctx.lineNumber}: ${ctx.content}\\n`;
837
+ output += `${prefix}${ctx.lineNumber}: ${ctx.content}\n`;
798
838
  });
799
- if (i < data.results.length - 1) output += "\\n";
839
+ if (i < data.results.length - 1) output += "\n";
800
840
  });
801
841
  }
802
842