Skip to main content
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

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.
For the deep dive on dynamic mode (token math, meta-tool semantics, performance comparison), see 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.
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.
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

See also