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

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 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 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. 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, 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

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


# 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


# 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.
  • 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 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.

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 →