ferrum-mcp 1.0.0 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.env.example +37 -0
- data/CHANGELOG.md +72 -1
- data/README.md +30 -9
- data/docs/API_REFERENCE.md +300 -21
- data/docs/CONFIGURATION.md +34 -1
- data/docs/DEPLOYMENT.md +1 -0
- data/docs/DOCKER_BOTBROWSER.md +4 -0
- data/lib/ferrum_mcp/browser_manager.rb +56 -32
- data/lib/ferrum_mcp/cli/command_handler.rb +4 -3
- data/lib/ferrum_mcp/cli/server_runner.rb +19 -6
- data/lib/ferrum_mcp/configuration.rb +77 -18
- data/lib/ferrum_mcp/image_resizer.rb +43 -0
- data/lib/ferrum_mcp/server.rb +58 -91
- data/lib/ferrum_mcp/session.rb +67 -9
- data/lib/ferrum_mcp/session_manager.rb +15 -15
- data/lib/ferrum_mcp/tools/accept_cookies_tool.rb +11 -31
- data/lib/ferrum_mcp/tools/base_tool.rb +79 -38
- data/lib/ferrum_mcp/tools/clear_cookies_tool.rb +14 -43
- data/lib/ferrum_mcp/tools/click_tool.rb +58 -181
- data/lib/ferrum_mcp/tools/close_session_tool.rb +8 -30
- data/lib/ferrum_mcp/tools/close_tab_tool.rb +44 -0
- data/lib/ferrum_mcp/tools/create_session_tool.rb +38 -110
- data/lib/ferrum_mcp/tools/definition.rb +106 -0
- data/lib/ferrum_mcp/tools/drag_and_drop_tool.rb +43 -145
- data/lib/ferrum_mcp/tools/evaluate_js_tool.rb +6 -28
- data/lib/ferrum_mcp/tools/execute_script_tool.rb +6 -30
- data/lib/ferrum_mcp/tools/fill_form_tool.rb +37 -52
- data/lib/ferrum_mcp/tools/find_by_text_tool.rb +39 -114
- data/lib/ferrum_mcp/tools/get_attribute_tool.rb +10 -39
- data/lib/ferrum_mcp/tools/get_cookies_tool.rb +23 -52
- data/lib/ferrum_mcp/tools/get_html_tool.rb +22 -35
- data/lib/ferrum_mcp/tools/get_session_info_tool.rb +5 -21
- data/lib/ferrum_mcp/tools/get_text_tool.rb +22 -46
- data/lib/ferrum_mcp/tools/get_title_tool.rb +5 -28
- data/lib/ferrum_mcp/tools/get_url_tool.rb +5 -25
- data/lib/ferrum_mcp/tools/go_back_tool.rb +7 -30
- data/lib/ferrum_mcp/tools/go_forward_tool.rb +7 -30
- data/lib/ferrum_mcp/tools/hover_tool.rb +10 -51
- data/lib/ferrum_mcp/tools/list_sessions_tool.rb +5 -19
- data/lib/ferrum_mcp/tools/list_tabs_tool.rb +20 -0
- data/lib/ferrum_mcp/tools/navigate_tool.rb +17 -37
- data/lib/ferrum_mcp/tools/new_tab_tool.rb +36 -0
- data/lib/ferrum_mcp/tools/press_key_tool.rb +18 -63
- data/lib/ferrum_mcp/tools/query_shadow_dom_tool.rb +50 -205
- data/lib/ferrum_mcp/tools/refresh_tool.rb +7 -30
- data/lib/ferrum_mcp/tools/screenshot_tool.rb +29 -86
- data/lib/ferrum_mcp/tools/scroll_tool.rb +64 -0
- data/lib/ferrum_mcp/tools/select_option_tool.rb +56 -0
- data/lib/ferrum_mcp/tools/session_tool.rb +10 -9
- data/lib/ferrum_mcp/tools/set_cookie_tool.rb +15 -56
- data/lib/ferrum_mcp/tools/set_viewport_tool.rb +32 -0
- data/lib/ferrum_mcp/tools/snapshot_tool.rb +175 -0
- data/lib/ferrum_mcp/tools/solve_captcha_tool.rb +17 -35
- data/lib/ferrum_mcp/tools/switch_tab_tool.rb +29 -0
- data/lib/ferrum_mcp/tools/tab_tool.rb +40 -0
- data/lib/ferrum_mcp/tools/upload_file_tool.rb +50 -0
- data/lib/ferrum_mcp/tools/wait_for_network_idle_tool.rb +32 -0
- data/lib/ferrum_mcp/tools/wait_for_selector_tool.rb +59 -0
- data/lib/ferrum_mcp/tools/wait_for_text_tool.rb +53 -0
- data/lib/ferrum_mcp/transport/api_key_authenticator.rb +112 -0
- data/lib/ferrum_mcp/transport/client_address.rb +23 -0
- data/lib/ferrum_mcp/transport/http_server.rb +11 -1
- data/lib/ferrum_mcp/transport/rate_limiter.rb +2 -6
- data/lib/ferrum_mcp/transport/stdio_server.rb +8 -30
- data/lib/ferrum_mcp/url_policy.rb +88 -0
- data/lib/ferrum_mcp/version.rb +1 -1
- metadata +21 -3
data/docs/API_REFERENCE.md
CHANGED
|
@@ -22,9 +22,13 @@ Comprehensive documentation for all FerrumMCP browser automation tools.
|
|
|
22
22
|
- [press_key](#press_key)
|
|
23
23
|
- [hover](#hover)
|
|
24
24
|
- [drag_and_drop](#drag_and_drop)
|
|
25
|
+
- [scroll](#scroll)
|
|
26
|
+
- [select_option](#select_option)
|
|
27
|
+
- [upload_file](#upload_file)
|
|
25
28
|
- [accept_cookies](#accept_cookies)
|
|
26
29
|
- [solve_captcha](#solve_captcha)
|
|
27
30
|
- [Extraction](#extraction)
|
|
31
|
+
- [snapshot](#snapshot)
|
|
28
32
|
- [get_text](#get_text)
|
|
29
33
|
- [get_html](#get_html)
|
|
30
34
|
- [screenshot](#screenshot)
|
|
@@ -39,16 +43,22 @@ Comprehensive documentation for all FerrumMCP browser automation tools.
|
|
|
39
43
|
- [clear_cookies](#clear_cookies)
|
|
40
44
|
- [get_attribute](#get_attribute)
|
|
41
45
|
- [query_shadow_dom](#query_shadow_dom)
|
|
42
|
-
- [Waiting
|
|
43
|
-
- [
|
|
44
|
-
- [
|
|
45
|
-
- [
|
|
46
|
+
- [Waiting](#waiting)
|
|
47
|
+
- [wait_for_selector](#wait_for_selector)
|
|
48
|
+
- [wait_for_text](#wait_for_text)
|
|
49
|
+
- [wait_for_network_idle](#wait_for_network_idle)
|
|
50
|
+
- [Tabs & Viewport](#tabs--viewport)
|
|
51
|
+
- [list_tabs](#list_tabs)
|
|
52
|
+
- [new_tab](#new_tab)
|
|
53
|
+
- [switch_tab](#switch_tab)
|
|
54
|
+
- [close_tab](#close_tab)
|
|
55
|
+
- [set_viewport](#set_viewport)
|
|
46
56
|
|
|
47
57
|
---
|
|
48
58
|
|
|
49
59
|
## Overview
|
|
50
60
|
|
|
51
|
-
FerrumMCP provides
|
|
61
|
+
FerrumMCP provides 40 browser automation tools through the Model Context Protocol (MCP). All tools return responses in a standardized JSON format with a `success` boolean and either `data` or `error` fields.
|
|
52
62
|
|
|
53
63
|
## Important Notes
|
|
54
64
|
|
|
@@ -57,7 +67,8 @@ FerrumMCP provides 27+ browser automation tools through the Model Context Protoc
|
|
|
57
67
|
3. **Session Lifecycle**: Sessions auto-close after 30 minutes of inactivity or can be manually closed with `close_session`
|
|
58
68
|
4. **Multiple Sessions**: You can run multiple concurrent browser sessions with different configurations
|
|
59
69
|
5. **Screenshot Format**: The `screenshot` tool returns base64-encoded image data
|
|
60
|
-
6. **Selector Support**:
|
|
70
|
+
6. **Selector Support**: Every element-based tool accepts a CSS selector, an XPath (`xpath:` prefix or starting with `//`) or a snapshot ref (`ref:e12`, see [snapshot](#snapshot))
|
|
71
|
+
7. **Tabs**: tools act on the session's current tab; use [new_tab](#new_tab) / [switch_tab](#switch_tab) to change it
|
|
61
72
|
|
|
62
73
|
---
|
|
63
74
|
|
|
@@ -76,9 +87,9 @@ Create a new browser session with custom options. Returns a `session_id` to use
|
|
|
76
87
|
| bot_profile_id | string | No | BotBrowser profile ID from `ferrum://bot-profiles` resource |
|
|
77
88
|
| browser_path | string | No | Path to browser executable (legacy) |
|
|
78
89
|
| botbrowser_profile | string | No | Path to BotBrowser profile (legacy) |
|
|
79
|
-
| headless | boolean | No | Run in headless mode (default:
|
|
90
|
+
| headless | boolean | No | Run in headless mode (default: `BROWSER_HEADLESS`) |
|
|
80
91
|
| timeout | number | No | Browser timeout in seconds (default: 60) |
|
|
81
|
-
| browser_options | object | No |
|
|
92
|
+
| browser_options | object | No | Extra Chrome flags without the leading dashes (e.g., `{"window-size": "1920,1080", "lang": "fr-FR"}`) |
|
|
82
93
|
| metadata | object | No | Custom metadata for this session |
|
|
83
94
|
|
|
84
95
|
**Example Request:**
|
|
@@ -90,7 +101,7 @@ Create a new browser session with custom options. Returns a `session_id` to use
|
|
|
90
101
|
"headless": true,
|
|
91
102
|
"timeout": 60,
|
|
92
103
|
"browser_options": {
|
|
93
|
-
"
|
|
104
|
+
"window-size": "1920,1080"
|
|
94
105
|
},
|
|
95
106
|
"metadata": {
|
|
96
107
|
"user": "john",
|
|
@@ -110,7 +121,7 @@ Create a new browser session with custom options. Returns a `session_id` to use
|
|
|
110
121
|
"headless": true,
|
|
111
122
|
"timeout": 60,
|
|
112
123
|
"browser_options": {
|
|
113
|
-
"
|
|
124
|
+
"window-size": "1920,1080"
|
|
114
125
|
}
|
|
115
126
|
}
|
|
116
127
|
}
|
|
@@ -255,6 +266,8 @@ Navigate to a specific URL in the browser.
|
|
|
255
266
|
| Name | Type | Required | Description |
|
|
256
267
|
|------|------|----------|-------------|
|
|
257
268
|
| url | string | Yes | URL to navigate to (must include http:// or https://) |
|
|
269
|
+
| wait_for_idle | boolean | No | Wait for the network to settle after navigation (default: true) |
|
|
270
|
+
| timeout | number | No | Seconds to wait for network idle (default: 30) |
|
|
258
271
|
| session_id | string | Yes | Session ID to use |
|
|
259
272
|
|
|
260
273
|
**Example Request:**
|
|
@@ -280,7 +293,8 @@ Navigate to a specific URL in the browser.
|
|
|
280
293
|
|
|
281
294
|
**Notes:**
|
|
282
295
|
- URL must start with `http://` or `https://`
|
|
283
|
-
- Automatically waits for network to be idle after navigation
|
|
296
|
+
- Automatically waits for network to be idle after navigation (disable with `wait_for_idle: false` for pages that stream forever)
|
|
297
|
+
- Refused when the host is denied by `ALLOWED_HOSTS` / `BLOCKED_HOSTS` (see the Configuration Guide)
|
|
284
298
|
- Throws timeout error if navigation takes longer than browser timeout
|
|
285
299
|
|
|
286
300
|
---
|
|
@@ -403,7 +417,7 @@ Click on an element using a CSS selector or XPath.
|
|
|
403
417
|
|
|
404
418
|
| Name | Type | Required | Description |
|
|
405
419
|
|------|------|----------|-------------|
|
|
406
|
-
| selector | string | Yes | CSS selector
|
|
420
|
+
| selector | string | Yes | CSS selector, XPath (`xpath:` prefix) or snapshot ref (`ref:e12`) |
|
|
407
421
|
| wait | number | No | Seconds to wait for element (default: 5) |
|
|
408
422
|
| force | boolean | No | Force click even if hidden/not visible (default: false) |
|
|
409
423
|
| session_id | string | Yes | Session ID to use |
|
|
@@ -453,8 +467,9 @@ Fill one or more form fields with values.
|
|
|
453
467
|
|
|
454
468
|
| Name | Type | Required | Description |
|
|
455
469
|
|------|------|----------|-------------|
|
|
456
|
-
| selector | string | Yes | CSS selector of the field |
|
|
457
|
-
| value | string | Yes | Value to
|
|
470
|
+
| selector | string | Yes | CSS selector, XPath or snapshot ref of the field |
|
|
471
|
+
| value | string | Yes | Value to type |
|
|
472
|
+
| clear | boolean | No | Clear the field before typing (default: false) |
|
|
458
473
|
|
|
459
474
|
**Example Request:**
|
|
460
475
|
|
|
@@ -631,6 +646,92 @@ Drag an element and drop it onto another element or coordinates.
|
|
|
631
646
|
|
|
632
647
|
---
|
|
633
648
|
|
|
649
|
+
### scroll
|
|
650
|
+
|
|
651
|
+
Scroll the page or a scrollable element, or bring an element into view.
|
|
652
|
+
|
|
653
|
+
**Parameters:**
|
|
654
|
+
|
|
655
|
+
| Name | Type | Required | Description |
|
|
656
|
+
|------|------|----------|-------------|
|
|
657
|
+
| selector | string | No | Element to scroll into view, or the scrollable container when `direction` is given |
|
|
658
|
+
| direction | string | No | `up`, `down`, `left`, `right`, `top`, `bottom` |
|
|
659
|
+
| amount | number | No | Pixels for up/down/left/right (default: 500) |
|
|
660
|
+
| x | number | No | Absolute horizontal scroll position |
|
|
661
|
+
| y | number | No | Absolute vertical scroll position |
|
|
662
|
+
| session_id | string | Yes | Session ID to use |
|
|
663
|
+
|
|
664
|
+
**Example Request:**
|
|
665
|
+
|
|
666
|
+
```json
|
|
667
|
+
{ "name": "scroll", "arguments": { "direction": "down", "amount": 800, "session_id": "uuid-1234" } }
|
|
668
|
+
```
|
|
669
|
+
|
|
670
|
+
**Example Response:**
|
|
671
|
+
|
|
672
|
+
```json
|
|
673
|
+
{ "x": 0, "y": 800, "target": "window" }
|
|
674
|
+
```
|
|
675
|
+
|
|
676
|
+
**Notes:**
|
|
677
|
+
- `selector` alone scrolls that element into the middle of the viewport
|
|
678
|
+
- `selector` + `direction` scrolls inside that container (infinite lists, modals)
|
|
679
|
+
|
|
680
|
+
---
|
|
681
|
+
|
|
682
|
+
### select_option
|
|
683
|
+
|
|
684
|
+
Select option(s) in a `<select>` element and fire `input`/`change` events.
|
|
685
|
+
|
|
686
|
+
**Parameters:**
|
|
687
|
+
|
|
688
|
+
| Name | Type | Required | Description |
|
|
689
|
+
|------|------|----------|-------------|
|
|
690
|
+
| selector | string | Yes | Selector of the `<select>` |
|
|
691
|
+
| value | string | No | Option value |
|
|
692
|
+
| label | string | No | Option visible text |
|
|
693
|
+
| index | integer | No | Zero-based option index |
|
|
694
|
+
| values | array | No | Several option values (multiple selects) |
|
|
695
|
+
| session_id | string | Yes | Session ID to use |
|
|
696
|
+
|
|
697
|
+
**Example Request:**
|
|
698
|
+
|
|
699
|
+
```json
|
|
700
|
+
{ "name": "select_option", "arguments": { "selector": "#plan", "label": "Pro plan", "session_id": "uuid-1234" } }
|
|
701
|
+
```
|
|
702
|
+
|
|
703
|
+
**Example Response:**
|
|
704
|
+
|
|
705
|
+
```json
|
|
706
|
+
{ "selector": "#plan", "selected": [{ "value": "pro", "label": "Pro plan" }] }
|
|
707
|
+
```
|
|
708
|
+
|
|
709
|
+
---
|
|
710
|
+
|
|
711
|
+
### upload_file
|
|
712
|
+
|
|
713
|
+
Attach files from the **server's** filesystem to an `<input type="file">`.
|
|
714
|
+
|
|
715
|
+
**Parameters:**
|
|
716
|
+
|
|
717
|
+
| Name | Type | Required | Description |
|
|
718
|
+
|------|------|----------|-------------|
|
|
719
|
+
| selector | string | Yes | Selector of the file input |
|
|
720
|
+
| paths | array | Yes | Absolute paths of the files |
|
|
721
|
+
| session_id | string | Yes | Session ID to use |
|
|
722
|
+
|
|
723
|
+
**Example Request:**
|
|
724
|
+
|
|
725
|
+
```json
|
|
726
|
+
{ "name": "upload_file", "arguments": { "selector": "#avatar", "paths": ["/tmp/avatar.png"], "session_id": "uuid-1234" } }
|
|
727
|
+
```
|
|
728
|
+
|
|
729
|
+
**Notes:**
|
|
730
|
+
- Files must live under one of `UPLOAD_ALLOWED_DIRS` (default: current directory and the temp dir); symlinks are resolved
|
|
731
|
+
- Missing files are rejected before touching the browser
|
|
732
|
+
|
|
733
|
+
---
|
|
734
|
+
|
|
634
735
|
### accept_cookies
|
|
635
736
|
|
|
636
737
|
Automatically detect and accept cookie consent banners.
|
|
@@ -736,6 +837,50 @@ Automatically detect and solve audio CAPTCHA challenges using Whisper speech rec
|
|
|
736
837
|
|
|
737
838
|
## Extraction
|
|
738
839
|
|
|
840
|
+
### snapshot
|
|
841
|
+
|
|
842
|
+
Compact, agent-friendly view of the page: interactive elements (links, buttons, inputs, selects, custom roles) and headings, each with a **stable ref** that every other tool accepts as selector (`ref:e12`). Far cheaper than `get_html` for deciding what to do next.
|
|
843
|
+
|
|
844
|
+
**Parameters:**
|
|
845
|
+
|
|
846
|
+
| Name | Type | Required | Description |
|
|
847
|
+
|------|------|----------|-------------|
|
|
848
|
+
| mode | string | No | `interactive` (default) or `full` (adds images, labels, paragraphs, alerts) |
|
|
849
|
+
| selector | string | No | Restrict the snapshot to a subtree |
|
|
850
|
+
| include_hidden | boolean | No | Include hidden elements (default: false) |
|
|
851
|
+
| max_elements | integer | No | Maximum number of elements (default: 200) |
|
|
852
|
+
| format | string | No | `text` (default) or `json` |
|
|
853
|
+
| session_id | string | Yes | Session ID to use |
|
|
854
|
+
|
|
855
|
+
**Example Request:**
|
|
856
|
+
|
|
857
|
+
```json
|
|
858
|
+
{ "name": "snapshot", "arguments": { "session_id": "uuid-1234" } }
|
|
859
|
+
```
|
|
860
|
+
|
|
861
|
+
**Example Response (text):**
|
|
862
|
+
|
|
863
|
+
```json
|
|
864
|
+
{
|
|
865
|
+
"url": "https://example.com/signup",
|
|
866
|
+
"title": "Sign up",
|
|
867
|
+
"count": 6,
|
|
868
|
+
"total": 6,
|
|
869
|
+
"truncated": false,
|
|
870
|
+
"snapshot": "[e1] heading(1) \"Create your account\"\n[e2] textbox \"Email address\" type=email placeholder=\"you@example.com\"\n[e3] checkbox \"newsletter\" checked\n[e4] combobox \"plan\"\n[e5] button \"Create account\"\n[e6] link \"Documentation\" href=/docs"
|
|
871
|
+
}
|
|
872
|
+
```
|
|
873
|
+
|
|
874
|
+
**Example Response (json):** `elements` is an array of `{ ref, role, tag, name, selector, href?, type?, placeholder?, value?, checked?, disabled?, level? }`.
|
|
875
|
+
|
|
876
|
+
**Notes:**
|
|
877
|
+
- Refs are stored on the elements (`data-fmcp-ref`) and stay stable across snapshots until the page navigates
|
|
878
|
+
- Typical loop: `snapshot` → `click ref:e5` → `wait_for_text` → `snapshot`
|
|
879
|
+
- Names follow accessibility rules: `aria-label`, `<label for>`, placeholder, name, then visible text
|
|
880
|
+
- Radio buttons also show their `value`, so unlabeled buttons of one group stay distinguishable
|
|
881
|
+
|
|
882
|
+
---
|
|
883
|
+
|
|
739
884
|
### get_text
|
|
740
885
|
|
|
741
886
|
Extract text content from one or more elements.
|
|
@@ -744,8 +889,10 @@ Extract text content from one or more elements.
|
|
|
744
889
|
|
|
745
890
|
| Name | Type | Required | Description |
|
|
746
891
|
|------|------|----------|-------------|
|
|
747
|
-
| selector | string | Yes | CSS selector
|
|
892
|
+
| selector | string | Yes | CSS selector, XPath (`xpath:` prefix) or snapshot ref |
|
|
748
893
|
| multiple | boolean | No | Extract from all matching elements (default: false) |
|
|
894
|
+
| wait | number | No | Seconds to wait for the element (default: 5) |
|
|
895
|
+
| raw | boolean | No | Return the text exactly as in the DOM (default: false, whitespace is collapsed and trimmed) |
|
|
749
896
|
| session_id | string | Yes | Session ID to use |
|
|
750
897
|
|
|
751
898
|
**Example Request (Single Element):**
|
|
@@ -809,7 +956,8 @@ Get HTML content of the page or a specific element.
|
|
|
809
956
|
|
|
810
957
|
| Name | Type | Required | Description |
|
|
811
958
|
|------|------|----------|-------------|
|
|
812
|
-
| selector | string | No |
|
|
959
|
+
| selector | string | No | Selector of the element to get the outerHTML of |
|
|
960
|
+
| max_length | integer | No | Truncate the HTML to this many characters (response has `truncated: true`) |
|
|
813
961
|
| session_id | string | Yes | Session ID to use |
|
|
814
962
|
|
|
815
963
|
**Example Request (Full Page):**
|
|
@@ -1040,6 +1188,134 @@ Find elements by their text content using XPath.
|
|
|
1040
1188
|
|
|
1041
1189
|
---
|
|
1042
1190
|
|
|
1191
|
+
## Waiting
|
|
1192
|
+
|
|
1193
|
+
### wait_for_selector
|
|
1194
|
+
|
|
1195
|
+
Wait until an element reaches a state.
|
|
1196
|
+
|
|
1197
|
+
**Parameters:**
|
|
1198
|
+
|
|
1199
|
+
| Name | Type | Required | Description |
|
|
1200
|
+
|------|------|----------|-------------|
|
|
1201
|
+
| selector | string | Yes | CSS selector, XPath or snapshot ref |
|
|
1202
|
+
| state | string | No | `visible` (default), `hidden`, `attached`, `detached` |
|
|
1203
|
+
| timeout | number | No | Maximum seconds to wait (default: 10) |
|
|
1204
|
+
| session_id | string | Yes | Session ID to use |
|
|
1205
|
+
|
|
1206
|
+
**Example Response:**
|
|
1207
|
+
|
|
1208
|
+
```json
|
|
1209
|
+
{ "found": true, "state": "visible", "selector": "#results", "elapsed_ms": 640 }
|
|
1210
|
+
```
|
|
1211
|
+
|
|
1212
|
+
On timeout the tool fails with `Timed out after 10s waiting for #results to be visible`.
|
|
1213
|
+
|
|
1214
|
+
---
|
|
1215
|
+
|
|
1216
|
+
### wait_for_text
|
|
1217
|
+
|
|
1218
|
+
Wait until text is visible on the page (optionally inside an element).
|
|
1219
|
+
|
|
1220
|
+
**Parameters:**
|
|
1221
|
+
|
|
1222
|
+
| Name | Type | Required | Description |
|
|
1223
|
+
|------|------|----------|-------------|
|
|
1224
|
+
| text | string | Yes | Text to wait for |
|
|
1225
|
+
| selector | string | No | CSS selector of the element to search in (default: body) |
|
|
1226
|
+
| exact | boolean | No | Match the whole text exactly (default: substring) |
|
|
1227
|
+
| timeout | number | No | Maximum seconds to wait (default: 10) |
|
|
1228
|
+
| session_id | string | Yes | Session ID to use |
|
|
1229
|
+
|
|
1230
|
+
---
|
|
1231
|
+
|
|
1232
|
+
### wait_for_network_idle
|
|
1233
|
+
|
|
1234
|
+
Wait until the page has no pending requests (after a click that triggers XHR/fetch, for instance).
|
|
1235
|
+
|
|
1236
|
+
**Parameters:**
|
|
1237
|
+
|
|
1238
|
+
| Name | Type | Required | Description |
|
|
1239
|
+
|------|------|----------|-------------|
|
|
1240
|
+
| timeout | number | No | Maximum seconds to wait (default: 30) |
|
|
1241
|
+
| connections | integer | No | Pending connections tolerated as idle (default: 0) |
|
|
1242
|
+
| session_id | string | Yes | Session ID to use |
|
|
1243
|
+
|
|
1244
|
+
**Example Response:**
|
|
1245
|
+
|
|
1246
|
+
```json
|
|
1247
|
+
{ "idle": true, "elapsed_ms": 120, "url": "https://example.com/results" }
|
|
1248
|
+
```
|
|
1249
|
+
|
|
1250
|
+
---
|
|
1251
|
+
|
|
1252
|
+
## Tabs & Viewport
|
|
1253
|
+
|
|
1254
|
+
Tools operate on the session's **current tab**. Links with `target="_blank"` open new tabs that you can activate with `switch_tab`.
|
|
1255
|
+
|
|
1256
|
+
### list_tabs
|
|
1257
|
+
|
|
1258
|
+
**Example Response:**
|
|
1259
|
+
|
|
1260
|
+
```json
|
|
1261
|
+
{
|
|
1262
|
+
"count": 2,
|
|
1263
|
+
"tabs": [
|
|
1264
|
+
{ "index": 0, "tab_id": "A1B2...", "url": "https://example.com/", "title": "Example", "current": false },
|
|
1265
|
+
{ "index": 1, "tab_id": "C3D4...", "url": "https://example.com/help", "title": "Help", "current": true }
|
|
1266
|
+
]
|
|
1267
|
+
}
|
|
1268
|
+
```
|
|
1269
|
+
|
|
1270
|
+
---
|
|
1271
|
+
|
|
1272
|
+
### new_tab
|
|
1273
|
+
|
|
1274
|
+
Open a new tab (optionally at a URL) and make it current.
|
|
1275
|
+
|
|
1276
|
+
| Name | Type | Required | Description |
|
|
1277
|
+
|------|------|----------|-------------|
|
|
1278
|
+
| url | string | No | URL to open (subject to the navigation policy) |
|
|
1279
|
+
| session_id | string | Yes | Session ID to use |
|
|
1280
|
+
|
|
1281
|
+
---
|
|
1282
|
+
|
|
1283
|
+
### switch_tab
|
|
1284
|
+
|
|
1285
|
+
Make another tab current, by `tab_id` (from `list_tabs`) or zero-based `index`.
|
|
1286
|
+
|
|
1287
|
+
| Name | Type | Required | Description |
|
|
1288
|
+
|------|------|----------|-------------|
|
|
1289
|
+
| tab_id | string | No | Tab to activate |
|
|
1290
|
+
| index | integer | No | Zero-based index (alternative to tab_id) |
|
|
1291
|
+
| session_id | string | Yes | Session ID to use |
|
|
1292
|
+
|
|
1293
|
+
---
|
|
1294
|
+
|
|
1295
|
+
### close_tab
|
|
1296
|
+
|
|
1297
|
+
Close a tab (the current one by default). The last remaining tab cannot be closed; closing the current tab activates the first remaining one.
|
|
1298
|
+
|
|
1299
|
+
| Name | Type | Required | Description |
|
|
1300
|
+
|------|------|----------|-------------|
|
|
1301
|
+
| tab_id | string | No | Tab to close (default: current) |
|
|
1302
|
+
| index | integer | No | Zero-based index (alternative to tab_id) |
|
|
1303
|
+
| session_id | string | Yes | Session ID to use |
|
|
1304
|
+
|
|
1305
|
+
---
|
|
1306
|
+
|
|
1307
|
+
### set_viewport
|
|
1308
|
+
|
|
1309
|
+
| Name | Type | Required | Description |
|
|
1310
|
+
|------|------|----------|-------------|
|
|
1311
|
+
| width | integer | Yes | Viewport width in pixels |
|
|
1312
|
+
| height | integer | Yes | Viewport height in pixels |
|
|
1313
|
+
| scale_factor | number | No | Device scale factor (0 keeps the default) |
|
|
1314
|
+
| mobile | boolean | No | Emulate a mobile device (default: false) |
|
|
1315
|
+
| session_id | string | Yes | Session ID to use |
|
|
1316
|
+
|
|
1317
|
+
---
|
|
1318
|
+
|
|
1043
1319
|
## Advanced
|
|
1044
1320
|
|
|
1045
1321
|
### execute_script
|
|
@@ -1179,6 +1455,7 @@ Set a cookie in the browser.
|
|
|
1179
1455
|
| path | string | No | Cookie path (default: /) |
|
|
1180
1456
|
| secure | boolean | No | Secure flag (default: false) |
|
|
1181
1457
|
| httponly | boolean | No | HttpOnly flag (default: false) |
|
|
1458
|
+
| expires | integer | No | Expiry as a Unix timestamp in seconds |
|
|
1182
1459
|
| session_id | string | Yes | Session ID to use |
|
|
1183
1460
|
|
|
1184
1461
|
**Example Request:**
|
|
@@ -1387,7 +1664,7 @@ All tools return responses in this standard format:
|
|
|
1387
1664
|
1. **Missing session_id**: "session_id is required. Create a session first using create_session tool."
|
|
1388
1665
|
2. **Invalid session**: "Session not found: {session_id}"
|
|
1389
1666
|
3. **Element not found**: "Element not found: {selector}"
|
|
1390
|
-
4. **Timeout errors**: "Navigation timed out" / "
|
|
1667
|
+
4. **Timeout errors**: "Navigation timed out" / "Timed out after 10s waiting for #x to be visible"
|
|
1391
1668
|
5. **JavaScript errors**: "Failed to execute script: {error}"
|
|
1392
1669
|
|
|
1393
1670
|
---
|
|
@@ -1397,10 +1674,12 @@ All tools return responses in this standard format:
|
|
|
1397
1674
|
1. **Always create a session first**: Use `create_session` before any browser operations
|
|
1398
1675
|
2. **Use resource discovery**: Query `ferrum://browsers` and `ferrum://bot-profiles` to see available configurations
|
|
1399
1676
|
3. **Handle sessions properly**: Close sessions when done to free resources
|
|
1400
|
-
4. **
|
|
1401
|
-
5. **
|
|
1402
|
-
6. **
|
|
1403
|
-
7. **
|
|
1677
|
+
4. **Snapshot before acting**: call `snapshot` and use the returned refs (`ref:e12`) instead of guessing selectors
|
|
1678
|
+
5. **Wait explicitly**: use `wait_for_selector` / `wait_for_text` / `wait_for_network_idle` rather than fixed delays
|
|
1679
|
+
6. **Use appropriate selectors**: Prefer CSS selectors for performance, XPath for complex queries
|
|
1680
|
+
7. **Set timeouts wisely**: Increase timeout for slow-loading pages
|
|
1681
|
+
8. **Force clicks sparingly**: Only use `force: true` when necessary, as it bypasses visibility checks
|
|
1682
|
+
9. **Screenshot optimization**: Use `jpeg` format for smaller file sizes when quality is not critical
|
|
1404
1683
|
|
|
1405
1684
|
---
|
|
1406
1685
|
|
data/docs/CONFIGURATION.md
CHANGED
|
@@ -17,12 +17,18 @@ See `.env.example` for a complete example configuration.
|
|
|
17
17
|
| `MCP_SERVER_HOST` | HTTP server host | `0.0.0.0` |
|
|
18
18
|
| `MCP_SERVER_PORT` | HTTP server port | `3000` |
|
|
19
19
|
| `LOG_LEVEL` | Logging level (debug/info/warn/error) | `info` |
|
|
20
|
+
| `LOG_FILE` | Log destination: a file path or `stderr` | `./logs/ferrum_mcp.log` |
|
|
21
|
+
| `TRUST_PROXY` | Use `X-Forwarded-For` for rate limiting and audit logs (only behind a trusted proxy) | `false` |
|
|
22
|
+
|
|
23
|
+
**Logging**: logs never go to STDOUT because the stdio transport uses it for the protocol. The default file lives
|
|
24
|
+
under the current working directory (never inside the installed gem); when that directory is not writable the
|
|
25
|
+
server falls back to the system temp directory. Use `LOG_FILE=stderr` in containers.
|
|
20
26
|
|
|
21
27
|
### Browser Defaults
|
|
22
28
|
|
|
23
29
|
| Variable | Description | Default |
|
|
24
30
|
|----------|-------------|---------|
|
|
25
|
-
| `BROWSER_HEADLESS` | Run browser in headless mode | `
|
|
31
|
+
| `BROWSER_HEADLESS` | Run browser in headless mode | `false` |
|
|
26
32
|
| `BROWSER_TIMEOUT` | Browser timeout in seconds | `60` |
|
|
27
33
|
|
|
28
34
|
### Session Management
|
|
@@ -42,6 +48,33 @@ See `.env.example` for a complete example configuration.
|
|
|
42
48
|
| `RATE_LIMIT_WINDOW` | Time window in seconds | `60` |
|
|
43
49
|
|
|
44
50
|
**Note**: Rate limiting is applied per client IP address. When exceeded, HTTP 429 (Too Many Requests) is returned with a `Retry-After` header.
|
|
51
|
+
The client address is the socket peer unless `TRUST_PROXY=true`, in which case the first `X-Forwarded-For` entry is used.
|
|
52
|
+
Never enable `TRUST_PROXY` on a server reachable directly by clients: they could rotate the header to escape the limit.
|
|
53
|
+
|
|
54
|
+
### Navigation Policy
|
|
55
|
+
|
|
56
|
+
| Variable | Description | Default |
|
|
57
|
+
|----------|-------------|---------|
|
|
58
|
+
| `ALLOWED_HOSTS` | Comma-separated hosts that may be visited (only these when set) | _(unset)_ |
|
|
59
|
+
| `BLOCKED_HOSTS` | Comma-separated hosts that may never be visited (wins over the allow list) | _(unset)_ |
|
|
60
|
+
|
|
61
|
+
Entries accept an exact host (`example.com`), a wildcard suffix (`*.example.com`, which also matches
|
|
62
|
+
`example.com`) or a CIDR range for IP literals (`10.0.0.0/8`). The policy is enforced by `navigate` and
|
|
63
|
+
`new_tab` before the browser is touched. It checks the URL as written and does not resolve DNS, so treat it
|
|
64
|
+
as a guard rail for exposed HTTP deployments rather than a full SSRF defense.
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
BLOCKED_HOSTS=localhost,127.0.0.0/8,10.0.0.0/8,172.16.0.0/12,192.168.0.0/16,169.254.0.0/16,*.internal
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
### File Uploads
|
|
71
|
+
|
|
72
|
+
| Variable | Description | Default |
|
|
73
|
+
|----------|-------------|---------|
|
|
74
|
+
| `UPLOAD_ALLOWED_DIRS` | Comma-separated directories `upload_file` may read from | current directory, temp dir |
|
|
75
|
+
|
|
76
|
+
The `upload_file` tool attaches files from the **server's** filesystem. Paths are resolved (symlinks included)
|
|
77
|
+
and must live under one of the allowed directories.
|
|
45
78
|
|
|
46
79
|
## Multi-Browser Configuration
|
|
47
80
|
|
data/docs/DEPLOYMENT.md
CHANGED
|
@@ -489,6 +489,7 @@ server {
|
|
|
489
489
|
proxy_set_header X-Real-IP $remote_addr;
|
|
490
490
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
|
491
491
|
proxy_set_header X-Forwarded-Proto $scheme;
|
|
492
|
+
# Set TRUST_PROXY=true on the FerrumMCP side so rate limiting uses this header
|
|
492
493
|
|
|
493
494
|
# Timeouts
|
|
494
495
|
proxy_connect_timeout 60s;
|
data/docs/DOCKER_BOTBROWSER.md
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
# Docker with BotBrowser Integration
|
|
2
2
|
|
|
3
|
+
> **BotBrowser version**: the image installs the latest upstream BotBrowser release by default, because old
|
|
4
|
+
> releases are removed upstream. Pin one with `--build-arg BOTBROWSER_VERSION=155.0.8059.5`. Building behind a
|
|
5
|
+
> shared IP may hit the GitHub API rate limit; pass a token with `--secret id=github_token,env=GITHUB_TOKEN`.
|
|
6
|
+
|
|
3
7
|
This guide explains how to build and run FerrumMCP Docker images with BotBrowser support for anti-detection capabilities.
|
|
4
8
|
|
|
5
9
|
## Overview
|
|
@@ -9,6 +9,32 @@ module FerrumMCP
|
|
|
9
9
|
@config = config
|
|
10
10
|
@logger = config.logger
|
|
11
11
|
@browser = nil
|
|
12
|
+
@page = nil
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
# Current tab. Tools operate on this page so that tab switching works.
|
|
16
|
+
def page
|
|
17
|
+
raise BrowserError, 'Browser is not active' unless @browser
|
|
18
|
+
|
|
19
|
+
@page = nil if @page && !page_open?(@page)
|
|
20
|
+
@page ||= @browser.page
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# All open tabs of the default browser context
|
|
24
|
+
def pages
|
|
25
|
+
raise BrowserError, 'Browser is not active' unless @browser
|
|
26
|
+
|
|
27
|
+
@browser.pages
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def select_page(page)
|
|
31
|
+
@page = page
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def create_page
|
|
35
|
+
raise BrowserError, 'Browser is not active' unless @browser
|
|
36
|
+
|
|
37
|
+
@browser.create_page
|
|
12
38
|
end
|
|
13
39
|
|
|
14
40
|
def start
|
|
@@ -45,10 +71,12 @@ module FerrumMCP
|
|
|
45
71
|
|
|
46
72
|
logger.info 'Stopping browser...'
|
|
47
73
|
@browser.quit
|
|
48
|
-
@browser = nil
|
|
49
74
|
logger.info 'Browser stopped'
|
|
50
75
|
rescue StandardError => e
|
|
51
76
|
logger.error "Error stopping browser: #{e.message}"
|
|
77
|
+
ensure
|
|
78
|
+
@browser = nil
|
|
79
|
+
@page = nil
|
|
52
80
|
end
|
|
53
81
|
|
|
54
82
|
def restart
|
|
@@ -60,41 +88,37 @@ module FerrumMCP
|
|
|
60
88
|
!@browser.nil?
|
|
61
89
|
end
|
|
62
90
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
options
|
|
91
|
+
# True when a browser is started and its Chrome process still exists.
|
|
92
|
+
# Cheap (no CDP round-trip): a signal-0 check on the process id.
|
|
93
|
+
def healthy?
|
|
94
|
+
return false unless @browser
|
|
95
|
+
|
|
96
|
+
pid = @browser.process&.pid
|
|
97
|
+
return true unless pid
|
|
98
|
+
|
|
99
|
+
Process.kill(0, pid)
|
|
100
|
+
true
|
|
101
|
+
rescue Errno::ESRCH
|
|
102
|
+
logger.warn "Browser process #{pid} is gone"
|
|
103
|
+
false
|
|
104
|
+
rescue Errno::EPERM
|
|
105
|
+
true
|
|
80
106
|
end
|
|
81
107
|
|
|
82
|
-
|
|
83
|
-
options = {
|
|
84
|
-
'no-sandbox' => nil,
|
|
85
|
-
'disable-dev-shm-usage' => nil,
|
|
86
|
-
'disable-blink-features' => 'AutomationControlled',
|
|
87
|
-
'disable-gpu' => nil
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
# Additional options for CI environments
|
|
91
|
-
options['disable-setuid-sandbox'] = nil if ENV['CI']
|
|
108
|
+
private
|
|
92
109
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
110
|
+
def page_open?(page)
|
|
111
|
+
@browser.pages.any? { |p| p.target_id == page.target_id }
|
|
112
|
+
rescue StandardError
|
|
113
|
+
false
|
|
114
|
+
end
|
|
97
115
|
|
|
116
|
+
# Browser flags come from the session configuration (defaults merged with
|
|
117
|
+
# session-specific options, keys without leading dashes).
|
|
118
|
+
def computed_browser_options
|
|
119
|
+
options = config.merged_browser_options
|
|
120
|
+
logger.info "Using BotBrowser profile: #{options['bot-profile']}" if options['bot-profile']
|
|
121
|
+
logger.info "Using user profile: #{options['user-data-dir']}" if options['user-data-dir']
|
|
98
122
|
options
|
|
99
123
|
end
|
|
100
124
|
end
|
|
@@ -21,9 +21,10 @@ module FerrumMCP
|
|
|
21
21
|
end
|
|
22
22
|
|
|
23
23
|
def self.start_server(options)
|
|
24
|
-
# Load
|
|
25
|
-
|
|
26
|
-
require 'dotenv
|
|
24
|
+
# Load .env (if any) only when starting the server. No bundler/setup
|
|
25
|
+
# here: the installed gem must work outside a Bundler project.
|
|
26
|
+
require 'dotenv'
|
|
27
|
+
Dotenv.load
|
|
27
28
|
|
|
28
29
|
# Set environment variables from options
|
|
29
30
|
ENV['MCP_SERVER_HOST'] = options[:host]
|