rolldev_composer
Execute Composer commands such as install, update, or require packages inside the php-fpm container by specifying the project path and command.
Instructions
Run Composer commands inside the php-fpm container
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| project_path | Yes | Path to the project directory | |
| command | Yes | Composer command to execute (e.g., 'install', 'update', 'require symfony/console', 'require-commerce') | |
| save_output_to_file | No | Save full output to a log file for later investigation (useful for long output) |
Implementation Reference
- server.js:239-263 (registration)Tool registration for 'rolldev_composer' within the ListToolsRequestSchema handler. Defines the tool name, description, and input schema requiring 'project_path' (string) and 'command' (string), with optional 'save_output_to_file' (boolean, default false).
{ name: "rolldev_composer", description: "Run Composer commands inside the php-fpm container", inputSchema: { type: "object", properties: { project_path: { type: "string", description: "Path to the project directory", }, command: { type: "string", description: "Composer command to execute (e.g., 'install', 'update', 'require symfony/console', 'require-commerce')", }, save_output_to_file: { type: "boolean", description: "Save full output to a log file for later investigation (useful for long output)", default: false, }, }, required: ["project_path", "command"], }, }, - server.js:312-313 (handler)Handler dispatch: the 'rolldev_composer' case in the CallToolRequestSchema switch statement calls this.runComposer(request.params.arguments).
case "rolldev_composer": return await this.runComposer(request.params.arguments); - server.js:611-740 (handler)Actual implementation of the runComposer method. Validates inputs (project_path exists, command exists, directory exists), constructs and executes 'roll composer {command}' via executeCommand with a 10-minute timeout, formats response with output previews and optional log-file saving.
async runComposer(args) { const { project_path, command, save_output_to_file = false } = args; if (!project_path) { throw new Error("project_path is required"); } if (!command) { throw new Error("command is required"); } const normalizedProjectPath = project_path.replace(/\/+$/, ""); const absoluteProjectPath = resolve(normalizedProjectPath); if (!existsSync(absoluteProjectPath)) { throw new Error( `Project directory does not exist: ${absoluteProjectPath}`, ); } try { // Parse the command string to handle arguments properly const commandParts = command.trim().split(/\s+/); const rollCommand = [ "composer", ...commandParts, ]; // 10 minute timeout for composer operations (can be slow) const timeoutMs = 600000; const result = await this.executeCommand( "roll", rollCommand, absoluteProjectPath, timeoutMs, ); const commandStr = `roll composer ${command}`; const isSuccess = result.code === 0; // Save output to file only when explicitly requested const logFilePath = save_output_to_file ? this.saveOutputToFile(result.stdout, result.stderr, commandStr, absoluteProjectPath) : null; let responseText; if (logFilePath) { const outputPreview = (result.stdout || "").substring(0, 500); const stderrPreview = (result.stderr || "").substring(0, 500); responseText = `Composer command ${isSuccess ? "completed successfully" : "failed"}! Command: ${commandStr} Working directory: ${absoluteProjectPath} Exit Code: ${result.code}${result.timedOut ? " (TIMED OUT)" : ""} 📁 Full output saved to file: ${logFilePath} Output Preview (first 500 chars): ${outputPreview || "(no output)"}${(result.stdout || "").length > 500 ? "\n...(truncated)" : ""} Errors Preview (first 500 chars): ${stderrPreview || "(no errors)"}${(result.stderr || "").length > 500 ? "\n...(truncated)" : ""}`; } else { responseText = `Composer command ${isSuccess ? "completed successfully" : "failed"}! Command: ${commandStr} Working directory: ${absoluteProjectPath} Exit Code: ${result.code}${result.timedOut ? " (TIMED OUT)" : ""} Output: ${result.stdout || "(no output)"} Errors: ${result.stderr || "(no errors)"}`; } return { content: [ { type: "text", text: responseText, }, ], isError: !isSuccess, }; } catch (error) { const commandStr = `roll composer ${command}`; // Save error output to file only when explicitly requested const logFilePath = save_output_to_file ? this.saveOutputToFile(error.stdout, error.stderr, commandStr, absoluteProjectPath) : null; let responseText; if (logFilePath) { responseText = `Failed to execute Composer command: Command: ${commandStr} Working directory: ${absoluteProjectPath} Error: ${error.message} 📁 Full output saved to file: ${logFilePath}`; } else { responseText = `Failed to execute Composer command: Command: ${commandStr} Working directory: ${absoluteProjectPath} Error: ${error.message} Output: ${error.stdout || "(no output)"} Errors: ${error.stderr || "(no errors)"}`; } return { content: [ { type: "text", text: responseText, }, ], isError: true, }; } }