# hunglp97/tabpilot-mcp [Health: Active]

**Category:** 📂 Browser Automation  
**Repository:** https://github.com/hunglp97/tabpilot-mcp  
**GitHub Stars:** 5  
**Views:** 0  
**Installs:** 0  
**Upvotes:** 0  
**Directory Page:** https://allmcps.com/mcp/hunglp97-tabpilot-mcp

## Description
Drive your open, logged-in Chrome with token-budgeted page reading, complex SPA form and survey handling, and headless Ubuntu support.

## Tools
Capabilities this server exposes over MCP:

- **browser_status** — Report which backend is connected, what it can do, and how many tabs are open.
- **list_tabs** — List open tabs with their ids, titles and URLs.

        Args:
            url_pattern: Optional regex; only tabs whose URL matches are listed.
        
- **read_tab** — Read a tab's content, converted to markdown and capped in size.

        Args:
            tab_id: Exact tab id from list_tabs. Prefer url_pattern.
            url_pattern: Regex matched against tab URLs.
            mode: 'readable' (markdown, boilerplate stripped), 'text', or 'html'.
            selector: Read only this element's subtree. Use it — it is the
                cheapest way to keep a read small and on-topic.
            max_chars: Character budget. Defaults to the server's --max-chars.
            include_links: Keep links as markdown in 'readable' mode.
        
- **query_dom** — Inspect specific elements: tag, text, attributes, size, visibility.

        Use this instead of reading the page when the question is about
        particular elements — form fields, buttons, error messages, option lists.

        Args:
            selector: CSS selector.
            tab_id: Exact tab id. Prefer url_pattern.
            url_pattern: Regex matched against tab URLs.
            limit: Maximum elements to return.
            attrs: Only report these attributes. Defaults to a useful set
                (id, name, type, href, value, placeholder, role, aria-label,
                data-testid, class) plus disabled/checked/selected state.
            text_max: Truncate each element's text at this many characters.
            visible_only: Skip elements that are not rendered.
        
- **open_tab** — Open a URL in a new tab and return its id.

        Args:
            url: Absolute URL to open.
            activate: Bring the new tab to the front.
            wait_for_load: Wait until the document finishes loading.
        
- **close_tab** — Close a tab.

        Args:
            tab_id: Exact tab id.
            url_pattern: Regex matched against tab URLs. Must identify one tab.
        
- **navigate** — Point an existing tab at a new URL.

        Args:
            url: Absolute URL to load.
            tab_id: Exact tab id.
            url_pattern: Regex matched against the tab's *current* URL.
            wait_for_load: Wait until the document finishes loading.
        
- **activate_tab** — Bring a target browser tab to the front and focus its window.

        Side effects: Changes system window focus and switches the user's active
        tab viewport. Does not reload the page or alter DOM state.

        Usage guidelines:
        - When to use: Use when a human user needs to observe the active page,
          or before capturing desktop-wide OS screenshots and video screencasts.
        - When NOT to use: Do NOT call this before reading or interacting with tabs.
          TabPilot tools (`read_tab`, `query_dom`, `click`, `fill`, `eval_js`,
          `screenshot`) work off-screen in background tabs without stealing focus.

        Args:
            tab_id: Exact tab identifier (e.g. from `list_tabs`). If omitted,
                uses the frontmost tab or matches by `url_pattern`.
            url_pattern: Optional regex pattern matched against tab URLs
                (e.g. 'github\.com').

        Returns:
            Confirmation message containing the activated tab ID and title/URL.
            Returns an error message if no matching tab is found.
        
- **eval_js** — Run a JavaScript expression in a tab and return its value as JSON.

        The expression's value is returned, so write `document.title`, not
        `return document.title`. Promises are awaited on the CDP backend.

        Do not use this to dump `document.body.innerText` — read_tab and
        query_dom answer those questions for a fraction of the tokens.

        Args:
            expression: A JavaScript expression.
            tab_id: Exact tab id.
            url_pattern: Regex matched against tab URLs.
            timeout_ms: Per-call timeout.
        
- **click** — Click an element, scrolling it into view first.

        On the CDP backend this is a real mouse event at the element's centre, so
        widgets that only react to genuine mousedown behave correctly.

        Args:
            selector: CSS selector.
            text: Visible text to match. Combined with selector it narrows within
                those matches; alone it searches clickable elements. Exact matches
                win over partial ones, and the innermost match wins.
            tab_id: Exact tab id.
            url_pattern: Regex matched against tab URLs.
            nth: Which match to click, 0-based.
        
- **fill** — Set a form field's value so React, Vue and jQuery all notice.

        Works on text inputs, textareas, contenteditable, checkboxes and radios
        (pass 'true'/'false' for those).

        Args:
            selector: CSS selector for the field.
            value: Value to set.
            tab_id: Exact tab id.
            url_pattern: Regex matched against tab URLs.
            clear: Empty the field first.
            nth: Which match to fill, 0-based.
            press_enter: Press Enter afterwards, for search boxes.
        
- **select_option** — Choose option(s) in a native <select> or a Select2 widget.

        Pass several values for a multi-select. If the element turns out to be a
        React-Select style widget with no underlying <select>, this says so and
        you should use select_option_ui instead.

        Args:
            selector: CSS selector for the <select>.
            values: Option values or labels to select.
            tab_id: Exact tab id.
            url_pattern: Regex matched against tab URLs.
            by: Match options by 'value', 'label', or 'auto' (either).
            nth: Which match to use, 0-based.
        
- **select_option_ui** — Choose an option in a React-Select / Headless UI dropdown by driving its UI.

        These have no <select> to set, so the menu is opened, awaited, and the
        option clicked — which cannot be done in a single JavaScript call because
        the menu does not exist yet when the click would fire.

        Args:
            control_selector: CSS selector for the control that opens the menu.
            option_text: Visible text of the option to choose.
            tab_id: Exact tab id.
            url_pattern: Regex matched against tab URLs.
            option_selector: Override the option selector if the defaults miss.
            timeout_ms: How long to wait for the menu to appear.
        
- **scan_matrix** — List the matrix/grid questions on a page and which rows are unanswered.

        Call this before fill_matrix to see the shape of the question and to
        confirm the row indices.

        Args:
            tab_id: Exact tab id.
            url_pattern: Regex matched against tab URLs.
            selector: Override the matrix container selector.
        
- **fill_matrix** — Answer a matrix/grid question one row at a time, then verify the result.

        Rows are clicked in separate JavaScript tasks with a delay between them.
        Clicking them in a loop instead makes React batch the updates and commit
        only the last row, leaving the rest blank and the page stuck failing
        validation.

        Args:
            tab_id: Exact tab id.
            url_pattern: Regex matched against tab URLs.
            selector: Override the matrix container selector.
            question_index: Which matrix question, as numbered by scan_matrix.
            column_index: Which answer column to pick. Negative counts from the
                right, so -1 is the last column.
            rows: Specific row indices. Defaults to whichever rows need answering.
            only_unanswered: Skip rows that already have an answer.
            delay_ms: Delay between rows. Raise it to 150-250 if rows stay blank.
        
- **wait_for** — Poll until a condition holds, then return. Fails with what it last saw.

        Args:
            selector: CSS selector to watch.
            state: 'visible', 'hidden', 'present', 'absent', 'enabled', or
                'text' (with the text argument).
            text: Substring to look for when state is 'text'.
            predicate: A JavaScript expression to poll instead of a selector,
                e.g. "document.readyState === 'complete'".
            tab_id: Exact tab id.
            url_pattern: Regex matched against tab URLs.
            timeout_ms: How long to keep polling.
            poll_ms: Interval between checks, minimum 50.
        
- **screenshot** — Capture a tab and save it to disk.

        Captures the tab that was asked for even if it is not frontmost, and
        works headless — so this is also how evidence gets captured on a server.
        Needs the CDP backend.

        Args:
            tab_id: Exact tab id.
            url_pattern: Regex matched against tab URLs.
            full_page: Capture the whole scrollable page, not just the viewport.
            selector: Capture only this element.
            image_format: 'png' or 'jpeg'.
            quality: JPEG quality, 1-100. Ignored for PNG.
            label: Included in the filename, to make the file findable later.
            return_image: Inline the image in the result. Defaults on when the
                client is on another machine (TABPILOT_REMOTE / --return-images),
                since a saved path means nothing there.
        

## Claude Desktop Quick Installation
Install path detected from listing signals. Uses `uvx` (confidence: high):

```json
"mcpServers": {
  "tabpilot-mcp": {
    "command": "uvx",
    "args": ["tabpilot-mcp"]
  }
}
```

## Documentation

## What hunglp97/tabpilot-mcp does

The hunglp97/tabpilot-mcp MCP server lets an MCP-compatible agent control a real Google Chrome profile that already contains its cookies, sessions, and authentication state. It is intended for workflows where opening a new browser would require repeated login or would not behave like a normal user browser.

The server supports both page inspection and interaction. Agents can read focused sections instead of receiving an entire document, inspect specific DOM properties, click controls, populate inputs, choose options from portal-based interfaces, complete multi-row survey grids, and take screenshots without bringing a background tab to the foreground.

This makes it relevant to the 📂 Browser Automation category, especially for authenticated web applications and single-page applications.

## How it works

The hunglp97/tabpilot-mcp MCP server communicates with Chrome through the Chrome DevTools Protocol when Chrome is started with remote debugging enabled. On macOS, it can alternatively use AppleScript to drive an already-open Chrome window when the required JavaScript-from-Apple-Events setting is enabled.

Page reading uses progressive extraction. `query_dom(selector)` is intended for small checks such as button state, fields, badges, or alerts. `read_tab(selector)` limits output to a selected section, while `read_tab()` applies link-density filtering to remove much of the surrounding navigation and page clutter. Reads have a default maximum of 20,000 characters and report truncation so the agent can narrow its next query.

Form actions account for common SPA behavior. Native property setters and input/change events help update React-controlled fields. `fill_matrix` schedules survey-grid interactions row by row, and `select_option_ui` opens portal-rendered controls before selecting an option.

## Setup and configuration

Install or test the package with Python tooling. The documented diagnostic command is:

```bash
uvx tabpilot-mcp doctor
```

Installation through pip is also supported:

```bash
pip install tabpilot-mcp
```

Register the server with an MCP client using the `uvx` command and `tabpilot-mcp` argument. Chrome must then be fully closed and relaunched with a remote debugging port and a persistent user-data directory. The README provides commands for macOS, Linux, and Windows.

On macOS, the debugging launch is optional if AppleScript fallback is used. In that case, enable “Allow JavaScript from Apple Events” in Chrome under View → Developer.

## Tools and capabilities

The documented capabilities include:

- Reading full pages with link-density filtering or reading selected elements with `read_tab`.
- Querying focused DOM information with `query_dom`.
- Clicking controls and filling forms using browser events.
- Handling React, Vue, and Svelte-style form behavior.
- Selecting options rendered by React-Select or similar portal-based UI components with `select_option_ui`.
- Completing multi-row survey grids with `fill_matrix`.
- Capturing screenshots from tabs that are not currently visible.
- Running against headless Ubuntu with a systemd-based deployment stack.

## Limitations and notes

The server requires access to a Chrome profile and does not create the authenticated session for you. Remote debugging must be configured when using CDP, and the macOS AppleScript route has its own Chrome permission prerequisite. Using a dedicated persistent profile is shown in the setup commands.

Read output is intentionally bounded and may be truncated. Agents should respond by querying a narrower selector rather than assuming omitted content.

_Full upstream README: https://allmcps.com/mcp/hunglp97-tabpilot-mcp/readme_

