# How Cua's MCP Server Session Management Works for Long-Running Agent Tasks

> Discover how Cua's MCP server uses session management and idle detection to efficiently handle long-running agent tasks without resource waste.

- Repository: [Cua/cua](https://github.com/trycua/cua)
- Tags: internals
- Published: 2026-04-27

---

**Cua's MCP server isolates each client in a dedicated session with a VM instance, tracking active tasks to prevent premature cleanup while using background idle detection to reclaim resources automatically.**

The `trycua/cua` repository implements a Model-Control-Plane (MCP) server that orchestrates computer-use agents across persistent VM sessions. Understanding Cua's MCP server session management is essential for building reliable, long-running automation workflows that maintain state across multiple tool calls without exhausting system resources.

## Session Lifecycle Architecture

### Session Creation and VM Binding

When a client initiates work, `SessionManager.get_session()` in [`libs/python/mcp-server/mcp_server/session_manager.py`](https://github.com/trycua/cua/blob/main/libs/python/mcp-server/mcp_server/session_manager.py) handles session instantiation. If the provided `session_id` does not exist in the internal `_sessions` dictionary, the manager creates a new `SessionInfo` object and acquires a `Computer` instance from the `ComputerPool`. This binds a dedicated VM to the session, ensuring complete isolation between clients.

### Task Registration for Long-Running Protection

To prevent the session from being reclaimed during extended operations, the server registers each task using `SessionManager.register_task()`. This adds the `task_id` to the session's `active_tasks` set. The `_cleanup_loop` background task, which runs every minute, explicitly skips any session with non-empty `active_tasks` during its idle timeout check.

### Execution Within Async Contexts

Agent tasks execute within an `async with session_manager.get_session(session_id)` context block, as implemented in [`libs/python/mcp-server/mcp_server/server.py`](https://github.com/trycua/cua/blob/main/libs/python/mcp-server/mcp_server/server.py) lines 53-56. This ensures the session's `computer` instance remains available for screenshots, tool calls, and stateful interactions throughout the agent's lifecycle.

### Deregistration and Cleanup Eligibility

Upon task completion or exception, `SessionManager.unregister_task()` removes the `task_id` from `active_tasks`. When the task set becomes empty, the session becomes eligible for the idle cleanup mechanism. Clients may also invoke `cleanup_session()` to force immediate termination regardless of active tasks.

## Computer Pool Resource Management

### VM Reuse and Acquisition

The `ComputerPool` class maintains a bounded collection of VM instances with a default `max_size=5`. The `acquire()` method either returns an idle computer from the pool or provisions a new instance if capacity permits. This pooling strategy prevents the overhead of spawning new VMs for every request while capping resource consumption.

### Resource Release and Shutdown

When sessions end, `ComputerPool.release()` returns the VM to the idle pool for reuse by other clients. During server shutdown, `SessionManager.stop()` triggers cleanup of all remaining sessions and shuts down the `ComputerPool`, ensuring no orphaned VM processes remain.

## Background Idle Cleanup Mechanism

### Automated Resource Reclamation

A dedicated asyncio task `_cleanup_loop` executes every 60 seconds in [`session_manager.py`](https://github.com/trycua/cua/blob/main/session_manager.py). It iterates through `_sessions` and removes any entry that exceeds the `idle_timeout` (default 10 minutes) provided no active tasks are registered. This automatic reclamation prevents resource exhaustion from abandoned client connections.

### Graceful Shutdown Handling

The server implements graceful shutdown through `run_server` in [`server.py`](https://github.com/trycua/cua/blob/main/server.py), which initializes the session manager at startup and ensures `shutdown_session_manager` runs during termination. This sequence guarantees that long-running tasks complete their current operations before VMs are released.

## Implementation Examples

### Managing Long-Running Tasks with Explicit Registration

```python
from mcp_server.session_manager import get_session_manager
import asyncio

async def execute_long_task():
    manager = get_session_manager()
    
    async with manager.get_session() as session:
        task_id = "data-processing-job"
        await manager.register_task(session.session_id, task_id)
        
        try:
            # Access the computer interface for screenshots

            screenshot = await session.computer.interface.screenshot()
            # Simulate long-running work (5 minutes)

            await asyncio.sleep(300)
        finally:
            # Critical: deregister to allow cleanup

            await manager.unregister_task(session.session_id, task_id)

```

### Reusing Sessions Across Multiple Tool Calls

```python

# First call creates the session and acquires VM

await run_cua_task(ctx, "Initialize browser", session_id="user-123")

# Subsequent calls reuse the same VM instance

await run_cua_task(ctx, "Navigate to dashboard", session_id="user-123")
await screenshot_cua(ctx, session_id="user-123")

```

### Forcing Immediate Resource Cleanup

```python

# Force immediate session termination and VM release

await cleanup_session(ctx, session_id="user-123")

```

## Summary

- **Session isolation**: Each client receives a dedicated `SessionInfo` with a bound `Computer` VM instance managed through [`libs/python/mcp-server/mcp_server/session_manager.py`](https://github.com/trycua/cua/blob/main/libs/python/mcp-server/mcp_server/session_manager.py).
- **Task protection**: `register_task()` and `unregister_task()` maintain an `active_tasks` set that prevents the idle cleaner from terminating long-running work.
- **Resource bounds**: `ComputerPool` limits concurrent VMs to `max_size=5` and reuses idle instances across sessions.
- **Automatic reclamation**: The `_cleanup_loop` runs every minute to remove sessions idle longer than 10 minutes when no tasks are active.
- **Graceful lifecycle**: Async context managers in [`server.py`](https://github.com/trycua/cua/blob/main/server.py) ensure proper acquisition and release of session resources without blocking the event loop.

## Frequently Asked Questions

### How does Cua prevent active sessions from being killed during long-running tasks?

The session manager tracks active task IDs in a per-session set. When `register_task()` adds a task ID, the background cleanup loop ignores that session during idle checks. Only when `unregister_task()` removes the final task ID does the session become eligible for timeout-based cleanup.

### What is the default idle timeout for MCP sessions in Cua?

The default `idle_timeout` is 10 minutes. The `_cleanup_loop` task runs every 60 seconds to scan for sessions exceeding this threshold that have no active tasks, automatically reclaiming the associated VM resources.

### How many concurrent VMs can the MCP server support simultaneously?

The `ComputerPool` defaults to `max_size=5`, meaning the server maintains a maximum of five VM instances. These are shared across sessions through acquire/release mechanics, though each active session exclusively owns its computer during task execution.

### Can clients force immediate cleanup of a specific session?

Yes. Clients can call `cleanup_session(session_id)` to immediately remove the session from `_sessions` and return the VM to the pool, bypassing the idle timeout check. This is useful when a user disconnects or when deterministic resource release is required.