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
SessionInfowith a boundComputerVM instance managed throughlibs/python/mcp-server/mcp_server/session_manager.py. - Task protection:
register_task()andunregister_task()maintain anactive_tasksset that prevents the idle cleaner from terminating long-running work. - Resource bounds:
ComputerPoollimits concurrent VMs tomax_size=5and reuses idle instances across sessions. - Automatic reclamation: The
_cleanup_loopruns every minute to remove sessions idle longer than 10 minutes when no tasks are active. - Graceful lifecycle: Async context managers in
server.pyensure 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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →