How to Create Custom Site Skills in ego-browser: A Complete Guide for Website-Specific Automation

Custom site skills in ego-browser extend the harness with domain-specific tools by creating a learning folder under skills/ego-browser/learnings/<site-id>/ with a manifest.json that declares matching domains and implements node or browser tools.

The ego-browser harness from the citrolabs/ego-lite repository enables you to extend its capabilities through site-specific learned skills. These skills automatically activate when the browser visits matching domains, exposing custom tools that agents can invoke for automation tasks like data extraction, form interaction, or API calls.

Understanding Site Skill Architecture

Core Components

Component Purpose Location
Learning folder Container for all skill assets skills/ego-browser/learnings/<site-id>/
manifest.json Declares site identity, domains, and available tools Root of learning folder
Node tools Execute in the Node.js process (network requests, computation) tools/ subdirectory
Browser tools Execute in browser context via CDP (DOM interaction) browser-tools/ subdirectory
Notes Markdown documentation for human reference notes/ subdirectory

The harness loads skills dynamically through src/learning/learnContext.ts, which scans for **/manifest.json files and registers valid tools into the helper context available to agent scripts.

Creating a New Site Skill: Step-by-Step

Step 1: Create the Learning Directory

Choose a short, lowercase identifier and create the folder structure:

mkdir -p skills/ego-browser/learnings/demo-site/{tools,browser-tools,notes}

Step 2: Define the Manifest

Create skills/ego-browser/learnings/demo-site/manifest.json with this structure:

{
  "id": "demo-site",
  "name": "Demo Site Automation",
  "domains": ["demo.example.com", "*.demo.example.com"],
  "notes": ["notes/overview.md"],
  "nodeTools": {
    "fetchApiData": {
      "description": "Fetch JSON data from the demo API",
      "path": "tools/fetchApi.js",
      "callable": "fetchApiData",
      "args": {
        "endpoint": {
          "type": "string",
          "required": true,
          "description": "API endpoint path (e.g., /users)"
        }
      },
      "returns": {
        "type": "object",
        "description": "Parsed JSON response"
      }
    }
  },
  "browserTools": {
    "extractFormData": {
      "description": "Extract all input values from forms on the page",
      "path": "browser-tools/extractForms.js",
      "callable": "extractFormData",
      "args": {},
      "returns": {
        "type": "array",
        "description": "Array of form data objects"
      }
    }
  }
}

Critical manifest fields:

  • id — Unique identifier matching the folder name
  • domains — Hostnames that trigger this skill (wildcards supported)
  • nodeTools/browserTools — Tool declarations with path relative to learning folder
  • callable — Must match the exported function name in the source file

Step 3: Implement Node Tools

Create skills/ego-browser/learnings/demo-site/tools/fetchApi.js:

/** Fetch data from demo API with authentication headers */
export async function fetchApiData(endpoint) {
  const baseUrl = 'https://demo.example.com/api';
  const response = await fetch(`${baseUrl}${endpoint}`, {
    headers: {
      'Accept': 'application/json',
      'Authorization': `Bearer ${process.env.DEMO_API_TOKEN}`
    }
  });
  
  if (!response.ok) {
    throw new Error(`API error: ${response.status} ${response.statusText}`);
  }
  
  return await response.json();
}

Step 4: Implement Browser Tools

Create skills/ego-browser/learnings/demo-site/browser-tools/extractForms.js:

/** Extract all form input values from the current page */
export async function extractFormData() {
  return await ego.evaluate(() => {
    const forms = document.querySelectorAll('form');
    return Array.from(forms).map(form => {
      const inputs = form.querySelectorAll('input, select, textarea');
      const data = {};
      inputs.forEach(input => {
        if (input.name) {
          data[input.name] = input.value;
        }
      });
      return {
        formId: form.id || null,
        formAction: form.action || null,
        fields: data
      };
    });
  });
}

All browser tools use the ego.evaluate() helper to run code in the browser context via Chrome DevTools Protocol (CDP).

Step 5: Add Documentation

Create skills/ego-browser/learnings/demo-site/notes/overview.md:


# Demo Site Skill

Provides automation tools for demo.example.com.

## Available Tools

- `fetchApiData(endpoint)` — Call the REST API from node context
- `extractFormData()` — Read all form values from the current page

## Usage

Navigate to any demo.example.com page, then invoke tools through the helper context.

Validating and Testing Site Skills

Run the Validation Script

The repository includes a validator that checks manifest correctness:

npm run validate:site-skills

# or: npm run validate:learnings

This script (package/ego-browser/scripts/validate-site-skills.ts) verifies:

  • All declared file paths exist
  • Exported callable names are present in source files
  • Argument and return schemas follow the correct structure
  • Required fields are present in the manifest

Test in Agent Context

Once validated, start the harness and test your skill:

// Agent script executed within ego-browser
await navigate('https://demo.example.com/login');

// Browser tool: extract form data from the current page
const forms = await extractFormData();
console.log('Detected forms:', forms);

// Node tool: fetch user data from API
const users = await fetchApiData('/users');
console.log('Users:', users);

Tools are only available when the active page matches a declared domain. Attempting to call a skill's tools on a non-matching domain throws an ElementResolutionError with transient kind, allowing agents to handle navigation and retry.

Real-World Example: Google Skill

The repository provides a complete reference implementation in skills/ego-browser/learnings/google/. Its manifest demonstrates both node and browser tools:

{
  "id": "google",
  "name": "Google Search",
  "domains": ["google.com", "*.google.com"],
  "notes": ["notes/search.md", "notes/maps.md"],
  "nodeTools": {
    "searchAndExtract": {
      "description": "Perform Google search and extract results",
      "path": "tools/search.js",
      "callable": "searchAndExtract",
      "args": {
        "query": {
          "type": "string",
          "required": true,
          "description": "Search query string"
        },
        "maxResults": {
          "type": "integer",
          "required": false,
          "description": "Maximum results to return (default: 10)"
        }
      },
      "returns": {
        "type": "array",
        "description": "Array of search result objects with title, url, snippet"
      }
    }
  },
  "browserTools": {
    "getSearchResults": {
      "description": "Extract search results from current Google results page",
      "path": "browser-tools/extractResults.js",
      "callable": "getSearchResults",
      "args": {},
      "returns": {
        "type": "array",
        "description": "Array of result objects"
      }
    }
  }
}

Key Implementation Files

File Purpose
src/learning/learnContext.ts Scans, validates, and registers all site skills at startup
src/helpers.ts Generates the helper context exposing registered tools to agents
scripts/validate-site-skills.ts CLI validation for skill manifests
SKILL.md Documentation for agent authors on harness capabilities

Summary

  • Site skills extend ego-browser with domain-specific automation tools
  • Create a folder under skills/ego-browser/learnings/<id>/ with manifest.json, tool implementations, and optional notes
  • Node tools run in the Node.js process for API calls and computation
  • Browser tools use ego.evaluate() for direct DOM manipulation via CDP
  • Validate manifests with npm run validate:site-skills before deployment
  • Tools only activate on pages matching declared domains patterns

Frequently Asked Questions

How do I debug a site skill that isn't loading?

Run npm run validate:site-skills first to catch manifest syntax errors. Then check that your id matches the folder name and that domains includes the exact hostname you're testing. The harness logs registration status at startup—look for messages from learnContext.ts about successfully loaded skills.

Can I use TypeScript for tool implementations?

The repository uses ESM JavaScript with "type": "module" at the root. TypeScript tools should be compiled to JavaScript before deployment. Place the compiled .js files in tools/ or browser-tools/ and reference them in manifest.json with .js extensions.

What happens if two skills match the same domain?

The harness registers all matching skills, but tool names must be unique across the active skill set. If conflicts occur, the later-loaded skill's tools take precedence. Use descriptive tool names prefixed with your site ID (e.g., demoFetchApi instead of fetchApi) to avoid collisions.

How do I update an existing site skill?

Modify the tool source files or manifest.json, then restart the harness. Changes are not hot-reloaded. Run the validation script after any manifest edits to ensure the skill remains valid before restarting.

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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →