@bucketeer/docs-local-mcp-server
Version:
Local MCP Server to query Bucketeer documentation
155 lines (154 loc) • 3.75 kB
JavaScript
import { createHash } from 'node:crypto';
export function generateHash(content, algorithm = 'sha256') {
return createHash(algorithm).update(content).digest('hex');
}
export function formatDuration(seconds) {
const hours = Math.floor(seconds / 3600);
const minutes = Math.floor((seconds % 3600) / 60);
const secs = Math.floor(seconds % 60);
return [
hours.toString().padStart(2, '0'),
minutes.toString().padStart(2, '0'),
secs.toString().padStart(2, '0'),
].join(':');
}
export function urlToFilename(url) {
// Remove domain and protocol
let filename = url.replace(/^https?:\/\/docs\.bucketeer\.io\/?/, '');
// Replace invalid file characters
filename = filename.replace(/[\/?<>\\:*|"]/g, '-');
// Clean up repeated hyphens
filename = filename.replace(/-+/g, '-');
// Remove leading and trailing hyphens
filename = filename.replace(/^-|-$/g, '');
// Handle long filenames with hash
if (filename.length > 100) {
const hash = createHash('md5').update(url).digest('hex').substring(0, 8);
filename = `${filename.substring(0, 92)}-${hash}`;
}
// Default name for empty filename
if (!filename || filename === '-') {
filename = 'index';
}
return `${filename}.json`;
}
// Extract keywords from content - enhanced for Bucketeer docs
export function extractKeywords(content) {
// Technical terms common in Bucketeer docs
const technicalTerms = new Set([
'feature',
'flag',
'bucket',
'targeting',
'segment',
'variation',
'rollout',
'experiment',
'api',
'sdk',
'environment',
'evaluation',
'event',
'goal',
'conversion',
'track',
'metrics',
'webhook',
'integration',
'token',
]);
// Common stop words to filter out
const stopWords = new Set([
'a',
'an',
'the',
'and',
'or',
'but',
'is',
'are',
'was',
'were',
'be',
'been',
'being',
'in',
'on',
'at',
'to',
'for',
'with',
'by',
'about',
'against',
'between',
'into',
'through',
'during',
'before',
'after',
'above',
'below',
'from',
'up',
'down',
'of',
'this',
'that',
'these',
'those',
'it',
'its',
'they',
'them',
'their',
'what',
'which',
'who',
'whom',
'when',
'where',
'why',
'how',
'all',
'any',
'both',
'each',
'few',
'more',
'most',
'some',
'such',
'no',
'nor',
'not',
'only',
'own',
'same',
'so',
'than',
'too',
'very',
'can',
'will',
'just',
'should',
'now',
]);
// Extract all words
const words = content
.toLowerCase()
.replace(/```[\s\S]*?```/g, '') // Remove code blocks
.replace(/\[[^\]]*\]\([^)]*\)/g, '') // Remove markdown links
.replace(/[^\w\s-]/g, ' ') // Remove special chars except hyphen
.split(/\s+/)
.filter((word) => {
// Keep technical terms regardless of length
if (technicalTerms.has(word))
return true;
// Filter out stop words and short words
return word.length > 2 && !stopWords.has(word);
});
// Return unique keywords
return Array.from(new Set(words));
}