Web Search
Give the agent a governed web search tool backed by Brave, Linkup or Tavily, with per-query approval, quotas and prohibited terms
Preview. This capability is not included in current Levee releases. This page describes how it works so you can plan for it; contact Vediram to join the preview.
Web search gives the agent one search tool instead of open internet access. It is off by default. When it is on, each query the agent wants to run is shown to the user, who approves or denies it before it leaves the computer. Your policy picks the search vendor, limits how many queries a project may run, and can block queries that contain terms you list.
The agent never chooses the vendor and never holds a vendor key. Web search is configured in the proxies.search section of policy.
What does the agent see?
One search tool. When search is enabled, Levee registers a search tool for the agent (an MCP server named levee_search) and turns off the agent's built-in web search and web fetch tools, so this tool is the only way to search. Levee sets this up automatically for Claude Code. Codex CLI does not load this configuration: a Codex agent gets no search tool, and its built-in web tools are not turned off (they remain subject to Web Access rules).
The tool is called search and is described to the agent as: "Search the web through Levee. Each query is shown to the user for approval before it is sent upstream." Its inputs:
| Input | Rules |
|---|---|
query | Required. At most 256 characters by default. A longer query is refused, never truncated. |
max_results | 1 to 10, default 5. |
recency | any, day, week, month or year. |
include_domains / exclude_domains | Optional lists of host names, at most 16 each. |
There is no provider input: the vendor is chosen by policy.
Results come back as a list of titles, URLs and snippets (HTML removed, only http and https links kept), optionally with a short answer and its citations, and the name of the provider that answered.
Every failure looks the same to the agent: the tool returns Search was not completed. whether the query was refused by policy, blocked by a prohibited term, over quota, denied by the user, timed out, or the vendor was down. The reason is in the audit trail. Check the Search* events in the audit trail.
Search results let the agent read snippets; to open a result page, the agent still needs Web Access to that site.
Section shape
Turn search on, pick a vendor, set limits, and name the variable that holds the vendor's key.
{
"proxies": {
"search": {
"enabled": true,
"defaultProvider": "tavily",
"allowedProviders": ["tavily"],
"maxQueriesPerSession": 50,
"maxQueryLength": 256,
"maxDomainsPerList": 16,
"prohibitedTerms": ["(?i)\\bproject\\s+falcon\\b", "(?i)acme-internal"],
"approval": {
"mode": "per-query",
"decisionTimeoutSeconds": 120
},
"providerKeys": {
"tavily": "LEVEE_SEARCH_KEY_TAVILY"
}
}
}
}
| Key | Default | Meaning |
|---|---|---|
enabled | false | Turns the search tool on. When false or absent, the agent gets no search tool. |
defaultProvider | The vendor that answers every query: brave, linkup or tavily. Required when enabled. | |
allowedProviders | [] | Vendors permitted by policy. Must include defaultProvider. |
maxQueriesPerSession | 50 | Maximum queries per project run. |
maxQueryLength | 256 | Maximum query length in characters. |
maxDomainsPerList | 16 | Maximum entries in each of include_domains and exclude_domains. |
prohibitedTerms | [] | Regular expressions. A query, or any listed domain, that matches one is refused. |
approval.mode | per-query | Every query is shown to the user for approval. |
approval.decisionTimeoutSeconds | 120 | How long a query waits for the user's decision (1 to 3600). An unanswered query is not sent. |
providerKeys | {} | For each vendor, the name of the computer environment variable that holds its API key. |
Providers
Three search vendors are supported. You need your own account with the one you choose.
| Provider | Notes |
|---|---|
| Brave | Supports recency. Ignores include_domains and exclude_domains. |
| Linkup | Supports domain filters and returns a sourced answer. Ignores recency. |
| Tavily | Supports recency and domain filters, and can return a short answer. |
The estimated cost per query is shown to the user in the approval prompt.
Provider keys
The vendor's key is stored on each computer, never in policy. providerKeys maps each vendor to the name of a machine-level environment variable on the computer; the key is read on the host when a query is sent and never enters the sandbox, the policy file or the audit trail.
From an elevated PowerShell 7 prompt:
$key = Read-Host -Prompt 'Tavily API key' -MaskInput
[Environment]::SetEnvironmentVariable('LEVEE_SEARCH_KEY_TAVILY', $key, 'Machine')
Remove-Variable key
Restart-Computer
Restart the computer after setting or changing a key, because Windows services read machine-level environment variables at system startup. If the variable is missing, every query fails and the audit trail records credential-unresolved.
Machine-level environment variables are readable by any account on the computer. Use a key dedicated to Levee and set a spending limit with the vendor.
Approval
The user approves every query, every time. In per-query mode, each query pauses and the Levee app shows the user:
- the query text
- the provider and its estimated cost
- the recency and domain filters
The user approves or denies. Search approvals are never remembered: each query is decided on its own. If the user does not answer within decisionTimeoutSeconds, the query is not sent.
Quota is counted before approval, so a query the user denies still counts toward maxQueriesPerSession. The quota resets when the project is stopped and started again; publishing a new policy does not reset it.
Prohibited terms
Prohibited terms block a query that mentions something that must not leave the company, such as a client name. prohibitedTerms entries are .NET regular expressions, each evaluated with a 100 ms time limit. They are applied to the query and to every domain in the domain lists. Use (?i) for case-insensitive matching and \\b (escaped in JSON) for word boundaries. A pattern that times out refuses the query.
Prohibited terms are a guard against accidental disclosure, for example a client name or internal project code. Like any text filter, they can be evaded by rephrasing; the per-query approval is the stronger control.
Privacy
Only the query and its filters are sent to the search vendor. In detail:
- The query text is recorded in the local audit trail (as the event's resource) so that reviewers can see what left the machine. Treat the audit trail accordingly.
- Diagnostic logs never contain query text.
- Only the query and its filters are sent to the vendor. No project files, prompts or credentials are sent.
Audit
| Event | Meaning |
|---|---|
SearchRequested | The agent asked for a search. |
SearchApproved / SearchDenied | The user's decision (or decision-timeout). |
SearchExecuted | The vendor answered (provider and result count). |
SearchProviderError | The vendor failed. |
denied:select | Search is disabled, or the provider configuration is invalid (search-disabled, no-default-provider, provider-not-allowed, unknown-provider). |
denied:validation | The query was refused before approval (empty-query, too-many-domains, invalid-domain, prohibited-term, prohibited-term-timeout, session-quota-exceeded). |
denied:credential | The provider key was not found (credential-unresolved). |