> ## Documentation Index
> Fetch the complete documentation index at: https://vetta.sh/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Tools, connections & context

> Declare what the agent may use — built-ins, connection tools, and MCP servers — gate each one with a permission, and keep the context window small.

An agent's tools are what it works *with*: a shell, a filesystem, a browser, your third-party [connections](/docs/identity/connections), and any MCP server you point it at. You declare them on the agent, set a [permission](/docs/concepts/policies) per tool, and every call is priced against the [budget](/docs/concepts/budgets) before it runs.

## The `tools` config

An agent's tools are declared as one `tools` object: a `default_config` that sets the baseline for every tool, plus a `configs` map of per-tool overrides keyed by tool name. This is how you enable or disable individual tools and set each one's permission.

```jsonc theme={"system"}
{
  "tools": {
    "default_config": { "permission": "allow" },
    "configs": {
      "browser": {
        "enabled": true,
        "permission": "ask",
        "config": { "allowed_domains": ["example.com"] }
      },
      "bash": { "enabled": true, "permission": "allow" }
    }
  }
}
```

* `default_config` applies to every tool; a `configs` entry overrides one tool by name.
* `permission` is one of `allow` (run silently), `ask` (pause → `requires_action`), or `deny` (the tool is **not offered to the model at all**).
* Common patterns: **enable-only** (`default_config.enabled: false`, then enable individually) and **trust-by-default-except** (allow all, set one tool to `ask`).
* Running sessions keep the config they started with; edits apply to new sessions. Tools can also be re-selected per session while it is [idle](/docs/concepts/sessions#lifecycle).

## Built-in tools

| Tool                      | What it does                                                                                                                                                                                                                     |
| ------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `bash`                    | Run shell commands in the [computer](/docs/computer/shell).                                                                                                                                                                           |
| `read` · `write` · `edit` | Read, write, and string-replace files in the [workspace](/docs/computer/filesystem).                                                                                                                                                  |
| `ls` · `find`             | List a directory, and find files by name or pattern.                                                                                                                                                                             |
| `browser`                 | Drive a real browser: `goto`, `click`, `type`, `extract`, `screenshot`. See [Browser](/docs/computer/browser).                                                                                                                        |
| `read_skill`              | Pull a [skill](/docs/capabilities/skills) body into context on demand.                                                                                                                                                                |
| `publish_file`            | Promote a sandbox artifact into persistent [Files](/docs/capabilities/files).                                                                                                                                                         |
| `web_search`              | Search the web and get back titled results with links. [Priced per call](/docs/platform/pricing#priced-tools).                                                                                                                        |
| `web_fetch`               | Read one web page as text, so the agent can quote or summarise it. [Priced per call](/docs/platform/pricing#priced-tools).                                                                                                            |
| `generate_image`          | Generate an image from a description; the result lands in [Files](/docs/capabilities/files). [Priced per job](/docs/platform/pricing#priced-tools).                                                                                        |
| `generate_video`          | Generate a video from a description; the result lands in [Files](/docs/capabilities/files). [Priced per job](/docs/platform/pricing#priced-tools).                                                                                         |
| `clip_video`              | Cut the most engaging short clips out of a public video URL — transcribed, scored, captioned, optionally reframed to vertical; the clips land in [Files](/docs/capabilities/files). [Priced per job](/docs/platform/pricing#priced-tools). |
| `apps`                    | Provision, deploy, and operate the organization's hosted [web apps](/docs/api/apps): sites, secrets, domains, and (fullstack) a SQL database.                                                                                         |
| `ask_operator`            | Ask the person running the task a question and stop until they answer.                                                                                                                                                           |
| `send_to_agent`           | Hand one teammate a brief. Returns at once; the work runs alongside yours.                                                                                                                                                       |
| `wait_for_agents`         | Stop and wait for delegated work, either for all of it or for the first answer.                                                                                                                                                  |
| `list_agents`             | List the teammates on the roster, and how many are running now.                                                                                                                                                                  |
| `board_read`              | List the team board's cards, or read one card in full by id.                                                                                                                                                                     |
| `board_write`             | Create a card, update one, or comment on one.                                                                                                                                                                                    |

<Note>
  `ask_operator` is the one tool with **no `allow`**. The tool *is* the pause, so `allow` collapses to `ask`: the session stops, reports `awaiting_answer`, and the question waits in `pending_actions` until someone answers it — see the [awaiting-answer loop](/docs/concepts/session-operations#awaiting-answer-loop). Set `deny` (or `enabled: false`) to forbid an agent to ask at all. It is not offered on a harness that [cannot hold a call open for a person](/docs/concepts/harness-capabilities), and a session that enables it on one is refused when it starts.
</Note>

<Note>
  The last five are a **team's** tools: an agent with no roster is never offered one, a teammate is never offered `send_to_agent` — work is delegated one level, never onward — and the two board tools appear only for a team that declared a board. They are listed here because a tool you cannot name is a tool you cannot `deny`.

  Every other tool above is on by default. A tool the session has no [computer](/docs/computer/index) for is simply not offered — a text-only session gets none of the six sandbox tools (`bash`, `read`, `write`, `edit`, `ls`, `find`) and keeps the rest. The one exception is `read`: a text-only session that pinned [skills](/docs/capabilities/skills) still gets it, because a skill's body is a file the model is told the location of, and a location nothing can open would be a lie. It reads the skills and nothing else — there is no filesystem behind it. There is deliberately **no `grep`**: its search runs a local process the sandbox cannot redirect, so it would search the wrong machine. `bash` covers the capability against the right filesystem.
</Note>

## Web tools

`web_search` and `web_fetch` reach the open web, so each one takes its own filters and its own cap
on how much page text may enter the context. Both are configured in the same `configs` map as every
other tool:

```jsonc theme={"system"}
{
  "tools": {
    "configs": {
      "web_search": {
        "enabled": true,
        "permission": "allow",
        "config": { "blocked_domains": ["competitor.example"] }
      },
      "web_fetch": {
        "enabled": true,
        "permission": "allow",
        "config": {
          "allowed_domains": ["docs.example.com", "status.example.com"],
          "max_content_tokens": 4000
        }
      }
    }
  }
}
```

* **`allowed_domains`** — a non-empty list is a closed list: nothing outside it is reachable. Empty
  or absent means the open web. A domain covers its subdomains, so `example.com` matches
  `docs.example.com` and never `notexample.com`.
* **`blocked_domains`** — always wins over `allowed_domains`, so one entry carves a hole in a broad
  allow-list and the answer never depends on list order.
* **`max_content_tokens`** — how much of a fetched page may enter the context; the rest is truncated.
  Defaults to `4000`. Context you never load is context you never pay for.
* The two tools are configured **separately** — searching broadly while fetching only from a short
  list of trusted hosts is the common shape.
* A search result outside the policy is dropped from the results; a `web_fetch` outside it is
  refused before the page is requested. A page that **redirects** off the policy is refused too —
  the host that actually answered is the one that has to be admitted, so an open redirect on an
  allowed host cannot be used to reach past the list. That refusal comes after the fetch, so unlike
  the others it is billed.
* Both tools are **priced per call** against the [budget](/docs/concepts/budgets) and appear on
  `GET /v1/agents/{id}/spend?by=component` under `search`. The price is what the call cost, so it is
  booked after the call rather than quoted before it — a domain filter bounds *where* an agent can
  go, `cap_micro_usd` bounds how much it spends getting there.

## Generation tools

Both save what they produce to [Files](/docs/capabilities/files) and hand the agent a `file_id`; they
differ in *when*.

`generate_image` answers in the same call. An image renders in seconds, so the tool waits, saves the
result and returns its `file_id` there and then.

`generate_video` **does not block**. A render measured in minutes cannot happen inside a turn, so
the tool starts the job and returns a handle straight away and the agent carries on working. When
the job finishes, the file is saved and the session is woken and told its `file_id`. Nothing is
polled and nothing is waited on, so a long render never consumes a turn.

```jsonc theme={"system"}
{
  "tools": {
    "configs": {
      "generate_video": {
        "enabled": true,
        "permission": "allow",
        "config": { "models": ["bytedance/seedance-2.5", "openai/sora-2-pro"] }
      }
    }
  }
}
```

* **`models`** — which models this agent may generate with, out of the live catalogue at
  [`GET /v1/media/models`](/docs/api/media). It **narrows**; there is no fixed list to choose from and no
  enum, because the catalogue belongs to the provider and changes without a release of ours. Omit it
  and the agent may use anything the provider publishes. Name models and the agent is held to them,
  with the **first one you name** as its default.
* **Choosing a model.** With no `models` pinned, `generate_image` runs the cheapest model the
  provider publishes a price for, and `generate_video` asks the agent to name one — video carries no
  published price, so there is no cheapest to fall back to and we will not pick a favourite for you.
  A refusal lists ids the agent can actually use. Asking for a model outside the pin, or one the
  catalogue does not have, is refused before anything is submitted.
* **Before a job starts**, the room left under the agent's `max_task_usd`
  ([budgets](/docs/concepts/budgets)) is held. A generation job's cost cannot be known until it finishes,
  so what the reservation does is admit the job rather than price it: an agent with no real headroom
  left is refused and nothing is spent. The check happens before the render, not after it.
* **You are billed for what the finished job actually cost**, once per job, and the rest of the
  reservation is released the moment the job settles. It appears on
  `GET /v1/agents/{id}/spend?by=component` under `media`.
* A job that fails, or that never finishes, tells the session so and stores nothing.
* `seconds`, `aspect_ratio` and `seed` are optional; a model clamps them to what it supports.

## Permission policies

Every server-executed tool resolves to `allow`, `ask`, or `deny` before it runs — set a default in `default_config` and override per tool in `configs`, and respond to `ask` with a `tool.confirm` event. Full detail, including the confirmation flow and connection/primitive scoping, lives in [Policies](/docs/concepts/policies).

## Connection tools

Every active [connection](/docs/identity/connections) the session's agent can act through contributes its catalog tools, registered as `<connector>.<tool>` (e.g. `tracker.get_issue`) — the same `tools.configs` key you write `allow`/`ask`/`deny` against.

## MCP connector

Connect any MCP server as a toolset. Configuration is split so secrets never live on the agent definition.

The **agent** declares servers by name and URL:

```jsonc theme={"system"}
{
  "mcp_servers": [
    { "type": "url", "name": "tracker", "url": "https://mcp.example.com/mcp" }
  ]
}
```

A server's tools are selected and gated in the same `tools` wrapper, through `configs` entries keyed by the MCP tool name — the identical `default_config` + `configs` + `permission` pattern used for built-ins:

```jsonc theme={"system"}
{
  "tools": {
    "default_config": { "permission": "ask" },
    "configs": {
      "tracker.get_issue": { "enabled": true, "permission": "ask" },
      "tracker.delete_project": { "enabled": false }
    }
  }
}
```

* Constraints: up to 20 servers per agent; every declared server must be referenced by at least one enabled tool, and every referenced tool must resolve to a declared server.
* **Default permission is `ask`** for MCP tools, so a newly exposed server tool never auto-runs. To trust one, give it an explicit `"permission": "allow"` in `configs` — per tool, by name. There is no server-wide trust switch.
* **Auth is injected at session start**, never on the agent: reference [vault](/docs/identity/vault) credentials (`static_bearer` or `mcp_oauth`) which are matched to servers by URL and injected server-side. The sandbox never receives the token.
* A connection or auth failure doesn't stop the session — it surfaces as a `session.error` event naming the server, and the connection is retried on the next wake.

### Vetta as its own MCP server

`POST /v1/mcp` publishes **the Vetta API itself** as an MCP tool catalog, so an agent can operate a
Vetta account the way it operates any other connected system. It is a JSON-RPC endpoint speaking the
three methods a catalog needs (`initialize`, `tools/list`, `tools/call`) and nothing else.

```jsonc theme={"system"}
{
  "mcp_servers": [
    { "type": "url", "name": "vetta", "url": "https://api.vetta.sh/v1/mcp" }
  ]
}
```

* **The catalog is generated from the route table**, one tool per served route, named
  `resource_verb` — `agents_create`, `sessions_list_events`, `computers_exec_command`. Namespaced by
  the server, a tool is `vetta.agents_create` in `configs`, exactly like `tracker.get_issue` above.
* **Argument schemas are the request schemas.** Path parameters, declared query filters and body
  fields arrive as one flat object, validated by the same zod schema `/v1/openapi.json` publishes.
* **Every tool carries `annotations.readOnlyHint`**, so a caller can allow reads and `ask` on writes
  without maintaining a list.
* **A tool call is an ordinary API call.** It is dispatched back through the same middleware with the
  bearer that made it, so authentication, scope, the plan gate, rate limits and the strict request
  contract all apply once and identically. A tool can never reach further than its own key.
* **Nine routes are withheld from the catalog** and answer only over HTTP: this endpoint itself, the
  SSE stream (`GET /v1/sessions/{id}/stream`), the multipart upload (`POST /v1/files`), the four
  `/v1/api_keys` routes (they mint and return live secrets), `POST /v1/vaults/{id}/credentials`
  (the one route that accepts a secret value) and the [model proxy](/docs/api/proxy) (a credentialed,
  billed model call is a loop, and its streaming half never returns). None of them is a thing a
  model should be handed.

See [MCP server](/docs/api/mcp) in the API reference for the handshake, the tool schema, the result cap
and the withheld routes one by one, with the reason each is withheld.

## Tools from your apps

A `fullstack` [app](/docs/api/apps) that serves its own MCP endpoint declares the path once, on the app
(`"mcp": "/mcp"` on create or patch; `apps[].mcp` in a `naive.config`), and nothing on any agent:

```bash theme={"system"}
curl -fsSL -X PATCH https://api.vetta.sh/v1/apps/app_01H... \
  -H "authorization: Bearer sk_live_..." \
  -H "content-type: application/json" \
  -d '{ "mcp": "/mcp" }'
```

* **Every agent that can access the app gets its tools.** Access is the `apps` tool's
  `allowed_apps` config — absent means every app in the organization, present means exactly the
  listed ids. On each turn the platform resolves the app's current URL plus its `mcp` path and
  registers the endpoint's tools as `<app-name>.<tool>` (`storefront.list_orders`), so they are
  gated by the same `default_config` + `configs` allow/ask/deny as every other tool. No per-agent
  `mcp_servers` entry, and nothing to update when a custom domain moves the URL.
* **Auth is the platform's own token.** Setting `mcp` mints an opaque bearer, pushes it into the
  app as the write-only secret `VETTA_MCP_TOKEN` — the app requires it on its endpoint — and
  injects it as `Authorization: Bearer` server-side on every call. It never reaches the sandbox,
  the transcript or any read route. Setting `mcp` to `null` deletes the secret and forgets the
  token; re-sending the same path never rotates it.
* **An unreachable endpoint never fails a turn.** An app that is not yet `active`, or whose MCP
  endpoint is down or malformed, simply contributes no tools that turn; the platform logs one
  error line and the agent carries on with the rest of its toolset.

## Coming soon

* **`send_email` / `send_sms` as agent tools** — today email and SMS are sent via the [API/CLI](/docs/api/messaging) (`POST .../send`), not from inside a turn.
* **Custom tools** — organization-defined, client-executed tools: you describe the arguments as a JSON Schema, the model emits a tool-use event, your application runs it and returns the result.

## Context management

Keeping the context window small is the other half of cost control. Vetta manages it at three levels:

<CardGroup cols={3}>
  <Card title="Progressive disclosure" icon="layers">
    [Skills](/docs/capabilities/skills) load a small always-on index; full bodies are pulled only when a task needs them.
  </Card>

  <Card title="Automatic compaction" icon="minimize-2">
    When the transcript crosses a token threshold, the earlier turns are summarized into a compaction block and the pre-summary history is dropped, preserving tool pairing.
  </Card>

  <Card title="Context editing" icon="eraser">
    Stale tool results (and optionally older thinking) are cleared once they're no longer needed, keeping the live window lean without losing the durable record.
  </Card>
</CardGroup>

The full transcript is always retained durably (see [Runtime & durability](/docs/concepts/runtime)); context management only governs what is *re-sent to the model* each turn. Context that never enters the window is context you never pay for. The event log behind that transcript is replayable by [`seq` cursor](/docs/concepts/events-and-streaming) for **at least 72 hours**, so a consumer that falls behind can always resume without loss.

<Card title="Next: files" icon="folder" href="/docs/capabilities/files">
  Persist and retrieve artifacts across sessions.
</Card>
