What Is bootstrap.js and Why Import Order Is Critical for the libuv Threadpool

bootstrap.js is an initialization module that sets UV_THREADPOOL_SIZE before any file-system work begins, ensuring the libuv threadpool is created with sufficient threads to prevent I/O bottlenecks.

In the DesktopCommanderMCP repository, this tiny module solves a subtle but severe Node.js runtime limitation: the libuv threadpool size is locked at first use. If bootstrap.js is not the very first import, the pool initializes with only 4 threads, causing multi-minute hangs under heavy parallel file operations.

How libuv Threadpool Initialization Works

Node.js delegates asynchronous I/O operations to libuv, which manages a threadpool to execute blocking tasks like file system calls and DNS lookups. The size of this pool is governed by the UV_THREADPOOL_SIZE environment variable.

The critical detail is that libuv initializes the threadpool lazily—it creates the pool the first time any work is submitted (e.g., the first fs.readFile or fs.writeFile). At that moment, libuv reads process.env.UV_THREADPOOL_SIZE exactly once and never checks it again. If the variable is unset, the pool defaults to 4 threads. Once created, the pool size cannot be changed for the lifetime of the process.

What bootstrap.js Does

The module located at src/bootstrap.ts (compiled to dist/bootstrap.js) performs a single, time-sensitive assignment.

Source Location and Compilation

The Implementation Logic

The code checks if UV_THREADPOOL_SIZE is undefined, and if so, assigns a safe default of 16:

/**
 * Threadpool bootstrap. MUST be the first import in index.ts.
 *
 * libuv reads UV_THREADPOOL_SIZE only when the pool is first initialized
 * (on first submitted work). This assignment must therefore happen before
 * any fs operation.
 */
const DEFAULT_THREADPOOL_SIZE = 16;

if (!process.env.UV_THREADPOOL_SIZE) {
  process.env.UV_THREADPOOL_SIZE = String(DEFAULT_THREADPOOL_SIZE);
}

Because this runs at the top level of the module, it executes immediately upon import—before the importing module finishes loading.

Why Import Order Is Critical

If any module that triggers I/O (even indirectly) is imported before bootstrap.js, the threadpool initializes with the default 4 threads. Subsequent changes to process.env.UV_THREADPOOL_SIZE are ignored, leaving the application vulnerable to threadpool starvation when multiple agents perform concurrent file operations on slow or cloud-synced filesystems.

Correct Usage in the Entry Point

In src/index.ts, the import must appear first, accompanied by an explicit warning comment:

#!/usr/bin/env node

// MUST be first: raises the libuv threadpool size before any fs work is
// submitted. See src/bootstrap.ts for why import order matters.
import './bootstrap.js';                       // ← first import
import { FilteredStdioServerTransport } from './custom-stdio.js';
import { server, flushDeferredMessages } from './server.js';
/* … rest of the application … */

Violating this order means the application may suffer severe performance degradation or hangs, as file operations block the tiny default threadpool.

Testing the Threadpool Configuration

The repository includes a reproducible test at test/repro/test-bootstrap-threadpool.js that verifies the bootstrap logic. The test imports bootstrap.js first, exactly like the main entry point, then validates that UV_THREADPOOL_SIZE is set before any file work begins:

import '../../dist/bootstrap.js';   // first import, exactly like index.ts
import { execSync } from 'child_process';
import fs from 'fs/promises';
import os from 'os';
import path from 'path';

// Verify that UV_THREADPOOL_SIZE is set before any file work.
console.log(`UV_THREADPOOL_SIZE after bootstrap = ${process.env.UV_THREADPOOL_SIZE}`);

This test creates deliberately blocked reads to confirm that with the larger threadpool, subsequent write operations complete promptly rather than queuing behind the blocked threads.

Summary

  • bootstrap.js sets UV_THREADPOOL_SIZE to 16 (if unset) at module load time.
  • libuv initializes the threadpool on first I/O, reading the environment variable once and locking the size forever.
  • Import order is critical: bootstrap.js must be the first import in src/index.ts to ensure the variable is set before any file system module triggers pool creation.
  • Failure to import first results in a 4-thread pool that causes hangs under parallel file loads, as demonstrated in the repository's reproduction test.

Frequently Asked Questions

What happens if I import bootstrap.js after other modules in DesktopCommanderMCP?

If bootstrap.js is imported after modules that perform file system or DNS operations, the libuv threadpool will have already initialized with the default 4 threads. Your subsequent assignment to process.env.UV_THREADPOOL_SIZE will be ignored, and the application may experience severe stalls when multiple file operations run concurrently.

Can I change the threadpool size while the application is running?

No. According to the libuv implementation used by Node.js, the threadpool size is determined once at initialization and cannot be changed dynamically. The environment variable is read only when the first work item is submitted to the pool, making early configuration via bootstrap.js the only reliable method.

Why does libuv use a fixed-size threadpool instead of creating threads dynamically?

libuv uses a fixed pool to avoid the overhead of thread creation and destruction during I/O operations, and to prevent unbounded resource consumption under heavy load. While this design is efficient, it requires applications to size the pool appropriately before starting work—exactly what the bootstrap.js module accomplishes in DesktopCommanderMCP.

How can I verify that the threadpool size is set correctly in my Node.js process?

Check process.env.UV_THREADPOOL_SIZE immediately after importing bootstrap.js but before any asynchronous I/O operations. The repository's test file test/repro/test-bootstrap-threadpool.js demonstrates this verification pattern, logging the value to confirm it matches the expected default of 16 or your custom setting.

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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →