Skip to content

Web Tools

The WebToolProvider provides web fetching and search capabilities.

WebToolProvider

stirrup.tools.web.WebToolProvider

WebToolProvider(
    *,
    timeout: float = 60 * 3,
    brave_api_key: str | None = None,
)

Bases: ToolProvider

Provides web tools (web_fetch, web_search) with managed HTTP resources.

WebToolProvider implements the Tool lifecycle protocol (has_lifecycle=True), so it can be used directly in Agent's tools list. Web fetch ignores environment proxies and does not send or retain cookies; its transport and state are isolated from web search. Provider resources are safely cleaned up after interrupted startup.

Usage as Tool in Agent (preferred): from stirrup.clients.chat_completions_client import ChatCompletionsClient

client = ChatCompletionsClient(model="gpt-5.6-luna", max_tokens=8_192, context_window_tokens=1_000_000)
agent = Agent(
    client=client,
    name="assistant",
    tools=[LocalCodeExecToolProvider(), WebToolProvider(), CALCULATOR_TOOL],
)

async with agent.session(output_dir="./output") as session:
    await session.run("Search the web and fetch a page")
Standalone usage

async with WebToolProvider() as provider: tools = provider.get_tools()

Initialize WebToolProvider.

Parameters:

Name Type Description Default
timeout float

Total web fetch timeout and per-operation web search timeout in seconds (default: 180)

60 * 3
brave_api_key str | None

Brave Search API key for web_search tool. If None, uses BRAVE_API_KEY environment variable. Web search is only available if API key is provided.

None
Source code in src/stirrup/tools/web.py
def __init__(
    self,
    *,
    timeout: float = 60 * 3,
    brave_api_key: str | None = None,
) -> None:
    """Initialize WebToolProvider.

    Args:
        timeout: Total web fetch timeout and per-operation web search timeout in seconds (default: 180)
        brave_api_key: Brave Search API key for web_search tool.
                      If None, uses BRAVE_API_KEY environment variable.
                      Web search is only available if API key is provided.
    """
    self._timeout = timeout
    self._brave_api_key = brave_api_key or os.getenv("BRAVE_API_KEY")
    self._fetch_client: httpx.AsyncClient | None = None
    self._search_client: httpx.AsyncClient | None = None
    self._client_stack: AsyncExitStack | None = None

__aenter__ async

__aenter__() -> list[Tool[Any, Any]]

Enter async context: create HTTP clients and return web tools.

Returns:

Type Description
list[Tool[Any, Any]]

List of Tool objects (web_fetch, and web_search if API key available).

Source code in src/stirrup/tools/web.py
async def __aenter__(self) -> list[Tool[Any, Any]]:
    """Enter async context: create HTTP clients and return web tools.

    Returns:
        List of Tool objects (web_fetch, and web_search if API key available).
    """
    stack = AsyncExitStack()
    await stack.__aenter__()
    try:
        self._fetch_client = await stack.enter_async_context(
            httpx.AsyncClient(
                timeout=self._timeout,
                follow_redirects=False,
                trust_env=False,
            )
        )
        if self._brave_api_key:
            self._search_client = await stack.enter_async_context(
                httpx.AsyncClient(timeout=self._timeout, follow_redirects=True)
            )
        tools = self.get_tools()
    except BaseException:
        self._fetch_client = None
        self._search_client = None
        with CancelScope(shield=True):
            await stack.aclose()
        raise
    self._client_stack = stack
    return tools

__aexit__ async

__aexit__(
    exc_type: type[BaseException] | None,
    exc_val: BaseException | None,
    exc_tb: TracebackType | None,
) -> None

Exit async context: close HTTP clients.

Source code in src/stirrup/tools/web.py
async def __aexit__(
    self,
    exc_type: type[BaseException] | None,
    exc_val: BaseException | None,
    exc_tb: TracebackType | None,
) -> None:
    """Exit async context: close HTTP clients."""
    stack = self._client_stack
    self._client_stack = None
    self._fetch_client = None
    self._search_client = None
    if stack is not None:
        await stack.__aexit__(exc_type, exc_val, exc_tb)

Web Fetch Tool

Fetches a web page and returns its content as markdown.

Fetch security

Web fetch resolves and validates every destination and redirect, rejects non-public addresses, ignores environment proxy settings, and neither sends nor retains cookies. Its transport and state are isolated from search, and provider cleanup is safe even when startup is interrupted.

Limits

  • The response body is not bounded while it is downloaded and decompressed; only the extracted markdown is truncated, to 40 000 characters. A compressed body is the sharper case, since it expands without bound and httpx decodes a whole chunk before any size could be measured. Refusing compressed responses outright was considered and rejected: it taxes every fetch with full uncompressed bandwidth and breaks origins that ignore Accept-Encoding: identity, while still leaving the uncompressed body unbounded. The bounded fix is streaming reads plus an incremental decompressor, which is out of scope here. Revisit if a fetch is observed to exhaust memory in practice.
  • There is no destination-port policy once an address validates: any port on a public address is reachable.
  • Internal services hosted on public IP addresses remain reachable by construction.

stirrup.tools.web.FetchWebPageParams

Bases: BaseModel

Parameters for web page fetch tool.

url instance-attribute

url: Annotated[
    str,
    Field(
        description="Full HTTP or HTTPS URL of the web page to fetch and extract"
    ),
]

Web Search Tool

Searches the web using the Brave Search API.

Note

Requires BRAVE_API_KEY environment variable.

stirrup.tools.web.WebSearchParams

Bases: BaseModel

Parameters for web search tool.

query instance-attribute

query: Annotated[
    str,
    Field(
        description="Natural language search query for Brave Search (similar to Google search syntax)"
    ),
]