Skip to content

Commit 865b174

Browse files
author
browser-mcp
committed
fix: Browser persistence - singleton pattern + lifecycle tools
- Add browser singleton to persist across MCP tool calls - Add reset_browser tool to create new browser instance - Add close_browser tool for proper cleanup - Add close() method to browser.py for resource cleanup - Update CHANGELOG to v0.4.0
1 parent da2333d commit 865b174

3 files changed

Lines changed: 57 additions & 2 deletions

File tree

‎CHANGELOG.md‎

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,17 @@ All notable changes to this project will be documented in this file.
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

8+
## [0.4.0] - 2026-04-03
9+
10+
### Added
11+
- Browser singleton pattern - browser persists across MCP tool calls
12+
- reset_browser tool - close current and create new browser instance
13+
- close_browser tool - properly cleanup browser resources
14+
15+
### Fixed
16+
- Browser no longer closes between tool calls
17+
- Session state maintained across multiple navigations
18+
819
## [0.3.0] - 2024-04-03
920

1021
### Fixed

‎browser_mcp/browser.py‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,13 @@ def set_size(self, width: int, height: int):
9090
self._window.resize(width, height)
9191
self._process_events(0.2)
9292

93+
def close(self):
94+
"""Close the browser and cleanup resources."""
95+
try:
96+
self._window.destroy()
97+
except Exception:
98+
pass
99+
93100
def _on_load_changed(self, view, load_event):
94101
if load_event == WebKit.LoadEvent.FINISHED:
95102
self._ready.set()

‎browser_mcp/server.py‎

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,17 +17,36 @@
1717

1818
server = Server("browser-mcp")
1919

20+
_browser_instance = None
21+
2022

2123
def get_browser() -> "WebKitBrowser":
22-
"""Create and return a browser instance."""
24+
"""Get or create a persistent browser instance."""
25+
global _browser_instance
26+
2327
if WebKitBrowser is None:
2428
raise ImportError(
2529
"WebKitGTK not available. Install with:\n"
2630
" Ubuntu/Debian: sudo apt install libwebkit2gtk-4.1-dev python3-gi python3-gi-cairo gir1.2-webkit2-4.1\n"
2731
" Fedora: sudo dnf install webkit2gtk4.1-devel pygobject3 cairo-gobject\n"
2832
" Arch: sudo pacman -S webkit2gtk4.1 python-gobject python-cairo gobject-introspection"
2933
)
30-
return WebKitBrowser()
34+
35+
if _browser_instance is None:
36+
_browser_instance = WebKitBrowser()
37+
38+
return _browser_instance
39+
40+
41+
def reset_browser() -> None:
42+
"""Reset the browser instance - closes current and creates new one."""
43+
global _browser_instance
44+
if _browser_instance is not None:
45+
try:
46+
_browser_instance.close()
47+
except Exception:
48+
pass
49+
_browser_instance = None
3150

3251

3352
@server.list_tools()
@@ -195,6 +214,16 @@ async def list_tools() -> list[Tool]:
195214
description="Get path to the last auto-screenshot",
196215
inputSchema={"type": "object", "properties": {}},
197216
),
217+
Tool(
218+
name="reset_browser",
219+
description="Reset browser - close current instance and create new one",
220+
inputSchema={"type": "object", "properties": {}},
221+
),
222+
Tool(
223+
name="close_browser",
224+
description="Close the browser and cleanup resources",
225+
inputSchema={"type": "object", "properties": {}},
226+
),
198227
]
199228

200229

@@ -315,6 +344,14 @@ async def call_tool(name: str, arguments: dict[str, Any]) -> list[TextContent]:
315344
return [TextContent(type="text", text=f"Screenshot: {path}")]
316345
return [TextContent(type="text", text="No screenshot available")]
317346

347+
elif name == "reset_browser":
348+
reset_browser()
349+
return [TextContent(type="text", text="Browser reset - new instance created")]
350+
351+
elif name == "close_browser":
352+
reset_browser()
353+
return [TextContent(type="text", text="Browser closed")]
354+
318355
else:
319356
return [TextContent(type="text", text=f"Unknown tool: {name}")]
320357

0 commit comments

Comments
 (0)