Skip to content

Commit 3de06e5

Browse files
authored
Merge pull request #4501 from seleniumbase/mcp-server-patch-14
MCP Server: Patch 14
2 parents 82e3b51 + 4c0aaa7 commit 3de06e5

7 files changed

Lines changed: 88 additions & 60 deletions

File tree

‎mcp_servers/README.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,7 @@ Tools here are grouped around a shared `selector` convention. Several near-ident
168168
| Assertions | `assert_condition(check: element_present/element_visible/text_visible/title/url/url_contains)` |
169169
| Cookies & storage | `manage_cookies(action: get_all/clear/save/load)`, `manage_storage(storage: local/session, action: get/set)` |
170170
| Scrolling | `scroll_page(direction: up/down/top/bottom, amount)` |
171-
| Windows & tabs | `manage_window(action: get_rect/set_rect/maximize/minimize)`, `manage_tabs(action: list/open/switch/switch_newest/close_active)` |
171+
| Windows & tabs | `manage_window(action: get_rect/set_rect/maximize/minimize)`, `manage_tabs(action: list_tabs/open_new_tab/switch_to_tab/switch_to_newest_tab/close_active_tab)` |
172172
| Captcha | `solve_captcha` |
173173
| Output & misc | `save_page(format: screenshot/html/pdf)`, `run_javascript` |
174174

‎mcp_servers/pyproject.toml‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[project]
22
name = "seleniumbase-mcp"
3-
version = "1.3.4dev0"
3+
version = "1.3.5dev0"
44
description = "MCP server exposing SeleniumBase CDP Mode as tools for MCP clients."
55
readme = "README.md"
66
requires-python = ">=3.10"

‎mcp_servers/server.py‎

Lines changed: 81 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -700,11 +700,11 @@ def click_element(
700700
parent element.
701701
702702
Selection behavior:
703-
- `nth` is 1-based and takes precedence over every other click mode.
704-
- Otherwise, `all_matches=True` clicks every currently visible match.
705-
- Otherwise, `only_if_visible=True` clicks only if a match is visible.
706-
- Otherwise, `parent_selector` scopes the click to a nested element.
707-
- With none of the above, performs a normal SeleniumBase click.
703+
- `nth` is 1-based and takes precedence over every other click mode.
704+
- Otherwise, `all_matches=True` clicks every currently visible match.
705+
- Otherwise, `only_if_visible=True` clicks only if a match is visible.
706+
- Otherwise, `parent_selector` scopes the click to a nested element.
707+
- With none of the above, performs a normal SeleniumBase click.
708708
709709
Args:
710710
selector: CSS selector, XPath selector, or supported SeleniumBase
@@ -720,8 +720,8 @@ def click_element(
720720
in order of appearance. Ignored when `nth` is provided. Use only
721721
when multiple clicks are intentionally desired, such as for
722722
clicking all the checkboxes in a section of a webpage.
723-
If a click induces page navigation, then subsequent clicks are
724-
cancelled.
723+
If any of the click actions induces page navigation, then
724+
subsequent clicks are cancelled without any exceptions raised.
725725
726726
only_if_visible: If True, click only when the target is already
727727
visible; do not wait for it to become visible.
@@ -737,14 +737,23 @@ def click_element(
737737
indexed click. Default: True.
738738
739739
Examples:
740-
- Click one element: `click_element("button.submit")`
741-
- Click the 2nd matching element: `click_element("button", nth=2)`
742-
- Click all visible matches:
743-
`click_element(".dismiss", all_matches=True)`
744-
- Click only if already visible:
745-
`click_element("#menu", only_if_visible=True)`
746-
- Click inside a container:
747-
`click_element(".item", parent_selector="#result")`
740+
- Click the first button: `click_element("button")`
741+
- Click the 2nd button: `click_element("button", nth=2)`
742+
- Click all checkboxes:
743+
`click_element('input[type="checkbox"]', all_matches=True)`
744+
- Click the first visible link:
745+
`click_element("a", only_if_visible=True)`
746+
- Click the first button that's inside the first iframe:
747+
`click_element("button", parent_selector="iframe")`
748+
749+
Error behavior:
750+
With the exception of using 'only_if_visible=True', if there's no
751+
matching element found within the timeout, then @handle_sb_errors
752+
returns details from the exception raised.
753+
754+
When not to use:
755+
- Do not use this tool if you need to hover an element first before
756+
clicking; use hover_action with action="hover_and_click" instead.
748757
"""
749758
sb = _get_sb()
750759

@@ -815,11 +824,15 @@ def hover_action(
815824
Returns:
816825
A confirmation message describing the performed operation's result.
817826
818-
Errors:
827+
Error behavior:
819828
If a required element cannot be found or interacted with within the
820829
applicable wait period, or if an error occurs during the action, the
821830
resulting exception message is returned through @handle_sb_errors.
831+
Failing actions such as failed hover_and_click will raise exceptions.
822832
833+
When not to use:
834+
- Do not use this tool to click if you don't need to hover an element
835+
before clicking another; use 'click' instead.
823836
"""
824837
sb = _get_sb()
825838

@@ -1410,11 +1423,14 @@ def scroll_page(
14101423
up/down scrolling. For example, amount=25 scrolls approximately
14111424
one quarter of the viewport height.
14121425
1413-
Values greater than 100 for `amount` are allowed.
1414-
For example, 200 means approximately two viewport heights.
1426+
Notes:
1427+
Values greater than 100 for `amount` are allowed.
1428+
For example, 200 means approximately two viewport heights.
14151429
1416-
Use focus_element(action="scroll_to_element") when the goal is to reveal
1417-
a specific element rather than scroll the page by a relative amount.
1430+
Tool selection:
1431+
- Need to reveal a specific element ->
1432+
use 'focus_element' with action="scroll_to_element".
1433+
- Need to scroll the page by a relative amount -> use 'scroll_page'.
14181434
"""
14191435
sb = _get_sb()
14201436

@@ -1475,8 +1491,9 @@ def manage_window(
14751491
14761492
height: Window height for "set_rect".
14771493
1478-
Use this tool for browser-window geometry and state.
1479-
Use `manage_tabs` for switching between browser tabs.
1494+
Notes:
1495+
Use this tool for browser-window geometry and state.
1496+
Use `manage_tabs` for switching between browser tabs.
14801497
"""
14811498
sb = _get_sb()
14821499

@@ -1507,12 +1524,12 @@ def manage_window(
15071524
@handle_sb_errors
15081525
def manage_tabs(
15091526
action: Literal[
1510-
"list",
1511-
"open",
1512-
"switch",
1513-
"switch_newest",
1514-
"close_active",
1515-
] = "list",
1527+
"list_tabs",
1528+
"open_new_tab",
1529+
"switch_to_tab",
1530+
"switch_to_newest_tab",
1531+
"close_active_tab",
1532+
] = "list_tabs",
15161533
url: str | None = None,
15171534
tab_index: int | None = None,
15181535
switch_to: bool = True,
@@ -1524,25 +1541,34 @@ def manage_tabs(
15241541
15251542
Args:
15261543
action:
1527-
- "list": Return each tab's index, URL, and title.
1528-
Use this to find the tab_index for "switch".
1529-
- "open": Open a new tab, optionally navigating it to `url`.
1530-
- "switch": Switch to the tab at tab_index from "list".
1531-
- "switch_newest": Switch to the newest tab.
1532-
- "close_active": Close the active tab.
1544+
- "list_tabs": Return each tab's index, URL, and title.
1545+
Use this to find the tab_index for "switch_to_tab".
1546+
- "open_new_tab": Open a new tab, optionally navigating to `url`.
1547+
- "switch_to_tab": Switch to the tab at tab_index from "list_tabs".
1548+
- "switch_to_newest_tab": Switch to the newest tab.
1549+
- "close_active_tab": Close the active tab. This action must be
1550+
followed by a 'manage_tabs' action that switches to a new
1551+
tab, such as "switch_to_tab" or "switch_to_newest_tab".
1552+
1553+
url: URL for "open_new_tab". If not provided, "about:blank" is used.
15331554
1534-
url: URL for "open".
1555+
tab_index: Tab index from "list_tabs" that is only used for the
1556+
"switch_to_tab" action.)
15351557
1536-
tab_index: Tab index from "list" for "switch".
1558+
switch_to: If using "open_new_tab", switch to the new tab when True.
15371559
1538-
switch_to: For "open", switch to the new tab when True.
1560+
Notes:
1561+
Tab indexes are session-relative and may change after tabs are opened
1562+
or closed. Use "list_tabs" to get current indexes before switching
1563+
by index.
15391564
1540-
Tab indexes are session-relative and may change after tabs are opened or
1541-
closed. Use "list" to get current indexes before switching by index.
1565+
Error behavior:
1566+
If there's an error during any of the tab actions, then
1567+
@handle_sb_errors will propagate the exception as an error message.
15421568
"""
15431569
sb = _get_sb()
15441570

1545-
if action == "list":
1571+
if action == "list_tabs":
15461572
tabs = sb.get_tabs()
15471573
return [
15481574
{
@@ -1553,11 +1579,13 @@ def manage_tabs(
15531579
for i, t in enumerate(tabs)
15541580
]
15551581

1556-
if action == "open":
1582+
if action == "open_new_tab":
1583+
if not url:
1584+
url = "about:blank"
15571585
sb.open_new_tab(url=url, switch_to=switch_to)
15581586
return f"Opened new tab (url={url!r}, switch_to={switch_to})"
15591587

1560-
if action == "switch":
1588+
if action == "switch_to_tab":
15611589
if tab_index is None:
15621590
return (
15631591
"Error: action='switch' requires tab_index "
@@ -1575,17 +1603,17 @@ def manage_tabs(
15751603
sb.switch_to_tab(tabs[tab_index])
15761604
return f"Switched to tab {tab_index}"
15771605

1578-
if action == "switch_newest":
1606+
if action == "switch_to_newest_tab":
15791607
sb.switch_to_newest_tab()
15801608
return "Switched to newest tab."
15811609

1582-
if action == "close_active":
1610+
if action == "close_active_tab":
15831611
sb.close_active_tab()
15841612
return "Closed active tab."
15851613

15861614
return (
1587-
f"Error: unknown action '{action}'. Use 'list', 'open', 'switch', "
1588-
f"'switch_newest', or 'close_active'."
1615+
f"Error: unknown action '{action}'. Use 'list_tabs', 'open_new_tab', "
1616+
f"'switch_to_tab', 'switch_to_newest_tab', or 'close_active_tab'."
15891617
)
15901618

15911619

@@ -1601,12 +1629,12 @@ def solve_captcha() -> str:
16011629
16021630
This tool attempts to interact with CAPTCHA controls such as Cloudflare
16031631
Turnstile, reCAPTCHA, hCaptcha, DataDome Slider, or FriendlyCaptcha via
1604-
the Chrome DevTools Protocol (CDP), which is stealthier than JavaScript
1605-
actions because CDP actions can avoid triggering `isTrusted: false`.
1632+
the Chrome DevTools Protocol (CDP), which is usually stealthier than
1633+
JavaScript because CDP actions can avoid triggering `isTrusted: false`.
16061634
16071635
This tool automatically detects the coordinates of CAPTCHA checkboxes
16081636
for determining the correct location to perform the click. If no CAPTCHA
1609-
is detected on the current page, then no click action is performed.
1637+
is detected on the current page, then no click action is attempted.
16101638
16111639
The tool does not guarantee that the CAPTCHA was solved. Some CAPTCHA
16121640
controls are embedded inside shadow DOM or otherwise do not expose an
@@ -1616,13 +1644,13 @@ def solve_captcha() -> str:
16161644
Tool workflow:
16171645
1. Inspect the webpage with get_content when you need to
16181646
determine whether CAPTCHA-related controls are present.
1619-
2. Call solve_captcha to attempt the CAPTCHA interaction.
1620-
3. Use get_page_info, get_content, check_if_condition,
1621-
or manage_cookies to inspect resulting page/session state.
1647+
2. Call 'solve_captcha' to attempt the CAPTCHA interaction.
1648+
3. Use 'get_page_info', 'get_content', 'check_if_condition',
1649+
or 'manage_cookies' to inspect resulting page/session state.
16221650
16231651
Returns:
16241652
A message confirming that the CAPTCHA interaction was attempted.
1625-
(There's no guarantee that the CAPTCHA challenge was solved.)
1653+
The message is the same for both successful and failed attempts.
16261654
"""
16271655
sb = _get_sb()
16281656
sb.solve_captcha()

‎requirements.txt‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ rich>=15.0.0,<16
6565
# --- Testing Requirements --- #
6666
# ("pip install -r requirements.txt" also installs this, but "pip install -e ." won't.)
6767

68-
coverage>=7.16.0
68+
coverage>=7.16.1
6969
pytest-cov>=7.1.0
7070
flake8==7.3.0
7171
mccabe==0.7.0

‎seleniumbase/__version__.py‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
# seleniumbase package
2-
__version__ = "4.54.4"
2+
__version__ = "4.54.5"

‎server.json‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
"name": "io.github.seleniumbase/seleniumbase",
44
"title": "SeleniumBase MCP",
55
"description": "Stealthy browser automation, testing, and web-scraping via CDP Mode.",
6-
"version": "4.54.4",
6+
"version": "4.54.5",
77
"repository": {
88
"url": "https://github.com/seleniumbase/SeleniumBase",
99
"source": "github"
@@ -13,7 +13,7 @@
1313
{
1414
"registryType": "pypi",
1515
"identifier": "seleniumbase",
16-
"version": "4.54.4",
16+
"version": "4.54.5",
1717
"transport": {
1818
"type": "stdio"
1919
},

‎setup.py‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -240,7 +240,7 @@
240240
# pip install -e .[coverage]
241241
# Usage: coverage run -m pytest; coverage html; coverage report
242242
"coverage": [
243-
'coverage>=7.16.0',
243+
'coverage>=7.16.1',
244244
'pytest-cov>=7.1.0',
245245
],
246246
# pip install -e .[flake8]

0 commit comments

Comments
 (0)