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