Skip to main content
Glama

Browser-MCP Server

by Euraxluo
test_browser_fastmcp_client.py63.6 kB
import unittest import json from fastmcp import Client from browser_fastmcp_server import mcp as browsers_mcp class TestBrowserFastMCPClient(unittest.IsolatedAsyncioTestCase): async def asyncSetUp(self): self.client = Client(browsers_mcp) async def asyncTearDown(self): await self.client.__aexit__(None, None, None) async def test_create_chrome_instance(self): async with self.client: result = await self.client.call_tool("create_chrome_instance", {"headless": True}) print("create_chrome_instance:", result) self.assertTrue("session_id" in str(result)) await self.client.call_tool("close_instance", {"session_id": result.data.session_id}) async def test_close_instance(self): async with self.client: session = await self.client.call_tool("create_chrome_instance", {"headless": True}) session_id = session.data.session_id result = await self.client.call_tool("close_instance", {"session_id": session_id}) self.assertIn(f"Chrome instance {session_id} closed successfully.", result.data) async def test_get_instance_info(self): async with self.client: session = await self.client.call_tool("create_chrome_instance", {"headless": True}) session_id = session.data.session_id result = await self.client.call_tool("get_instance_info", {"session_id": session_id}) print("get_instance_info:", result) self.assertEqual(result.data.session_id, session_id) await self.client.call_tool("close_instance", {"session_id": session_id}) async def test_get_browser_status(self): async with self.client: result = await self.client.call_tool("get_browser_status", {}) print("get_browser_status:", result) self.assertEqual(result.data, []) session = await self.client.call_tool("create_chrome_instance", {"headless": True}) result = await self.client.call_tool("get_browser_status", {}) print("get_browser_status:", result) self.assertEqual(len(result.data), 1) self.assertEqual(result.data[0]['session_id'], session.data.session_id) await self.client.call_tool("close_instance", {"session_id": session.data.session_id}) async def test_close_all_instances(self): async with self.client: await self.client.call_tool("create_chrome_instance", {"headless": True}) result = await self.client.call_tool("close_all_instances", {}) print("close_all_instances:", result) self.assertIn("All browser instances closed.", str(result)) async def test_set_and_get_browser_config(self): async with self.client: session = await self.client.call_tool("create_chrome_instance", {"headless": True}) await self.client.call_tool("wait", {"seconds": 3}) session_id = session.data.session_id # Set headless to False and change viewport size await self.client.call_tool("set_browser_config", { "session_id": session_id, "headless": True, "viewport_width": 250, "viewport_height": 720 }) await self.client.call_tool("wait", {"seconds": 3}) result = await self.client.call_tool("get_browser_config", {"session_id": session_id}) print("get_browser_config:", result) self.assertEqual(result.data.session_id, session_id) self.assertEqual(result.data.config.viewport_width, 250) self.assertEqual(result.data.config.viewport_height, 720) await self.client.call_tool("close_instance", {"session_id": session_id}) async def test_navigate_to_and_back_forward(self): async with self.client: session = await self.client.call_tool("create_chrome_instance", {"headless": True}) session_id = session.data.session_id urls = [ "https://httpbin.org/html", "https://example.com/", "https://www.baidu.com/" ] await self.client.call_tool("navigate_to", {"session_id": session_id, "url": urls[0]}) await self.client.call_tool("wait", {"seconds": 1}) state = await self.client.call_tool("get_page_state", {"session_id": session_id}) self.assertTrue(urls[0].rstrip('/') in state.data.url) await self.client.call_tool("navigate_to", {"session_id": session_id, "url": urls[1]}) await self.client.call_tool("wait", {"seconds": 1}) state = await self.client.call_tool("get_page_state", {"session_id": session_id}) self.assertTrue(urls[1].rstrip('/') in state.data.url) await self.client.call_tool("navigate_to", {"session_id": session_id, "url": urls[2]}) await self.client.call_tool("wait", {"seconds": 1}) state = await self.client.call_tool("get_page_state", {"session_id": session_id}) self.assertTrue("baidu.com" in state.data.url) await self.client.call_tool("navigate_back", {"session_id": session_id}) await self.client.call_tool("wait", {"seconds": 1}) state = await self.client.call_tool("get_page_state", {"session_id": session_id}) self.assertTrue(urls[1].rstrip('/') in state.data.url) await self.client.call_tool("navigate_back", {"session_id": session_id}) await self.client.call_tool("wait", {"seconds": 1}) state = await self.client.call_tool("get_page_state", {"session_id": session_id}) self.assertTrue(urls[0].rstrip('/') in state.data.url) await self.client.call_tool("navigate_forward", {"session_id": session_id}) await self.client.call_tool("wait", {"seconds": 1}) state = await self.client.call_tool("get_page_state", {"session_id": session_id}) self.assertTrue(urls[1].rstrip('/') in state.data.url) await self.client.call_tool("navigate_forward", {"session_id": session_id}) await self.client.call_tool("wait", {"seconds": 1}) state = await self.client.call_tool("get_page_state", {"session_id": session_id}) self.assertTrue("baidu.com" in state.data.url) await self.client.call_tool("close_instance", {"session_id": session_id}) async def test_get_tabs_info_and_switch_close_tab(self): async with self.client: # 1. create browser instance session = await self.client.call_tool("create_chrome_instance", {"headless": True}) session_id = session.data.session_id # 2. open first tab, visit page A await self.client.call_tool("navigate_to", {"session_id": session_id, "url": "https://httpbin.org/html"}) await self.client.call_tool("wait", {"seconds": 3}) tabs = await self.client.call_tool("get_tabs_info", {"session_id": session_id}) self.assertEqual(len(tabs.data), 1, "should have one tab after first navigation") page_id1 = tabs.data[0]['id'] # 3. open second tab, visit page B await self.client.call_tool("navigate_to", {"session_id": session_id, "url": "https://example.com/", "new_tab": True}) await self.client.call_tool("wait", {"seconds": 3}) tabs = await self.client.call_tool("get_tabs_info", {"session_id": session_id}) self.assertEqual(len(tabs.data), 2, "should have two tabs after opening new tab") page_id2 = tabs.data[1]['id'] # 4. switch to first tab, verify page A await self.client.call_tool("switch_tab", {"session_id": session_id, "page_id": page_id1}) await self.client.call_tool("wait", {"seconds": 3}) state1 = await self.client.call_tool("get_page_state", {"session_id": session_id}) self.assertIn("httpbin.org/html", state1.data.url) # 5. switch to second tab, verify page B await self.client.call_tool("switch_tab", {"session_id": session_id, "page_id": page_id2}) await self.client.call_tool("wait", {"seconds": 3}) state2 = await self.client.call_tool("get_page_state", {"session_id": session_id}) self.assertIn("example.com", state2.data.url) # 6. close second tab, verify first tab still exists and is available await self.client.call_tool("close_tab", {"session_id": session_id, "page_id": page_id2}) await self.client.call_tool("wait", {"seconds": 3}) tabs_after = await self.client.call_tool("get_tabs_info", {"session_id": session_id}) self.assertEqual(len(tabs_after.data), 1, "should have one tab after closing second tab") self.assertEqual(tabs_after.data[0]['id'], page_id1, "remaining tab should be the first one") # verify page A again state1_again = await self.client.call_tool("get_page_state", {"session_id": session_id}) self.assertIn("httpbin.org/html", state1_again.data.url) # 7. close first tab, verify all tabs are closed or become about:blank await self.client.call_tool("close_tab", {"session_id": session_id, "page_id": page_id1}) await self.client.call_tool("wait", {"seconds": 3}) tabs_final = await self.client.call_tool("get_tabs_info", {"session_id": session_id}) self.assertEqual(len(tabs_final.data), 1, "should have one tab (about:blank) after closing all tabs") self.assertEqual(tabs_final.data[0]['url'], 'about:blank', "tab should be blank after all closed") await self.client.call_tool("close_instance", {"session_id": session_id}) async def test_multi_tab_navigation_and_switch(self): async with self.client: # 1. create browser instance session = await self.client.call_tool("create_chrome_instance", {"headless": True}) session_id = session.data.session_id # 2. open first tab, visit page A await self.client.call_tool("navigate_to", {"session_id": session_id, "url": "https://httpbin.org/image/png"}) await self.client.call_tool("wait", {"seconds": 1}) tabs = await self.client.call_tool("get_tabs_info", {"session_id": session_id}) page_id1 = tabs.data[0]['id'] tab1 = next(tab for tab in tabs.data if tab['id'] == page_id1) self.assertTrue(tab1['url'].startswith("https://httpbin.org/image/png"), f"Tab1 url should start with https://httpbin.org/image/png, got {tab1['url']}") # 3. open second tab, visit page B await self.client.call_tool("navigate_to", {"session_id": session_id, "url": "https://httpbin.org/json", "new_tab": True}) await self.client.call_tool("wait", {"seconds": 1}) tabs = await self.client.call_tool("get_tabs_info", {"session_id": session_id}) page_id2 = tabs.data[-1]['id'] tab2 = next(tab for tab in tabs.data if tab['id'] == page_id2) self.assertEqual(tab2['url'], "https://httpbin.org/json", f"Tab2 url should be https://httpbin.org/json, got {tab2['url']}") # switch to tab1, verify tab1's history is independent await self.client.call_tool("switch_tab", {"session_id": session_id, "page_id": page_id1}) await self.client.call_tool("wait", {"seconds": 1}) tabs = await self.client.call_tool("get_tabs_info", {"session_id": session_id}) tab1 = next(tab for tab in tabs.data if tab['id'] == page_id1) self.assertTrue(tab1['url'].startswith("https://httpbin.org/image/png"), f"Tab1 url should start with https://httpbin.org/image/png after switch, got {tab1['url']}") # 4. switch to first tab, forward/backward navigation await self.client.call_tool("switch_tab", {"session_id": session_id, "page_id": page_id1}) await self.client.call_tool("navigate_to", {"session_id": session_id, "url": "https://httpbin.org/image/png?2"}) await self.client.call_tool("wait", {"seconds": 1}) tabs = await self.client.call_tool("get_tabs_info", {"session_id": session_id}) tab1 = next(tab for tab in tabs.data if tab['id'] == page_id1) self.assertTrue(tab1['url'].startswith("https://httpbin.org/image/png?2"), f"Tab1 url should start with https://httpbin.org/image/png?2, got {tab1['url']}") await self.client.call_tool("switch_tab", {"session_id": session_id, "page_id": page_id1}) await self.client.call_tool("navigate_back", {"session_id": session_id}) await self.client.call_tool("wait", {"seconds": 1}) tabs = await self.client.call_tool("get_tabs_info", {"session_id": session_id}) tab1 = next(tab for tab in tabs.data if tab['id'] == page_id1) self.assertTrue(tab1['url'].startswith("https://httpbin.org/image/png"), f"Tab1 url should start with https://httpbin.org/image/png after back, got {tab1['url']}") # 5. switch to second tab, forward/backward navigation await self.client.call_tool("switch_tab", {"session_id": session_id, "page_id": page_id2}) await self.client.call_tool("navigate_to", {"session_id": session_id, "url": "https://httpbin.org/json?2"}) await self.client.call_tool("wait", {"seconds": 1}) tabs = await self.client.call_tool("get_tabs_info", {"session_id": session_id}) tab2 = next(tab for tab in tabs.data if tab['id'] == page_id2) self.assertTrue(tab2['url'].startswith("https://httpbin.org/json?2"), f"Tab2 url should start with https://httpbin.org/json?2, got {tab2['url']}") await self.client.call_tool("switch_tab", {"session_id": session_id, "page_id": page_id2}) await self.client.call_tool("navigate_back", {"session_id": session_id}) await self.client.call_tool("wait", {"seconds": 1}) tabs = await self.client.call_tool("get_tabs_info", {"session_id": session_id}) tab2 = next(tab for tab in tabs.data if tab['id'] == page_id2) self.assertEqual(tab2['url'], "https://httpbin.org/json", f"Tab2 url should be https://httpbin.org/json after back navigation, got {tab2['url']}") # 7. close one tab, verify remaining tab status await self.client.call_tool("close_tab", {"session_id": session_id, "page_id": page_id2}) tabs_after = await self.client.call_tool("get_tabs_info", {"session_id": session_id}) self.assertEqual(len(tabs_after.data), 1, "Only one tab should remain after closing tab2") remaining_tab = tabs_after.data[0] self.assertEqual(remaining_tab['id'], page_id1, "The remaining tab should be tab1") self.assertTrue(remaining_tab['url'].startswith("https://httpbin.org/image/png"), f"Remaining tab url should start with https://httpbin.org/image/png, got {remaining_tab['url']}") await self.client.call_tool("close_instance", {"session_id": session_id}) async def test_get_page_state(self): async with self.client: session = await self.client.call_tool("create_chrome_instance", {"headless": True}) session_id = session.data.session_id await self.client.call_tool("navigate_to", {"session_id": session_id, "url": "https://httpbin.org/html"}) await self.client.call_tool("wait", {"seconds": 2}) result = await self.client.call_tool("get_page_state", {"session_id": session_id}) await self.client.call_tool("wait", {"seconds": 2}) print("get_page_state:", result) self.assertTrue(result.data.url.startswith("https://httpbin.org/html")) await self.client.call_tool("close_instance", {"session_id": session_id}) async def test_click_news_link(self): async with self.client: session = await self.client.call_tool("create_chrome_instance", {"headless": True}) session_id = session.data.session_id await self.client.call_tool("navigate_to", {"session_id": session_id, "url": "https://www.baidu.com"}) await self.client.call_tool("wait", {"seconds": 10}) state = await self.client.call_tool("get_page_state", {"session_id": session_id}) news_element = next((el for el in state.data.elements if "新闻" in el.text and el.tag == "a"), None) self.assertIsNotNone(news_element, "not found '新闻' link") print("news_element:", news_element) await self.client.call_tool("click_element_by_xpath", {"session_id": session_id, "xpath": news_element.xpath}) await self.client.call_tool("wait", {"seconds": 10}) await self.client.call_tool("take_screenshot", {"session_id": session_id}) tabs = await self.client.call_tool("get_tabs_info", {"session_id": session_id}) new_tab = None for tab in tabs.data: if "news.baidu.com" in tab['url']: new_tab = tab break await self.client.call_tool("switch_tab", {"session_id": session_id, "page_id": new_tab['id']}) new_state = await self.client.call_tool("get_page_state", {"session_id": session_id}) self.assertIn("news.baidu.com", new_state.data.url) await self.client.call_tool("close_instance", {"session_id": session_id}) async def test_click_bing_link(self): async with self.client: session = await self.client.call_tool("create_chrome_instance", {"headless": True}) session_id = session.data.session_id await self.client.call_tool("navigate_to", {"session_id": session_id, "url": "https://www.bing.com"}) await self.client.call_tool("wait", {"seconds": 10}) await self.client.call_tool("take_screenshot", {"session_id": session_id}) import time timeout = 20 # wait for 10 seconds start = time.time() while True: await self.client.call_tool("wait", {"seconds": 5}) result = await self.client.call_tool("take_screenshot", {"session_id": session_id}) print("take_screenshot:", result) new_state = await self.client.call_tool("get_page_state", {"session_id": session_id}) news_element = next((el for el in new_state.data.elements if ( "图片" in el.text or "images" in el.text.lower()) and el.tag == "a"), None) if news_element: break if time.time() - start > timeout: raise TimeoutError("wait for image page timeout") await self.client.call_tool("wait", {"seconds": 2}) await self.client.call_tool("click_element", {"session_id": session_id, "index": news_element.index}) await self.client.call_tool("wait", {"seconds": 1}) tabs = await self.client.call_tool("get_tabs_info", {"session_id": session_id}) await self.client.call_tool("wait", {"seconds": 1}) new_tab = None for tab in tabs.data: if "bing.com/images" in tab['url']: new_tab = tab break await self.client.call_tool("wait", {"seconds": 1}) await self.client.call_tool("switch_tab", {"session_id": session_id, "page_id": new_tab['id']}) await self.client.call_tool("wait", {"seconds": 1}) new_state = await self.client.call_tool("get_page_state", {"session_id": session_id}) self.assertIn("bing.com/images", new_state.data.url) await self.client.call_tool("close_instance", {"session_id": session_id}) async def test_input_text(self): async with self.client: session = await self.client.call_tool("create_chrome_instance", {"headless": True}) session_id = session.data.session_id if hasattr(session, 'data') else session.session_id await self.client.call_tool("navigate_to", {"session_id": session_id, "url": "https://www.baidu.com"}) import time timeout = 10 start = time.time() input_idx = None while True: state = await self.client.call_tool("get_page_state", {"session_id": session_id}) for el in state.data.elements: if el.tag.lower() == "textarea" or el.tag.lower() == "input": input_idx = el.index break if input_idx is not None: break if time.time() - start > timeout: raise TimeoutError("wait for input element timeout") await self.client.call_tool("wait", {"seconds": 1}) result = await self.client.call_tool("input_text", {"session_id": session_id, "index": input_idx, "text": "Playwright Python"}) self.assertIn("Input text", str(result)) await self.client.call_tool("click_element_by_xpath", {"session_id": session_id, "xpath": el.xpath}) await self.client.call_tool("wait", {"seconds": 1}) timeout = 10 start = time.time() found_result = False while True: state = await self.client.call_tool("get_page_state", {"session_id": session_id}) for el in state.data.elements: if el.tag.lower() == "li" or el.tag.lower() == "div": if "playwright" in el.text.lower() and "python" in el.text.lower(): found_result = True break if found_result: break if time.time() - start > timeout: raise TimeoutError("wait for search result timeout") await self.client.call_tool("wait", {"seconds": 2}) self.assertTrue(found_result, "not found search result") await self.client.call_tool("close_instance", {"session_id": session_id}) async def test_send_keys(self): async with self.client: session = await self.client.call_tool("create_chrome_instance", {"headless": True}) session_id = session.data.session_id if hasattr(session, 'data') else session.session_id await self.client.call_tool("navigate_to", {"session_id": session_id, "url": "https://www.baidu.com"}) import time timeout = 10 start = time.time() input_idx = None while True: state = await self.client.call_tool("get_page_state", {"session_id": session_id}) for el in state.data.elements: if el.tag.lower() == "textarea" or el.tag.lower() == "input": input_idx = el.index break if input_idx is not None: break if time.time() - start > timeout: raise TimeoutError("wait for input element timeout") await self.client.call_tool("wait", {"seconds": 1}) result = await self.client.call_tool("input_text", {"session_id": session_id, "index": input_idx, "text": "Playwright Python"}) self.assertIn("Input text", str(result)) await self.client.call_tool("click_element_by_xpath", {"session_id": session_id, "xpath": el.xpath}) result = await self.client.call_tool("send_keys", {"session_id": session_id, "keys": "Enter"}) self.assertIn("Sent keys", str(result)) timeout = 10 start = time.time() found_result = False while True: state = await self.client.call_tool("get_page_state", {"session_id": session_id}) for el in state.data.elements: print(el.xpath) if "div/div/div[1]/div/h3/a/div/div/p/span/span/span" in el.xpath: found_result = True break if time.time() - start > timeout: break await self.client.call_tool("wait", {"seconds": 1}) self.assertTrue(found_result, "not found search result page") await self.client.call_tool("send_keys", {"session_id": session_id, "keys": "PageDown"}) await self.client.call_tool("wait", {"seconds": 1}) await self.client.call_tool("send_keys", {"session_id": session_id, "keys": "PageUp"}) await self.client.call_tool("wait", {"seconds": 1}) await self.client.call_tool("send_keys", {"session_id": session_id, "keys": "End"}) await self.client.call_tool("wait", {"seconds": 1}) await self.client.call_tool("send_keys", {"session_id": session_id, "keys": "Home"}) await self.client.call_tool("wait", {"seconds": 1}) await self.client.call_tool("close_instance", {"session_id": session_id}) async def test_set_element_value(self): async with self.client: session = await self.client.call_tool("create_chrome_instance", {"headless": True}) session_id = session.data.session_id if hasattr(session, 'data') else session.session_id await self.client.call_tool("navigate_to", {"session_id": session_id, "url": "https://www.baidu.com"}) import time timeout = 10 start = time.time() input_idx = None while True: state = await self.client.call_tool("get_page_state", {"session_id": session_id}) for el in state.data.elements: if el.tag.lower() == "textarea" or el.tag.lower() == "input": input_idx = el.index break if input_idx is not None: break if time.time() - start > timeout: raise TimeoutError("wait for input element timeout") await self.client.call_tool("wait", {"seconds": 1}) result = await self.client.call_tool("set_element_value", {"session_id": session_id, "index": input_idx, "value": "playwright python"}) self.assertIn("Set value", str(result)) await self.client.call_tool("click_element_by_xpath", {"session_id": session_id, "xpath": el.xpath}) result = await self.client.call_tool("send_keys", {"session_id": session_id, "keys": "Enter"}) self.assertIn("Sent keys", str(result)) timeout = 10 start = time.time() found_result = False while True: state = await self.client.call_tool("get_page_state", {"session_id": session_id}) for el in state.data.elements: print(el.xpath) if "div/div/div[1]/div/h3/a/div/div/p/span/span/span" in el.xpath: found_result = True break if time.time() - start > timeout: break await self.client.call_tool("wait", {"seconds": 1}) self.assertTrue(found_result, "not found search result page") await self.client.call_tool("send_keys", {"session_id": session_id, "keys": "PageDown"}) await self.client.call_tool("wait", {"seconds": 1}) await self.client.call_tool("send_keys", {"session_id": session_id, "keys": "PageUp"}) await self.client.call_tool("wait", {"seconds": 1}) await self.client.call_tool("send_keys", {"session_id": session_id, "keys": "End"}) await self.client.call_tool("wait", {"seconds": 1}) await self.client.call_tool("send_keys", {"session_id": session_id, "keys": "Home"}) await self.client.call_tool("wait", {"seconds": 1}) await self.client.call_tool("close_instance", {"session_id": session_id}) async def test_generate_pdf_with_url(self): async with self.client: session = await self.client.call_tool("create_chrome_instance", {"headless": True}) session_id = session.data.session_id url = "https://httpbin.org/html" result = await self.client.call_tool("generate_pdf", {"session_id": session_id, "url": url}) print("generate_pdf with url:", result) self.assertTrue(result.data.success) self.assertTrue(result.data.file_path) await self.client.call_tool("close_instance", {"session_id": session_id}) async def test_generate_pdf_with_html_content(self): async with self.client: session = await self.client.call_tool("create_chrome_instance", {"headless": True}) session_id = session.data.session_id html_content = "<html><body><h1>Test PDF</h1></body></html>" result = await self.client.call_tool("generate_pdf", {"session_id": session_id, "html_content": html_content}) print("generate_pdf with html_content:", result) self.assertTrue(result.data.success) self.assertTrue(result.data.file_path) await self.client.call_tool("close_instance", {"session_id": session_id}) async def test_generate_pdf_with_output_filename(self): async with self.client: session = await self.client.call_tool("create_chrome_instance", {"headless": True}) session_id = session.data.session_id output_filename = "custom_name.pdf" result = await self.client.call_tool("generate_pdf", {"session_id": session_id, "output_filename": output_filename}) print("generate_pdf with output_filename:", result) self.assertTrue(result.data.success) self.assertEqual(result.data.file_name, output_filename) await self.client.call_tool("close_instance", {"session_id": session_id}) async def test_generate_pdf_with_print_background(self): async with self.client: session = await self.client.call_tool("create_chrome_instance", {"headless": True}) session_id = session.data.session_id result = await self.client.call_tool("generate_pdf", {"session_id": session_id, "print_background": False}) print("generate_pdf with print_background=False:", result) self.assertTrue(result.data.success) self.assertTrue(result.data.file_path) await self.client.call_tool("close_instance", {"session_id": session_id}) async def test_generate_pdf_with_margins(self): async with self.client: session = await self.client.call_tool("create_chrome_instance", {"headless": True}) session_id = session.data.session_id result = await self.client.call_tool("generate_pdf", { "session_id": session_id, "margin_top": 1.0, "margin_bottom": 1.0, "margin_left": 1.0, "margin_right": 1.0 }) print("generate_pdf with custom margins:", result) self.assertTrue(result.data.success) self.assertTrue(result.data.file_path) await self.client.call_tool("close_instance", {"session_id": session_id}) async def test_generate_pdf_with_paper_size(self): async with self.client: session = await self.client.call_tool("create_chrome_instance", {"headless": True}) session_id = session.data.session_id result = await self.client.call_tool("generate_pdf", { "session_id": session_id, "paper_width": 10.0, "paper_height": 15.0 }) print("generate_pdf with custom paper size:", result) self.assertTrue(result.data.success) self.assertTrue(result.data.file_path) await self.client.call_tool("close_instance", {"session_id": session_id}) async def test_generate_pdf_with_all_params(self): async with self.client: session = await self.client.call_tool("create_chrome_instance", {"headless": True}) session_id = session.data.session_id result = await self.client.call_tool("generate_pdf", { "session_id": session_id, "url": "https://httpbin.org/html", "output_filename": "all_params.pdf", "print_background": True, "margin_top": 2, "margin_bottom": 2, "margin_left": 4, "margin_right": 0.5, "paper_width": 9.0, "paper_height": 12.0 }) print("generate_pdf with all params:", result) self.assertTrue(result.data.success) self.assertEqual(result.data.file_name, "all_params.pdf") self.assertTrue(result.data.file_path) await self.client.call_tool("close_instance", {"session_id": session_id}) async def test_download_file(self): async with self.client: session = await self.client.call_tool("create_chrome_instance", {"headless": True}) session_id = session.data.session_id url = "https://proof.ovh.net/files/" await self.client.call_tool("navigate_to", {"session_id": session_id, "url": url}) await self.client.call_tool("wait", {"seconds": 3}) await self.client.call_tool("scroll_page", {"session_id": session_id, "direction": "down"}) await self.client.call_tool("wait", {"seconds": 1}) import time timeout = 20 # 增加超时时间 start = time.time() retry_count = 0 max_retries = 3 while True: try: await self.client.call_tool("wait", {"seconds": 1}) await self.client.call_tool("take_screenshot", {"session_id": session_id}) # 尝试获取页面状态 try: new_state = await self.client.call_tool("get_page_state", {"session_id": session_id}) news_element = next((el for el in new_state.data.elements if "1 mio file" in el.text.lower() and el.tag == "a"), None) if news_element: print("download link:", news_element) break except Exception as e: # 如果get_page_state失败,检查浏览器健康状态 if "Page is not accessible" in str(e) or "Failed to get page state" in str(e): print(f"⚠️ Page accessibility issue detected: {e}") # 检查浏览器健康状态 try: health_status = await self.client.call_tool("check_browser_health", {"session_id": session_id}) print(f"🔍 Browser health check: {health_status}") except Exception as health_error: print(f"❌ Health check failed: {health_error}") # 尝试刷新页面 if retry_count < max_retries: retry_count += 1 print(f"🔄 Attempting to refresh page (attempt {retry_count}/{max_retries})") try: await self.client.call_tool("refresh_page", {"session_id": session_id}) await self.client.call_tool("wait", {"seconds": 3}) continue except Exception as refresh_error: print(f"❌ Page refresh failed: {refresh_error}") else: print("❌ Max retries reached, creating new instance") # 关闭当前实例并创建新实例 await self.client.call_tool("close_instance", {"session_id": session_id}) session = await self.client.call_tool("create_chrome_instance", {"headless": True}) session_id = session.data.session_id await self.client.call_tool("navigate_to", {"session_id": session_id, "url": url}) await self.client.call_tool("wait", {"seconds": 3}) retry_count = 0 continue else: # 其他类型的错误,直接抛出 raise e except Exception as e: print(f"❌ Error in main loop: {e}") if retry_count < max_retries: retry_count += 1 print(f"🔄 Retrying (attempt {retry_count}/{max_retries})") await self.client.call_tool("wait", {"seconds": 2}) continue else: raise Exception(f"Failed after {max_retries} retries: {e}") if time.time() - start > timeout: raise TimeoutError("wait for download link timeout") await self.client.call_tool("wait", {"seconds": 1}) # 下载文件 try: result = await self.client.call_tool("download_file", {"session_id": session_id, "url": news_element.attributes.get("href")}) print("download_file:", result) self.assertTrue(result.data.success) print("download file:", result.data.file_path) except Exception as e: print(f"❌ Download failed: {e}") raise e finally: await self.client.call_tool("close_instance", {"session_id": session_id}) async def test_upload_file(self): async with self.client: session = await self.client.call_tool("create_chrome_instance", {"headless": True}) session_id = session.data.session_id url = "https://proof.ovh.net/files/" await self.client.call_tool("navigate_to", {"session_id": session_id, "url": url}) await self.client.call_tool("wait", {"seconds": 3}) await self.client.call_tool("scroll_page", {"session_id": session_id, "direction": "down"}) await self.client.call_tool("wait", {"seconds": 3}) import time timeout = 20 start = time.time() while True: await self.client.call_tool("wait", {"seconds": 3}) await self.client.call_tool("take_screenshot", {"session_id": session_id}) await self.client.call_tool("wait", {"seconds": 3}) new_state = await self.client.call_tool("get_page_state", {"session_id": session_id}) news_element = next((el for el in new_state.data.elements if "1 mio file" in el.text.lower() and el.tag == "a"), None) if news_element: print("download link:", news_element) break if time.time() - start > timeout: raise TimeoutError("wait for download link timeout") await self.client.call_tool("wait", {"seconds": 1}) result = await self.client.call_tool("download_file", {"session_id": session_id, "url": news_element.attributes.get("href")}) print("download_file:", result) self.assertTrue(result.data.success) print("download file:", result.data.file_path) file_path = result.data.file_path await self.client.call_tool("navigate_to", {"session_id": session_id, "url": "https://pqina.nl/filepond/"}) await self.client.call_tool("wait", {"seconds": 4}) import time timeout = 20 start = time.time() while True: new_state = await self.client.call_tool("get_page_state", {"session_id": session_id}) news_element = next((el for el in new_state.data.elements if "file" in el.attributes.get("type", "").lower() and el.tag == "input"), None) if news_element: print("file input element:", news_element) break if time.time() - start > timeout: raise TimeoutError("wait for file input element timeout") await self.client.call_tool("wait", {"seconds": 1}) result = await self.client.call_tool("upload_file", {"session_id": session_id, "index": news_element.index, "file_path": file_path}) await self.client.call_tool("wait", {"seconds": 10}) print("upload_file:", result) self.assertEqual("Successfully uploaded file" in result.data, True) import time timeout = 10 start = time.time() while True: new_state = await self.client.call_tool("get_page_state", {"session_id": session_id}) await self.client.call_tool("wait", {"seconds": 1}) upload_btn = next((el for el in new_state.data.elements if "upload" in el.text.lower() and el.tag == "button"), None) if upload_btn: print("upload button element:", upload_btn) break if time.time() - start > timeout: raise TimeoutError("wait for upload button timeout") await self.client.call_tool("wait", {"seconds": 1}) print("upload button element:", upload_btn) result = await self.client.call_tool("click_element", {"session_id": session_id, "index": upload_btn.index}) await self.client.call_tool("wait", {"seconds": 10}) print("click upload button result:", result) timeout = 10 start = time.time() while True: new_state = await self.client.call_tool("get_page_state", {"session_id": session_id}) complete_element = next((el for el in new_state.data.elements if "upload complete" in el.text.lower()), None) if complete_element: print("upload complete element:", complete_element) break if time.time() - start > timeout: raise TimeoutError("wait for upload complete timeout") await self.client.call_tool("wait", {"seconds": 1}) self.assertTrue(complete_element, "not found upload complete on page") await self.client.call_tool("close_instance", {"session_id": session_id}) async def test_download_image(self): async with self.client: session = await self.client.call_tool("create_chrome_instance", {"headless": True}) session_id = session.data.session_id url = "https://httpbin.org/image/png" await self.client.call_tool("navigate_to", {"session_id": session_id, "url": url}) await self.client.call_tool("wait", {"seconds": 1}) result = await self.client.call_tool("download_image", {"session_id": session_id, "image_url": url}) print("download_image:", result) self.assertTrue(result and not result.is_error) await self.client.call_tool("close_instance", {"session_id": session_id}) async def test_download_images_from_bing(self): async with self.client: session = await self.client.call_tool("create_chrome_instance", {"headless": True}) session_id = session.data.session_id url = "https://www.bing.com/images" await self.client.call_tool("navigate_to", {"session_id": session_id, "url": url}) await self.client.call_tool("wait", {"seconds": 2}) await self.client.call_tool("scroll_page", {"session_id": session_id, "direction": "down"}) await self.client.call_tool("wait", {"seconds": 1}) await self.client.call_tool("scroll_page", {"session_id": session_id, "direction": "down"}) await self.client.call_tool("wait", {"seconds": 1}) state = await self.client.call_tool("get_page_state", {"session_id": session_id}) image_elements = [el for el in state.data.elements if el.tag.lower() == "a" and json.loads(el.attributes.get("m", "{}")).get("turl", "")] self.assertTrue(len(image_elements) >= 3, f"Not enough images found on Bing Images page, found {len(image_elements)}") for i, img_el in enumerate(image_elements[:3]): img_url = json.loads(img_el.attributes.get("m", "{}")).get("turl", "") result = await self.client.call_tool("download_image", {"session_id": session_id, "image_url": img_url}) print(f"download_image {i+1}:", result) self.assertTrue(result and not result.is_error, f"Image {i+1} download failed") await self.client.call_tool("close_instance", {"session_id": session_id}) async def test_take_screenshot(self): async with self.client: session = await self.client.call_tool("create_chrome_instance", {"headless": True}) session_id = session.data.session_id await self.client.call_tool("navigate_to", {"session_id": session_id, "url": "https://httpbin.org/html"}) await self.client.call_tool("wait", {"seconds": 1}) # 1. default parameters result = await self.client.call_tool("take_screenshot", {"session_id": session_id}) print("default:", result) self.assertTrue(result and not result.is_error) # 2. specify target element result = await self.client.call_tool("take_screenshot", { "session_id": session_id, "target": "h1" }) print("target h1:", result) self.assertTrue(result and not result.is_error) # 2.1 non-existent target with self.assertRaises(Exception): await self.client.call_tool("take_screenshot", { "session_id": session_id, "target": "#not-exist" }) # 3. custom viewport result = await self.client.call_tool("take_screenshot", { "session_id": session_id, "width": 800, "height": 300, "full_page": False }) print("custom viewport:", result) self.assertTrue(result and not result.is_error) # 4. format=jpeg, quality=50 result = await self.client.call_tool("take_screenshot", { "session_id": session_id, "format": "jpeg", "quality": 50 }) print("jpeg quality 50:", result) self.assertTrue(result and not result.is_error) # 5. full_page=False result = await self.client.call_tool("take_screenshot", { "session_id": session_id, "full_page": False }) print("full_page False:", result) self.assertTrue(result and not result.is_error) # 6. invalid format with self.assertRaises(Exception): await self.client.call_tool("take_screenshot", { "session_id": session_id, "format": "bmp" }) await self.client.call_tool("close_instance", {"session_id": session_id}) async def test_set_and_get_cookies(self): async with self.client: session = await self.client.call_tool("create_chrome_instance", {"headless": True}) session_id = session.data.session_id await self.client.call_tool("navigate_to", {"session_id": session_id, "url": "https://httpbin.org/cookies"}) await self.client.call_tool("wait", {"seconds": 1}) await self.client.call_tool("set_cookie", {"session_id": session_id, "name": "testcookie", "value": "cookieval", "domain": "httpbin.org"}) cookies = await self.client.call_tool("get_cookies", {"session_id": session_id}) print("get_cookies:", cookies) self.assertTrue(any(c.get("name") == "testcookie" for c in cookies.data)) await self.client.call_tool("close_instance", {"session_id": session_id}) async def test_wait(self): async with self.client: session = await self.client.call_tool("create_chrome_instance", {"headless": True}) session_id = session.data.session_id result = await self.client.call_tool("wait", {"seconds": 1}) print("wait:", result) self.assertIn("Waited", str(result)) await self.client.call_tool("close_instance", {"session_id": session_id}) async def test_search_bing(self): async with self.client: session = await self.client.call_tool("create_chrome_instance", {"headless": True}) session_id = session.data.session_id result = await self.client.call_tool("search_bing", {"session_id": session_id, "query": "python"}) await self.client.call_tool("wait", {"seconds": 2}) page_state = await self.client.call_tool("get_page_state", {"session_id": session_id}) print("search_bing:", result) print("page_state:", page_state) # assume page_state.data.html or page_state.data.text contains page content self.assertIn("python", str(page_state).lower()) await self.client.call_tool("close_instance", {"session_id": session_id}) async def test_scroll_page(self): async with self.client: session = await self.client.call_tool("create_chrome_instance", {"headless": True}) session_id = session.data.session_id await self.client.call_tool("navigate_to", {"session_id": session_id, "url": "https://httpbin.org/html"}) await self.client.call_tool("wait", {"seconds": 1}) await self.client.call_tool("set_browser_config", {"session_id": session_id, "viewport_width": 150, "viewport_height": 1000}) await self.client.call_tool("wait", {"seconds": 1}) await self.client.call_tool("send_keys", {"session_id": session_id, "keys": "End"}) await self.client.call_tool("wait", {"seconds": 1}) result = await self.client.call_tool("scroll_page", {"session_id": session_id, "direction": "up"}) print("scroll_page:", result) self.assertIn("Scrolled", str(result)) result = await self.client.call_tool("take_screenshot", { "session_id": session_id, "full_page": False, "width": 150, "height": 1000 }) print("scroll_page:", result) self.assertTrue(result and not result.is_error) await self.client.call_tool("close_instance", {"session_id": session_id}) async def test_extract_content(self): async with self.client: session = await self.client.call_tool("create_chrome_instance", {"headless": True}) session_id = session.data.session_id await self.client.call_tool("navigate_to", {"session_id": session_id, "url": "https://httpbin.org/html"}) await self.client.call_tool("wait", {"seconds": 1}) await self.client.call_tool("take_screenshot", {"session_id": session_id}) result = await self.client.call_tool("extract_content", {"session_id": session_id, "query": "Herman"}) print("extract_content:", result) self.assertIn("Content matching", str(result)) await self.client.call_tool("close_instance", {"session_id": session_id}) async def test_get_dropdown_options(self): async with self.client: session = await self.client.call_tool("create_chrome_instance", {"headless": True}) session_id = session.data.session_id await self.client.call_tool("navigate_to", {"session_id": session_id, "url": "https://www.w3schools.com/tags/tryit.asp?filename=tryhtml_select"}) await self.client.call_tool("wait", {"seconds": 1}) state = await self.client.call_tool("get_page_state", {"session_id": session_id}) select_idx = None el = None for el in state.data.elements: if el.tag.lower() == "select": select_idx = el.index el = el break if select_idx is not None: # get dropdown options (structured return) result = await self.client.call_tool("get_dropdown_options", {"session_id": session_id, "index": select_idx}) print("get_dropdown_options:", result) # assert structured return self.assertTrue(hasattr(result.data, "options")) options = result.data.options self.assertTrue(len(options) >= 2) # set select's value to second option set_result = await self.client.call_tool("set_element_value", {"session_id": session_id, "index": select_idx, "value": options[1].value}) print("set_element_value:", set_result) self.assertIn("Set value", str(set_result)) # click to test click_result = await self.client.call_tool("click_element_by_xpath", {"session_id": session_id, "xpath": el.xpath}) print("click_element:", click_result) self.assertIn("Clicked", str(click_result)) # check if element's value has changed to second_value element_info = await self.client.call_tool("get_element_info", {"session_id": session_id, "index": select_idx}) print("element_info after set:", element_info) self.assertEqual(element_info.data.value, options[1].value) await self.client.call_tool("close_instance", {"session_id": session_id}) async def test_browser_tips(self): async with self.client: result = await self.client.call_tool("browser_tips", {}) self.assertIn("Best Practices", str(result)) async def test_resource_status(self): async with self.client: result = await self.client.read_resource("browser://status") print("resource_status:", result) self.assertIn("Session-Based Browser Manager Status", str(result)) async def test_resource_instances(self): async with self.client: result = await self.client.read_resource("browser://instances") print("resource_instances:", result) self.assertTrue( "Browser Sessions" in str(result) or "No browser sessions active" in str(result) ) async def test_resource_help(self): async with self.client: result = await self.client.read_resource("browser://help") print("resource_help:", result) self.assertIn("API Reference", str(result)) async def test_prompt_web_testing(self): async with self.client: result = await self.client.get_prompt("web_testing", {"url": "https://httpbin.org/html", "test_scenario": "basic page load"}) print("prompt_web_testing:", result) self.assertIn("test", str(result).lower()) async def test_prompt_data_extraction(self): async with self.client: result = await self.client.get_prompt("data_extraction", {"url": "https://httpbin.org/html", "data_type": "table"}) print("prompt_data_extraction:", result) self.assertIn("extract", str(result).lower()) async def test_prompt_form_filling(self): async with self.client: result = await self.client.get_prompt("form_filling", {"url": "https://httpbin.org/forms/post", "form_data": "name=foo&email=bar"}) print("prompt_form_filling:", result) self.assertTrue(result) async def test_prompt_automation_troubleshooting(self): async with self.client: result = await self.client.get_prompt("automation_troubleshooting", {}) print("prompt_automation_troubleshooting:", result) self.assertIn("troubleshooting", str(result).lower()) async def test_resource_instance_page(self): async with self.client: session = await self.client.call_tool("create_chrome_instance", {"headless": True}) session_id = session.data.session_id await self.client.call_tool("navigate_to", {"session_id": session_id, "url": "https://httpbin.org/html"}) await self.client.call_tool("wait", {"seconds": 1}) result = await self.client.read_resource(f"browser://instance/{session_id}/page") print("instance_page:", result) self.assertIn("Current Page", str(result)) self.assertIn("httpbin.org/html", str(result)) await self.client.call_tool("close_instance", {"session_id": session_id}) async def test_resource_instance_tabs(self): async with self.client: session = await self.client.call_tool("create_chrome_instance", {"headless": True}) session_id = session.data.session_id await self.client.call_tool("navigate_to", {"session_id": session_id, "url": "https://httpbin.org/html"}) await self.client.call_tool("navigate_to", {"session_id": session_id, "url": "https://example.com/", "new_tab": True}) await self.client.call_tool("wait", {"seconds": 1}) result = await self.client.read_resource(f"browser://instance/{session_id}/tabs") print("instance_tabs:", result) self.assertIn("Open Tabs", str(result)) self.assertIn("example.com", str(result)) await self.client.call_tool("close_instance", {"session_id": session_id}) async def test_resource_instance_screenshots(self): async with self.client: session = await self.client.call_tool("create_chrome_instance", {"headless": True}) session_id = session.data.session_id await self.client.call_tool("navigate_to", {"session_id": session_id, "url": "https://httpbin.org/html"}) await self.client.call_tool("wait", {"seconds": 1}) await self.client.call_tool("take_screenshot", {"session_id": session_id}) result = await self.client.read_resource(f"browser://instance/{session_id}/screenshots") print("instance_screenshots:", result) self.assertIn("Screenshots for Instance", str(result)) self.assertIn(".png", str(result)) await self.client.call_tool("close_instance", {"session_id": session_id}) async def test_resource_instance_status(self): async with self.client: session = await self.client.call_tool("create_chrome_instance", {"headless": True}) session_id = session.data.session_id await self.client.call_tool("navigate_to", {"session_id": session_id, "url": "https://httpbin.org/html"}) await self.client.call_tool("wait", {"seconds": 1}) result = await self.client.read_resource(f"browser://instance/{session_id}/status") print("instance_status:", result) self.assertIn("session_id", str(result)) self.assertIn("current_url", str(result)) await self.client.call_tool("close_instance", {"session_id": session_id}) async def test_resource_instance_files_and_file(self): async with self.client: session = await self.client.call_tool("create_chrome_instance", {"headless": True}) session_id = session.data.session_id # take screenshot await self.client.call_tool("navigate_to", {"session_id": session_id, "url": "https://httpbin.org/html"}) await self.client.call_tool("wait", {"seconds": 1}) await self.client.call_tool("take_screenshot", {"session_id": session_id}) # download a file url = "https://proof.ovh.net/files/" await self.client.call_tool("navigate_to", {"session_id": session_id, "url": url}) await self.client.call_tool("wait", {"seconds": 3}) await self.client.call_tool("scroll_page", {"session_id": session_id, "direction": "down"}) await self.client.call_tool("wait", {"seconds": 1}) await self.client.call_tool("take_screenshot", {"session_id": session_id}) await self.client.call_tool("wait", {"seconds": 1}) state = await self.client.call_tool("get_page_state", {"session_id": session_id}) download_link = next((el for el in state.data.elements if "1 mio file" in el.text.lower() and el.tag == "a"), None) self.assertIsNotNone(download_link) download_result = await self.client.call_tool("download_file", {"session_id": session_id, "url": download_link.attributes.get("href")}) self.assertTrue(download_result.data.success) file_name = download_result.data.file_name # get file list files_result = await self.client.read_resource(f"browser://instance/{session_id}/files") print("instance_files:", files_result) # read single file content file_content = await self.client.read_resource(f"browser://instance/{session_id}/file/{file_name}") print("instance_file_content:", file_content) self.assertTrue(file_content) await self.client.call_tool("close_instance", {"session_id": session_id}) async def test_resource_instance_cookies(self): async with self.client: session = await self.client.call_tool("create_chrome_instance", {"headless": True}) session_id = session.data.session_id await self.client.call_tool("navigate_to", {"session_id": session_id, "url": "https://httpbin.org/cookies"}) await self.client.call_tool("wait", {"seconds": 1}) await self.client.call_tool("set_cookie", {"session_id": session_id, "name": "testcookie", "value": "cookieval", "domain": "httpbin.org"}) cookies_result = await self.client.read_resource(f"browser://instance/{session_id}/cookies") print("instance_cookies:", cookies_result) self.assertTrue(any("testcookie" in str(c) for c in str(cookies_result).splitlines())) await self.client.call_tool("close_instance", {"session_id": session_id}) if __name__ == "__main__": unittest.main()

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/Euraxluo/browser-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server