init_portfolio
Initialize a GitHub repository for organizing and storing DollhouseMCP AI persona elements with proper directory structure and documentation.
Instructions
Initialize a new GitHub portfolio repository for storing your DollhouseMCP elements. Creates the repository structure with proper directories and README.
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| repository_name | No | Name for the portfolio repository. Defaults to 'dollhouse-portfolio' if not specified. | |
| private | No | Whether to create a private repository. Defaults to false (public). | |
| description | No | Repository description. Defaults to 'My DollhouseMCP element portfolio'. |
Implementation Reference
- src/server/tools/PortfolioTools.ts:76-102 (registration)MCP tool registration for 'init_portfolio' including name, description, input schema, and handler function that maps args to server.initPortfolio call.tool: { name: "init_portfolio", description: "Initialize a new GitHub portfolio repository for storing your DollhouseMCP elements. Creates the repository structure with proper directories and README.", inputSchema: { type: "object", properties: { repository_name: { type: "string", description: "Name for the portfolio repository. Defaults to 'dollhouse-portfolio' if not specified.", }, private: { type: "boolean", description: "Whether to create a private repository. Defaults to false (public).", }, description: { type: "string", description: "Repository description. Defaults to 'My DollhouseMCP element portfolio'.", }, }, }, }, handler: (args: InitPortfolioArgs) => server.initPortfolio({ repositoryName: args?.repository_name, private: args?.private, description: args?.description }) },
- TypeScript interface defining input arguments for the init_portfolio tool.interface InitPortfolioArgs { repository_name?: string; private?: boolean; description?: string; }
- Core implementation logic for creating the GitHub portfolio repository, called by server.initPortfolio. Handles consent, repo creation via GitHub API, and structure initialization.async createPortfolio(username: string, consent: boolean | undefined): Promise<string> { // MEDIUM FIX: Normalize username to prevent Unicode attacks (DMCP-SEC-004) const normalizedUsername = UnicodeValidator.normalize(username).normalizedContent; // CRITICAL: Validate consent is explicitly provided if (consent === undefined) { throw new Error('Consent is required for portfolio creation'); } if (!consent) { logger.info(`User declined portfolio creation for ${username}`); throw new Error('User declined portfolio creation'); } // Log consent for audit trail logger.info(`User consented to portfolio creation for ${normalizedUsername}`); // LOW FIX: Add security audit logging (DMCP-SEC-006) SecurityMonitor.logSecurityEvent({ type: 'PORTFOLIO_INITIALIZATION', severity: 'LOW', source: 'PortfolioRepoManager.createPortfolio', details: `User ${normalizedUsername} consented to portfolio creation`, metadata: { username: normalizedUsername } }); // Check if portfolio already exists const existingRepo = await this.githubRequest( `/repos/${normalizedUsername}/${this.repositoryName}` ); if (existingRepo && existingRepo.html_url) { logger.info(`Portfolio already exists for ${normalizedUsername}`); return existingRepo.html_url; } // Create the portfolio repository try { const repo = await this.githubRequest( '/user/repos', 'POST', { name: this.repositoryName, description: PortfolioRepoManager.DEFAULT_DESCRIPTION, private: false, auto_init: true } ); // Initialize portfolio structure await this.generatePortfolioStructure(normalizedUsername); return repo.html_url; } catch (error: any) { // Handle race condition: if repository was created between our check and creation attempt if (error.message && error.message.includes('name already exists')) { logger.info(`Portfolio repository already exists for ${normalizedUsername} (race condition handled)`); // Re-check for the existing repository and return its URL try { const existingRepo = await this.githubRequest( `/repos/${normalizedUsername}/${this.repositoryName}` ); if (existingRepo && existingRepo.html_url) { return existingRepo.html_url; } } catch (recheckError) { ErrorHandler.logError('PortfolioRepoManager.recheckExistingRepo', recheckError, { username: normalizedUsername }); } // If we can't get the existing repo, throw a more specific error throw new Error(`Portfolio repository already exists for ${normalizedUsername}. Please check your GitHub account.`); } ErrorHandler.logError('PortfolioRepoManager.createPortfolioRepo', error, { username: normalizedUsername }); throw ErrorHandler.wrapError(error, `Failed to create portfolio repository for ${normalizedUsername}. ${error.message || 'Unknown error occurred.'}`, ErrorCategory.NETWORK_ERROR); } }
- src/server/ServerSetup.ts:68-70 (registration)Registration of all portfolio tools (including init_portfolio) into the ToolRegistry during server setup.// Portfolio tools (including sync_portfolio with new safety features) this.toolRegistry.registerMany(getPortfolioTools(instance));
- src/server/types.ts:68-69 (schema)IToolHandler interface defining the server.initPortfolio method signature used by the tool handler.initPortfolio(options: {repositoryName?: string; private?: boolean; description?: string}): Promise<any>; portfolioConfig(options: {autoSync?: boolean; defaultVisibility?: string; autoSubmit?: boolean; repositoryName?: string}): Promise<any>;