@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.
- package/package.json +1 -1
- 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.
|
|
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) => {
|