@mrxkun/mcfast-mcp 3.5.2 → 3.5.4

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 +222 -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.4",
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,227 @@ 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
+ if (VERBOSE) {
518
+ console.error(`${colors.dim}[Audit] Skipped - MCFAST_ENABLE_AUDIT not set to 'true'${colors.reset}`);
519
+ }
520
+ return;
521
+ }
522
+
523
+ // Check if we have a token
524
+ if (!TOKEN) {
525
+ if (VERBOSE) {
526
+ console.error(`${colors.yellow}[Audit] Skipped - No MCFAST_TOKEN provided${colors.reset}`);
527
+ }
528
+ return;
529
+ }
530
+
531
+ const auditData = {
532
+ timestamp: new Date().toISOString(),
533
+ session_id: process.env.MCFAST_SESSION_ID || 'local',
534
+ tool: data.tool || 'unknown',
535
+ status: data.status || 'unknown',
536
+ latency_ms: data.latency_ms || 0,
537
+ instruction: data.instruction || '',
538
+ files_count: data.files_count || 0,
539
+ input_tokens: data.input_tokens || 0,
540
+ output_tokens: data.output_tokens || 0,
541
+ strategy: data.strategy || null,
542
+ error: data.error || null,
543
+ metadata: data.metadata || {}
544
+ };
545
+
546
+ // Send to dashboard API
547
+ const DASHBOARD_API_URL = process.env.MCFAST_DASHBOARD_URL || 'https://mcfast.vercel.app/api/v1/logs/audit';
548
+
549
+ if (VERBOSE) {
550
+ console.error(`${colors.dim}[Audit] Sending to: ${DASHBOARD_API_URL}${colors.reset}`);
551
+ console.error(`${colors.dim}[Audit] Tool: ${auditData.tool}, Status: ${auditData.status}${colors.reset}`);
552
+ }
553
+
554
+ const response = await fetch(DASHBOARD_API_URL, {
555
+ method: 'POST',
556
+ headers: {
557
+ 'Content-Type': 'application/json',
558
+ 'Authorization': `Bearer ${TOKEN}`
559
+ },
560
+ body: JSON.stringify(auditData)
561
+ });
562
+
563
+ if (!response.ok) {
564
+ const errorText = await response.text().catch(() => 'Unknown error');
565
+ if (VERBOSE) {
566
+ console.error(`${colors.yellow}[Audit] Failed - HTTP ${response.status}: ${errorText}${colors.reset}`);
567
+ }
568
+ } else {
569
+ if (VERBOSE) {
570
+ console.error(`${colors.green}[Audit] Logged successfully ✓${colors.reset}`);
571
+ }
572
+ }
573
+
574
+ // Also log to local file if configured
575
+ if (process.env.MCFAST_AUDIT_FILE) {
576
+ const logLine = JSON.stringify(auditData) + '\n';
577
+ await fs.appendFile(process.env.MCFAST_AUDIT_FILE, logLine).catch((err) => {
578
+ if (VERBOSE) {
579
+ console.error(`${colors.yellow}[Audit] Failed to write to file:${colors.reset}`, err.message);
580
+ }
581
+ });
582
+ }
583
+ } catch (error) {
584
+ // Silent fail - audit should never break the tool
585
+ if (VERBOSE) {
586
+ console.error(`${colors.yellow}[Audit] Error (non-critical):${colors.reset}`, error.message);
587
+ }
588
+ }
589
+ }
590
+
591
+ // ============================================
592
+ // FILESYSTEM SEARCH HANDLER
593
+ // ============================================
594
+
595
+ /**
596
+ * Handle filesystem search using fast-glob and pattern matching
597
+ * Fast alternative to API-based search for local files
598
+ */
599
+ async function handleSearchFilesystem({ query, path: searchPath, isRegex = false, caseSensitive = false }) {
600
+ const start = Date.now();
601
+
602
+ try {
603
+ // Determine search pattern
604
+ let pattern = query;
605
+ if (!isRegex) {
606
+ // Convert literal query to glob pattern for file search
607
+ pattern = `**/*${query}*`;
608
+ }
609
+
610
+ // Default to current directory if no path specified
611
+ const cwd = searchPath || process.cwd();
612
+
613
+ // Use fast-glob to find files
614
+ const files = await fg([pattern], {
615
+ cwd,
616
+ ignore: ['node_modules/**', '.git/**', 'dist/**', 'build/**'],
617
+ dot: false,
618
+ caseSensitive,
619
+ absolute: false
620
+ });
621
+
622
+ // Also search file contents if it's not a file pattern
623
+ const contentMatches = [];
624
+
625
+ // For simple queries, also search in common code files
626
+ if (!isRegex && query.length > 1) {
627
+ const codeFiles = await fg(['**/*.{js,ts,jsx,tsx,py,go,rs,java,rb,php}'], {
628
+ cwd,
629
+ ignore: ['node_modules/**', '.git/**'],
630
+ absolute: false
631
+ });
632
+
633
+ // Search in first 50 files to avoid performance issues
634
+ for (const file of codeFiles.slice(0, 50)) {
635
+ try {
636
+ const content = await fs.readFile(path.join(cwd, file), 'utf-8');
637
+ if (content.includes(query)) {
638
+ const lines = content.split('\n');
639
+ const matches = [];
640
+
641
+ lines.forEach((line, idx) => {
642
+ if (line.includes(query)) {
643
+ matches.push({
644
+ line: idx + 1,
645
+ content: line.trim()
646
+ });
647
+ }
648
+ });
649
+
650
+ if (matches.length > 0) {
651
+ contentMatches.push({
652
+ file,
653
+ matches: matches.slice(0, 5) // Limit to 5 matches per file
654
+ });
655
+ }
656
+ }
657
+ } catch (e) {
658
+ // Skip files that can't be read
659
+ }
660
+ }
661
+ }
662
+
663
+ const latency = Date.now() - start;
664
+
665
+ // Format output
666
+ let output = `${colors.bold}Filesystem Search Results${colors.reset}\n\n`;
667
+ output += `Query: "${query}"\n`;
668
+ output += `Path: ${cwd}\n`;
669
+ output += `Files found: ${files.length}\n`;
670
+ output += `Content matches: ${contentMatches.length} files\n`;
671
+ output += `Latency: ${latency}ms\n\n`;
672
+
673
+ if (files.length > 0) {
674
+ output += `${colors.bold}Matching Files:${colors.reset}\n`;
675
+ files.slice(0, 20).forEach(file => {
676
+ output += ` ${colors.cyan}•${colors.reset} ${file}\n`;
677
+ });
678
+ if (files.length > 20) {
679
+ output += ` ${colors.dim}... and ${files.length - 20} more${colors.reset}\n`;
680
+ }
681
+ output += '\n';
682
+ }
683
+
684
+ if (contentMatches.length > 0) {
685
+ output += `${colors.bold}Content Matches:${colors.reset}\n`;
686
+ contentMatches.slice(0, 10).forEach(({ file, matches }) => {
687
+ output += `\n${colors.yellow}${file}${colors.reset}\n`;
688
+ matches.forEach(match => {
689
+ output += ` ${colors.dim}${match.line}:${colors.reset} ${match.content}\n`;
690
+ });
691
+ });
692
+ }
693
+
694
+ // Report audit
695
+ reportAudit({
696
+ tool: 'search_filesystem',
697
+ instruction: `Search: ${query}`,
698
+ status: 'success',
699
+ latency_ms: latency,
700
+ files_count: files.length,
701
+ input_tokens: Math.ceil(query.length / 4),
702
+ output_tokens: Math.ceil(output.length / 4)
703
+ });
704
+
705
+ return {
706
+ content: [{ type: "text", text: output }]
707
+ };
708
+ } catch (error) {
709
+ const latency = Date.now() - start;
710
+
711
+ reportAudit({
712
+ tool: 'search_filesystem',
713
+ instruction: `Search: ${query}`,
714
+ status: 'error',
715
+ latency_ms: latency,
716
+ error: error.message
717
+ });
718
+
719
+ return {
720
+ content: [{ type: "text", text: `❌ Search error: ${error.message}` }],
721
+ isError: true
722
+ };
723
+ }
724
+ }
725
+
505
726
  // Smart retry handler
506
727
  async function handleReapply({ instruction, files, errorContext = "", attempt = 1 }) {
507
728
  const MAX_ATTEMPTS = 3;
@@ -941,8 +1162,7 @@ async function handleBatchRead(filePaths, start_line, end_line, max_lines_per_fi
941
1162
  let output = `📚 Batch Read Complete (${filePaths.length} files)\n`;
942
1163
  output += `✅ Successful: ${successful} ❌ Failed: ${failed}\n`;
943
1164
  output += `📊 Total: ${totalLines} lines, ${(totalSize / 1024).toFixed(1)} KB\n`;
944
- output += `⏱️ Latency: ${Date.now() - start}ms\n`;
945
- output += `========================================\n\n`;
1165
+ output += `⏱️ Latency: ${Date.now() - start}ms\n\n`;
946
1166
 
947
1167
  // Add each file's content
948
1168
  results.forEach((result, index) => {