AgentWorks

AgentWorks documentation

Browser System

Complete reference for browser automation in mcp-agent-builder-go — session limits, CDP local browser, Playwright artifacts, and known bugs.


Table of Contents


Session Limit Manager

The session tracker prevents unbounded browser process growth by enforcing per-workflow and global limits.

Source: agent_go/pkg/browser/session_tracker.go

Limits

Limit Value Enforcement
Per workflow/chat MaxBrowserSessionsPerChat = 1 Returns error to LLM — must close existing session first
Global (all sessions) MaxBrowserSessionsGlobal = 8 Auto-evicts oldest (LRU) session

How It Works

  1. Registration: When the LLM calls agent_browser(command="open", ...), the executor extracts the chatSessionID from context and calls tracker.CheckLimits().
  2. Per-chat check: If the workflow already has 1 active session, returns an error:
    ERROR: Cannot open browser session "<name>" — you already have 1 active browser sessions
    (max 1 per workflow). Active sessions: [...]. Close one first using
    agent_browser(command="close", session="<name>") before opening a new one.
    
  3. Global check: If 8+ sessions exist globally, the oldest (least-recently-used) session is auto-closed.
  4. Reuse: If the named session already exists in the tracker, it's a reuse — always allowed.
  5. Touch: Every agent_browser call updates the session's lastUsed timestamp.

Session Lifecycle

1. Workflow starts
   └─ Browser tool executor created with chatSessionID in context

2. LLM calls agent_browser(command="open", session="my_session")
   └─ CheckLimits() validates per-chat (< 1) and global (< 8)
   └─ Touch() registers session with timestamps

3. During workflow
   └─ Each agent_browser call updates lastUsed via Touch()

4. Workflow ends (stop/clear/completion)
   └─ CloseAllForChat(sessionID) closes all browser processes
   └─ RemoveAllForChat() removes tracker entries

5. Server restart
   └─ In-memory tracker cleared
   └─ Kill-all sent to workspace-api for orphaned chromium processes

Tracking Data Structure

type browserSessionInfo struct {
    browserSession string    // e.g., "twitter_research"
    chatSessionID  string    // owning workflow/chat session
    lastUsed       time.Time
    createdAt      time.Time
}

Key Functions

Function Purpose
Touch(browserSession, chatSessionID) Register or update last-used time
CheckLimits(browserSession, chatSessionID) Validate per-chat and global limits
CountForChat(chatSessionID) Count active sessions for a workflow
SessionsForChat(chatSessionID) List browser session names for a workflow
GetOldestSession() Find LRU session globally (for auto-eviction)
GetOldestSessionForChat(chatSessionID) Find LRU session for a specific workflow
RemoveAllForChat(chatSessionID) Remove all tracker entries for a workflow
CloseAllForChat(chatSessionID, client) Close browser processes + remove entries
Clear() Remove all tracked sessions (server restart)

Key Files

File Role
agent_go/pkg/browser/session_tracker.go Core tracker with limits
agent_go/pkg/browser/executor.go Limit check + enforcement on each command
agent_go/cmd/server/server.go Cleanup on workflow stop (cleanupBrowserSessions)
agent_go/cmd/server/virtual-tools/workspace_browser_tools.go Injects chatSessionID into context
workspace/handlers/browser_session_tracker.go Workspace-side tracker (code execution mode)

Frontend Monitoring

frontend/src/components/workspace/BrowserProcesses.tsx displays:

API endpoints: /api/browser/sessions (agent_go), /api/browser/processes (workspace-api).


CDP Local Browser Connection

Connect the agent's browser tools to your real Chrome browser via Chrome DevTools Protocol (CDP), allowing you to watch the agent navigate in real-time and reuse your logged-in sessions.

Overview

By default, browser-based MCP servers (like playwright or agent-browser) run an isolated, headless browser inside a container. By using CDP mode, you can point these tools to a Chrome instance running on your host machine.

Foreground behavior: CDP is connected to a visible real Chrome window. Browser actions may bring Chrome to the foreground and steal keyboard focus from the user. This is expected for shared visible Chrome and is separate from tab isolation. Use headless mode for background-safe runs, or run schedules against a dedicated automation Chrome profile/port instead of the user's primary Chrome.

How it Works

  1. Launch Chrome with Remote Debugging: You start Chrome on your host with a specific port (default 9222).
  2. Connection String: The agent is given a CDP URL (e.g., http://host.docker.internal:9222).
  3. Connectivity Check: The frontend verifies the connection before starting the session.
  4. Shared browser session: In CDP mode, agent_browser commands are remapped to a shared raw agent-browser session per CDP port, e.g. shared-cdp-9222. This lets multiple workflows reuse the same Chrome while still forcing each command to name the tab it intends to use.

Shared CDP Tabs

Native agent-browser tracks an active tab per --session. In a shared workflow environment that is too implicit: whichever workflow last selected a tab can influence the next page action. The project wrapper therefore requires an explicit tab for CDP work. Use the tab command to choose/create the tab, call open with URL-only args, then include an inline tab argument on later page actions.

Allowed page action forms:

{"command": "tab", "args": ["profile"], "session": "workflow_a"}
{"command": "open", "args": ["https://example.com"], "session": "workflow_a"}
{"command": "snapshot", "args": ["tab", "profile", "-i"], "session": "workflow_a"}
{"command": "click", "args": ["--tab", "profile", "@e1"], "session": "workflow_a"}
{"command": "eval", "args": ["tab", "profile", "document.title"], "session": "workflow_a"}

If a CDP page action omits the tab, the tool returns an error with the selected-tab hint for the current workflow. The LLM is expected to reuse that selected tab or create a labeled tab, then retry the command. open is the exception: it uses the workflow's previously selected tab and passes only the URL to agent-browser.

Tab management commands:

{"command": "tab", "args": [], "session": "workflow_a"}
{"command": "tab", "args": ["new", "--label", "profile", "https://example.com"], "session": "workflow_a"}
{"command": "tab", "args": ["profile"], "session": "workflow_a"}

Operational rules:

Configuration

Launch Chrome (Host)

macOS:

/Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome --remote-debugging-port=9222

Windows:

"C:\Program Files\Google\Chrome\Application\chrome.exe" --remote-debugging-port=9222

Connection Logic

The system uses a two-tier connectivity check (frontend/src/services/api.ts and agentApi.checkCdpPort):

  1. Agent API Check: Calls agent_go's /api/cdp-check — TCP dial from agent server to the port.
  2. Workspace API Check: If running in Docker, also tries the Workspace API's /api/cdp-check — critical because browser tools execute inside the workspace container (different network view).

Frontend UI Features

macOS "Damaged Package" Fix

xattr -cr /path/to/Chrome-CDP.app

Network Paths (Docker)

Troubleshooting


Playwright Artifacts and Output Location

The Problem

By default, @playwright/mcp might save artifacts in the process cwd. In containerized environments, files get lost in temporary directories or scattered across the repository root.

The Solution: working_dir Injection

The project uses a custom MCP client that supports a working_dir property in the server configuration.

Configuration (agent_go/configs/mcp_servers_clean.json)

"playwright": {
  "command": "npx",
  "args": [
    "@playwright/mcp@latest",
    "--output-dir",
    "../workspace-docs/Downloads",
    "--isolated"
  ],
  "working_dir": "../workspace-docs/Downloads"
}

Key Parameters:

Benefits

  1. Visibility: Artifacts land in workspace-docs/Downloads, indexed by semantic search and list_directory.
  2. Persistence: Files survive session restarts (persistent workspace-docs volume).
  3. Cleanliness: Prevents repo root from being cluttered with screenshot/download files.

Interaction with CDP Mode

Even when controlling your local browser via CDP, working_dir injection is active — downloads are still routed to the designated workspace folder.

Troubleshooting


Browser Session Identity Split Plan

Problem

A single MCP session ID currently does two jobs:

  1. Tool session identity — session-scoped MCP_API_URL, custom tool routing, code execution mode HTTP calls.
  2. Browser session identity — Playwright / agent_browser browser reuse, page state continuity, login persistence.

This coupling breaks in the workflow builder.

Concrete Failure: Builder + run_saved_main_py

Current share_browser Behavior

Available on call_sub_agent(), call_generic_agent(), and delegate():

Goal: Separate the Two Identities

Each agent/session should have:

Proposed Browser Session Key

For workflow builder: browser::<workspace-hash>::<group-id> (canonical group_id, not display name).

Desired Behavior After Split

What Must Change

  1. Session model: Introduce ToolSessionID + BrowserSessionID (fallback to tool session if browser session empty).
  2. Browser tools: Playwright and agent_browser must resolve browser_session_id first, fall back to tool_session_id.
  3. Code execution env: Add MCP_BROWSER_SESSION_ID or equivalent, keep MCP_API_URL on tool session.
  4. Workshop controller state: Track map[groupID]browserSessionID and optional lastActiveGroupID.
  5. Cleanup lifecycle: Closing a tool session must not auto-destroy a shared browser session.

Rollout Phases

  1. Phase 1: Internal split with compatibility fallback (browser falls back to tool session).
  2. Phase 2: Workflow builder/workshop adoption (run_saved_main_py, execute_step, builder inspection).
  3. Phase 3: Delegation adoption (share_browser=false isolates browser only).
  4. Phase 4: General multi-agent adoption (shared browser opt-in).

Files Involved

mcp-agent-builder-go:

mcpagent: Browser session registry/reuse logic, Playwright session lookup, agent_browser execution path.

Risk

If browser sessions are shared too broadly, two agents may interfere with the same page. Shared browser reuse should be explicit, scoped, and default-on only where already expected (parent/sub-agent in same task).


Known Bugs

Playwright: "transport error: transport closed"

Status: Known / Upstream

Symptom: failed to call tool browser_close: transport error: transport closed

Meaning: The MCP connection (stdio pipe to npx @playwright/mcp) is already closed by the time the call runs. Not that browser_close is invalid — the transport is gone.

Typical causes:

  1. Playwright MCP process exited (crash, OOM, uncaught exception).
  2. Connection was closed on our side (CloseSession called, workflow ended).
  3. Browser/process died earlier — MCP server closes transport or exits.
  4. Timeout or kill (subprocess killed, pipes close).

Fix implemented: "transport closed" is now treated as a broken-pipe error:

When it still happens: If retry also fails or the new process dies again. Start a new workflow/session.

What to do:

Playwright: Screenshots/Snapshots Saved to Repo Root

Status: Fixed

Problem: With --output-dir set, custom filenames (e.g., screenshot.png) were written to process root instead of the configured directory. Auto-generated filenames worked correctly.

Root cause:

  1. Upstream Playwright MCP resolves custom filenames relative to process cwd, not --output-dir (playwright-mcp#1390).
  2. No working-directory support when spawning the MCP subprocess.
  3. Session registry reuses connections by (sessionID, serverName) only — config not in key.

Fix:

  1. Added MCPServerConfig.WorkingDir and RuntimeConfigOverride.WorkingDir to mcpagent.
  2. StdioManager starts subprocess with cmd.Dir = workingDir.
  3. Workflow override (setupBrowserDownloadsPathOverride) sets both --output-dir and WorkingDir to the run's execution/Downloads.
  4. .gitignore fallback for any artifacts that still land at repo root.

Files: mcpagent/mcpclient/config.go, mcpagent/mcpclient/stdio_manager.go, mcpagent/mcpclient/client.go, agent_go/pkg/orchestrator/agents/workflow/step_based_workflow/controller_agent_factory.go.

Related Issues