diff --git a/README.md b/README.md index 29f8665..4cc88a2 100644 --- a/README.md +++ b/README.md @@ -97,7 +97,7 @@ The location of `claude_desktop_config.json` depends on your system: - macOS: `~/Library/Application Support/Claude/claude_desktop_config.json` - Windows: `%APPDATA%\Claude\claude_desktop_config.json` -Restart Claude Desktop. You should see a 🔨 tools icon indicating the server(s) connected, with tools like `start_browser`, `navigate`, `click`, etc. available. Only keep the entries you actually want. Three separate browser-automation servers is a lot if you only need one. +Restart Claude Desktop. You should see a 🔨 tools icon indicating the server(s) connected, with tools like `start_browser`, `goto_url`, `click_element`, etc. available. Only keep the entries you actually want. Three separate browser-automation servers is a lot if you only need one. ## 4. Connect it to Claude Code @@ -145,14 +145,14 @@ claude mcp add seleniumbase-sb -- uv run seleniumbase-sb |---|---| | `start_browser(browser, headless, uc, incognito)` | Launch a browser session (headless defaults to `False`) | | `close_browser()` | End the session | -| `navigate(url)` | Go to a URL | +| `goto_url(url)` | Go to a URL | | `go_back()` / `go_forward()` / `refresh_page()` | History navigation | | `get_current_url()` / `get_title()` | Page metadata | | `get_page_source()` | Full HTML | | `get_text(selector)` | Visible text of an element | | `find_elements_count(selector)` | Count matches | | `is_element_visible(selector)` | Visibility check | -| `click(selector, timeout)` | Click (CSS or XPath) | +| `click_element(selector, timeout)` | Click (CSS or XPath) | | `type_text(selector, text, clear_first, timeout)` | Fill a field | | `select_option_by_text(dropdown_selector, option)` | Choose a dropdown option | | `wait_for_element_present(selector, timeout)` | Explicit wait | @@ -199,16 +199,16 @@ in the loop at all. Reference: | Group | Tool(s) | | ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- | | Session | `start_browser(url, headless, use_chromium, browser_executable_path, incognito, guest, ad_block, proxy)`, `close_browser` | -| Navigation | `navigate`, `navigate_history(action: back/forward/reload)`, `get_page_info` (running status, url, title, origin, user agent, history in one call) | -| Finding & reading | `find_elements(selector, timeout, include_html)`, `get_content(selector, output_format: text/html/urls, include_shadow_dom)`, `get_attributes`, `check_state(check: present/visible/count/text_visible)` | -| Interacting | `click(selector, nth, all_matches, only_if_visible, parent_selector, timeout, scroll)`, `hover_action(selector1, selector2, action: none/click/drag_and_drop)`, `type_text(mode: fill_input/append/fast_type/set_value/clear_only)`, `select_option(by: text/value/index)`, `focus(action: scroll_to_element/focus/highlight)` | +| Navigation | `goto_url`, `manage_history(action: back/forward/reload/list)`, `get_page_info` (running status, url, title, origin, user agent, history in one call) | +| Finding & reading | `find_elements(selector, timeout, include_html)`, `get_content(selector, output_format: text/html/urls)`, `get_attributes`, `check_state(check: present/visible/count/text_visible)` | +| Interacting | `click_element(selector, nth, all_matches, only_if_visible, parent_selector, timeout, scroll)`, `hover_action(selector1, selector2, action: hover/hover_and_click/drag_and_drop)`, `type_text(mode: fill_input/append/fast_type/set_value/clear_only)`, `select_option(by: text/value/index)`, `focus_element(action: scroll_to_element/focus/highlight)` | | Waiting | `wait_for(state: present/visible/not_visible/absent/seconds_passed, text)` | | Assertions | `assert_condition(check: element_present/element_visible/text_visible/title/url/url_contains)` | | Cookies & storage | `manage_cookies(action: get_all/clear/save/load)`, `manage_storage(storage: local/session, action: get/set)` | -| Scrolling | `scroll(direction: up/down/top/bottom, amount)` | +| Scrolling | `scroll_page(direction: up/down/top/bottom, amount)` | | Windows & tabs | `manage_window(action: get_rect/set_rect/maximize/minimize)`, `manage_tabs(action: list/open/switch/switch_newest/close_active)` | | Captcha | `solve_captcha` | -| Output & misc | `save_output(format: screenshot/html/pdf)`, `run_javascript` | +| Output & misc | `save_page(format: screenshot/html/pdf)`, `run_javascript` | ### CDP-specific design notes diff --git a/cdp_server.py b/cdp_server.py index b073378..b1daf8d 100644 --- a/cdp_server.py +++ b/cdp_server.py @@ -34,7 +34,8 @@ Tool-selection philosophy: - Use 'start_browser'/'close_browser' for opening/quitting the web browser. -- Use 'navigate'/'manage_history' for browser navigation & history inspection. +- Use 'goto_url'/'manage_history' for browser navigation and history + inspection. - Use 'get_page_info' for reading browser/page metadata such as URL/title. - Use 'get_content'/'get_attributes' for reading text, HTML, or attributes. - Use 'find_elements' for discovering and inspecting multiple matching @@ -43,11 +44,12 @@ - Use 'wait_for' when the agent needs to wait for a condition to become true. - Use 'assert_condition' when the agent needs to verify an expected condition and treat failure as an assertion error. -- Use 'click'/'type_text'/'select_option' for standard page interactions. +- Use 'click_element'/'type_text'/'select_option' for standard page + interactions. - Use 'hover_action' for just a hover, with a click, or with a drag/drop. -- Use 'focus' for element positioning and visual focus. +- Use 'focus_element' for element positioning and visual focus. - Use 'solve_captcha' for clicking the checkbox of a CAPTCHA on the page. -- Use 'save_output' for saving page output as a PNG, a PDF, or an HTML file. +- Use 'save_page' for saving page output as a PNG, a PDF, or an HTML file. """ from __future__ import annotations import atexit @@ -109,8 +111,8 @@ def start_browser( ) -> str: """Launch a persistent SeleniumBase Pure CDP Mode browser session. - Call this before using browser interaction tools such as navigate, - get_content, click, type_text, or find_elements. The same browser + Call this before using browser interaction tools such as goto_url, + get_content, click_element, type_text, or find_elements. The same browser session remains active across subsequent MCP tool calls until close_browser is called or the server process exits. @@ -336,7 +338,7 @@ def get_page_info() -> dict[str, Any]: @mcp.tool() @handle_sb_errors -def navigate(url: str) -> str: +def goto_url(url: str) -> str: """Navigate the current browser tab to a URL. Use this when the browser needs to visit a new URL rather than move @@ -358,7 +360,7 @@ def navigate(url: str) -> str: A confirmation message containing the requested URL. Tool selection: - - Go to a new URL -> use navigate. + - Go to a new URL -> use goto_url. - Return to the previous page -> use manage_history(action="back"). - Go forward in history -> use manage_history(action="forward"). - Refresh the current page -> use manage_history(action="reload"). @@ -377,7 +379,7 @@ def manage_history( Use 'back' or 'forward' for history navigation, 'reload' to refresh while bypassing the cache, or 'list' to inspect history. - Use 'navigate' for an arbitrary URL. + Use 'goto_url' for navigation to an arbitrary URL. Args: action: @@ -473,7 +475,7 @@ def find_elements( use find_elements. - Need the visible text/HTML of a page or a single element -> use get_content. - - Need to click one of several matches -> use click with nth. + - Need to click one of several matches -> use click_element with nth. - Need to know whether an element is present/visible -> use check_condition. @@ -524,6 +526,7 @@ def get_content( Args: selector: CSS selector or SeleniumBase-supported XPath selector. + Default: "body". output_format: - "text": Return visible text from the selected element. @@ -532,18 +535,18 @@ def get_content( selected element. Returned URLs are normalized to full URLs with their protocol prefixes. - timeout: Maximum seconds to wait for the target element. Default: 5. Tool selection: - Need URL, title, origin, or User-Agent -> use get_page_info. - - Need visible text -> use output_format="text". - - Need page or element HTML -> use output_format="html". - - Need URLs from the page or an element -> use output_format="urls". + - Need visible text, html, or URLs on a page -> use get_content. - Need structured information about matching elements -> use find_elements. - Need to check element presence/visibility -> use check_condition. - Need to wait for content to appear -> use wait_for. + + If there's no matching element found within the timeout, + then @handle_sb_errors returns details from the exception raised. """ sb = _get_sb() @@ -583,9 +586,6 @@ def get_attributes( timeout: Maximum seconds to wait for the target element. Default: 5. - Returns: - The requested attribute(s). - Tool selection: - Need one or more HTML attribute values from a specific element -> use this tool. @@ -597,7 +597,7 @@ def get_attributes( This is a read-only operation. If there's no matching element found within the timeout, - then @handle_sb_errors will return details from the exception raised. + then @handle_sb_errors returns details from the exception raised. """ sb = _get_sb() @@ -680,7 +680,7 @@ def check_condition( @mcp.tool() @handle_sb_errors -def click( +def click_element( selector: str, nth: int | None = None, all_matches: bool = False, @@ -733,11 +733,14 @@ def click( indexed click. Default: True. Examples: - - Click one element: `click("button.submit")` - - Click the 2nd matching element: `click("button", nth=2)` - - Click all visible matches: `click(".dismiss", all_matches=True)` - - Click only if already visible: `click("#menu", only_if_visible=True)` - - Click inside a container: `click(".item", parent_selector="#result")` + - Click one element: `click_element("button.submit")` + - Click the 2nd matching element: `click_element("button", nth=2)` + - Click all visible matches: + `click_element(".dismiss", all_matches=True)` + - Click only if already visible: + `click_element("#menu", only_if_visible=True)` + - Click inside a container: + `click_element(".item", parent_selector="#result")` """ sb = _get_sb() @@ -768,11 +771,7 @@ def click( def hover_action( selector1: str, selector2: str | None = None, - action: Literal[ - "none", - "click", - "drag_and_drop", - ] = "none", + action: Literal["hover", "hover_and_click", "drag_and_drop"] = "hover", ) -> str: """Hover over an element, optionally click another, or drag-and-drop. @@ -782,46 +781,39 @@ def hover_action( Args: selector1: The primary element selector. - For action="none", this is the element to hover over. - For action="click", this is the element to hover over before - clicking selector2. + For action="hover", this is the element to hover over. + For action="hover_and_click", this is the element to hover over + before clicking selector2. For action="drag_and_drop", this is the draggable source element. selector2: The secondary element selector. - Required for action="click", where it identifies the element - revealed or targeted after hovering selector1. + Required for action="hover_and_click", where it identifies + the element revealed or targeted after hovering selector1. Required for action="drag_and_drop", where it identifies the destination/drop target. - Not used for action="none". + Not used for action="hover". action: - - "none": Hover over selector1 only. - - "click": Hover over selector1, then click selector2. + - "hover": Hover over selector1 only. + - "hover_and_click": Hover over selector1, then click selector2. - "drag_and_drop": Drag selector1 and drop it onto selector2. Returns: A confirmation message describing the performed operation. Tool selection: - - Simple hover -> action="none". - - Hover over one element and then click another -> action="click". + - Simple hover -> action="hover". + - Hover over one element and click another -> action="hover_and_click". - Drag one element onto another -> action="drag_and_drop". - - Notes: - For action="click", selector1 is the hover target and selector2 is - the click target. - - For action="drag_and_drop", selector1 is the source and selector2 - is the destination. """ sb = _get_sb() - if action == "none": + if action == "hover": sb.hover_element(selector1) return f"Hovered {selector1}" - if action == "click": + if action == "hover_and_click": if selector2 is None: return "Error: action='click' requires selector2." sb.hover_and_click(selector1, selector2) @@ -835,7 +827,7 @@ def hover_action( return ( f"Error: unknown action '{action}'. " - "Use 'none', 'click', or 'drag_and_drop'." + "Use 'hover', 'hover_and_click', or 'drag_and_drop'." ) @@ -934,8 +926,8 @@ def select_option( An error when the dropdown or requested option cannot be found. This tool is for native element to upload a local file.""" _get_sb().choose_file(selector, file_path, timeout=timeout) return f"Set file input {selector} to {file_path}" diff --git a/setup.py b/setup.py index e5b5bcf..3da46f1 100755 --- a/setup.py +++ b/setup.py @@ -70,7 +70,7 @@ setup( name="seleniumbase-mcp", - version="1.3.2", + version="1.3.3", description="MCP servers exposing SeleniumBase as tools for MCP clients.", long_description=long_description, long_description_content_type="text/markdown", @@ -140,7 +140,7 @@ ], python_requires=">=3.10", install_requires=[ - "seleniumbase[mcp]>=4.54.2", + "seleniumbase[mcp]>=4.54.3", "mcp[cli]>=2.2.0,<3.0.0", ], extras_require={ diff --git a/tests/test_mcp.py b/tests/test_mcp.py index 95b6f73..c76b382 100644 --- a/tests/test_mcp.py +++ b/tests/test_mcp.py @@ -24,7 +24,7 @@ async def test_server(name: str, command: str) -> None: assert "start_browser" in tools assert "close_browser" in tools - assert "navigate" in tools + assert "goto_url" in tools result = await client.call_tool( "start_browser", @@ -33,7 +33,7 @@ async def test_server(name: str, command: str) -> None: assert not result.is_error result = await client.call_tool( - "navigate", + "goto_url", { "url": ( "data:text/html,"