How to Implement a Custom Browser Tool in the ego-browser Learning Subsystem
To add a custom browser tool in ego-browser, create a learning folder with a manifest.json declaring your tool, write a JavaScript module that exports a function, and invoke it via site.runBrowserTool() — no core engine modifications required.
The ego-browser learning subsystem enables sites to expose browser tools: small JavaScript snippets that execute inside the browser context and return data to agent scripts. This article walks through the complete implementation flow based on the actual citrolabs/ego-lite source code.
Step 1: Create the Learning Folder Structure
Every custom browser tool belongs to a site-specific learning folder. Create a new directory under skills/ego-browser/learnings/<site-id>/.
The folder name becomes your site ID — used for discovery and tool invocation. For example, the Google learning implementation lives at:
skills/ego-browser/learnings/google/
Step 2: Declare Browser Tools in manifest.json
Inside your learning folder, create a manifest.json with a browserTools object. This declares each tool's metadata and schema.
manifest.json structure:
| Field | Purpose |
|---|---|
id |
Unique site identifier (match folder name) |
name |
Human-readable site name |
domains |
Host patterns for auto-discovery |
notes |
Optional Markdown documentation paths |
browserTools |
Object mapping tool names to definitions |
Each browser tool definition requires:
description— What the tool doespath— Relative path to the JS file (typicallybrowser-tools/<name>.js)args— JSON schema of expected parametersreturns— Description of the return value
Example manifest declaring a fetch_title tool:
{
"id": "example",
"name": "Example Site",
"domains": ["example.com", "*.example.com"],
"notes": ["notes/overview.md"],
"browserTools": {
"fetch_title": {
"description": "Read the page's `<title>` element.",
"path": "browser-tools/fetch-title.js",
"args": {},
"returns": {
"type": "string",
"description": "The page title."
}
}
}
}
Step 3: Implement the Browser Tool
Create the JavaScript file referenced by path. The implementation must export a function (named or default) that receives arguments and returns a value or Promise.
The runtime loads this file as plain text via loadBrowserToolSource() and wraps it with wrapBrowserTool() before browser execution.
/**
* Browser-tool: fetch_title
* Returns the document title string.
*/
export async function fetchTitle() {
return document.title;
}
Key implementation rules:
- The function runs in actual browser context — use standard DOM APIs (
document,window, etc.) - Async functions are fully supported
- Arguments from the agent script are injected automatically
Step 4: Add Optional Documentation
Place Markdown files under notes/ and reference them in manifest.json. These notes feed into loadLearnedContext() and appear when agents call help().
Example: notes/overview.md describing site-specific behavior and tool usage patterns.
Step 5: Runtime Execution Flow
When an agent invokes your tool, ego-browser executes this pipeline:
// Agent script
const title = await site.runBrowserTool('example', 'fetch_title');
console.log('Page title →', title);
Internal flow (src/helpers.ts → src/learning/index.ts):
loadBrowserToolSource(siteId, toolName)— reads the JS file from diskwrapBrowserTool(source)— builds an async IIFE injecting the supplied argumentscdpEval(wrapped)— transmits the script to the browser via CDP (Chrome DevTools Protocol)
The CDP bridge lives in src/cdp-eval.ts with cdp()/js() functions handling the transport.
Step 6: Validate with Built-in Testing
Run the validation suite before deployment:
npm test
# or
node --test
The validator (src/learning/validate-learning-format.ts) enforces:
- Every
browserToolsentry has a validpath - Referenced files exist on disk
- JSON schemas are well-formed
- Manifest structure conforms to specification
This catches wiring errors before agents attempt to use the tool.
How Discovery and Loading Work
Three core mechanisms connect your tool to running agents:
| Mechanism | Location | Purpose |
|---|---|---|
siteSkillsForUrl() |
src/learning/index.ts |
Scans learning directories, matches URL hostnames against domains, returns applicable site-skills |
loadLearnedContext() |
src/learning/index.ts |
Aggregates notes and builds tool signatures for help() output |
runBrowserTool() |
src/helpers.ts |
Public API executing the six-step flow above |
Summary
- Create a folder under
skills/ego-browser/learnings/<site-id>/ - Declare tools in
manifest.jsonwithbrowserToolsdefinitions - Implement JavaScript modules that export functions using standard DOM APIs
- Invoke via
await site.runBrowserTool(siteId, toolName, args) - Validate with
npm testto verify manifest and file integrity
No core engine changes are needed — the plugin architecture automatically discovers and loads your implementation.
Frequently Asked Questions
What arguments can a browser tool receive?
Any JSON-serializable object. Define the schema in manifest.json under args, then access properties as function parameters. The wrapBrowserTool() function in src/learning/index.ts serializes and injects arguments into the browser execution context.
Can browser tools interact with the page DOM?
Yes — they execute in the live browser context with full access to document, window, and all standard Web APIs. This enables extraction, manipulation, and interaction patterns that would be impossible from Node.js alone.
How does ego-browser handle tool errors?
Uncaught exceptions in your browser tool propagate through the Promise chain. Since wrapBrowserTool() creates an async IIFE, errors reject the Promise returned by site.runBrowserTool(), allowing standard try/catch handling in agent scripts.
What's the difference between browser tools and node tools?
Browser tools run inside the browser via CDP (defined in manifest.json with browserTools), while node tools execute in the Node.js runtime on the agent side. Node tools are declared separately and processed by runNodeSiteTool() in the same src/learning/index.ts module.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →