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.
594 lines • 27.2 kB
JavaScript
/**
* MUSHCODE code explanation engine
*/
import { ValidationError } from '../utils/errors.js';
export class MushcodeExplainer {
knowledgeBase;
constructor(knowledgeBase) {
this.knowledgeBase = knowledgeBase;
}
/**
* Explain MUSHCODE functionality
*/
async explain(request) {
this.validateRequest(request);
// Get server dialect information if specified
const dialect = request.serverType ?
this.knowledgeBase.dialects.get(request.serverType) :
null;
// Parse the code into sections
const codeSections = this.parseCodeSections(request.code);
// Analyze each section
const codeBreakdown = await this.analyzeCodeSections(codeSections, request, dialect);
// Generate overall explanation
const explanation = this.generateOverallExplanation(codeBreakdown, request);
// Extract concepts used
const conceptsUsed = this.extractConcepts(codeBreakdown);
// Find related examples
const relatedExamples = await this.findRelatedExamples(conceptsUsed, request);
// Determine difficulty level
const difficultyLevel = this.determineDifficultyLevel(codeBreakdown);
// Generate learning resources
const learningResources = this.generateLearningResources(conceptsUsed, request);
// Extract security considerations
const securityConsiderations = this.extractSecurityConsiderations(codeBreakdown);
// Extract performance notes
const performanceNotes = this.extractPerformanceNotes(codeBreakdown, request);
const result = {
explanation,
codeBreakdown,
conceptsUsed,
relatedExamples,
difficultyLevel
};
if (learningResources.length > 0) {
result.learningResources = learningResources;
}
if (securityConsiderations.length > 0) {
result.securityConsiderations = securityConsiderations;
}
if (performanceNotes.length > 0) {
result.performanceNotes = performanceNotes;
}
return result;
}
/**
* Validate the explanation request
*/
validateRequest(request) {
if (!request.code || request.code.trim().length === 0) {
throw new ValidationError('Code is required');
}
if (request.code.length > 10000) {
throw new ValidationError('Code is too long (max 10000 characters)');
}
if (request.serverType && !this.knowledgeBase.dialects.has(request.serverType)) {
throw new ValidationError(`Unknown server type: ${request.serverType}`);
}
const validDetailLevels = ['basic', 'intermediate', 'advanced'];
if (!validDetailLevels.includes(request.detailLevel)) {
throw new ValidationError(`Invalid detail level. Must be one of: ${validDetailLevels.join(', ')}`);
}
if (request.focusAreas) {
const validFocusAreas = ['syntax', 'logic', 'security', 'performance', 'best_practices', 'concepts'];
for (const area of request.focusAreas) {
if (!validFocusAreas.includes(area)) {
throw new ValidationError(`Invalid focus area: ${area}. Must be one of: ${validFocusAreas.join(', ')}`);
}
}
}
}
/**
* Parse code into logical sections
*/
parseCodeSections(code) {
const lines = code.split('\n');
const sections = [];
for (let i = 0; i < lines.length; i++) {
const line = lines[i];
if (line && line.trim().length > 0) {
sections.push({
lineNumber: i + 1,
code: line.trim()
});
}
}
return sections;
}
/**
* Analyze each code section
*/
async analyzeCodeSections(sections, request, dialect) {
const analyzedSections = [];
for (const section of sections) {
const analysis = await this.analyzeCodeSection(section, request, dialect);
analyzedSections.push(analysis);
}
return analyzedSections;
}
/**
* Analyze a single code section
*/
async analyzeCodeSection(section, request, dialect) {
const code = section.code;
const concepts = [];
const functions = [];
const securityNotes = [];
// Identify MUSHCODE functions and concepts
const identifiedFunctions = this.identifyFunctions(code, dialect);
functions.push(...identifiedFunctions);
// Identify concepts based on code patterns
const identifiedConcepts = this.identifyConcepts(code);
concepts.push(...identifiedConcepts);
// Check for security considerations
const securityIssues = this.checkSecurity(code);
securityNotes.push(...securityIssues);
// Generate explanation based on detail level
const context = {
functions: identifiedFunctions,
concepts: identifiedConcepts
};
if (request.focusAreas !== undefined) {
context.focusAreas = request.focusAreas;
}
if (dialect !== undefined) {
context.dialect = dialect;
}
const explanation = this.generateSectionExplanation(code, request.detailLevel, context);
// Determine complexity
const complexity = this.determineComplexity(code, identifiedFunctions, identifiedConcepts);
const result = {
lineNumber: section.lineNumber,
code,
explanation,
concepts,
complexity
};
if (functions.length > 0) {
result.functions = functions;
}
if (securityNotes.length > 0) {
result.securityNotes = securityNotes;
}
return result;
}
/**
* Identify MUSHCODE functions in code
*/
identifyFunctions(code, dialect) {
const functions = [];
// Common MUSHCODE function patterns
const functionPatterns = [
/\b(switch|if|iter|map|filter|fold|setq|r|v|get|set|add|sub|mul|div|mod|eq|neq|lt|gt|lte|gte|and|or|not|strlen|mid|left|right|words|first|rest|last|match|grab|extract|sort|shuffle|reverse|unique|union|intersect|diff|member|index|replace|edit|tr|secure|escape|unescape|encrypt|decrypt|hash|rand|die|time|secs|convsecs|convtime|strftime|gmtime|localtime|tz|mudname|version|config|default|null|space|repeat|center|ljust|rjust|wrap|columns|table|ansi|strip|stripansi|accent|unaccent|art|beep|conn|doing|finger|fullname|hasflag|haspower|hastype|idle|last|locate|lwho|money|name|num|objeval|owner|parent|pmatch|powers|room|type|where|who|zone|create|destroy|clone|dig|open|link|unlink|lock|unlock|set|wipe|mvattr|cpattr|lattr|nattr|hasattr|get_eval|u|ulocal|ufun|ulambda|udefault|trigger|pemit|remit|oemit|emit|say|pose|semipose|think|page|mail|channel|chat|addcom|delcom|comlist|comtitle|alias|unalias|home|move|teleport|go|look|examine|inventory|score|quit|who|doing|page|whisper|say|pose|think|help|news|events|motd|connect|disconnect|create|destroy|||||||||||||||||||||||||||||||||||||||||||||||||||||||)\b/gi
];
for (const pattern of functionPatterns) {
const matches = code.match(pattern);
if (matches) {
functions.push(...matches.map(match => match.toLowerCase()));
}
}
// Check dialect-specific functions
if (dialect) {
for (const func of dialect.functionLibrary) {
const funcPattern = new RegExp(`\\b${func.name}\\b`, 'gi');
if (funcPattern.test(code)) {
functions.push(func.name.toLowerCase());
}
}
}
return [...new Set(functions)]; // Remove duplicates
}
/**
* Identify MUSHCODE concepts in code
*/
identifyConcepts(code) {
const concepts = [];
// Pattern matching for different concepts
const conceptPatterns = [
{ pattern: /@\w+/, concept: 'commands' },
{ pattern: /&\w+/, concept: 'attributes' },
{ pattern: /%[0-9qrv]/, concept: 'registers' },
{ pattern: /%#|%!|%@|%\*|%\+/, concept: 'substitutions' },
{ pattern: /\$\w+/, concept: 'functions' },
{ pattern: /switch\(/, concept: 'conditional_logic' },
{ pattern: /iter\(/, concept: 'iteration' },
{ pattern: /setq\(/, concept: 'variable_assignment' },
{ pattern: /u\(/, concept: 'user_functions' },
{ pattern: /trigger\(/, concept: 'triggers' },
{ pattern: /pemit|remit|oemit/, concept: 'messaging' },
{ pattern: /lock\(|unlock\(/, concept: 'security' },
{ pattern: /create\(|destroy\(/, concept: 'object_management' },
{ pattern: /get\(|set\(/, concept: 'attribute_manipulation' },
{ pattern: /match\(|grab\(/, concept: 'pattern_matching' },
{ pattern: /ansi\(/, concept: 'formatting' },
{ pattern: /rand\(|die\(/, concept: 'randomization' },
{ pattern: /time\(|secs\(/, concept: 'time_functions' },
{ pattern: /strlen\(|mid\(|left\(|right\(/, concept: 'string_manipulation' },
{ pattern: /add\(|sub\(|mul\(|div\(/, concept: 'arithmetic' },
{ pattern: /eq\(|neq\(|lt\(|gt\(/, concept: 'comparison' },
{ pattern: /and\(|or\(|not\(/, concept: 'boolean_logic' }
];
for (const { pattern, concept } of conceptPatterns) {
if (pattern.test(code)) {
concepts.push(concept);
}
}
return [...new Set(concepts)]; // Remove duplicates
}
/**
* Check for security considerations
*/
checkSecurity(code) {
const securityNotes = [];
// Check against security rules
for (const rule of this.knowledgeBase.securityRules.values()) {
try {
const pattern = new RegExp(rule.pattern, 'gi');
if (pattern.test(code)) {
securityNotes.push(`${rule.severity.toUpperCase()}: ${rule.description} - ${rule.recommendation}`);
}
}
catch (error) {
// Skip invalid regex patterns
console.warn(`Invalid regex pattern in security rule ${rule.ruleId}: ${rule.pattern}`);
}
}
// Additional security checks
const securityPatterns = [
{ pattern: /|/i, note: 'CRITICAL: Administrative commands that affect server operation' },
{ pattern: //i, note: 'HIGH: force command can bypass normal security restrictions' },
{ pattern: /|/i, note: 'HIGH: Password manipulation requires careful permission checking' },
{ pattern: /eval\(|ufun\(/i, note: 'MEDIUM: Dynamic code execution - validate inputs carefully' },
{ pattern: /%#/i, note: 'LOW: Using %# executor reference - ensure proper permission checks' }
];
for (const { pattern, note } of securityPatterns) {
if (pattern.test(code)) {
securityNotes.push(note);
}
}
return securityNotes;
}
/**
* Generate explanation for a code section
*/
generateSectionExplanation(code, detailLevel, context) {
const explanationParts = [];
// Basic explanation
if (code.startsWith('@')) {
explanationParts.push(`This is a MUSHCODE command: ${code.split(' ')[0]}`);
}
else if (code.includes('(') && code.includes(')')) {
explanationParts.push('This line contains function calls that process data and return results.');
}
else if (code.startsWith('&')) {
explanationParts.push('This sets an attribute on an object to store data or code.');
}
else {
explanationParts.push('This is a MUSHCODE expression that performs operations.');
}
// Add function-specific explanations
if (context.functions.length > 0) {
const functionExplanations = this.getFunctionExplanations(context.functions, context.dialect);
if (functionExplanations.length > 0) {
explanationParts.push(`Functions used: ${functionExplanations.join(', ')}`);
}
}
// Add concept explanations based on detail level
if (detailLevel !== 'basic' && context.concepts.length > 0) {
const conceptExplanations = this.getConceptExplanations(context.concepts, detailLevel);
if (conceptExplanations.length > 0) {
explanationParts.push(conceptExplanations.join(' '));
}
}
// Add focus area specific explanations
if (context.focusAreas) {
const focusExplanations = this.getFocusAreaExplanations(code, context.focusAreas, detailLevel);
if (focusExplanations.length > 0) {
explanationParts.push(focusExplanations.join(' '));
}
}
return explanationParts.join(' ');
}
/**
* Get explanations for functions
*/
getFunctionExplanations(functions, dialect) {
const explanations = [];
const functionDescriptions = {
'switch': 'evaluates conditions and returns different results',
'if': 'performs conditional logic',
'iter': 'loops through a list of items',
'setq': 'stores a value in a register for later use',
'get': 'retrieves an attribute value from an object',
'set': 'assigns a value to an attribute',
'pemit': 'sends a private message to a player',
'remit': 'sends a message to all players in a room',
'u': 'calls a user-defined function',
'strlen': 'returns the length of a string',
'mid': 'extracts a substring from a string',
'add': 'performs addition',
'eq': 'tests for equality',
'and': 'performs logical AND operation',
'or': 'performs logical OR operation'
};
for (const func of functions) {
if (functionDescriptions[func]) {
explanations.push(`${func}() ${functionDescriptions[func]}`);
}
else if (dialect) {
// Check dialect-specific function library
const funcDef = dialect.functionLibrary.find(f => f.name.toLowerCase() === func);
if (funcDef) {
explanations.push(`${func}() ${funcDef.description}`);
}
}
}
return explanations;
}
/**
* Get explanations for concepts
*/
getConceptExplanations(concepts, detailLevel) {
const explanations = [];
const conceptDescriptions = {
'registers': {
'intermediate': 'Uses registers (%q, %r, %v) to store temporary values during execution.',
'advanced': 'Utilizes MUSHCODE registers for efficient data storage and manipulation, allowing complex calculations and data passing between function calls.'
},
'substitutions': {
'intermediate': 'Uses substitution patterns (%#, %!, etc.) to reference dynamic values.',
'advanced': 'Employs MUSHCODE substitution patterns for context-aware programming, enabling code that adapts to different execution environments.'
},
'conditional_logic': {
'intermediate': 'Implements conditional logic to make decisions based on data.',
'advanced': 'Uses sophisticated conditional branching to create complex decision trees and program flow control.'
},
'iteration': {
'intermediate': 'Loops through data to process multiple items.',
'advanced': 'Implements iterative processing patterns for efficient bulk data manipulation and transformation.'
},
'messaging': {
'intermediate': 'Sends messages to players or rooms.',
'advanced': 'Utilizes MUSHCODE messaging system for player communication and game event notification.'
},
'security': {
'intermediate': 'Implements security measures and permission checks.',
'advanced': 'Employs comprehensive security patterns including permission validation, input sanitization, and access control.'
}
};
for (const concept of concepts) {
const descriptions = conceptDescriptions[concept];
if (descriptions && descriptions[detailLevel]) {
explanations.push(descriptions[detailLevel]);
}
}
return explanations;
}
/**
* Get explanations for specific focus areas
*/
getFocusAreaExplanations(code, focusAreas, detailLevel) {
const explanations = [];
for (const area of focusAreas) {
switch (area) {
case 'syntax':
if (detailLevel !== 'basic') {
explanations.push('Syntax follows MUSHCODE conventions with function calls, attribute references, and command structures.');
}
break;
case 'logic':
if (code.includes('switch(') || code.includes('if(')) {
explanations.push('The logic flow uses conditional statements to control program execution.');
}
break;
case 'security':
if (code.includes('%#') || code.includes('lock(') || code.includes('@force') || code.includes('@shutdown')) {
explanations.push('Security considerations include permission checking and input validation.');
}
break;
case 'performance':
if (code.includes('iter(') || code.includes('map(')) {
explanations.push('Performance can be optimized by minimizing iterations and using efficient functions.');
}
break;
case 'best_practices':
explanations.push('Follows MUSHCODE best practices for readability and maintainability.');
break;
}
}
return explanations;
}
/**
* Determine complexity of a code section
*/
determineComplexity(code, functions, concepts) {
let complexityScore = 0;
// Function complexity (reduced weight)
complexityScore += functions.length * 0.5;
// Concept complexity (reduced weight)
complexityScore += concepts.length * 0.5;
// Nesting complexity
const nestingLevel = (code.match(/\(/g) || []).length;
complexityScore += nestingLevel * 0.5;
// Special patterns that increase complexity
if (code.includes('switch('))
complexityScore += 1;
if (code.includes('iter('))
complexityScore += 2;
if (code.includes('u('))
complexityScore += 1;
if (code.includes('eval('))
complexityScore += 3;
if (complexityScore <= 2)
return 'simple';
if (complexityScore <= 5)
return 'moderate';
return 'complex';
}
/**
* Generate overall explanation
*/
generateOverallExplanation(codeBreakdown, request) {
const parts = [];
// Determine overall purpose
const hasCommands = codeBreakdown.some(section => section.code.startsWith('@'));
const hasFunctions = codeBreakdown.some(section => section.functions && section.functions.length > 0);
const hasAttributes = codeBreakdown.some(section => section.code.startsWith('&'));
if (hasCommands) {
parts.push('This MUSHCODE contains administrative or action commands that perform operations on the MUD.');
}
else if (hasAttributes) {
parts.push('This MUSHCODE defines attributes that store data or executable code on objects.');
}
else if (hasFunctions) {
parts.push('This MUSHCODE consists of function calls that process data and return results.');
}
else {
parts.push('This MUSHCODE performs various operations and calculations.');
}
// Add complexity assessment
const complexSections = codeBreakdown.filter(section => section.complexity === 'complex').length;
const moderateSections = codeBreakdown.filter(section => section.complexity === 'moderate').length;
if (complexSections > 0) {
parts.push(`The code contains ${complexSections} complex section${complexSections > 1 ? 's' : ''} that require advanced MUSHCODE knowledge.`);
}
else if (moderateSections > 0) {
parts.push(`The code has ${moderateSections} moderately complex section${moderateSections > 1 ? 's' : ''} suitable for intermediate developers.`);
}
else {
parts.push('The code is relatively straightforward and suitable for beginners.');
}
// Add server-specific notes
if (request.serverType) {
parts.push(`This explanation is tailored for ${request.serverType} server dialect.`);
}
return parts.join(' ');
}
/**
* Extract all concepts used in the code
*/
extractConcepts(codeBreakdown) {
const allConcepts = new Set();
for (const section of codeBreakdown) {
for (const concept of section.concepts) {
allConcepts.add(concept);
}
}
return Array.from(allConcepts);
}
/**
* Find related examples
*/
async findRelatedExamples(concepts, request) {
if (!request.includeExamples) {
return [];
}
const examples = [];
// Search for examples that match the concepts
for (const concept of concepts.slice(0, 3)) { // Limit to top 3 concepts
const searchQuery = {
query: concept,
includeExamples: true,
includePatterns: false,
limit: 2
};
if (request.serverType !== undefined) {
searchQuery.serverType = request.serverType;
}
const searchResult = this.knowledgeBase.search(searchQuery);
for (const exampleMatch of searchResult.examples) {
const example = this.knowledgeBase.examples.get(exampleMatch.exampleId);
if (example) {
examples.push(`${example.title}: ${example.description}`);
}
}
}
return [...new Set(examples)]; // Remove duplicates
}
/**
* Determine overall difficulty level
*/
determineDifficultyLevel(codeBreakdown) {
const complexityScores = codeBreakdown.map(section => {
switch (section.complexity) {
case 'simple': return 1;
case 'moderate': return 2;
case 'complex': return 3;
default: return 1;
}
});
const averageComplexity = complexityScores.reduce((sum, score) => sum + score, 0) / complexityScores.length;
if (averageComplexity <= 1.3)
return 'beginner';
if (averageComplexity <= 2.3)
return 'intermediate';
return 'advanced';
}
/**
* Generate learning resources
*/
generateLearningResources(concepts, request) {
const resources = [];
// Add concept-specific resources
const resourceMap = {
'commands': 'MUSHCODE Command Reference - Learn about administrative and action commands',
'attributes': 'Attribute System Guide - Understanding object data storage',
'functions': 'Function Library - Complete reference of MUSHCODE functions',
'conditional_logic': 'Control Flow Tutorial - Mastering if/switch statements',
'iteration': 'Looping and Iteration - Processing lists and data sets',
'messaging': 'Player Communication - Sending messages and notifications',
'security': 'Security Best Practices - Protecting your MUSHCODE',
'registers': 'Register Usage Guide - Efficient data storage techniques'
};
for (const concept of concepts.slice(0, 5)) { // Limit to top 5 concepts
if (resourceMap[concept]) {
resources.push(resourceMap[concept]);
}
}
// Add general resources based on difficulty
if (request.detailLevel === 'basic') {
resources.push('MUSHCODE Basics - Getting started with MUD programming');
}
else if (request.detailLevel === 'advanced') {
resources.push('Advanced MUSHCODE Techniques - Expert-level programming patterns');
}
return resources;
}
/**
* Extract security considerations
*/
extractSecurityConsiderations(codeBreakdown) {
const considerations = new Set();
for (const section of codeBreakdown) {
if (section.securityNotes) {
for (const note of section.securityNotes) {
considerations.add(note);
}
}
}
return Array.from(considerations);
}
/**
* Extract performance notes
*/
extractPerformanceNotes(codeBreakdown, request) {
const notes = [];
if (!request.focusAreas || request.focusAreas.includes('performance')) {
const hasIteration = codeBreakdown.some(section => section.functions && section.functions.includes('iter'));
const hasNestedFunctions = codeBreakdown.some(section => (section.code.match(/\(/g) || []).length > 3);
if (hasIteration) {
notes.push('Consider optimizing iterations for better performance with large datasets.');
}
if (hasNestedFunctions) {
notes.push('Deeply nested functions may impact performance - consider breaking into smaller parts.');
}
const hasEval = codeBreakdown.some(section => section.functions && section.functions.includes('eval') ||
section.code.includes('eval('));
if (hasEval) {
notes.push('Dynamic evaluation can be slow - use sparingly and cache results when possible.');
}
}
return notes;
}
}
//# sourceMappingURL=explainer.js.map