Skip to main content
Glama
knowledge-base-manager.ts11.6 kB
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; import { z } from 'zod'; import sqlite3 from 'sqlite3'; import fs from 'fs-extra'; import { default as Fuse, IFuseOptions } from 'fuse.js'; export default function knowledgeBaseManager(server: McpServer) { server.registerTool( 'knowledge-base-manager', { title: 'Knowledge Base Manager', description: 'A comprehensive tool for managing a SQLite-based knowledge base. It allows creating the database, creating/dropping tables, adding, updating, deleting, and searching for knowledge items containing titles, content, and tags. The tool also supports listing all items, getting schema information, and executing raw SQL queries for advanced control.', inputSchema: { operation: z.enum([ 'create_db', 'create_table', 'drop_table', 'add_item', 'update_item', 'delete_item', 'search_items', 'get_item', 'list_all_items', 'get_schema', 'execute_sql', 'list_tags', ]), title: z.string().optional(), content: z.string().optional(), tags: z.string().optional(), id: z.number().int().optional(), query: z.string().optional(), search_tags: z.string().optional(), limit: z.number().int().min(1).optional(), offset: z.number().int().min(0).optional(), sql_query: z.string().optional(), fuzzy_threshold: z.number().min(0).max(1).optional(), fuzzy_keys: z.string().optional(), // Comma-separated string of keys }, }, async (params) => { const dbPath = '/home/youusef/knowledge_base.db'; let sqlQuery = ''; let queryParams: any[] = []; try { // Ensure the directory exists await fs.ensureDir(require('path').dirname(dbPath)); // Open database const db = new sqlite3.Database(dbPath); const dbRun = (sql: string, params?: any[]) => { return new Promise((resolve, reject) => { db.run(sql, params, function(err) { if (err) reject(err); else resolve(this); }); }); }; const dbAll = (sql: string, params?: any[]) => { return new Promise<any[]>((resolve, reject) => { db.all(sql, params, (err, rows) => { if (err) reject(err); else resolve(rows); }); }); }; const dbClose = () => { return new Promise<void>((resolve, reject) => { db.close((err) => { if (err) reject(err); else resolve(); }); }); }; let resultText = ''; switch (params.operation) { case 'create_db': // Database is created when opened resultText = `SQLite database created at ${dbPath}`; break; case 'create_table': sqlQuery = ` CREATE TABLE IF NOT EXISTS knowledge_items ( id INTEGER PRIMARY KEY AUTOINCREMENT, title TEXT NOT NULL, content TEXT, tags TEXT ); `; await dbRun(sqlQuery); resultText = `Table 'knowledge_items' created successfully`; break; case 'drop_table': sqlQuery = `DROP TABLE IF EXISTS knowledge_items;`; await dbRun(sqlQuery); resultText = `Table 'knowledge_items' dropped successfully`; break; case 'add_item': if (params.title === undefined || params.content === undefined) { await dbClose(); return { content: [ { type: 'text', text: 'Error: title and content are required for add_item operation', }, ], isError: true, }; } sqlQuery = `INSERT INTO knowledge_items (title, content, tags) VALUES (?, ?, ?);`; queryParams = [params.title, params.content, params.tags || '']; const result: any = await dbRun(sqlQuery, queryParams); resultText = `Item added successfully with ID: ${result.lastID}`; break; case 'update_item': if (params.id === undefined) { await dbClose(); return { content: [ { type: 'text', text: 'Error: id is required for update_item operation', }, ], isError: true, }; } // Build SQL dynamically based on provided fields const updates: string[] = []; const updateParams: any[] = []; if (params.title !== undefined) { updates.push('title = ?'); updateParams.push(params.title); } if (params.content !== undefined) { updates.push('content = ?'); updateParams.push(params.content); } if (params.tags !== undefined) { updates.push('tags = ?'); updateParams.push(params.tags); } if (updates.length === 0) { await dbClose(); return { content: [ { type: 'text', text: 'No fields provided for update.', }, ], }; } sqlQuery = `UPDATE knowledge_items SET ${updates.join(', ')} WHERE id = ?;`; queryParams = [...updateParams, params.id]; await dbRun(sqlQuery, queryParams); resultText = `Item ${params.id} updated successfully`; break; case 'delete_item': if (params.id === undefined) { await dbClose(); return { content: [ { type: 'text', text: 'Error: id is required for delete_item operation', }, ], isError: true, }; } sqlQuery = `DELETE FROM knowledge_items WHERE id = ?;`; queryParams = [params.id]; await dbRun(sqlQuery, queryParams); resultText = `Item ${params.id} deleted successfully`; break; case 'get_item': if (params.id === undefined) { await dbClose(); return { content: [ { type: 'text', text: 'Error: id is required for get_item operation', }, ], isError: true, }; } sqlQuery = `SELECT id, title, content, tags FROM knowledge_items WHERE id = ?;`; queryParams = [params.id]; const rows = await dbAll(sqlQuery, queryParams); if (rows.length === 0) { await dbClose(); return { content: [ { type: 'text', text: `Item with ID ${params.id} not found`, }, ], isError: true, }; } resultText = JSON.stringify(rows[0], null, 2); break; case 'search_items': const allItems = await dbAll('SELECT * FROM knowledge_items'); if (params.query) { const fuseOptions: IFuseOptions<any> = { keys: params.fuzzy_keys ? params.fuzzy_keys.split(',').map(key => key.trim()) : ['title', 'content', 'tags'], includeScore: true, threshold: params.fuzzy_threshold !== undefined ? params.fuzzy_threshold : 0.4, }; const fuse = new Fuse(allItems, fuseOptions); const searchResults = fuse.search(params.query); resultText = JSON.stringify(searchResults.map((result: { item: any }) => result.item), null, 2); } else if (params.search_tags) { const searchTagsArray = params.search_tags.split(/[\s,]+/).filter(Boolean); // Filter out empty strings const filteredResults = allItems.filter(item => { const itemTags = item.tags ? item.tags.split(/[\s,]+/).filter(Boolean) : []; return searchTagsArray.some(searchTag => itemTags.includes(searchTag)); }); resultText = JSON.stringify(filteredResults, null, 2); } else { resultText = JSON.stringify(allItems, null, 2); } break; case 'list_all_items': let allLimitOffset = ''; if (params.limit !== undefined) allLimitOffset += ` LIMIT ${params.limit}`; if (params.offset !== undefined) allLimitOffset += ` OFFSET ${params.offset}`; sqlQuery = `SELECT id, title, content, tags FROM knowledge_items ${allLimitOffset};`; const listResults = await dbAll(sqlQuery); resultText = JSON.stringify(listResults, null, 2); break; case 'get_schema': sqlQuery = `PRAGMA table_info(knowledge_items);`; const schemaResults = await dbAll(sqlQuery); resultText = JSON.stringify(schemaResults, null, 2); break; case 'execute_sql': if (params.sql_query === undefined) { await dbClose(); return { content: [ { type: 'text', text: 'Error: sql_query is required for execute_sql operation', }, ], isError: true, }; } const sqlResults = await dbAll(params.sql_query); resultText = JSON.stringify(sqlResults, null, 2); break; case 'list_tags': // Fetch distinct tag strings from the database const distinctTagStrings = await dbAll('SELECT DISTINCT tags FROM knowledge_items WHERE tags IS NOT NULL AND tags != ""'); const tagSet = new Set<string>(); distinctTagStrings.forEach(item => { if (item.tags) { // Split each distinct tag string and add individual tags to the set item.tags.split(/[\s,]+/).filter(Boolean).forEach((tag: string) => tagSet.add(tag.trim())); } }); resultText = JSON.stringify(Array.from(tagSet), null, 2); break; default: await dbClose(); return { content: [ { type: 'text', text: `Unknown operation: ${params.operation}`, }, ], isError: true, }; } // Close database connection await dbClose(); return { content: [{ type: 'text', text: resultText }] }; } catch (error: any) { return { content: [{ type: 'text', text: `Database error: ${error.message}` }], isError: true }; } }, ); }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/Yussefgafer/MyMCP'

If you have feedback or need assistance with the MCP directory API, please join our Discord server