Skip to content

Multi-Agent Coordination

Multiple Claude Code agents share a single Firefox window with a 12-tab pool. Claudezilla enforces ownership rules and provides coordination mechanisms to prevent conflicts.

Every tab tracks the agent that created it. Only the creator can close or navigate their tabs.

// Agent A creates a tab
const tab = firefox_create_window({ url: "https://example.com" })
// tab.ownerId = "agent_a1b2c3..."
// Agent B tries to close it
firefox_close_tab({ tabId: tab.tabId })
// Error: OWNERSHIP: Cannot close tab 42 (owned by agent_a1b2...)

All content commands (getContent, click, type, screenshot, etc.) also verify ownership. Use firefox_get_tabs to see which agent owns each tab:

firefox_get_tabs()
// {
// tabCount: 3,
// maxTabs: 12,
// tabs: [
// { tabId: 42, url: "...", title: "...", ownerId: "agent_a1b2..." },
// { tabId: 43, url: "...", title: "...", ownerId: "agent_d4e5..." }
// ]
// }

The tab pool is capped at 12 tabs. When an agent tries to open a 13th tab, it gets a POOL_FULL error instead of silently evicting another agent’s tab.

POOL_FULL: Tab pool is full (12/12).
Your tabs: 4
Other agents: 8
Hint: Close one of your tabs, or use firefox_request_tab_space.

Agents can only auto-evict their own oldest tab. If all 12 tabs belong to other agents, the mercy system is required.

The mercy system lets blocked agents request tab space from other agents through slot reservations.

When blocked by POOL_FULL, queue a request:

firefox_request_tab_space()
// { queued: true, position: 1 }

When an agent sees pending requests (or closes a tab), a 30-second slot reservation is created for the waiting agent:

// The granting agent calls:
firefox_grant_tab_space()
// Closes their oldest tab, creates reservation for waiting agent

The waiting agent polls for its reservation:

firefox_get_slot_requests()
// {
// pendingRequests: [],
// youHaveReservation: true,
// reservationExpiresInMs: 28000
// }
// Reservation found — claim it immediately
firefox_create_window({ url: "https://example.com" })
// Bypasses POOL_FULL check during reservation window

Reservations expire after 30 seconds. If unclaimed, the slot becomes available to any agent.

When a Claude session ends normally (Ctrl+C, task completion), the MCP server sends a goodbye command that immediately closes all tabs owned by that agent.

[claudezilla] Agent agent_a1b2... disconnecting, cleaning up 3 tab(s)

This also removes the agent’s pending slot requests and active reservations.

If an agent crashes hard (SIGKILL, OOM) without sending goodbye, tabs become orphaned. Claudezilla handles this with heartbeat tracking:

  • Heartbeat: Every command updates the agent’s last-seen timestamp
  • Timeout: Agent is considered dead after 10 minutes of inactivity
  • Cleanup: Every 60 seconds, the MCP server checks for orphaned agents and closes their tabs

This prevents “ghost tabs” from permanently consuming pool slots after crashes.

Screenshots are serialized across all agents to prevent tab-switching races. If the mutex is held for more than 3 seconds, you get MUTEX_BUSY:

MUTEX_BUSY: Screenshot mutex held by another agent.
Hint: Use getPageState (no mutex) or retry after delay.

firefox_get_page_state is a good alternative when you need page structure without a visual capture.