@mrxkun/mcfast-mcp 3.5.2 → 3.5.3

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 +1 -1
  2. package/src/index.js +198 -2
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mrxkun/mcfast-mcp",
3
- "version": "3.5.2",
3
+ "version": "3.5.3",
4
4
  "description": "Ultra-fast code editing with WASM acceleration, fuzzy patching, multi-layer caching, and 8 unified tools. Optimized for AI code assistants with 80-98% latency reduction.",
5
5
  "type": "module",
6
6
  "bin": {
package/src/index.js CHANGED
@@ -502,6 +502,203 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
502
502
  return result;
503
503
  });
504
504
 
505
+ // ============================================
506
+ // AUDIT LOGGING - Dashboard Integration
507
+ // ============================================
508
+
509
+ /**
510
+ * Report audit log to Dashboard
511
+ * Sends operation logs to mcfast Dashboard for monitoring
512
+ */
513
+ async function reportAudit(data) {
514
+ try {
515
+ // Only report if dashboard logging is enabled
516
+ if (process.env.MCFAST_ENABLE_AUDIT !== 'true') {
517
+ return;
518
+ }
519
+
520
+ const auditData = {
521
+ timestamp: new Date().toISOString(),
522
+ session_id: process.env.MCFAST_SESSION_ID || 'local',
523
+ tool: data.tool || 'unknown',
524
+ status: data.status || 'unknown',
525
+ latency_ms: data.latency_ms || 0,
526
+ instruction: data.instruction || '',
527
+ files_count: data.files_count || 0,
528
+ input_tokens: data.input_tokens || 0,
529
+ output_tokens: data.output_tokens || 0,
530
+ strategy: data.strategy || null,
531
+ error: data.error || null,
532
+ metadata: data.metadata || {}
533
+ };
534
+
535
+ // Send to dashboard API
536
+ const DASHBOARD_API_URL = process.env.MCFAST_DASHBOARD_URL || 'https://mcfast.vercel.app/api/v1/audit';
537
+
538
+ if (TOKEN) {
539
+ fetch(DASHBOARD_API_URL, {
540
+ method: 'POST',
541
+ headers: {
542
+ 'Content-Type': 'application/json',
543
+ 'Authorization': `Bearer ${TOKEN}`
544
+ },
545
+ body: JSON.stringify(auditData)
546
+ }).catch(err => {
547
+ // Silent fail - don't break operations if audit fails
548
+ if (VERBOSE) {
549
+ console.error(`${colors.dim}Audit log failed (non-critical):${colors.reset}`, err.message);
550
+ }
551
+ });
552
+ }
553
+
554
+ // Also log to local file if configured
555
+ if (process.env.MCFAST_AUDIT_FILE) {
556
+ const logLine = JSON.stringify(auditData) + '\n';
557
+ await fs.appendFile(process.env.MCFAST_AUDIT_FILE, logLine).catch(() => {});
558
+ }
559
+ } catch (error) {
560
+ // Silent fail - audit should never break the tool
561
+ if (VERBOSE) {
562
+ console.error(`${colors.dim}Audit error (non-critical):${colors.reset}`, error.message);
563
+ }
564
+ }
565
+ }
566
+
567
+ // ============================================
568
+ // FILESYSTEM SEARCH HANDLER
569
+ // ============================================
570
+
571
+ /**
572
+ * Handle filesystem search using fast-glob and pattern matching
573
+ * Fast alternative to API-based search for local files
574
+ */
575
+ async function handleSearchFilesystem({ query, path: searchPath, isRegex = false, caseSensitive = false }) {
576
+ const start = Date.now();
577
+
578
+ try {
579
+ // Determine search pattern
580
+ let pattern = query;
581
+ if (!isRegex) {
582
+ // Convert literal query to glob pattern for file search
583
+ pattern = `**/*${query}*`;
584
+ }
585
+
586
+ // Default to current directory if no path specified
587
+ const cwd = searchPath || process.cwd();
588
+
589
+ // Use fast-glob to find files
590
+ const files = await fg([pattern], {
591
+ cwd,
592
+ ignore: ['node_modules/**', '.git/**', 'dist/**', 'build/**'],
593
+ dot: false,
594
+ caseSensitive,
595
+ absolute: false
596
+ });
597
+
598
+ // Also search file contents if it's not a file pattern
599
+ const contentMatches = [];
600
+
601
+ // For simple queries, also search in common code files
602
+ if (!isRegex && query.length > 1) {
603
+ const codeFiles = await fg(['**/*.{js,ts,jsx,tsx,py,go,rs,java,rb,php}'], {
604
+ cwd,
605
+ ignore: ['node_modules/**', '.git/**'],
606
+ absolute: false
607
+ });
608
+
609
+ // Search in first 50 files to avoid performance issues
610
+ for (const file of codeFiles.slice(0, 50)) {
611
+ try {
612
+ const content = await fs.readFile(path.join(cwd, file), 'utf-8');
613
+ if (content.includes(query)) {
614
+ const lines = content.split('\n');
615
+ const matches = [];
616
+
617
+ lines.forEach((line, idx) => {
618
+ if (line.includes(query)) {
619
+ matches.push({
620
+ line: idx + 1,
621
+ content: line.trim()
622
+ });
623
+ }
624
+ });
625
+
626
+ if (matches.length > 0) {
627
+ contentMatches.push({
628
+ file,
629
+ matches: matches.slice(0, 5) // Limit to 5 matches per file
630
+ });
631
+ }
632
+ }
633
+ } catch (e) {
634
+ // Skip files that can't be read
635
+ }
636
+ }
637
+ }
638
+
639
+ const latency = Date.now() - start;
640
+
641
+ // Format output
642
+ let output = `${colors.bold}Filesystem Search Results${colors.reset}\n\n`;
643
+ output += `Query: "${query}"\n`;
644
+ output += `Path: ${cwd}\n`;
645
+ output += `Files found: ${files.length}\n`;
646
+ output += `Content matches: ${contentMatches.length} files\n`;
647
+ output += `Latency: ${latency}ms\n\n`;
648
+
649
+ if (files.length > 0) {
650
+ output += `${colors.bold}Matching Files:${colors.reset}\n`;
651
+ files.slice(0, 20).forEach(file => {
652
+ output += ` ${colors.cyan}•${colors.reset} ${file}\n`;
653
+ });
654
+ if (files.length > 20) {
655
+ output += ` ${colors.dim}... and ${files.length - 20} more${colors.reset}\n`;
656
+ }
657
+ output += '\n';
658
+ }
659
+
660
+ if (contentMatches.length > 0) {
661
+ output += `${colors.bold}Content Matches:${colors.reset}\n`;
662
+ contentMatches.slice(0, 10).forEach(({ file, matches }) => {
663
+ output += `\n${colors.yellow}${file}${colors.reset}\n`;
664
+ matches.forEach(match => {
665
+ output += ` ${colors.dim}${match.line}:${colors.reset} ${match.content}\n`;
666
+ });
667
+ });
668
+ }
669
+
670
+ // Report audit
671
+ reportAudit({
672
+ tool: 'search_filesystem',
673
+ instruction: `Search: ${query}`,
674
+ status: 'success',
675
+ latency_ms: latency,
676
+ files_count: files.length,
677
+ input_tokens: Math.ceil(query.length / 4),
678
+ output_tokens: Math.ceil(output.length / 4)
679
+ });
680
+
681
+ return {
682
+ content: [{ type: "text", text: output }]
683
+ };
684
+ } catch (error) {
685
+ const latency = Date.now() - start;
686
+
687
+ reportAudit({
688
+ tool: 'search_filesystem',
689
+ instruction: `Search: ${query}`,
690
+ status: 'error',
691
+ latency_ms: latency,
692
+ error: error.message
693
+ });
694
+
695
+ return {
696
+ content: [{ type: "text", text: `❌ Search error: ${error.message}` }],
697
+ isError: true
698
+ };
699
+ }
700
+ }
701
+
505
702
  // Smart retry handler
506
703
  async function handleReapply({ instruction, files, errorContext = "", attempt = 1 }) {
507
704
  const MAX_ATTEMPTS = 3;
@@ -941,8 +1138,7 @@ async function handleBatchRead(filePaths, start_line, end_line, max_lines_per_fi
941
1138
  let output = `📚 Batch Read Complete (${filePaths.length} files)\n`;
942
1139
  output += `✅ Successful: ${successful} ❌ Failed: ${failed}\n`;
943
1140
  output += `📊 Total: ${totalLines} lines, ${(totalSize / 1024).toFixed(1)} KB\n`;
944
- output += `⏱️ Latency: ${Date.now() - start}ms\n`;
945
- output += `========================================\n\n`;
1141
+ output += `⏱️ Latency: ${Date.now() - start}ms\n\n`;
946
1142
 
947
1143
  // Add each file's content
948
1144
  results.forEach((result, index) => {