install_content
Add AI customization elements to your local portfolio using specified paths. Supports downloading personas, skills, templates, or agents for enhanced AI functionality and management.
Instructions
Install AI customization elements from the collection to your local portfolio. Use this when users ask to download/install any element type (personas, skills, templates, or agents). Examples: 'install the creative writer persona', 'get the code review skill', 'download the meeting notes template'.
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| path | Yes | The collection path to the AI customization element. Format: 'library/[type]/[element].md' where type is personas, skills, templates, or agents. Example: 'library/skills/code-review.md'. |
Implementation Reference
- src/types/mcp.ts:43-45 (schema)Zod schema defining the input arguments for the install_content tool: an object with a 'path' string parameter.export const InstallContentArgsSchema = z.object({ path: z.string().describe("Path to the content file in the collection repository") });
- Core handler function installContent that executes the installation logic from a collection path by delegating to installFromCollection after security validation.async installContent(inputPath: string): Promise<{ success: boolean; message: string; metadata?: IElementMetadata; elementType?: ElementType; filename?: string; }> { return await this.installFromCollection(inputPath); }
- Supporting method installFromCollection containing the detailed logic for fetching, validating, and atomically writing content from the collection.private async installFromCollection(collectionPath: string): Promise<InstallResult> { // ENHANCEMENT (PR #1453): Log collection installation attempt logger.debug('Installing from collection', { collectionPath }); // SECURITY: Validate and sanitize the input path first const sanitizedPath = validatePath(collectionPath); const elementType = this.validateAndExtractElementType(sanitizedPath); // STEP 1: FETCH CONTENT INTO MEMORY (NO DISK OPERATIONS YET) const content = await this.fetchCollectionContent(sanitizedPath); // STEP 2: PERFORM ALL VALIDATION BEFORE ANY DISK OPERATIONS const { sanitizedContent, metadata } = await this.validateCollectionContent(content); // STEP 3: PREPARE FILE PATH AND CHECK EXISTENCE // FIX (SonarCloud L704): Remove unused elementDir variable const { filename, localPath } = this.prepareFilePath(sanitizedPath, elementType); // SECURITY: Check if file already exists before any write operations const existsResult = await this.checkFileExists(localPath, filename); if (existsResult) { logger.debug('Element already exists in collection', { filename, elementType }); return existsResult; } // STEP 4: ALL VALIDATION COMPLETE - NOW PERFORM ATOMIC WRITE OPERATION await this.atomicWriteFile(localPath, sanitizedContent); // ENHANCEMENT (PR #1453): Log successful installation logger.debug('Element installed successfully from collection', { elementName: metadata.name, elementType, filename, localPath }); return { success: true, message: `AI customization element installed successfully!`, metadata, filename, elementType }; }
- src/server/tools/CollectionTools.ts:114-129 (registration)Tool registration for install_collection_content (noted as renamed from install_content in tests), which calls server.installContent - the entry point for the tool logic.tool: { name: "install_collection_content", description: "Install AI customization elements FROM the DollhouseMCP collection TO your local portfolio. Use this when users ask to download/install any element type (personas, skills, templates, agents, or memories) from the collection. Examples: 'install the creative writer persona from the collection', 'get the code review skill from collection', 'download the meeting notes template from collection', 'get the project context memory from collection'.", inputSchema: { type: "object", properties: { path: { type: "string", description: "The collection path to the AI customization element. Format: 'library/[type]/[element].md' where type is personas, skills, templates, agents, or memories. Example: 'library/skills/code-review.md'.", }, }, required: ["path"], }, }, handler: (args: any) => server.installContent(args.path) },
- Security-critical helper for atomic file writes with guaranteed cleanup to prevent partial malicious content persistence.private async atomicWriteFile(destination: string, content: string): Promise<void> { // Generate unique temporary file name to avoid collisions const tempFile = `${destination}.tmp.${Date.now()}.${Math.random().toString(36).substring(2)}`; try { // SECURITY: Write to temporary file first // If this fails, no files are left on disk await fs.writeFile(tempFile, content, 'utf-8'); // SECURITY: Atomic rename operation // On most filesystems, rename is atomic - the file appears with complete content // or doesn't appear at all. This prevents partial file corruption. await fs.rename(tempFile, destination); } catch (error) { // SECURITY: Guaranteed cleanup of temporary file on ANY failure // This ensures no temporary files are left behind even if the // rename operation fails after successful write try { await fs.unlink(tempFile); } catch (cleanupError) { // Ignore cleanup errors - the file may not exist if writeFile failed // The original error is more important to propagate } // Re-throw the original error to maintain error handling semantics throw error; } }