diff --git a/src/browser/tools.zig b/src/browser/tools.zig index 464b28a6..cd6a3c1c 100644 --- a/src/browser/tools.zig +++ b/src/browser/tools.zig @@ -818,12 +818,7 @@ fn execTree(arena: std.mem.Allocator, session: *lp.Session, registry: *CDPNode.R const args = try parseArgsOrDefault(TreeParams, arena, arguments); const page = try ensurePage(session, registry, args.url, args.timeout, args.waitUntil); - var root_node = page.document.asNode(); - if (args.backendNodeId) |node_id| { - if (registry.lookup_by_id.get(node_id)) |n| { - root_node = n.dom; - } - } + const root_node = (try resolveOptionalNode(registry, args.backendNodeId)) orelse page.document.asNode(); const st = lp.SemanticTree{ .dom_node = root_node, diff --git a/src/mcp/tools.zig b/src/mcp/tools.zig index 5f9fa096..3d3b0e04 100644 --- a/src/mcp/tools.zig +++ b/src/mcp/tools.zig @@ -627,6 +627,20 @@ test "MCP - scriptStep accepts comment line" { try testing.expect(std.mem.indexOf(u8, out.written(), "\"isError\":true") == null); } +test "MCP - tree rejects stale backendNodeId instead of dumping whole document" { + defer testing.reset(); + var out: std.io.Writer.Allocating = .init(testing.arena_allocator); + const server = try testLoadPage("about:blank", &out.writer); + defer server.deinit(); + + const msg = + \\{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"tree","arguments":{"backendNodeId":999999}}} + ; + try router.handleMessage(server, testing.arena_allocator, msg); + const written = out.written(); + try testing.expect(std.mem.indexOf(u8, written, "NodeNotFound") != null); +} + test "MCP - PascalCase argument keys from LLMs are normalized to canonical" { defer testing.reset(); var out: std.io.Writer.Allocating = .init(testing.arena_allocator);