Skip to main content
Glama
index.ts33.8 kB
#!/usr/bin/env node import { Server } from '@modelcontextprotocol/sdk/server/index.js'; import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'; import { CallToolRequestSchema, ListToolsRequestSchema } from '@modelcontextprotocol/sdk/types.js'; import { KnowledgeManager } from './KnowledgeManager.js'; import { onboardingForms, quickForms, parseFormInput } from './forms.js'; import { AGENT_PERSONAS, getPersonasByCategory, getPersonaCategories, applyPersonaToKnowledgeBase } from './personas.js'; import { AgentManager } from './AgentManager.js'; import { getAgentTools, validateToolAccess } from './agent-tools.js'; import chalk from 'chalk'; // Initialize the knowledge manager and agent manager const km = new KnowledgeManager(); const am = new AgentManager(); // Create MCP server const server = new Server( { name: 'personal-knowledge-base', version: '1.0.0', }, { capabilities: { tools: {} } } ); // Define all available tools const tools: any[] = [ // ONBOARDING & SETUP TOOLS { name: 'kb_initialize', description: 'Initialize or check knowledge base status. Returns current profile summary and whether onboarding is needed.', inputSchema: { type: 'object', properties: { profileId: { type: 'string', description: 'Profile ID to use (default: "default")', default: 'default' } } } }, { name: 'kb_onboard', description: 'Start interactive onboarding to collect initial information. Returns questions for the specified category.', inputSchema: { type: 'object', properties: { category: { type: 'string', enum: ['initial', 'professional', 'preferences', 'projects', 'all'], description: 'Category of questions to ask', default: 'all' } } } }, { name: 'kb_quick_setup', description: 'Quick setup using predefined forms for common scenarios', inputSchema: { type: 'object', properties: { formType: { type: 'string', enum: ['identity', 'technical', 'organization'], description: 'Type of quick setup form' }, data: { type: 'object', description: 'Form data as key-value pairs' } }, required: ['formType', 'data'] } }, // INFORMATION UPDATE TOOLS { name: 'kb_update_personal', description: 'Update personal information (name, location, languages, etc.)', inputSchema: { type: 'object', properties: { name: { type: 'string' }, birthYear: { type: 'number' }, birthPlace: { type: 'string' }, currentLocation: { type: 'string' }, languages: { type: 'array', items: { type: 'string' } }, nationality: { type: 'string' }, timezone: { type: 'string' }, pronouns: { type: 'string' } } } }, { name: 'kb_update_professional', description: 'Update professional information (job, skills, experience, etc.)', inputSchema: { type: 'object', properties: { occupation: { type: 'string' }, yearsOfExperience: { type: 'number' }, industry: { type: 'string' }, specializations: { type: 'array', items: { type: 'string' } }, currentCompany: { type: 'string' }, role: { type: 'string' }, skills: { type: 'array', items: { type: 'string' } }, certifications: { type: 'array', items: { type: 'string' } }, education: { type: 'array', items: { type: 'string' } } } } }, { name: 'kb_update_preferences', description: 'Update user preferences (communication style, technical level, etc.)', inputSchema: { type: 'object', properties: { communicationStyle: { type: 'string' }, learningStyle: { type: 'string' }, workingHours: { type: 'string' }, responseDetail: { type: 'string', enum: ['concise', 'detailed', 'balanced'] }, technicalLevel: { type: 'string', enum: ['beginner', 'intermediate', 'expert'] }, favoriteTools: { type: 'array', items: { type: 'string' } }, interests: { type: 'array', items: { type: 'string' } } } } }, { name: 'kb_update_projects', description: 'Update project context (current projects, technologies, goals)', inputSchema: { type: 'object', properties: { currentProjects: { type: 'array', items: { type: 'string' } }, technologies: { type: 'array', items: { type: 'string' } }, goals: { type: 'array', items: { type: 'string' } }, challenges: { type: 'array', items: { type: 'string' } }, teamSize: { type: 'number' }, methodology: { type: 'string' } } } }, // CUSTOM KNOWLEDGE TOOLS { name: 'kb_add_custom', description: 'Add custom knowledge to any category', inputSchema: { type: 'object', properties: { category: { type: 'string', description: 'Category name (e.g., "tools", "workflows", "contacts")' }, key: { type: 'string', description: 'Knowledge key/identifier' }, value: { description: 'Knowledge value (can be string, object, array, etc.)' }, tags: { type: 'array', items: { type: 'string' }, description: 'Optional tags for categorization' } }, required: ['category', 'key', 'value'] } }, { name: 'kb_remove_custom', description: 'Remove custom knowledge', inputSchema: { type: 'object', properties: { category: { type: 'string' }, key: { type: 'string' } }, required: ['category', 'key'] } }, // RETRIEVAL TOOLS { name: 'kb_get_all', description: 'Get complete knowledge base as formatted JSON', inputSchema: { type: 'object', properties: { format: { type: 'string', enum: ['full', 'summary', 'categories'], default: 'full' } } } }, { name: 'kb_get_personal', description: 'Get personal information', inputSchema: { type: 'object', properties: {} } }, { name: 'kb_get_professional', description: 'Get professional information', inputSchema: { type: 'object', properties: {} } }, { name: 'kb_get_preferences', description: 'Get user preferences', inputSchema: { type: 'object', properties: {} } }, { name: 'kb_get_projects', description: 'Get project context', inputSchema: { type: 'object', properties: {} } }, { name: 'kb_get_custom', description: 'Get custom knowledge by category', inputSchema: { type: 'object', properties: { category: { type: 'string', description: 'Category to retrieve (optional, returns all if not specified)' } } } }, { name: 'kb_search', description: 'Search knowledge base using semantic search', inputSchema: { type: 'object', properties: { query: { type: 'string', description: 'Search query' }, limit: { type: 'number', default: 10, description: 'Maximum results to return' } }, required: ['query'] } }, { name: 'kb_get_context', description: 'Get AI-ready context string for LLM consumption', inputSchema: { type: 'object', properties: { categories: { type: 'array', items: { type: 'string' }, description: 'Categories to include (default: all)', default: ['personal', 'professional', 'preferences', 'projects'] } } } }, // HISTORY & MANAGEMENT TOOLS { name: 'kb_get_history', description: 'Get recent history of knowledge base changes', inputSchema: { type: 'object', properties: { limit: { type: 'number', default: 20, description: 'Number of history entries to return' } } } }, { name: 'kb_export', description: 'Export knowledge base as JSON string', inputSchema: { type: 'object', properties: {} } }, { name: 'kb_import', description: 'Import knowledge base from JSON string', inputSchema: { type: 'object', properties: { data: { type: 'string', description: 'JSON string of knowledge base data' } }, required: ['data'] } }, { name: 'kb_list_forms', description: 'List available forms and their structures', inputSchema: { type: 'object', properties: { type: { type: 'string', enum: ['onboarding', 'quick', 'all'], default: 'all' } } } }, // AI AGENT PERSONA TOOLS { name: 'agent_list', description: 'List all available AI agent personas', inputSchema: { type: 'object', properties: {} } }, { name: 'agent_activate', description: 'Activate a specialized AI agent (IT Expert, Hacker, Sales, Blue/Red/Purple Team)', inputSchema: { type: 'object', properties: { agent: { type: 'string', enum: ['it-expert', 'ethical-hacker', 'sales-expert', 'blue-team', 'red-team', 'purple-team'], description: 'Agent ID to activate' } }, required: ['agent'] } }, { name: 'agent_switch_quick', description: 'Quick switch agent using shorthand (it/hacker/sales/blue/red/purple)', inputSchema: { type: 'object', properties: { type: { type: 'string', enum: ['it', 'hacker', 'sales', 'blue', 'red', 'purple'], description: 'Quick switch type' } }, required: ['type'] } }, { name: 'agent_get_active', description: 'Get the currently active agent', inputSchema: { type: 'object', properties: {} } }, { name: 'agent_get_tools', description: 'Get available tools for current agent', inputSchema: { type: 'object', properties: {} } }, // SPECIAL ONBOARDING TOOL FOR CHATGPT DESKTOP { name: 'mcp_instruct_onboarding', description: 'Start the MCP Instruct onboarding process - sets up personal profile and AI agent', inputSchema: { type: 'object', properties: { action: { type: 'string', enum: ['start', 'check_status', 'quick_setup'], default: 'start', description: 'Onboarding action to perform' }, data: { type: 'object', description: 'Optional data for quick setup', properties: { name: { type: 'string' }, role: { type: 'string' }, preferredAgent: { type: 'string' } } } } } } ]; // Handler for listing tools server.setRequestHandler(ListToolsRequestSchema, async () => { return { tools }; }); // Handler for tool execution server.setRequestHandler(CallToolRequestSchema, async (request) => { const { name, arguments: args = {} } = request.params; try { // Initialize if not already done if (!(km as any).initialized) { await km.initialize(); (km as any).initialized = true; } switch (name) { // INITIALIZATION case 'kb_initialize': { const isNew = km.isNew(); const kb = km.getKnowledgeBase(); return { content: [ { type: 'text', text: JSON.stringify({ status: 'initialized', isNew, needsOnboarding: isNew, profile: { id: kb.id, createdAt: kb.createdAt, lastUpdated: kb.lastUpdated, hasPersonal: Object.keys(kb.personal).length > 0, hasProfessional: Object.keys(kb.professional).length > 0, hasPreferences: Object.keys(kb.preferences).length > 0, hasProjects: Object.keys(kb.projects).length > 0, customCategories: [...new Set(kb.custom.map(c => c.category))] } }, null, 2) } ] }; } // ONBOARDING case 'kb_onboard': { const category = (args as any).category || 'all'; let questions: any[] = []; if (category === 'all') { questions = [ ...onboardingForms.initial, ...onboardingForms.professional, ...onboardingForms.preferences, ...onboardingForms.projects ]; } else if (onboardingForms[category as keyof typeof onboardingForms]) { questions = onboardingForms[category as keyof typeof onboardingForms]; } return { content: [ { type: 'text', text: JSON.stringify({ category, totalQuestions: questions.length, questions: questions.map(q => ({ id: q.id, question: q.question, type: q.type, field: q.field, required: q.required, options: q.options })) }, null, 2) } ] }; } case 'kb_quick_setup': { const { formType, data } = args as any; const form = quickForms[formType]; if (!form) { throw new Error(`Unknown form type: ${formType}`); } const updates: any = {}; for (const [key, value] of Object.entries(data)) { const fieldDef = form.fields[key]; if (fieldDef) { updates[key] = parseFormInput(value as string, fieldDef.type); } } // Apply updates based on form type if (formType === 'identity') { await km.updatePersonal({ name: updates.name, currentLocation: updates.location, languages: updates.languages }); await km.updateProfessional({ occupation: updates.occupation }); } else if (formType === 'technical') { await km.updateProfessional({ role: updates.role, yearsOfExperience: updates.experience, skills: updates.skills }); await km.updatePreferences({ favoriteTools: updates.tools }); } else if (formType === 'organization') { await km.addCustomKnowledge('organization', 'name', updates.orgName); await km.addCustomKnowledge('organization', 'industry', updates.industry); if (updates.size) await km.addCustomKnowledge('organization', 'size', updates.size); if (updates.mission) await km.addCustomKnowledge('organization', 'mission', updates.mission); } return { content: [ { type: 'text', text: `✅ Quick setup completed for ${formType}` } ] }; } // UPDATE TOOLS case 'kb_update_personal': { await km.updatePersonal(args as any); return { content: [ { type: 'text', text: '✅ Personal information updated successfully' } ] }; } case 'kb_update_professional': { await km.updateProfessional(args as any); return { content: [ { type: 'text', text: '✅ Professional information updated successfully' } ] }; } case 'kb_update_preferences': { await km.updatePreferences(args as any); return { content: [ { type: 'text', text: '✅ Preferences updated successfully' } ] }; } case 'kb_update_projects': { await km.updateProjects(args as any); return { content: [ { type: 'text', text: '✅ Project context updated successfully' } ] }; } // CUSTOM KNOWLEDGE case 'kb_add_custom': { const { category, key, value, tags } = args as any; await km.addCustomKnowledge(category, key, value, tags); return { content: [ { type: 'text', text: `✅ Added custom knowledge: ${category}/${key}` } ] }; } case 'kb_remove_custom': { const { category, key } = args as any; const removed = await km.removeCustomKnowledge(category, key); return { content: [ { type: 'text', text: removed ? `✅ Removed custom knowledge: ${category}/${key}` : `❌ Not found: ${category}/${key}` } ] }; } // RETRIEVAL TOOLS case 'kb_get_all': { const format = (args as any).format || 'full'; const kb = km.getKnowledgeBase(); let result: any; if (format === 'summary') { result = { personal: { name: kb.personal.name, location: kb.personal.currentLocation, languages: kb.personal.languages }, professional: { occupation: kb.professional.occupation, experience: kb.professional.yearsOfExperience, skills: kb.professional.skills }, preferences: { communicationStyle: kb.preferences.communicationStyle, technicalLevel: kb.preferences.technicalLevel }, projects: { current: kb.projects.currentProjects, technologies: kb.projects.technologies }, customCategories: [...new Set(kb.custom.map(c => c.category))] }; } else if (format === 'categories') { result = { categories: { personal: Object.keys(kb.personal).filter(k => (kb.personal as any)[k] !== undefined), professional: Object.keys(kb.professional).filter(k => (kb.professional as any)[k] !== undefined), preferences: Object.keys(kb.preferences).filter(k => (kb.preferences as any)[k] !== undefined), projects: Object.keys(kb.projects).filter(k => (kb.projects as any)[k] !== undefined), custom: [...new Set(kb.custom.map(c => c.category))] } }; } else { result = { personal: kb.personal, professional: kb.professional, preferences: kb.preferences, projects: kb.projects, custom: kb.custom }; } return { content: [ { type: 'text', text: JSON.stringify(result, null, 2) } ] }; } case 'kb_get_personal': { return { content: [ { type: 'text', text: JSON.stringify(km.getPersonal(), null, 2) } ] }; } case 'kb_get_professional': { return { content: [ { type: 'text', text: JSON.stringify(km.getProfessional(), null, 2) } ] }; } case 'kb_get_preferences': { return { content: [ { type: 'text', text: JSON.stringify(km.getPreferences(), null, 2) } ] }; } case 'kb_get_projects': { return { content: [ { type: 'text', text: JSON.stringify(km.getProjects(), null, 2) } ] }; } case 'kb_get_custom': { const category = (args as any).category; const custom = km.getCustom(category); return { content: [ { type: 'text', text: JSON.stringify(custom, null, 2) } ] }; } case 'kb_search': { const { query, limit = 10 } = args as any; const results = km.search(query).slice(0, limit); return { content: [ { type: 'text', text: JSON.stringify(results, null, 2) } ] }; } case 'kb_get_context': { const categories = (args as any).categories || ['personal', 'professional', 'preferences', 'projects']; const kb = km.getKnowledgeBase(); let context = '=== USER CONTEXT ===\n\n'; if ((categories as any[]).includes('personal') && Object.keys(kb.personal).length > 0) { context += '**Personal Information:**\n'; if (kb.personal.name) context += `- Name: ${kb.personal.name}\n`; if (kb.personal.currentLocation) context += `- Location: ${kb.personal.currentLocation}\n`; if (kb.personal.languages?.length) context += `- Languages: ${kb.personal.languages.join(', ')}\n`; if (kb.personal.birthYear) context += `- Birth Year: ${kb.personal.birthYear}\n`; if (kb.personal.pronouns) context += `- Pronouns: ${kb.personal.pronouns}\n`; context += '\n'; } if ((categories as any[]).includes('professional') && Object.keys(kb.professional).length > 0) { context += '**Professional Background:**\n'; if (kb.professional.occupation) context += `- Occupation: ${kb.professional.occupation}\n`; if (kb.professional.yearsOfExperience) context += `- Experience: ${kb.professional.yearsOfExperience} years\n`; if (kb.professional.industry) context += `- Industry: ${kb.professional.industry}\n`; if (kb.professional.skills?.length) context += `- Skills: ${kb.professional.skills.join(', ')}\n`; if (kb.professional.specializations?.length) context += `- Specializations: ${kb.professional.specializations.join(', ')}\n`; context += '\n'; } if ((categories as any[]).includes('preferences') && Object.keys(kb.preferences).length > 0) { context += '**Communication Preferences:**\n'; if (kb.preferences.communicationStyle) context += `- Style: ${kb.preferences.communicationStyle}\n`; if (kb.preferences.responseDetail) context += `- Response Detail: ${kb.preferences.responseDetail}\n`; if (kb.preferences.technicalLevel) context += `- Technical Level: ${kb.preferences.technicalLevel}\n`; if (kb.preferences.favoriteTools?.length) context += `- Favorite Tools: ${kb.preferences.favoriteTools.join(', ')}\n`; context += '\n'; } if ((categories as any[]).includes('projects') && Object.keys(kb.projects).length > 0) { context += '**Current Projects:**\n'; if (kb.projects.currentProjects?.length) context += `- Projects: ${kb.projects.currentProjects.join(', ')}\n`; if (kb.projects.technologies?.length) context += `- Technologies: ${kb.projects.technologies.join(', ')}\n`; if (kb.projects.goals?.length) context += `- Goals: ${kb.projects.goals.join(', ')}\n`; context += '\n'; } context += '=== END CONTEXT ==='; return { content: [ { type: 'text', text: context } ] }; } // HISTORY & MANAGEMENT case 'kb_get_history': { const limit = (args as any).limit || 20; const history = km.getHistory(limit); return { content: [ { type: 'text', text: JSON.stringify(history, null, 2) } ] }; } case 'kb_export': { const data = await km.exportKnowledgeBase(); return { content: [ { type: 'text', text: data } ] }; } case 'kb_import': { const { data } = args as any; await km.importKnowledgeBase(data); return { content: [ { type: 'text', text: '✅ Knowledge base imported successfully' } ] }; } case 'kb_list_forms': { const type = (args as any).type || 'all'; let forms: any = {}; if (type === 'onboarding' || type === 'all') { forms.onboarding = onboardingForms; } if (type === 'quick' || type === 'all') { forms.quick = quickForms; } return { content: [ { type: 'text', text: JSON.stringify(forms, null, 2) } ] }; } // AGENT TOOLS case 'agent_list': { await am.initialize(); const agents = am.getTemplates(); return { content: [ { type: 'text', text: JSON.stringify({ agents: agents.map(a => ({ id: a.id, name: a.name, category: a.metadata.category, role: a.metadata.role?.substring(0, 100) + '...' })) }, null, 2) } ] }; } case 'agent_activate': { await am.initialize(); const { agent } = args as any; const success = am.setActiveAgent(agent); if (!success) { throw new Error(`Agent not found: ${agent}`); } const activeAgent = am.getActiveAgent(); const tools = getAgentTools(agent); return { content: [ { type: 'text', text: `✅ Activated ${activeAgent?.name}\n\nTools available: ${tools?.defaultTools.join(', ') || 'none'}` } ] }; } case 'agent_switch_quick': { await am.initialize(); const { type } = args as any; const success = am.quickSwitch(type); if (!success) { throw new Error(`Invalid agent type: ${type}`); } const activeAgent = am.getActiveAgent(); return { content: [ { type: 'text', text: `✅ Switched to ${activeAgent?.name}` } ] }; } case 'agent_get_active': { await am.initialize(); const activeAgent = am.getActiveAgent(); if (!activeAgent) { return { content: [ { type: 'text', text: 'No agent currently active. Use agent_activate to select one.' } ] }; } return { content: [ { type: 'text', text: JSON.stringify({ active: { id: activeAgent.id, name: activeAgent.name, category: activeAgent.metadata.category, role: activeAgent.metadata.role } }, null, 2) } ] }; } case 'agent_get_tools': { await am.initialize(); const activeAgent = am.getActiveAgent(); if (!activeAgent) { return { content: [ { type: 'text', text: 'No agent active. Activate an agent first to see available tools.' } ] }; } const tools = getAgentTools(activeAgent.id); return { content: [ { type: 'text', text: JSON.stringify({ agent: activeAgent.name, tools: tools?.tools || [], defaultTools: tools?.defaultTools || [] }, null, 2) } ] }; } // SPECIAL ONBOARDING FOR CHATGPT DESKTOP case 'mcp_instruct_onboarding': { await am.initialize(); const { action = 'start', data } = args as any; switch (action) { case 'start': const isNew = km.isNew(); if (isNew) { return { content: [ { type: 'text', text: `🎉 Welcome to MCP Instruct!\n\nI'll help you set up your personal knowledge base and AI agent.\n\nTell me:\n1. Your name\n2. Your role/occupation\n3. Preferred agent mode (IT, Security, Sales, etc.)\n\nOr use quick setup: mcp_instruct_onboarding with action="quick_setup"` } ] }; } else { const kb = km.getKnowledgeBase(); const activeAgent = am.getActiveAgent(); return { content: [ { type: 'text', text: `Welcome back, ${kb.personal.name || 'User'}!\n\nProfile: ${kb.professional.occupation || 'Not set'}\nActive Agent: ${activeAgent?.name || 'None'}\n\nYou can switch agents or update your profile anytime.` } ] }; } case 'check_status': const kb = km.getKnowledgeBase(); const agent = am.getActiveAgent(); return { content: [ { type: 'text', text: JSON.stringify({ isNew: km.isNew(), profile: { name: kb.personal.name, role: kb.professional.occupation }, activeAgent: agent?.name }, null, 2) } ] }; case 'quick_setup': if (data?.name) { await km.updatePersonal({ name: data.name }); } if (data?.role) { await km.updateProfessional({ occupation: data.role }); // Auto-select agent based on role if (data.role.toLowerCase().includes('it')) { am.setActiveAgent('it-expert'); } else if (data.role.toLowerCase().includes('security')) { am.setActiveAgent('ethical-hacker'); } else if (data.role.toLowerCase().includes('sales')) { am.setActiveAgent('sales-expert'); } } if (data?.preferredAgent) { am.setActiveAgent(data.preferredAgent); } return { content: [ { type: 'text', text: `✅ Setup complete!\n\nProfile: ${data?.name} - ${data?.role}\nActive Agent: ${am.getActiveAgent()?.name || 'None'}\n\nYour profile is saved and will persist across sessions.` } ] }; default: return { content: [ { type: 'text', text: 'Unknown onboarding action' } ] }; } } default: throw new Error(`Unknown tool: ${name}`); } } catch (error: any) { return { content: [ { type: 'text', text: `Error: ${error.message}` } ] }; } }); // Start the server async function main() { console.log(chalk.cyan('🧠 Personal Knowledge Base MCP Server')); console.log(chalk.gray('Initializing...')); await km.initialize(); if (km.isNew()) { console.log(chalk.yellow('📝 New knowledge base detected. Run kb_initialize to start onboarding.')); } else { console.log(chalk.green('✅ Existing knowledge base loaded.')); } const transport = new StdioServerTransport(); await server.connect(transport); console.log(chalk.green('🚀 Server running on stdio')); } main().catch((error) => { console.error(chalk.red('Fatal error:'), error); process.exit(1); });

Implementation Reference

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/hlsitechio/mcp-instruct'

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