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

# tools_mode reference

> Three ways to expose your registered tools to the model on a thread message — dynamic, tenant, explicit. Pick one per request based on the shape of your app.

When you `POST /v1/threads/:id/messages`, the `tools_mode` field decides
**which subset of your registered tools the model sees** for that turn.
Three modes, each mapping to a different app archetype.

## At a glance

| Mode       | What the model sees                                                                                                                        | Best for                                                                       |
| ---------- | ------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------ |
| `dynamic`  | 4 meta-tools (search/get/multi-execute/manage). Model discovers + calls tools on demand, including per-user MCPs end-users connect inline. | Consumer apps where end-users own their data (Notion, Stripe, GitHub, …)       |
| `tenant`   | **Every tenant-shared tool you own**, auto-attached. Built-ins, tenant-mode MCP servers, webhook tools. No meta-tools, no per-user MCPs.   | Company-internal agents, support bots, automation — "use OUR tools every turn" |
| `explicit` | Exactly the tool IDs you list in the `tools` field. Nothing else.                                                                          | Surgical control. You change the available toolset between calls.              |

## Defaults

If you don't set `tools_mode` explicitly:

* `tools` field present → defaults to `explicit`
* `tools` field absent → defaults to `dynamic`

You never need to set `tools_mode` for the common cases. Set it only
when you want `tenant` or want to override the default.

## `dynamic` — discover-then-call

The model receives 4 meta-tools instead of your registered ones:

* `qlaud_search_tools(intent)` — keyword + embedding search across your
  catalog AND every per-user MCP catalog entry.
* `qlaud_get_tool_schemas(names)` — fetch full input schemas for picked tools.
* `qlaud_multi_execute(calls)` — fan-out execute multiple tools in one turn.
* `qlaud_manage_connections(action, tool)` — bring up the per-user OAuth /
  paste-API-key flow inline in chat for end-users.

This is the right mode for **consumer-facing apps**. Token overhead stays
bounded regardless of how many tools you have, and end-users self-serve
their own integrations (their Notion, their Stripe, their Calendar)
without you having to register anything per-user.

```bash theme={null}
curl https://api.qlaud.ai/v1/threads/$THREAD_ID/messages \
  -H "Authorization: Bearer $QLAUD_KEY" \
  -d '{
    "model": "claude-sonnet-4-6",
    "max_tokens": 1024,
    "tools_mode": "dynamic",
    "content": [{"type":"text","text":"Find any GitHub issues I'\''m mentioned in this week"}]
  }'
```

For the deep dive on dynamic mode (token math, meta-tool semantics,
performance comparison), see [`tools_mode: dynamic`](/api-reference/tools-mode-dynamic).

## `tenant` — auto-attach all your company tools

Every tenant-shared tool you've registered is sent to the model, every
turn, with no setup per request:

* All your **built-ins** (Resend, Linear, Twilio, Slack, GitHub,
  http-call wrappers — anything with an encrypted config you control).
* Every tool from your **tenant-mode MCP servers** (your shared Notion
  workspace, your shared Linear team, etc.).
* Every **webhook tool** you host.

Per-user MCPs are explicitly excluded — `tenant` mode never asks
end-users to connect their own accounts.

```bash theme={null}
curl https://api.qlaud.ai/v1/threads/$THREAD_ID/messages \
  -H "Authorization: Bearer $QLAUD_KEY" \
  -d '{
    "model": "claude-sonnet-4-6",
    "max_tokens": 1024,
    "tools_mode": "tenant",
    "content": [{"type":"text","text":"Customer is asking for a refund. Look them up and file a Linear ticket if it'\''s a known issue."}]
  }'
```

Maps directly to the dashboard's **"Connect with your company's key"**
section. If a tool shows up there, it'll be in `tenant` mode's auto-attach
list. Register a new http-call wrapper in that section, and it's available
to the model on the next request — no code change in your app, no need
to update an `tools: [...]` array.

### When to use `tenant`

* **Company-internal AI agents** that should ALWAYS have your tools.
* **Customer support bots** where the AI should email/ticket/Slack on
  behalf of YOUR company.
* **Automation pipelines** where end-users (if any) shouldn't see
  per-user OAuth UX.
* **Wrapping internal APIs** with `qlaud-builtin/http-call` and exposing
  them broadly without per-message ID enumeration.

### When NOT to use `tenant`

* Your end-users need to connect their own accounts (use `dynamic`).
* You want to gate which tools are available per-conversation (use `explicit`).
* You have 50+ tenant-shared tools and want to reduce token overhead
  (use `dynamic` — it's bounded regardless of tool count).

### Hard cap

Tenant mode loads up to **200 tenant-shared tools** per request. If
you exceed that, switch to `dynamic` (no token cost from the bloat) or
to `explicit` with a curated subset.

## `explicit` — exactly these IDs

You enumerate the tool IDs in the `tools` field. The model sees those
verbatim, nothing else.

```bash theme={null}
curl https://api.qlaud.ai/v1/threads/$THREAD_ID/messages \
  -H "Authorization: Bearer $QLAUD_KEY" \
  -d '{
    "model": "claude-sonnet-4-6",
    "max_tokens": 1024,
    "tools": ["tool_a1b2c3", "tool_d4e5f6"],
    "content": [{"type":"text","text":"Use only these two tools to answer."}]
  }'
```

Use this when:

* Your conversation logic decides per-message which tools the model
  may use (e.g. step 1 = research only, step 2 = action only).
* You're building a guided wizard where each step exposes one tool.
* You're testing a new tool in isolation before going broader.

## Mixing modes across a thread

`tools_mode` is per-message, not per-thread. You can call message 1 with
`tenant` to give the model your full company toolset, then message 2
with `explicit: ["tool_xyz"]` to constrain it. The model sees a different
tool array on each call; previous tool\_use blocks remain in history but
no longer dispatch unless the tool is in the current request's set.

## Errors

| Status | Meaning                                                                                                                   |
| ------ | ------------------------------------------------------------------------------------------------------------------------- |
| 400    | `tools_mode` is set to anything other than `dynamic`, `tenant`, or `explicit`.                                            |
| 400    | `tools_mode: "dynamic"` but you also passed `tools: [...]`. Drop the `tools` field — meta-tools handle discovery.         |
| 400    | `tools_mode: "tenant"` but you also passed `tools: [...]`. Drop the `tools` field — tenant mode auto-attaches everything. |
| 400    | `tools_mode: "explicit"` (or default with `tools`) but one of the listed IDs is unknown / revoked.                        |

## See also

* [`tools_mode: dynamic` deep dive](/api-reference/tools-mode-dynamic) — token math, meta-tool semantics
* [Built-in catalog](/api-reference/builtins) — every built-in shows up in `tenant` mode automatically
* [Custom HTTP endpoint](/api-reference/http-call) — the universal "wrap any REST API" tool, perfect companion to `tenant` mode
* [Threads API](/api-reference/threads) — the thread message endpoint that consumes `tools_mode`
