UNPKG

@sofianedjerbi/knowledge-tree-mcp

Version:

MCP server for hierarchical project knowledge management

182 lines 5.88 kB
/** * Index knowledge tool implementation * Provides a comprehensive overview/map of all knowledge entries */ import { join } from 'path'; import { INDEX_DEFAULTS, PRIORITY_LEVELS } from '../constants/index.js'; import { readFile, getFileStats } from '../utils/index.js'; /** * Handler for the index_knowledge tool */ export const indexKnowledgeHandler = async (args, context) => { const { format = INDEX_DEFAULTS.FORMAT, include_content = INDEX_DEFAULTS.INCLUDE_CONTENT, max_entries = INDEX_DEFAULTS.MAX_ENTRIES } = args; const allEntries = await context.scanKnowledgeTree(); const entries = []; // Load all entries with basic stats for (const path of allEntries.slice(0, max_entries)) { const fullPath = join(context.knowledgeRoot, path); try { const content = await readFile(fullPath); const entry = JSON.parse(content); let stats; if (format === "summary") { const fileStat = await getFileStats(fullPath); stats = { created: fileStat.birthtime.toISOString(), modified: fileStat.mtime.toISOString(), size: fileStat.size }; } entries.push({ path, entry, stats }); } catch (error) { // Skip invalid entries } } let result = { total_entries: allEntries.length, showing: entries.length, format: format, timestamp: new Date().toISOString() }; switch (format) { case "tree": result.index = buildTreeIndex(entries, include_content); break; case "list": result.index = buildListIndex(entries, include_content); break; case "summary": result.index = buildSummaryIndex(entries); break; case "categories": result.index = buildCategoriesIndex(entries); break; } // Add quick stats result.statistics = { by_priority: countByPriority(entries), by_category: countByCategory(entries), with_relationships: entries.filter(e => e.entry.related_to?.length).length, with_code: entries.filter(e => e.entry.code).length }; return { content: [ { type: "text", text: JSON.stringify(result, null, 2), }, ], }; }; /** * Build tree-structured index */ function buildTreeIndex(entries, includeContent) { const tree = {}; for (const { path, entry } of entries) { const parts = path.split('/'); let current = tree; // Build nested structure for (let i = 0; i < parts.length - 1; i++) { const part = parts[i]; if (!current[part]) { current[part] = {}; } current = current[part]; } // Add the file const filename = parts[parts.length - 1].replace('.json', ''); current[filename] = { priority: entry.priority, problem: includeContent ? entry.problem : entry.problem.substring(0, 50) + "...", links: entry.related_to?.length || 0 }; if (includeContent && entry.solution) { current[filename].solution = entry.solution.substring(0, 100) + "..."; } } return tree; } /** * Build list-structured index */ function buildListIndex(entries, includeContent) { return entries.map(({ path, entry }) => ({ path, priority: entry.priority, problem: includeContent ? entry.problem : entry.problem.substring(0, 60) + "...", solution: includeContent ? entry.solution.substring(0, 100) + "..." : undefined, relationships: entry.related_to?.length || 0, has_code: !!entry.code })); } /** * Build summary index with file stats */ function buildSummaryIndex(entries) { return entries.map(({ path, entry, stats }) => ({ path, priority: entry.priority, title: entry.problem.substring(0, 40) + "...", relationships: entry.related_to?.length || 0, features: { has_code: !!entry.code, has_examples: !!entry.examples, has_links: !!(entry.related_to?.length) }, file_info: stats })); } /** * Build category-grouped index */ function buildCategoriesIndex(entries) { const categories = {}; for (const { path, entry } of entries) { const pathParts = path.split('/'); const category = pathParts.slice(0, -1).join('/') || 'root'; if (!categories[category]) { categories[category] = { count: 0, priorities: {}, entries: [] }; } categories[category].count++; categories[category].priorities[entry.priority] = (categories[category].priorities[entry.priority] || 0) + 1; categories[category].entries.push({ filename: pathParts[pathParts.length - 1], priority: entry.priority, problem: entry.problem.substring(0, 50) + "..." }); } return categories; } /** * Count entries by priority */ function countByPriority(entries) { const counts = {}; // Initialize with all priority levels for (const priority of PRIORITY_LEVELS) { counts[priority] = 0; } for (const { entry } of entries) { counts[entry.priority]++; } return counts; } /** * Count entries by category */ function countByCategory(entries) { const counts = {}; for (const { path } of entries) { const category = path.split('/').slice(0, -1).join('/') || 'root'; counts[category] = (counts[category] || 0) + 1; } return counts; } //# sourceMappingURL=indexKnowledge.js.map