From 70417d1c6daf0eb3c8593e3965dffb6b996f8f8f Mon Sep 17 00:00:00 2001 From: Lawrence Chen Date: Fri, 4 Sep 2026 17:41:29 +0000 Subject: [PATCH] docs(examples): add reference browser tool definitions for custom tools #364 lets a caller serve its own tools, but the documented example was abstract. These are the eight browser tools the feature was evaluated with -- four navigation, four DOM -- in the shape the API expects, so a user can paste them into `tools=` and implement `run_custom_tool` against a known-good schema. Data and docs only; nothing imports this file. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01PHkcDvvjaw6TNukT99af2o --- examples/README.md | 23 ++++++ examples/browser_tools.json | 136 ++++++++++++++++++++++++++++++++++++ 2 files changed, 159 insertions(+) create mode 100644 examples/browser_tools.json diff --git a/examples/README.md b/examples/README.md index 59f7586..98bc9bb 100644 --- a/examples/README.md +++ b/examples/README.md @@ -91,3 +91,26 @@ uv run examples/navigator_n2_daytona.py \ Pass `--record` to capture the desktop during the run; the video is downloaded to `n2-daytona-run.mp4` before the sandbox is deleted. To drive your own Mac instead, use [Yutori MCP](https://github.com/yutori-ai/yutori-mcp) (`uvx yutori-mcp computer-use setup`). Walkthrough: [Run n2 on Daytona](https://docs.yutori.com/reference/n2-daytona). + +## browser_tools.json + +Reference tool definitions for driving a browser from `N2ComputerAgent(tools=[...])`, in the +shape the API expects. Eight tools: `goto_url`, `go_back`, `go_forward`, `refresh`, +`extract_elements`, `find`, `set_element_value`, and `execute_js`. They are ordinary caller +tools — nothing here is served by the model's built-in tool set — so your computer adapter +implements `run_custom_tool(name, arguments)` to execute them, and the agent handles routing, +results and frames. `navigator_n2_daytona.py` shows an adapter; [api.md](../api.md)'s +"Custom tools" section documents the contract. + +```python +import json +from pathlib import Path + +tools = json.loads(Path("examples/browser_tools.json").read_text()) +agent = N2ComputerAgent(computer=computer, completions=..., model=..., tools=tools) +``` + +Serve the subset you actually implement. The four navigation tools are useful on their own; +the DOM tools (`extract_elements`, `find`, `set_element_value`, `execute_js`) need a way to +evaluate JavaScript in the page. Custom-tool results carry the post-action frame, so a tool +that changes the page hands the model the new screenshot with its result. diff --git a/examples/browser_tools.json b/examples/browser_tools.json new file mode 100644 index 0000000..1ab4246 --- /dev/null +++ b/examples/browser_tools.json @@ -0,0 +1,136 @@ +[ + { + "type": "function", + "function": { + "name": "goto_url", + "description": "Navigates directly to the specified URL.", + "parameters": { + "type": "object", + "properties": { + "url": { + "type": "string", + "description": "Destination URL." + } + }, + "required": [ + "url" + ] + } + } + }, + { + "type": "function", + "function": { + "name": "go_back", + "description": "Navigates back to the previous page in browser history.", + "parameters": { + "type": "object", + "properties": {}, + "required": [] + } + } + }, + { + "type": "function", + "function": { + "name": "go_forward", + "description": "Navigates forward to the next page in browser history.", + "parameters": { + "type": "object", + "properties": {}, + "required": [] + } + } + }, + { + "type": "function", + "function": { + "name": "refresh", + "description": "Reloads the current page.", + "parameters": { + "type": "object", + "properties": {}, + "required": [] + } + } + }, + { + "type": "function", + "function": { + "name": "extract_elements", + "description": "Extracts page elements and references, optionally filtering by a query.", + "parameters": { + "type": "object", + "properties": { + "filter": { + "type": "string", + "description": "Optional filter query for page content." + } + }, + "required": [] + } + } + }, + { + "type": "function", + "function": { + "name": "find", + "description": "Searches for text on the page.", + "parameters": { + "type": "object", + "properties": { + "text": { + "type": "string", + "description": "Text to search for." + } + }, + "required": [ + "text" + ] + } + } + }, + { + "type": "function", + "function": { + "name": "set_element_value", + "description": "Sets an input element's value by reference.", + "parameters": { + "type": "object", + "properties": { + "ref": { + "type": "string", + "description": "Element reference for the form input field." + }, + "value": { + "type": "string", + "description": "Value to set." + } + }, + "required": [ + "ref", + "value" + ] + } + } + }, + { + "type": "function", + "function": { + "name": "execute_js", + "description": "Executes JavaScript code on the page.", + "parameters": { + "type": "object", + "properties": { + "text": { + "type": "string", + "description": "JavaScript code to execute." + } + }, + "required": [ + "text" + ] + } + } + } +]