Skip to content

Commit b7ca550

Browse files
authored
Merge pull request #15 from seleniumbase/mcp-server-update-11
MCP Server: Update 11
2 parents c865d8a + 6944331 commit b7ca550

3 files changed

Lines changed: 83 additions & 79 deletions

File tree

cdp_server.py

Lines changed: 80 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -446,10 +446,16 @@ def find_elements(
446446
dictionaries. It does not return live SeleniumBase element objects.
447447
448448
Args:
449-
selector: A SeleniumBase-supported selector, typically CSS or XPath.
449+
selector: A CSS selector, or an XPath selector that SeleniumBase can
450+
convert to CSS. In sb.find_elements, SeleniumBase automatically
451+
attempts to convert XPath to CSS. Some XPath expressions, such
452+
as those using `contains(...)`, cannot be converted to CSS and
453+
therefore aren't supported by this tool.
450454
451455
timeout: Maximum number of seconds to wait for at least one matching
452-
element to appear.
456+
element to appear. If the selector is an XPath selector that
457+
cannot be converted into a valid CSS selector, then the wait
458+
might be less than the timeout.
453459
454460
include_html: If True, include each matching element's outer HTML.
455461
If False, return only tag name and text.
@@ -459,7 +465,8 @@ def find_elements(
459465
- count: Number of matching elements found.
460466
- matches: A list of element dictionaries containing tag_name and
461467
text, plus html when include_html=True.
462-
If there's an error, returns a string with error details.
468+
If there's an error during search, then "error" is added into the
469+
returned dictionary with error details.
463470
464471
Tool selection:
465472
- Need structured information about matching elements ->
@@ -470,77 +477,63 @@ def find_elements(
470477
- Need to know whether an element is present/visible ->
471478
use check_condition.
472479
473-
Note:
480+
Notes:
474481
Element handles cannot be persisted across MCP calls. If you find
475482
elements and then need to act on one, resolve it again with the
476483
appropriate interaction tool.
484+
485+
For uncaught errors, @handle_sb_errors returns strings.
477486
"""
478487
sb = _get_sb()
479-
elements = sb.find_elements(selector, timeout=timeout)
480-
481-
if include_html:
488+
try:
489+
elements = sb.find_elements(selector, timeout=timeout)
490+
except Exception as e:
482491
return {
483-
"count": len(elements),
484-
"matches": [
485-
{
486-
"tag_name": element.tag_name,
487-
"text": element.text,
488-
"html": element.get_html(),
489-
}
490-
for element in elements
491-
],
492+
"count": 0,
493+
"matches": [],
494+
"error": str(e),
492495
}
493496

497+
matches = []
498+
for element in elements:
499+
match = {
500+
"tag_name": element.tag_name,
501+
"text": element.text,
502+
}
503+
if include_html:
504+
match["html"] = element.get_html()
505+
matches.append(match)
506+
494507
return {
495-
"count": len(elements),
496-
"matches": [
497-
{
498-
"tag_name": element.tag_name,
499-
"text": element.text,
500-
}
501-
for element in elements
502-
],
508+
"count": len(matches),
509+
"matches": matches,
503510
}
504511

505512

506513
@mcp.tool()
507514
@handle_sb_errors
508515
def get_content(
509-
selector: str | None = None,
516+
selector: str = "body",
510517
output_format: Literal["text", "html", "urls"] = "text",
511-
include_shadow_dom: bool = True,
518+
timeout: float = 5,
512519
) -> str | list[str]:
513-
"""Read visible text, HTML, or discovered URLs from the current page.
520+
"""Read visible text, HTML, or discovered URLs from the selected element.
514521
515-
Use this tool when you need actual page content or URL information rather
516-
than page metadata.
522+
Use this tool when you need to get actual page content or URL information
523+
rather than page metadata.
517524
518525
Args:
519-
selector: Optional CSS selector or SeleniumBase text-matching selector
520-
identifying the element whose content should be read. For
521-
output_format="text" or "html", the selector scopes the returned
522-
content to that element. For output_format="urls", the selector
523-
scopes URL discovery to URLs within that element. When omitted,
524-
the operation applies to the whole page.
526+
selector: CSS selector or SeleniumBase-supported XPath selector.
525527
526528
output_format:
527-
- "text": Return visible text from the page or selected element.
528-
- "html": Return HTML from the page or selected element.
529-
- "urls": Return URLs discovered by SeleniumBase from the page
530-
or selected element. URLs associated with elements such as
531-
anchors, links, images, scripts, and metadata may be included.
532-
SeleniumBase returns full URLs with their URL protocol prefixes.
529+
- "text": Return visible text from the selected element.
530+
- "html": Return HTML from the selected element.
531+
- "urls": Return URLs discovered by SeleniumBase within the
532+
selected element. Returned URLs are normalized to full URLs
533+
with their protocol prefixes.
533534
534-
include_shadow_dom: When output_format="html" and selector is omitted,
535-
include any shadow-root HTML present in the page. This option has
536-
no effect for "text" or "urls", or when a selector is specified.
537535
538-
Returns:
539-
For output_format="text", a string containing visible text.
540-
For output_format="html", a string containing HTML.
541-
For output_format="urls", a list of URL strings. This is useful for
542-
crawling, link discovery, resource inspection, and finding candidate
543-
URLs before navigating to them.
536+
timeout: Maximum seconds to wait for the target element. Default: 5.
544537
545538
Tool selection:
546539
- Need URL, title, origin, or User-Agent -> use get_page_info.
@@ -554,43 +547,44 @@ def get_content(
554547
"""
555548
sb = _get_sb()
556549

557-
if output_format == "urls":
558-
return sb.get_all_urls(selector=selector)
559-
560-
if selector is None:
561-
if output_format == "html":
562-
return sb.get_page_source(
563-
include_shadow_dom=include_shadow_dom
564-
)
565-
return sb.get_text("body")
550+
if output_format == "text":
551+
return sb.get_text(selector, timeout=timeout)
566552

567553
if output_format == "html":
568-
return sb.get_element_html(selector)
554+
return sb.get_element_html(selector, timeout=timeout)
569555

570-
return sb.get_text(selector)
556+
if output_format == "urls":
557+
return sb.get_all_urls(selector=selector, timeout=timeout)
558+
559+
return (
560+
f"Error: unknown output_format '{output_format}'. "
561+
"Use 'text', 'html', or 'urls'."
562+
)
571563

572564

573565
@mcp.tool()
574566
@handle_sb_errors
575567
def get_attributes(
576568
selector: str,
577569
attribute: str | None = None,
578-
) -> Any:
579-
"""Read HTML attributes from a matching element.
570+
timeout: float = 5,
571+
) -> str | dict[str, Any] | None:
572+
"""Read HTML attributes from the first matching element.
580573
581-
Use this tool when you need the value of one or more HTML attributes
582-
such as href, src, value, class, id, name, type, aria-label, or data-*.
574+
Use this tool when you need the value of a specific HTML attribute,
575+
or all HTML attributes of an element. Attributes could be something
576+
such as href, src, value, class, id, name, type, aria-label, etc.
583577
584578
Args:
585-
selector: CSS selector or SeleniumBase text-matching selector for
586-
the target element.
579+
selector: CSS selector or SeleniumBase-supported XPath selector.
587580
588581
attribute: Specific HTML attribute to retrieve. When omitted, return
589-
all HTML attributes of the element as a dictionary.
582+
all HTML attributes of the first matching element as a dictionary.
583+
584+
timeout: Maximum seconds to wait for the target element. Default: 5.
590585
591586
Returns:
592-
The requested attribute value, or a dictionary containing all
593-
HTML attributes of the element when attribute is omitted.
587+
The requested attribute(s).
594588
595589
Tool selection:
596590
- Need one or more HTML attribute values from a specific element ->
@@ -601,13 +595,16 @@ def get_attributes(
601595
- Need to check element presence/visibility -> use 'check_condition'.
602596
603597
This is a read-only operation.
598+
599+
If there's no matching element found within the timeout,
600+
then @handle_sb_errors will return details from the exception raised.
604601
"""
605602
sb = _get_sb()
606603

607604
if attribute:
608-
return sb.get_attribute(selector, attribute)
605+
return sb.get_attribute(selector, attribute, timeout=timeout)
609606

610-
return sb.get_element_attributes(selector)
607+
return sb.get_element_attributes(selector, timeout=timeout)
611608

612609

613610
@mcp.tool()
@@ -963,6 +960,7 @@ def focus(
963960
"focus",
964961
"highlight",
965962
] = "scroll_to_element",
963+
timeout: float = 5,
966964
) -> str:
967965
"""Scroll to, focus, or highlight an element.
968966
@@ -976,16 +974,22 @@ def focus(
976974
- "scroll_to_element": Scroll the element into the viewport.
977975
- "focus": Move keyboard focus to the element.
978976
- "highlight": Temporarily highlight the element for debugging or
979-
demonstration. May affect timing and reduce stealth.
977+
demonstration by changing the border color. May affect timing
978+
and/or reduce stealth.
979+
980+
timeout: Maximum seconds to wait for the target element. Default: 5.
981+
982+
If there's no matching element found within the timeout,
983+
then @handle_sb_errors will return details from the exception raised.
980984
"""
981985
sb = _get_sb()
982986

983987
if action == "scroll_to_element":
984-
sb.scroll_into_view(selector)
988+
sb.scroll_into_view(selector, timeout=timeout)
985989
elif action == "focus":
986-
sb.find_element(selector).focus()
990+
sb.find_element(selector, timeout=timeout).focus()
987991
elif action == "highlight":
988-
sb.highlight(selector)
992+
sb.highlight(selector, timeout=timeout)
989993
else:
990994
return (
991995
f"Error: unknown action '{action}'. "

requirements.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
1-
seleniumbase[mcp]>=4.54.1
1+
seleniumbase[mcp]>=4.54.2
22
mcp[cli]>=2.2.0,<3.0.0

setup.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@
7070

7171
setup(
7272
name="seleniumbase-mcp",
73-
version="1.3.1",
73+
version="1.3.2",
7474
description="MCP servers exposing SeleniumBase as tools for MCP clients.",
7575
long_description=long_description,
7676
long_description_content_type="text/markdown",
@@ -140,7 +140,7 @@
140140
],
141141
python_requires=">=3.10",
142142
install_requires=[
143-
"seleniumbase[mcp]>=4.54.1",
143+
"seleniumbase[mcp]>=4.54.2",
144144
"mcp[cli]>=2.2.0,<3.0.0",
145145
],
146146
extras_require={

0 commit comments

Comments
 (0)