@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
JavaScript
/**
* 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=