UNPKG

mushcode-mcp-server

Version:

A specialized Model Context Protocol server for MUSHCODE development assistance. Provides AI-powered code generation, validation, optimization, and examples for MUD development.

424 lines 15.8 kB
/** * get_examples tool implementation * Retrieves relevant MUSHCODE examples with categorization and learning paths */ import { ValidationError } from '../utils/errors.js'; // Tool definition export const getExamplesTool = { name: 'get_examples', description: 'Retrieve relevant MUSHCODE examples with categorization, difficulty filtering, and learning path generation', inputSchema: { type: 'object', properties: { topic: { type: 'string', description: 'Topic or concept to find examples for (e.g., "object creation", "conditionals", "functions")', minLength: 1, maxLength: 200 }, difficulty: { type: 'string', description: 'Filter examples by difficulty level', enum: ['beginner', 'intermediate', 'advanced'] }, server_type: { type: 'string', description: 'Filter examples by MUD server compatibility', enum: ['PennMUSH', 'TinyMUSH', 'RhostMUSH', 'TinyMUX', 'MUX'] }, category: { type: 'string', description: 'Filter examples by category', enum: ['building', 'administration', 'functions', 'commands', 'triggers', 'utilities', 'security'] }, max_results: { type: 'integer', description: 'Maximum number of examples to return', minimum: 1, maximum: 50, default: 10 }, include_learning_path: { type: 'boolean', description: 'Whether to include a suggested learning path for progressive skill development', default: true } }, required: ['topic'] } }; /** * Tool handler for get_examples */ export async function getExamplesHandler(args, knowledgeBase) { const startTime = Date.now(); try { // Validate and extract arguments const request = validateAndExtractArgs(args); // Search for relevant examples const searchResults = await searchExamples(request, knowledgeBase); // Generate learning path if requested let learningPath; if (request.includeLearningPath) { learningPath = await generateLearningPath(request, searchResults, knowledgeBase); } // Get additional resources const additionalResources = getAdditionalResources(request.topic, request.category); const executionTime = Date.now() - startTime; const result = { examples: searchResults.examples, additional_resources: additionalResources, total_found: searchResults.totalFound, search_metadata: { query: request.topic, filters_applied: buildFiltersApplied(request), execution_time_ms: executionTime } }; if (learningPath && learningPath.length > 0) { result.learning_path = learningPath; } return result; } catch (error) { if (error instanceof ValidationError) { throw error; } throw new Error(`Example retrieval failed: ${error instanceof Error ? error.message : 'Unknown error'}`); } } /** * Validate and extract arguments from the tool call */ function validateAndExtractArgs(args) { // Validate required fields if (!args['topic'] || typeof args['topic'] !== 'string') { throw new ValidationError('topic is required and must be a string'); } const topic = args['topic'].trim(); if (topic.length === 0) { throw new ValidationError('topic cannot be empty'); } if (topic.length > 200) { throw new ValidationError('topic is too long (max 200 characters)'); } // Validate optional fields let difficulty = undefined; if (args['difficulty'] !== undefined) { if (typeof args['difficulty'] !== 'string') { throw new ValidationError('difficulty must be a string'); } const validDifficulties = ['beginner', 'intermediate', 'advanced']; if (!validDifficulties.includes(args['difficulty'])) { throw new ValidationError(`difficulty must be one of: ${validDifficulties.join(', ')}`); } difficulty = args['difficulty']; } let serverType = undefined; if (args['server_type'] !== undefined) { if (typeof args['server_type'] !== 'string') { throw new ValidationError('server_type must be a string'); } const validServerTypes = ['PennMUSH', 'TinyMUSH', 'RhostMUSH', 'TinyMUX', 'MUX']; if (!validServerTypes.includes(args['server_type'])) { throw new ValidationError(`server_type must be one of: ${validServerTypes.join(', ')}`); } serverType = args['server_type']; } let category = undefined; if (args['category'] !== undefined) { if (typeof args['category'] !== 'string') { throw new ValidationError('category must be a string'); } const validCategories = ['building', 'administration', 'functions', 'commands', 'triggers', 'utilities', 'security']; if (!validCategories.includes(args['category'])) { throw new ValidationError(`category must be one of: ${validCategories.join(', ')}`); } category = args['category']; } let maxResults = 10; // default value if (args['max_results'] !== undefined) { if (typeof args['max_results'] !== 'number' || !Number.isInteger(args['max_results'])) { throw new ValidationError('max_results must be an integer'); } if (args['max_results'] < 1 || args['max_results'] > 50) { throw new ValidationError('max_results must be between 1 and 50'); } maxResults = args['max_results']; } let includeLearningPath = true; // default value if (args['include_learning_path'] !== undefined) { if (typeof args['include_learning_path'] !== 'boolean') { throw new ValidationError('include_learning_path must be a boolean'); } includeLearningPath = args['include_learning_path']; } const request = { topic, maxResults, includeLearningPath }; if (difficulty !== undefined) { request.difficulty = difficulty; } if (serverType !== undefined) { request.serverType = serverType; } if (category !== undefined) { request.category = category; } return request; } /** * Search for examples based on the request parameters */ async function searchExamples(request, knowledgeBase) { const examples = knowledgeBase.getAllExamples(); const matchedExamples = []; // Search through examples for (const example of examples) { // Apply hard filters first if (request.difficulty && example.difficulty !== request.difficulty) { continue; } if (request.category && example.category !== request.category) { continue; } if (request.serverType && !example.serverCompatibility.includes(request.serverType)) { continue; } const score = calculateRelevanceScore(example, request); if (score > 0) { matchedExamples.push({ example, score }); } } // Sort by relevance score (descending) matchedExamples.sort((a, b) => b.score - a.score); // Apply limit const limitedResults = matchedExamples.slice(0, request.maxResults); // Convert to result format const exampleResults = limitedResults.map(({ example, score }) => ({ id: example.id, title: example.title, description: example.description, code: example.code, explanation: example.explanation, difficulty: example.difficulty, category: example.category, tags: example.tags, server_compatibility: example.serverCompatibility, related_concepts: example.relatedConcepts, learning_objectives: example.learningObjectives, source: example.source ? { url: example.source.url, ...(example.source.author && { author: example.source.author }) } : undefined, relevance_score: Math.round(score * 100) / 100 })); return { examples: exampleResults, totalFound: matchedExamples.length }; } /** * Calculate relevance score for an example based on search criteria */ function calculateRelevanceScore(example, request) { let score = 0; const topic = request.topic.toLowerCase(); // Title match (highest weight) if (example.title.toLowerCase().includes(topic)) { score += 10; } // Description match if (example.description.toLowerCase().includes(topic)) { score += 8; } // Tags match for (const tag of example.tags) { if (tag.toLowerCase().includes(topic) || topic.includes(tag.toLowerCase())) { score += 6; } } // Related concepts match for (const concept of example.relatedConcepts) { if (concept.toLowerCase().includes(topic) || topic.includes(concept.toLowerCase())) { score += 5; } } // Category match if (example.category.toLowerCase().includes(topic) || topic.includes(example.category.toLowerCase())) { score += 4; } // Learning objectives match for (const objective of example.learningObjectives) { if (objective.toLowerCase().includes(topic)) { score += 3; } } // Code content match (lower weight to avoid false positives) if (example.code.toLowerCase().includes(topic)) { score += 2; } // Note: Hard filters are now applied before calling this function return score; } /** * Generate a learning path based on the search results */ async function generateLearningPath(request, searchResults, knowledgeBase) { // Try to find existing learning paths that match the topic const learningPaths = knowledgeBase.getAllLearningPaths(); const matchingPath = findMatchingLearningPath(request.topic, learningPaths); if (matchingPath) { return matchingPath.steps.map(step => ({ step_number: step.stepNumber, title: step.title, description: step.description, example_ids: step.exampleIds, objectives: step.objectives })); } // Generate a custom learning path from search results return generateCustomLearningPath(searchResults.examples, request); } /** * Find a learning path that matches the topic */ function findMatchingLearningPath(topic, learningPaths) { const topicLower = topic.toLowerCase(); for (const path of learningPaths) { if (path.name.toLowerCase().includes(topicLower) || path.description.toLowerCase().includes(topicLower) || path.id.toLowerCase().includes(topicLower)) { return path; } } return null; } /** * Generate a custom learning path from examples */ function generateCustomLearningPath(examples, request) { if (examples.length === 0) { return []; } // Group examples by difficulty const beginnerExamples = examples.filter(e => e.difficulty === 'beginner'); const intermediateExamples = examples.filter(e => e.difficulty === 'intermediate'); const advancedExamples = examples.filter(e => e.difficulty === 'advanced'); const steps = []; let stepNumber = 1; // Add beginner step if we have beginner examples if (beginnerExamples.length > 0) { steps.push({ step_number: stepNumber++, title: `Introduction to ${request.topic}`, description: `Learn the basics of ${request.topic} with simple examples`, example_ids: beginnerExamples.slice(0, 3).map(e => e.id), objectives: [ `Understand basic ${request.topic} concepts`, 'Practice with simple examples', 'Build foundational knowledge' ] }); } // Add intermediate step if we have intermediate examples if (intermediateExamples.length > 0) { steps.push({ step_number: stepNumber++, title: `Intermediate ${request.topic}`, description: `Explore more complex ${request.topic} patterns and techniques`, example_ids: intermediateExamples.slice(0, 3).map(e => e.id), objectives: [ `Apply ${request.topic} in practical scenarios`, 'Understand common patterns and best practices', 'Handle more complex use cases' ] }); } // Add advanced step if we have advanced examples if (advancedExamples.length > 0) { steps.push({ step_number: stepNumber++, title: `Advanced ${request.topic}`, description: `Master advanced ${request.topic} techniques and optimization`, example_ids: advancedExamples.slice(0, 3).map(e => e.id), objectives: [ `Implement complex ${request.topic} solutions`, 'Optimize for performance and maintainability', 'Understand edge cases and advanced patterns' ] }); } return steps; } /** * Get additional educational resources */ function getAdditionalResources(_topic, category) { const resources = [ { type: 'reference', title: 'MUSHCode.com Archive', url: 'https://mushcode.com', description: 'Comprehensive archive of MUSHCODE examples and tutorials' }, { type: 'documentation', title: 'PennMUSH Function Reference', url: 'https://pennmush.org/help', description: 'Official PennMUSH function documentation' }, { type: 'community', title: 'MUSH Development Community', url: 'https://reddit.com/r/MUD', description: 'Community discussions and help for MUD development' } ]; // Add category-specific resources if (category === 'building') { resources.push({ type: 'tutorial', title: 'MUSH Building Guide', url: 'https://mushcode.com/Category/Building', description: 'Comprehensive guide to building in MUSH environments' }); } else if (category === 'administration') { resources.push({ type: 'tutorial', title: 'MUSH Administration Best Practices', url: 'https://mushcode.com/Category/Administration', description: 'Best practices for MUSH administration and management' }); } else if (category === 'functions') { resources.push({ type: 'reference', title: 'MUSHCODE Function Library', url: 'https://mushcode.com/Category/Functions', description: 'Complete reference of MUSHCODE functions and utilities' }); } return resources; } /** * Build list of filters that were applied */ function buildFiltersApplied(request) { const filters = []; if (request.difficulty) { filters.push(`difficulty: ${request.difficulty}`); } if (request.serverType) { filters.push(`server: ${request.serverType}`); } if (request.category) { filters.push(`category: ${request.category}`); } filters.push(`max_results: ${request.maxResults}`); return filters; } //# sourceMappingURL=examples.js.map