> ## Documentation Index
> Fetch the complete documentation index at: https://browseruse-0aece648-mintlify-cli-docs-1773354647.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Add Tools

> Extend agents with custom Python functions. Add API calls, file operations, or any custom logic as agent tools.

Examples:

* deterministic clicks
* file handling
* calling APIs
* human-in-the-loop
* browser interactions
* calling LLMs
* get 2fa codes
* send emails
* Playwright integration (see [GitHub example](https://github.com/browser-use/browser-use/blob/main/examples/browser/playwright_integration.py))
* ...

Simply add `@tools.action(...)` to your function.

```python theme={null}
from browser_use import Tools, Agent, ActionResult

tools = Tools()

@tools.action(description='Ask human for help with a question')
async def ask_human(question: str) -> ActionResult:
    answer = input(f'{question} > ')
    return ActionResult(extracted_content=f'The human responded with: {answer}')
```

```python theme={null}
agent = Agent(task='...', llm=llm, tools=tools)
```

* **`description`** *(required)* - What the tool does, the LLM uses this to decide when to call it.
* **`allowed_domains`** - List of domains where tool can run (e.g. `['*.example.com']`), defaults to all domains

The Agent fills your function parameters based on their names, type hints, & defaults.

<Warning>
  **Common Pitfall**: Parameter names must match exactly! Use `browser_session: BrowserSession` (not `browser: Browser`).
  The agent injects special parameters by **name matching**, so using incorrect names will cause your tool to fail silently.
  See [Available Objects](#available-objects) below for the correct parameter names.
</Warning>

## Available Objects

Your function has access to these objects:

* **`browser_session: BrowserSession`** - Current browser session for CDP access
* **`cdp_client`** - Direct Chrome DevTools Protocol client
* **`page_extraction_llm: BaseChatModel`** - The LLM you pass into agent. This can be used to do a custom llm call here.
* **`file_system: FileSystem`** - File system access
* **`available_file_paths: list[str]`** - Available files for upload/processing
* **`has_sensitive_data: bool`** - Whether action contains sensitive data

## Browser Interaction Examples

You can use `browser_session` to directly interact with page elements using CSS selectors:

```python theme={null}
from browser_use import Tools, Agent, ActionResult, BrowserSession

tools = Tools()

@tools.action(description='Click the submit button using CSS selector')
async def click_submit_button(browser_session: BrowserSession):
    # Get the current page
    page = await browser_session.must_get_current_page()

    # Get element(s) by CSS selector
    elements = await page.get_elements_by_css_selector('button[type="submit"]')

    if not elements:
        return ActionResult(extracted_content='No submit button found')

    # Click the first matching element
    await elements[0].click()

    return ActionResult(extracted_content='Submit button clicked!')
```

Available methods on `Page`:

* `get_elements_by_css_selector(selector: str)` - Returns list of matching elements
* `get_element_by_prompt(prompt: str, llm)` - Returns element or None using LLM
* `must_get_element_by_prompt(prompt: str, llm)` - Returns element or raises error

Available methods on `Element`:

* `click()` - Click the element
* `type(text: str)` - Type text into the element
* `get_text()` - Get element text content
* See `browser_use/actor/element.py` for more methods

## Pydantic Input

You can use Pydantic for the tool parameters:

```python theme={null}
from pydantic import BaseModel

class Cars(BaseModel):
    name: str = Field(description='The name of the car, e.g. "Toyota Camry"')
    price: int = Field(description='The price of the car as int in USD, e.g. 25000')

@tools.action(description='Save cars to file')
def save_cars(cars: list[Cars]) -> str:
    with open('cars.json', 'w') as f:
        json.dump(cars, f)
    return f'Saved {len(cars)} cars to file'

task = "find cars and save them to file"
```

## Domain Restrictions

Limit tools to specific domains:

```python theme={null}
@tools.action(
    description='Fill out banking forms',
    allowed_domains=['https://mybank.com']
)
def fill_bank_form(account_number: str) -> str:
    # Only works on mybank.com
    return f'Filled form for account {account_number}'
```

## Advanced Example

For a comprehensive example of custom tools with Playwright integration, see:
**[Playwright Integration Example](https://github.com/browser-use/browser-use/blob/main/examples/browser/playwright_integration.py)**

This shows how to create custom actions that use Playwright's precise browser automation alongside Browser-Use.

## Common Pitfalls

<Warning>
  The agent injects special parameters **by name**, not by type. Using incorrect parameter names is the most common cause of tools failing silently.
</Warning>

### ❌ Wrong: Using `browser: Browser`

```python theme={null}
from browser_use import Tools, ActionResult, Browser

@tools.action('My action')
def my_action(browser: Browser) -> ActionResult:  # WRONG!
    # This will NOT receive the browser session
    pass
```

### ✅ Correct: Using `browser_session: BrowserSession`

```python theme={null}
from browser_use import Tools, ActionResult, BrowserSession

@tools.action('My action')
async def my_action(browser_session: BrowserSession) -> ActionResult:  # CORRECT!
    page = await browser_session.must_get_current_page()
    # Now you have access to the browser
    return ActionResult(extracted_content='Done')
```

### Key Points

1. **Use `browser_session: BrowserSession`** - not `browser: Browser`
2. **Use `async` functions** - recommended for consistency with browser operations
3. **Return `ActionResult`** - not plain strings (though strings work, `ActionResult` provides more control)
4. **Parameter names must match exactly** - see [Available Objects](#available-objects) for the full list of injectable parameters
