How DesktopCommanderMCP Ensures Chrome Availability for PDF Generation with ensureChromeAvailable
The ensureChromeAvailable function proactively guarantees a Chrome executable is present for PDF rendering by searching cached Puppeteer builds, scanning system installations, and automatically downloading Chrome via @puppeteer/browsers if necessary, all while running asynchronously during server startup to prevent blocking.
DesktopCommanderMCP generates PDF documents from Markdown using the md-to-pdf library, which requires a Chrome or Chromium binary to render HTML content. Because environments like CI pipelines or minimal containers often lack Chrome, the codebase implements a robust availability strategy centered on the ensureChromeAvailable helper exported from src/tools/pdf/markdown.ts.
The Three-Tier Chrome Resolution Strategy
When invoked, ensureChromeAvailable triggers a background resolution process that attempts three distinct strategies in sequence. The logic is coordinated by getChromePath, which orchestrates the lookup while protecting against race conditions.
1. Cached Puppeteer Build Detection
The resolution first attempts findPuppeteerChrome, which scans Puppeteer's private cache directory for platform-specific Chrome binaries. If a compatible build exists, the function calls pruneOldPuppeteerChromeBuilds to remove outdated versions and caches the executable path in the module-level cachedChromePath variable. This avoids redundant downloads when PDF generation runs multiple times.
2. System Chrome Fallback
If no cached build exists, the code executes findSystemChrome to search hard-coded common installation paths across Windows, macOS, and Linux. This includes standard locations like /usr/bin/google-chrome on Linux or /Applications/Google Chrome.app/Contents/MacOS/Google Chrome on macOS. When found, this system path is cached for subsequent calls.
3. Automatic Chrome Installation
As a last resort, the installChrome function uses @puppeteer/browsers to download the latest stable Chrome build for the detected platform. The download runs with progress output directed to stderr, and upon completion, the new binary path is cached and old builds are pruned to conserve disk space.
Caching and Concurrency Protection
The getChromePath function implements memoization to ensure the expensive resolution process runs only once. It guards concurrent invocations using a chromeCheckPromise variable that acts as a lock—subsequent calls await the same promise rather than triggering duplicate searches or downloads. The function returns Promise<string | undefined>, resolving to the executable path or undefined if all strategies fail.
Server Integration and Background Execution
The function is invoked during server startup in src/index.ts within the LSP server's oninitialized callback. This placement ensures Chrome detection runs only after the server has fully initialized, allowing the client to connect while the download proceeds in the background.
// src/index.ts (simplified)
server.oninitialized = () => {
// Fire-and-forget: server continues serving requests while Chrome downloads
ensureChromeAvailable();
};
Because the call is fire-and-forget, the server remains responsive to other tool requests even if Chrome installation takes several minutes on a slow connection.
Runtime PDF Generation
When PDF generation actually occurs, parseMarkdownToPdf calls getChromePath to retrieve the resolved executable path and injects it into the launch_options passed to md-to-pdf. If the path remains undefined—meaning Chrome is absent and automatic installation failed—the function throws a descriptive error directing the user to manual installation steps.
// src/tools/pdf/markdown.ts
const chromePath = await getChromePath();
if (!chromePath) {
throw new Error('Chrome is not available. Please install Chrome manually.');
}
// Inject into md-to-pdf options
const options = {
launch_options: {
executablePath: chromePath
}
};
Practical Implementation Examples
Triggering Chrome Availability Checks
You can manually trigger the background check to ensure Chrome is ready before performing batch operations:
import { ensureChromeAvailable } from './src/tools/pdf/markdown.js';
// Starts background resolution; returns immediately
ensureChromeAvailable();
// Proceed with other initialization logic
console.log('Server starting while Chrome resolves in background...');
Converting Markdown to PDF
The public API abstracts Chrome management entirely, though it relies on the earlier availability check:
import { parseMarkdownToPdf } from './src/tools/pdf/markdown.js';
async function generateDocument() {
const markdown = '# Report\nGenerated content here.';
const pdfBuffer = await parseMarkdownToPdf(markdown, {
pdf_options: { format: 'A4', printBackground: true }
});
// pdfBuffer is a Node.js Buffer ready for file system or HTTP response
return pdfBuffer;
}
Summary
ensureChromeAvailableinitiates a three-tier resolution strategy (cache → system → download) during server startup.- Concurrency protection via
chromeCheckPromiseprevents duplicate downloads when multiple PDF requests arrive simultaneously. - Background execution in
src/index.tsensures the MCP server remains responsive while Chrome installs. - Runtime injection in
parseMarkdownToPdfsupplies the resolvedexecutablePathtomd-to-pdf, with clear error messaging if resolution fails. - Automatic maintenance through build pruning keeps the Puppeteer cache from consuming excessive disk space.
Frequently Asked Questions
What happens if Chrome cannot be found or installed automatically?
If findPuppeteerChrome, findSystemChrome, and installChrome all fail, getChromePath returns undefined. When parseMarkdownToPdf subsequently runs, it detects this condition and throws a helpful error message instructing the user to install Chrome manually, preventing cryptic failures from md-to-pdf.
Can I force the use of a system-installed Chrome instead of downloading a new build?
Yes. The resolution order prioritizes existing resources: if findSystemChrome locates a valid Chrome installation in standard system paths (such as /usr/bin/chromium-browser on Linux or C:\Program Files\Google\Chrome\Application\chrome.exe on Windows), that path is cached and used immediately, bypassing the download logic entirely.
Does the server block while Chrome downloads?
No. The invocation in src/index.ts uses a fire-and-forget pattern inside the oninitialized callback. The Promise returned by ensureChromeAvailable is not awaited, allowing the LSP server to complete initialization and serve other tool requests while Chrome downloads and installs in the background.
How does the caching mechanism handle concurrent PDF generation requests?
The getChromePath function uses a module-level chromeCheckPromise variable to guard against race conditions. If multiple calls occur while Chrome is still being located or downloaded, all callers receive the same Promise and resolve simultaneously once the executable path is determined, preventing duplicate installations.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →