Default MCP tab tools to the active tab
Testing / remote-protocol-compat (0.15.0) (push) Successful in 30s
Testing / test (push) Successful in 40s
Testing / remote-protocol-compat (0.16.0) (push) Successful in 24s

Requiring an explicit tab_id forced every navigate or close through a
preceding tabs_list call, which costs an MCP client a full round trip just to
learn the ID the browser already considers current.

navigate and tabs_close now resolve the active tab when tab_id is omitted,
matching the screenshot tool. Resolution is explicit rather than forwarding
None into the SDK, so the acting tool knows which tab it touched; tabs_close
reports it, since closing the wrong tab is not recoverable.

This stays in the MCP layer: the SDK and CLI signatures are unchanged.
This commit is contained in:
2026-08-09 20:41:41 +02:00
parent bd2a18baba
commit 581cd73cac
4 changed files with 49 additions and 8 deletions
+17
View File
@@ -26,6 +26,7 @@ class FakeClient:
list=self.tabs_list,
open=self.tabs_open,
close=self.tabs_close,
active=self.tabs_active,
status=self.tabs_status,
screenshot=self.tabs_screenshot,
)
@@ -46,6 +47,10 @@ class FakeClient:
self.calls.append(("close", tab_id))
return 1
def tabs_active(self):
self.calls.append(("active",))
return SimpleNamespace(id=7)
def tabs_status(self, tab_id):
self.calls.append(("status", tab_id))
return {"id": tab_id, "title": "Navigated", "url": "https://example.com/next"}
@@ -178,6 +183,18 @@ async def test_mutating_tools_use_sdk_and_return_fresh_state(client):
("navigate", 8, "https://example.com/next"), ("status", 8)
]
@pytest.mark.anyio
async def test_tab_tools_default_to_the_active_tab(client):
navigated = await client.call_tool("browser_navigate", {"url": "https://example.com/next"})
closed = await client.call_tool("browser_tabs_close", {})
assert navigated.structured_content["id"] == 7
assert closed.structured_content == {"closed": 1, "tab_id": 7}
assert FakeClient.instances[0].calls == [
("active",), ("navigate", 7, "https://example.com/next"), ("status", 7)
]
assert FakeClient.instances[1].calls == [("active",), ("close", 7)]
@pytest.mark.anyio
async def test_screenshot_returns_image_content(client):
result = await client.call_tool("browser_screenshot", {"tab_id": 7, "format": "png"})