# How TUUI Initializes MCP Servers with a 90-Second Timeout for Runtime Installations

> Learn how TUUI initializes MCP servers via a three-step pipeline. Discover its 90-second idle timeout for preventing runtime installation hangs.

- Repository: [AIQL/tuui](https://github.com/ai-ql/tuui)
- Tags: how-to-guide
- Published: 2026-02-23

---

**TUUI initializes MCP servers through a three-step pipeline that loads configuration, creates clients, and establishes Stdio transport, while enforcing a 90-second idle timeout to prevent hanging during runtime installations.**

TUUI (the AI-powered UI framework) handles Model Context Protocol (MCP) server initialization through a robust pipeline designed to manage runtime installations safely. The implementation in the `ai-ql/tuui` repository ensures that unresponsive servers cannot block the application by implementing a strict **90-second idle timeout** mechanism that aborts stalled connections.

## The Three-Step MCP Server Initialization Pipeline

The initialization process in [`src/main/mcp/init.ts`](https://github.com/ai-ql/tuui/blob/main/src/main/mcp/init.ts) follows a declarative pipeline that transforms configuration entries into active client connections.

### Step 1: Loading the MCP Configuration

The process begins with `loadConfig()`, which reads the JSON file defined in `Constants.ASSETS_PATH.mcp`. This function returns an array of metadata objects containing `{ name, configJson }` for each declared server.

*Source:* [`src/main/mcp/init.ts#L9-L15`](https://github.com/ai-ql/tuui/blob/main/src/main/mcp/init.ts#L9-L15)

### Step 2: Creating Clients for Each Entry

The `initClients()` function iterates over the metadata entries. For every *stdio*-type entry, it delegates to `initSingleClient()`, which forwards the configuration to the core initialization logic.

*Source:* [`src/main/mcp/init.ts#L32-L48`](https://github.com/ai-ql/tuui/blob/main/src/main/mcp/init.ts#L32-L48) and [`src/main/mcp/init.ts#L59-L66`](https://github.com/ai-ql/tuui/blob/main/src/main/mcp/init.ts#L59-L66)

### Step 3: Establishing Stdio Transport

The `initializeClient()` function in [`src/main/mcp/client.ts`](https://github.com/ai-ql/tuui/blob/main/src/main/mcp/client.ts) performs the actual transport setup. It creates a `StdioClientTransport`, wires the standard-error stream to an optional progress callback, and connects to the MCP server via `connect()`.

*Source:* [`src/main/mcp/client.ts#L44-L80`](https://github.com/ai-ql/tuui/blob/main/src/main/mcp/client.ts#L44-L80)

## How the 90-Second Idle Timeout Protects Against Hanging Installations

The critical safety mechanism in TUUI's MCP initialization is the **90-second idle timeout** that prevents runtime installations from blocking the application indefinitely.

### Timer Setup and Reset Mechanism

The timeout logic centers on a `NodeJS.Timeout` variable called `idleTimer`. The `resetTimer()` function clears any existing timer and schedules a new one that will reject the initialization promise after `idleTimeout` seconds (defaulting to 90). This timer is reset whenever activity occurs on the stderr stream, indicating the server is still alive during installation.

*Source:* [`src/main/mcp/client.ts#L18-L32`](https://github.com/ai-ql/tuui/blob/main/src/main/mcp/client.ts#L18-L32)

### The Race Between Transport and Timeout

The initialization uses `Promise.race([stdioPromise, timeoutPromise])` to ensure responsiveness. The `timeoutPromise` is a never-resolving promise that only rejects when the idle timer fires, with the reject function stored in `rejectFn` for `resetTimer()` to invoke.

If the server connects and sends data before 90 seconds, the Stdio promise resolves and the timeout is cleared. If the server hangs during runtime installation, the idle timer fires, `rejectFn` executes, and the initialization fails.

*Source:* [`src/main/mcp/client.ts#L34-L42`](https://github.com/ai-ql/tuui/blob/main/src/main/mcp/client.ts#L34-L42)

### Error Handling When Timeout Occurs

When the 90-second threshold is exceeded, the promise rejects with a descriptive error message:

```

Initialization of client for <name> timed out after 90 seconds of inactivity

```

This error propagates up through `initClients()`, preventing the stalled server from being added to the active client pool in `IPCs.clients`.

## Practical Implementation Examples

### Starting All MCP Servers

The high-level entry point for TUUI's MCP initialization demonstrates the complete pipeline:

```typescript
import { loadConfig, initClients } from '@/main/mcp/init'

// Load the MCP manifest and start every declared server
async function startAllMcpServers(progressCallback) {
  const metadata = await loadConfig()
  const clients = await initClients(metadata, progressCallback)
  // `clients` now contains { name, connection, configJson } for each running server
  return clients
}

// Example usage in the main runner
await startAllMcpServers((name, msg, status) => {
  console.log(`[${status}] ${name}: ${msg}`)
})

```

*Source:* [`src/main/MainRunner.ts#L151`](https://github.com/ai-ql/tuui/blob/main/src/main/MainRunner.ts#L151)

### Configuring the 90-Second Timeout

The timeout duration is configurable when calling the initialization function, though it defaults to 90 seconds:

```typescript
await initializeClient('example', serverConfig, progressCb, /* idleTimeout = 90 */)
// If the server does not send anything on stderr for 90 seconds,
// the promise rejects with:
//   "Initialization of client for example timed out after 90 seconds of inactivity"

```

*Source:* See the timeout logic in [[`src/main/mcp/client.ts`](https://github.com/ai-ql/tuui/blob/main/src/main/mcp/client.ts)](https://github.com/ai-ql/tuui/blob/main/src/main/mcp/client.ts#L18-L42).

## Key Files in the TUUI MCP Initialization System

| File | Role | Link |
|------|------|------|
| [`src/main/mcp/init.ts`](https://github.com/ai-ql/tuui/blob/main/src/main/mcp/init.ts) | Loads MCP manifest, orchestrates per-client initialization | [view](https://github.com/ai-ql/tuui/blob/main/src/main/mcp/init.ts) |
| [`src/main/mcp/client.ts`](https://github.com/ai-ql/tuui/blob/main/src/main/mcp/client.ts) | Implements `initializeClient`, sets up Stdio transport and the 90s idle timeout | [view](https://github.com/ai-ql/tuui/blob/main/src/main/mcp/client.ts) |
| [`src/main/IPCs.ts`](https://github.com/ai-ql/tuui/blob/main/src/main/IPCs.ts) | Registers IPC handlers, stores initialized client descriptors, exposes them to the renderer | [view](https://github.com/ai-ql/tuui/blob/main/src/main/IPCs.ts) |
| [`src/main/MainRunner.ts`](https://github.com/ai-ql/tuui/blob/main/src/main/MainRunner.ts) | Entry point that calls `IPCs.initializeMCP` after clients are ready | [view](https://github.com/ai-ql/tuui/blob/main/src/main/MainRunner.ts) |
| [`src/main/utils/Constants.ts`](https://github.com/ai-ql/tuui/blob/main/src/main/utils/Constants.ts) | Holds path constants used for locating the MCP configuration assets | [view](https://github.com/ai-ql/tuui/blob/main/src/main/utils/Constants.ts) |

These files together define the complete lifecycle of MCP server startup, including the safety net of the 90-second idle timeout that guards against unresponsive runtime installations.

## Summary

- **TUUI initializes MCP servers** through a three-phase pipeline: loading configuration from `Constants.ASSETS_PATH.mcp`, creating clients via `initClients()`, and establishing Stdio transport through `initializeClient()`.
- **The 90-second idle timeout** is implemented in [`src/main/mcp/client.ts`](https://github.com/ai-ql/tuui/blob/main/src/main/mcp/client.ts) using a `NodeJS.Timeout` that rejects the initialization promise if no activity occurs on stderr within 90 seconds.
- **Promise.race()** ensures responsiveness by competing the Stdio connection promise against the timeout promise, guaranteeing that stalled runtime installations cannot block the application.
- **Error propagation** prevents unresponsive servers from entering the active client pool stored in `IPCs.clients`, maintaining application stability.

## Frequently Asked Questions

### What happens if an MCP server takes longer than 90 seconds to install?

If an MCP server exceeds the 90-second idle timeout during initialization, the `initializeClient()` function in [`src/main/mcp/client.ts`](https://github.com/ai-ql/tuui/blob/main/src/main/mcp/client.ts) rejects the promise with the error message: "Initialization of client for [name] timed out after 90 seconds of inactivity." This prevents the stalled server from blocking the TUUI application and excludes it from the active client pool.

### Can the 90-second timeout duration be customized?

Yes, the timeout duration is configurable through the `idleTimeout` parameter in the `initializeClient()` function. While the default value is 90 seconds, you can pass a different number of seconds when calling the function to accommodate slower runtime installations or stricter requirements.

### Where does TUUI store the MCP server configuration?

TUUI reads the MCP server configuration from a JSON file located at the path defined in `Constants.ASSETS_PATH.mcp`, typically found in [`src/main/utils/Constants.ts`](https://github.com/ai-ql/tuui/blob/main/src/main/utils/Constants.ts). The `loadConfig()` function in [`src/main/mcp/init.ts`](https://github.com/ai-ql/tuui/blob/main/src/main/mcp/init.ts) parses this file to extract server metadata including names and configuration objects.

### How does TUUI communicate initialized MCP clients to the renderer process?

After successful initialization, client descriptors are stored in `IPCs.clients` within [`src/main/IPCs.ts`](https://github.com/ai-ql/tuui/blob/main/src/main/IPCs.ts). The renderer process accesses these through the `list-clients` IPC handler, which reads from `IPCs.currentFeatures` populated by `IPCs.initializeMCP()` after all servers have been started.