Files
Nexus/mcp/firefox_server.py
T

118 lines
4.1 KiB
Python
Raw Normal View History

#!/usr/bin/env python3
"""Firefox Browser MCP — 控制 Firefox 进行 Web 测试"""
import json, time, base64, asyncio
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.firefox.options import Options
from selenium.webdriver.firefox.service import Service
from mcp.server import Server
from mcp.server.stdio import stdio_server
server = Server("firefox-mcp")
driver = None
def _get_driver():
global driver
if driver is None:
opts = Options()
opts.add_argument("--headless")
opts.add_argument("--no-sandbox")
opts.add_argument("--disable-dev-shm-usage")
driver = webdriver.Firefox(options=opts)
driver.set_page_load_timeout(30)
return driver
# ================================================================
# T1: 导航
# ================================================================
@server.tool()
async def firefox_navigate(url: str = "https://192.168.124.10/app/login.html"):
"""Firefox 打开指定 URL,返回页面标题和 URL"""
try:
d = _get_driver()
d.get(url)
return f"标题: {d.title}\nURL: {d.current_url}"
except Exception as e:
return f"导航失败: {e}"
# ================================================================
# T2: 截图
# ================================================================
@server.tool()
async def firefox_screenshot():
"""截取当前页面截图,返回 base64 PNG"""
try:
d = _get_driver()
png = d.get_screenshot_as_base64()
return f"截图大小: {len(png)} bytes (base64). 可用工具查看。\n前100字符: {png[:100]}..."
except Exception as e:
return f"截图失败: {e}"
# ================================================================
# T3: 获取页面文本
# ================================================================
@server.tool()
async def firefox_get_text():
"""获取当前页面可见文本内容(用于检查错误信息)"""
try:
d = _get_driver()
body = d.find_element(By.TAG_NAME, "body")
text = body.text[:3000]
return text if text else "(空页面)"
except Exception as e:
return str(e)
# ================================================================
# T4: 填充输入框并提交
# ================================================================
@server.tool()
async def firefox_fill(selector: str = "input[name='username']", value: str = "admin"):
"""在指定 CSS selector 的输入框中填入值"""
try:
d = _get_driver()
el = d.find_element(By.CSS_SELECTOR, selector)
el.clear()
el.send_keys(value)
return f"已填入 {selector} = {value}"
except Exception as e:
return str(e)
# ================================================================
# T5: 点击
# ================================================================
@server.tool()
async def firefox_click(selector: str = "button[type='submit']"):
"""点击指定 CSS selector 的元素"""
try:
d = _get_driver()
el = d.find_element(By.CSS_SELECTOR, selector)
el.click()
time.sleep(2)
return f"已点击 {selector}。当前页面: {d.title} ({d.current_url})"
except Exception as e:
return str(e)
# ================================================================
# T6: 控制台日志
# ================================================================
@server.tool()
async def firefox_console():
"""获取浏览器控制台日志"""
try:
d = _get_driver()
logs = d.get_log("browser")
entries = [f"[{e['level']}] {e['message'][:200]}" for e in logs[-20:]]
return "\n".join(entries) if entries else "(无日志)"
except Exception as e:
return str(e)
# ================================================================
# Main
# ================================================================
async def main():
async with stdio_server() as (read, write):
await server.run(read, write, server.create_initialization_options())
if __name__ == "__main__":
asyncio.run(main())