UNPKG

@sofianedjerbi/knowledge-tree-mcp

Version:

MCP server for hierarchical project knowledge management

321 lines 13.3 kB
/** * Category management tool for easy CRUD operations */ import { loadProjectConfig, saveProjectConfig } from '../utils/projectConfig.js'; import { readFileSync, writeFileSync } from 'fs'; import { join, dirname } from 'path'; import { fileURLToPath } from 'url'; const __filename = fileURLToPath(import.meta.url); const __dirname = dirname(__filename); export const categoriesHandler = async (args, context) => { const { action, category, keywords = [], subcategories = [], scope = 'project', description } = args; switch (action) { case 'list': { return listCategories(scope, context); } case 'add': { if (!category || keywords.length === 0) { return { content: [{ type: "text", text: "❌ Category name and keywords are required for 'add' action" }] }; } return addCategory(category, keywords, subcategories, scope, description, context); } case 'update': { if (!category) { return { content: [{ type: "text", text: "❌ Category name is required for 'update' action" }] }; } return updateCategory(category, keywords, subcategories, scope, description, context); } case 'remove': { if (!category) { return { content: [{ type: "text", text: "❌ Category name is required for 'remove' action" }] }; } return removeCategory(category, scope, context); } case 'merge': { if (!category || keywords.length === 0) { return { content: [{ type: "text", text: "❌ Category name and keywords are required for 'merge' action" }] }; } return mergeCategory(category, keywords, subcategories, scope, context); } default: { return { content: [{ type: "text", text: "❌ Invalid action. Use 'add', 'update', 'remove', 'list', or 'merge'" }] }; } } }; async function listCategories(scope, context) { let output = ""; // List project categories if (scope === 'project' || scope === 'both') { const projectConfig = await loadProjectConfig(context.knowledgeRoot); output += "📦 **Project Categories**\n\n"; if (projectConfig?.categories) { for (const [cat, data] of Object.entries(projectConfig.categories)) { output += `• **${cat}**\n`; output += ` Keywords: ${data.keywords.join(', ')}\n`; if (data.subcategories?.length) { output += ` Subcategories: ${data.subcategories.join(', ')}\n`; } output += "\n"; } } else { output += "_No project categories defined_\n\n"; } } // List system categories if (scope === 'system' || scope === 'both') { const systemCategories = loadSystemCategories(); output += "🌐 **System Categories**\n\n"; for (const [cat, keywords] of Object.entries(systemCategories)) { output += `• **${cat}**\n`; output += ` Keywords: ${keywords.slice(0, 5).join(', ')}`; if (keywords.length > 5) { output += ` ... (${keywords.length - 5} more)`; } output += "\n\n"; } } return { content: [{ type: "text", text: output.trim() }] }; } async function addCategory(category, keywords, subcategories, scope, description, context) { const normalizedCategory = category.toLowerCase().replace(/\s+/g, '-'); if (scope === 'project') { // Add to project config const config = await loadProjectConfig(context.knowledgeRoot) || { name: "Default Project", categories: {} }; if (config.categories?.[normalizedCategory]) { return { content: [{ type: "text", text: `❌ Category '${normalizedCategory}' already exists in project configuration` }] }; } if (!config.categories) config.categories = {}; config.categories[normalizedCategory] = { keywords, subcategories: subcategories.length > 0 ? subcategories : undefined }; await saveProjectConfig(context.knowledgeRoot, config); return { content: [{ type: "text", text: `✅ Added project category '${normalizedCategory}'${description ? `\n📝 ${description}` : ''}\n\nKeywords: ${keywords.join(', ')}${subcategories.length ? `\nSubcategories: ${subcategories.join(', ')}` : ''}` }] }; } else { // Add to system categories const categories = loadSystemCategories(); if (categories[normalizedCategory]) { return { content: [{ type: "text", text: `❌ Category '${normalizedCategory}' already exists in system configuration` }] }; } categories[normalizedCategory] = keywords; saveSystemCategories(categories); // Also update subcategories if provided if (subcategories.length > 0) { const systemSubcategories = loadSystemSubcategories(); for (const subcat of subcategories) { const normalizedSubcat = subcat.toLowerCase().replace(/\s+/g, '-'); if (!systemSubcategories[normalizedSubcat]) { systemSubcategories[normalizedSubcat] = []; } systemSubcategories[normalizedSubcat].push(normalizedSubcat); } saveSystemSubcategories(systemSubcategories); } return { content: [{ type: "text", text: `✅ Added system category '${normalizedCategory}'${description ? `\n📝 ${description}` : ''}\n\nKeywords: ${keywords.join(', ')}${subcategories.length ? `\nSubcategories: ${subcategories.join(', ')}` : ''}` }] }; } } async function updateCategory(category, keywords, subcategories, scope, description, context) { const normalizedCategory = category.toLowerCase().replace(/\s+/g, '-'); if (scope === 'project') { const config = await loadProjectConfig(context.knowledgeRoot); if (!config?.categories?.[normalizedCategory]) { return { content: [{ type: "text", text: `❌ Category '${normalizedCategory}' not found in project configuration` }] }; } // Update with new values or keep existing if (keywords.length > 0) { config.categories[normalizedCategory].keywords = keywords; } if (subcategories.length > 0) { config.categories[normalizedCategory].subcategories = subcategories; } await saveProjectConfig(context.knowledgeRoot, config); return { content: [{ type: "text", text: `✅ Updated project category '${normalizedCategory}'${description ? `\n📝 ${description}` : ''}\n\nKeywords: ${config.categories[normalizedCategory].keywords.join(', ')}${config.categories[normalizedCategory].subcategories ? `\nSubcategories: ${config.categories[normalizedCategory].subcategories.join(', ')}` : ''}` }] }; } else { const categories = loadSystemCategories(); if (!categories[normalizedCategory]) { return { content: [{ type: "text", text: `❌ Category '${normalizedCategory}' not found in system configuration` }] }; } if (keywords.length > 0) { categories[normalizedCategory] = keywords; saveSystemCategories(categories); } return { content: [{ type: "text", text: `✅ Updated system category '${normalizedCategory}'${description ? `\n📝 ${description}` : ''}\n\nKeywords: ${categories[normalizedCategory].join(', ')}` }] }; } } async function removeCategory(category, scope, context) { const normalizedCategory = category.toLowerCase().replace(/\s+/g, '-'); if (scope === 'project') { const config = await loadProjectConfig(context.knowledgeRoot); if (!config?.categories?.[normalizedCategory]) { return { content: [{ type: "text", text: `❌ Category '${normalizedCategory}' not found in project configuration` }] }; } delete config.categories[normalizedCategory]; await saveProjectConfig(context.knowledgeRoot, config); return { content: [{ type: "text", text: `✅ Removed category '${normalizedCategory}' from project configuration` }] }; } else { const categories = loadSystemCategories(); if (!categories[normalizedCategory]) { return { content: [{ type: "text", text: `❌ Category '${normalizedCategory}' not found in system configuration` }] }; } delete categories[normalizedCategory]; saveSystemCategories(categories); return { content: [{ type: "text", text: `✅ Removed category '${normalizedCategory}' from system configuration\n\n⚠️ Warning: Existing entries using this category may need to be recategorized` }] }; } } async function mergeCategory(category, keywords, subcategories, scope, context) { const normalizedCategory = category.toLowerCase().replace(/\s+/g, '-'); if (scope === 'project') { const config = await loadProjectConfig(context.knowledgeRoot) || { name: "Default Project", categories: {} }; if (!config.categories) config.categories = {}; const existing = config.categories[normalizedCategory] || { keywords: [], subcategories: [] }; // Merge keywords (unique) const mergedKeywords = [...new Set([...existing.keywords, ...keywords])]; // Merge subcategories (unique) const existingSubcats = existing.subcategories || []; const mergedSubcategories = [...new Set([...existingSubcats, ...subcategories])]; config.categories[normalizedCategory] = { keywords: mergedKeywords, subcategories: mergedSubcategories.length > 0 ? mergedSubcategories : undefined }; await saveProjectConfig(context.knowledgeRoot, config); return { content: [{ type: "text", text: `✅ Merged into project category '${normalizedCategory}'\n\nKeywords: ${mergedKeywords.join(', ')}${mergedSubcategories.length ? `\nSubcategories: ${mergedSubcategories.join(', ')}` : ''}` }] }; } else { const categories = loadSystemCategories(); const existing = categories[normalizedCategory] || []; const mergedKeywords = [...new Set([...existing, ...keywords])]; categories[normalizedCategory] = mergedKeywords; saveSystemCategories(categories); return { content: [{ type: "text", text: `✅ Merged into system category '${normalizedCategory}'\n\nKeywords: ${mergedKeywords.join(', ')}` }] }; } } // Helper functions function loadSystemCategories() { const categoriesPath = join(__dirname, '../config/categories.json'); return JSON.parse(readFileSync(categoriesPath, 'utf-8')); } function saveSystemCategories(categories) { const categoriesPath = join(__dirname, '../config/categories.json'); writeFileSync(categoriesPath, JSON.stringify(categories, null, 2)); } function loadSystemSubcategories() { const subcategoriesPath = join(__dirname, '../config/subcategories.json'); return JSON.parse(readFileSync(subcategoriesPath, 'utf-8')); } function saveSystemSubcategories(subcategories) { const subcategoriesPath = join(__dirname, '../config/subcategories.json'); writeFileSync(subcategoriesPath, JSON.stringify(subcategories, null, 2)); } //# sourceMappingURL=categories.js.map