UNPKG

@mickdarling/dollhousemcp

Version:

DollhouseMCP - A Model Context Protocol (MCP) server that enables dynamic AI persona management from markdown files, allowing Claude and other compatible AI assistants to activate and switch between different behavioral personas.

100 lines 14.4 kB
/** * Install personas from marketplace */ import * as fs from 'fs/promises'; import * as path from 'path'; import { validatePath, validateFilename, validateContentSize } from '../security/InputValidator.js'; import { SECURITY_LIMITS } from '../security/constants.js'; import { ContentValidator } from '../security/contentValidator.js'; import { SecureYamlParser } from '../security/secureYamlParser.js'; import { SecurityError } from '../errors/SecurityError.js'; export class PersonaInstaller { githubClient; personasDir; baseUrl = 'https://api.github.com/repos/DollhouseMCP/personas/contents/personas'; constructor(githubClient, personasDir) { this.githubClient = githubClient; this.personasDir = personasDir; } /** * Install a persona from the marketplace */ async installPersona(inputPath) { // Validate and sanitize the input path const sanitizedPath = validatePath(inputPath); // Ensure the path ends with .md if (!sanitizedPath.endsWith('.md')) { throw new Error('Invalid file type. Only .md files are allowed.'); } const url = `${this.baseUrl}/${sanitizedPath}`; const data = await this.githubClient.fetchFromGitHub(url); if (data.type !== 'file') { throw new Error('Path does not point to a file'); } // Check file size before downloading if (data.size > SECURITY_LIMITS.MAX_PERSONA_SIZE_BYTES) { throw new Error(`File too large (${data.size} bytes, max ${SECURITY_LIMITS.MAX_PERSONA_SIZE_BYTES} bytes)`); } // Decode Base64 content const content = Buffer.from(data.content, 'base64').toString('utf-8'); // Validate content size after decoding validateContentSize(content, SECURITY_LIMITS.MAX_PERSONA_SIZE_BYTES); // Sanitize content for security threats const sanitizedContent = ContentValidator.sanitizePersonaContent(content); // Use secure YAML parser let parsed; try { parsed = SecureYamlParser.safeMatter(sanitizedContent); } catch (error) { if (error instanceof SecurityError) { throw new Error(`Security threat in persona: ${error.message}`); } throw error; } const metadata = parsed.data; // Additional metadata validation for injection attacks const metadataValidation = ContentValidator.validateMetadata(metadata); if (!metadataValidation.isValid) { throw new Error(`Security validation failed: ${metadataValidation.detectedPatterns?.join(', ')}`); } // Validate metadata if (!metadata.name || !metadata.description) { throw new Error('Invalid persona: missing required name or description'); } // Generate and validate local filename const originalFilename = sanitizedPath.split('/').pop() || 'downloaded-persona.md'; const filename = validateFilename(originalFilename); const localPath = path.join(this.personasDir, filename); // Check if file already exists try { await fs.access(localPath); return { success: false, message: `Persona already exists: ${filename}\n\nUse \`reload_personas\` to refresh if you've updated it manually.` }; } catch { // File doesn't exist, proceed with installation } // Write the sanitized file await fs.writeFile(localPath, sanitizedContent, 'utf-8'); return { success: true, message: `Persona installed successfully!`, metadata, filename }; } /** * Format installation success message */ formatInstallSuccess(metadata, filename, totalPersonas, personaIndicator = '') { return `${personaIndicator}✅ **Persona Installed Successfully!**\n\n` + `🎭 **${metadata.name}** by ${metadata.author}\n` + `📁 Saved as: ${filename}\n` + `📊 Total personas: ${totalPersonas}\n\n` + `🎯 **Ready to use:** \`activate_persona "${metadata.name}"\``; } } //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiUGVyc29uYUluc3RhbGxlci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3NyYy9tYXJrZXRwbGFjZS9QZXJzb25hSW5zdGFsbGVyLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOztHQUVHO0FBRUgsT0FBTyxLQUFLLEVBQUUsTUFBTSxhQUFhLENBQUM7QUFDbEMsT0FBTyxLQUFLLElBQUksTUFBTSxNQUFNLENBQUM7QUFJN0IsT0FBTyxFQUFFLFlBQVksRUFBRSxnQkFBZ0IsRUFBRSxtQkFBbUIsRUFBRSxNQUFNLCtCQUErQixDQUFDO0FBQ3BHLE9BQU8sRUFBRSxlQUFlLEVBQUUsTUFBTSwwQkFBMEIsQ0FBQztBQUMzRCxPQUFPLEVBQUUsZ0JBQWdCLEVBQUUsTUFBTSxpQ0FBaUMsQ0FBQztBQUNuRSxPQUFPLEVBQUUsZ0JBQWdCLEVBQUUsTUFBTSxpQ0FBaUMsQ0FBQztBQUNuRSxPQUFPLEVBQUUsYUFBYSxFQUFFLE1BQU0sNEJBQTRCLENBQUM7QUFFM0QsTUFBTSxPQUFPLGdCQUFnQjtJQUNuQixZQUFZLENBQWU7SUFDM0IsV0FBVyxDQUFTO0lBQ3BCLE9BQU8sR0FBRyxzRUFBc0UsQ0FBQztJQUV6RixZQUFZLFlBQTBCLEVBQUUsV0FBbUI7UUFDekQsSUFBSSxDQUFDLFlBQVksR0FBRyxZQUFZLENBQUM7UUFDakMsSUFBSSxDQUFDLFdBQVcsR0FBRyxXQUFXLENBQUM7SUFDakMsQ0FBQztJQUVEOztPQUVHO0lBQ0gsS0FBSyxDQUFDLGNBQWMsQ0FBQyxTQUFpQjtRQU1wQyx1Q0FBdUM7UUFDdkMsTUFBTSxhQUFhLEdBQUcsWUFBWSxDQUFDLFNBQVMsQ0FBQyxDQUFDO1FBRTlDLGdDQUFnQztRQUNoQyxJQUFJLENBQUMsYUFBYSxDQUFDLFFBQVEsQ0FBQyxLQUFLLENBQUMsRUFBRSxDQUFDO1lBQ25DLE1BQU0sSUFBSSxLQUFLLENBQUMsZ0RBQWdELENBQUMsQ0FBQztRQUNwRSxDQUFDO1FBRUQsTUFBTSxHQUFHLEdB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TyxDQUFDLENBQUM7UUFFekQsT0FBTztZQUNMLE9BQU8sRUFBRSxJQUFJO1lBQ2IsT0FBTyxFQUFFLGlDQUFpQztZQUMxQyxRQUFRO1lBQ1IsUUFBUTtTQUNULENBQUM7SUFDSixDQUFDO0lBRUQ7O09BRUc7SUFDSCxvQkFBb0IsQ0FBQyxRQUF5QixFQUFFLFFBQWdCLEVBQUUsYUFBcUIsRUFBRSxtQkFBMkIsRUFBRTtRQUNwSCxPQUFPLEdBQUcsZ0JBQWdCLDJDQUEyQztZQUNuRSxRQUFRLFFBQVEsQ0FBQyxJQUFJLFNBQVMsUUFBUSxDQUFDLE1BQU0sSUFBSTtZQUNqRCxnQkFBZ0IsUUFBUSxJQUFJO1lBQzVCLHNCQUFzQixhQUFhLE1BQU07WUFDekMsNENBQTRDLFFBQVEsQ0FBQyxJQUFJLEtBQUssQ0FBQztJQUNuRSxDQUFDO0NBQ0YiLCJzb3VyY2VzQ29udGVudCI6WyIvKipcbiAqIEluc3RhbGwgcGVyc29uYXMgZnJvbSBtYXJrZXRwbGFjZVxuICovXG5cbmltcG9ydCAqIGFzIGZzIGZyb20gJ2ZzL3Byb21pc2VzJztcbmltcG9ydCAqIGFzIHBhdGggZnJvbSAncGF0aCc7XG5pbXBvcnQgbWF0dGVyIGZyb20gJ2dyYXktbWF0dGVyJztcbmltcG9ydCB7IEdpdEh1YkNsaWVudCB9IGZyb20gJy4vR2l0SHViQ2xpZW50LmpzJztcbmltcG9ydCB7IFBlcnNvbmFNZXRhZGF0YSB9IGZyb20gJy4uL3R5cGVzL3BlcnNvbmEuanMnO1xuaW1wb3J0IHsgdmFsaWRhdGVQYXRoLCB2YWxpZGF0ZUZpbGVuYW1lLCB2YWxpZGF0ZUNvbnRlbnRTaXplIH0gZnJvbSAnLi4vc2VjdXJpdHkvSW5wdXRWYWxpZGF0b3IuanMnO1xuaW1wb3J0IHsgU0VDVVJJVFlfTElNSVRTIH0gZnJvbSAnLi4vc2VjdXJpdHkvY29uc3RhbnRzLmpzJztcbmltcG9ydCB7IENvbnRlbnRWYWxpZGF0b3IgfSBmcm9tICcuLi9zZWN1cml0eS9jb250ZW50VmFsaWRhdG9yLmpzJztcbmltcG9ydCB7IFNlY3VyZVlhbWxQYXJzZXIgfSBmcm9tICcuLi9zZWN1cml0eS9zZWN1cmVZYW1sUGFyc2VyLmpzJztcbmltcG9ydCB7IFNlY3VyaXR5RXJyb3IgfSBmcm9tICcuLi9lcnJvcnMvU2VjdXJpdHlFcnJvci5qcyc7XG5cbmV4cG9ydCBjbGFzcyBQZXJzb25hSW5zdGFsbGVyIHtcbiAgcHJpdmF0ZSBnaXRodWJDbGllbnQ6IEdpdEh1YkNsaWVudDtcbiAgcHJpdmF0ZSBwZXJzb25hc0Rpcjogc3RyaW5nO1xuICBwcml2YXRlIGJhc2VVcmwgPSAnaHR0cHM6Ly9hcGkuZ2l0aHViLmNvbS9yZXBvcy9Eb2xsaG91c2VNQ1AvcGVyc29uYXMvY29udGVudHMvcGVyc29uYXMnO1xuICBcbiAgY29uc3RydWN0b3IoZ2l0aHViQ2xpZW50OiBHaXRIdWJDbGllbnQsIHBlcnNvbmFzRGlyOiBzdHJpbmcpIHtcbiAgICB0aGlzLmdpdGh1YkNsaWVudCA9IGdpdGh1YkNsaWVudDtcbiAgICB0aGlzLnBlcnNvbmFzRGlyID0gcGVyc29uYXNEaXI7XG4gIH1cbiAgXG4gIC8qKlxuICAgKiBJbnN0YWxsIGEgcGVyc29uYSBmcm9tIHRoZSBtYXJrZXRwbGFjZVxuICAgKi9cbiAgYXN5bmMgaW5zdGFsbFBlcnNvbmEoaW5wdXRQYXRoOiBzdHJpbmcpOiBQcm9taXNlPHsgXG4gICAgc3VjY2VzczogYm9vbGVhbjsgXG4gICAgbWVzc2FnZTogc3RyaW5nO1xuICAgIG1ldGFkYXRhPzogUGVyc29uYU1ldGFkYXRhO1xuICAgIGZpbGVuYW1lPzogc3RyaW5nO1xuICB9PiB7XG4gICAgLy8gVmFsaWRhdGUgYW5kIHNhbml0aXplIHRoZSBpbnB1dCBwYXRoXG4gICAgY29uc3Qgc2FuaXRpemVkUGF0aCA9IHZhbGlkYXRlUGF0aChpbnB1dFBhdGgpO1xuICAgIFxuICAgIC8vIEVuc3VyZSB0aGUgcGF0aCBlbmRzIHdpdGggLm1kXG4gICAgaWYgKCFzYW5pdGl6ZWRQYXRoLmVuZHNXaXRoKCcubWQnKSkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKCdJbnZhbGlkIGZpbGUgdHlwZS4gT25seSAubWQgZmlsZXMgYXJlIGFsbG93ZWQuJyk7XG4gICAgfVxuICAgIFxuICAgIGNvbnN0IHVybCA9IGAke3RoaXMuYmFzZVVybH0vJHtzYW5pdGl6ZWRQYXRofWA7XG4gICAgY29uc3QgZGF0YSA9IGF3YWl0IHRoaXMuZ2l0aHViQ2xpZW50LmZldGNoRnJvbUdpdEh1Yih1cmwpO1xuICAgIFxuICAgIGlmIChkYXRhLnR5cGUgIT09ICdmaWxlJykge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKCdQYXRoIGRvZXMgbm90IHBvaW50IHRvIGEgZmlsZScpO1xuICAgIH1cbiAgICBcbiAgICAvLyBDaGVjayBmaWxlIHNpemUgYmVmb3JlIGRvd25sb2FkaW5nXG4gICAgaWYgKGRhdGEuc2l6ZSA+IFNFQ1VSSVRZX0xJTUlUUy5NQVhfUEVSU09OQV9TSVpFX0JZVEVTKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoYEZpbGUgdG9vIGxhcmdlICgke2RhdGEuc2l6ZX0gYnl0ZXMsIG1heCAke1NFQ1VSSVRZX0xJTUlUUy5NQVhfUEVSU09OQV9TSVpFX0JZVEVTfSBieXRlcylgKTtcbiAgICB9XG4gICAgXG4gICAgLy8gRGVjb2RlIEJhc2U2NCBjb250ZW50XG4gICAgY29uc3QgY29udGVudCA9IEJ1ZmZlci5mcm9tKGRhdGEuY29udGVudCwgJ2Jhc2U2NCcpLnRvU3RyaW5nKCd1dGYtOCcpO1xuICAgIFxuICAgIC8vIFZhbGlkYXRlIGNvbnRlbnQgc2l6ZSBhZnRlciBkZWNvZGluZ1xuICAgIHZhbGlkYXRlQ29udGVudFNpemUoY29udGVudCwgU0VDVVJJVFlfTElNSVRTLk1BWF9QRVJTT05BX1NJWkVfQllURVMpO1xuICAgIFxuICAgIC8vIFNhbml0aXplIGNvbnRlbnQgZm9yIHNlY3VyaXR5IHRocmVhdHNcbiAgICBjb25zdCBzYW5pdGl6ZWRDb250ZW50ID0gQ29udGVudFZhbGlkYXRvci5zYW5pdGl6ZVBlcnNvbmFDb250ZW50KGNvbnRlbnQpO1xuICAgIFxuICAgIC8vIFVzZSBzZWN1cmUgWUFNTCBwYXJzZXJcbiAgICBsZXQgcGFyc2VkO1xuICAgIHRyeSB7XG4gICAgICBwYXJzZWQgPSBTZWN1cmVZYW1sUGFyc2VyLnNhZmVNYXR0ZXIoc2FuaXRpemVkQ29udGVudCk7XG4gICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgIGlmIChlcnJvciBpbnN0YW5jZW9mIFNlY3VyaXR5RXJyb3IpIHtcbiAgICAgICAgdGhyb3cgbmV3IEVycm9yKGBTZWN1cml0eSB0aHJlYXQgaW4gcGVyc29uYTogJHtlcnJvci5tZXNzYWdlfWApO1xuICAgICAgfVxuICAgICAgdGhyb3cgZXJyb3I7XG4gICAgfVxuICAgIFxuICAgIGNvbnN0IG1ldGFkYXRhID0gcGFyc2VkLmRhdGEgYXMgUGVyc29uYU1ldGFkYXRhO1xuICAgIFxuICAgIC8vIEFkZGl0aW9uYWwgbWV0YWRhdGEgdmFsaWRhdGlvbiBmb3IgaW5qZWN0aW9uIGF0dGFja3NcbiAgICBjb25zdCBtZXRhZGF0YVZhbGlkYXRpb24gPSBDb250ZW50VmFsaWRhdG9yLnZhbGlkYXRlTWV0YWRhdGEobWV0YWRhdGEpO1xuICAgIGlmICghbWV0YWRhdGFWYWxpZGF0aW9uLmlzVmFsaWQpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcihgU2VjdXJpdHkgdmFsaWRhdGlvbiBmYWlsZWQ6ICR7bWV0YWRhdGFWYWxpZGF0aW9uLmRldGVjdGVkUGF0dGVybnM/LmpvaW4oJywgJyl9YCk7XG4gICAgfVxuICAgIFxuICAgIC8vIFZhbGlkYXRlIG1ldGFkYXRhXG4gICAgaWYgKCFtZXRhZGF0YS5uYW1lIHx8ICFtZXRhZGF0YS5kZXNjcmlwdGlvbikge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKCdJbnZhbGlkIHBlcnNvbmE6IG1pc3NpbmcgcmVxdWlyZWQgbmFtZSBvciBkZXNjcmlwdGlvbicpO1xuICAgIH1cbiAgICBcbiAgICAvLyBHZW5lcmF0ZSBhbmQgdmFsaWRhdGUgbG9jYWwgZmlsZW5hbWVcbiAgICBjb25zdCBvcmlnaW5hbEZpbGVuYW1lID0gc2FuaXRpemVkUGF0aC5zcGxpdCgnLycpLnBvcCgpIHx8ICdkb3dubG9hZGVkLXBlcnNvbmEubWQnO1xuICAgIGNvbnN0IGZpbGVuYW1lID0gdmFsaWRhdGVGaWxlbmFtZShvcmlnaW5hbEZpbGVuYW1lKTtcbiAgICBjb25zdCBsb2NhbFBhdGggPSBwYXRoLmpvaW4odGhpcy5wZXJzb25hc0RpciwgZmlsZW5hbWUpO1xuICAgIFxuICAgIC8vIENoZWNrIGlmIGZpbGUgYWxyZWFkeSBleGlzdHNcbiAgICB0cnkge1xuICAgICAgYXdhaXQgZnMuYWNjZXNzKGxvY2FsUGF0aCk7XG4gICAgICByZXR1cm4ge1xuICAgICAgICBzdWNjZXNzOiBmYWxzZSxcbiAgICAgICAgbWVzc2FnZTogYFBlcnNvbmEgYWxyZWFkeSBleGlzdHM6ICR7ZmlsZW5hbWV9XFxuXFxuVXNlIFxcYHJlbG9hZF9wZXJzb25hc1xcYCB0byByZWZyZXNoIGlmIHlvdSd2ZSB1cGRhdGVkIGl0IG1hbnVhbGx5LmBcbiAgICAgIH07XG4gICAgfSBjYXRjaCB7XG4gICAgICAvLyBGaWxlIGRvZXNuJ3QgZXhpc3QsIHByb2NlZWQgd2l0aCBpbnN0YWxsYXRpb25cbiAgICB9XG4gICAgXG4gICAgLy8gV3JpdGUgdGhlIHNhbml0aXplZCBmaWxlXG4gICAgYXdhaXQgZnMud3JpdGVGaWxlKGxvY2FsUGF0aCwgc2FuaXRpemVkQ29udGVudCwgJ3V0Zi04Jyk7XG4gICAgXG4gICAgcmV0dXJuIHtcbiAgICAgIHN1Y2Nlc3M6IHRydWUsXG4gICAgICBtZXNzYWdlOiBgUGVyc29uYSBpbnN0YWxsZWQgc3VjY2Vzc2Z1bGx5IWAsXG4gICAgICBtZXRhZGF0YSxcbiAgICAgIGZpbGVuYW1lXG4gICAgfTtcbiAgfVxuICBcbiAgLyoqXG4gICAqIEZvcm1hdCBpbnN0YWxsYXRpb24gc3VjY2VzcyBtZXNzYWdlXG4gICAqL1xuICBmb3JtYXRJbnN0YWxsU3VjY2VzcyhtZXRhZGF0YTogUGVyc29uYU1ldGFkYXRhLCBmaWxlbmFtZTogc3RyaW5nLCB0b3RhbFBlcnNvbmFzOiBudW1iZXIsIHBlcnNvbmFJbmRpY2F0b3I6IHN0cmluZyA9ICcnKTogc3RyaW5nIHtcbiAgICByZXR1cm4gYCR7cGVyc29uYUluZGljYXRvcn3inIUgKipQZXJzb25hIEluc3RhbGxlZCBTdWNjZXNzZnVsbHkhKipcXG5cXG5gICtcbiAgICAgIGDwn46tICoqJHttZXRhZGF0YS5uYW1lfSoqIGJ5ICR7bWV0YWRhdGEuYXV0aG9yfVxcbmAgK1xuICAgICAgYPCfk4EgU2F2ZWQgYXM6ICR7ZmlsZW5hbWV9XFxuYCArXG4gICAgICBg8J+TiiBUb3RhbCBwZXJzb25hczogJHt0b3RhbFBlcnNvbmFzfVxcblxcbmAgK1xuICAgICAgYPCfjq8gKipSZWFkeSB0byB1c2U6KiogXFxgYWN0aXZhdGVfcGVyc29uYSBcIiR7bWV0YWRhdGEubmFtZX1cIlxcYGA7XG4gIH1cbn0iXX0=