Cereal docs
Tools and integrations

Provider-executed tools

Web search, code execution and a gateway's own tools, run by the model provider inside the turn.

tool() declares code your application runs when Cereal calls it. providerTool() declares a capability the model provider runs inside the turn, so there is no handler, nothing to serve, and nothing for sync to register.

The argument is the provider's own tool object, verbatim. Cereal sends it as written and checks only that it is an object with a non-empty type.

import { defineAgent, providerTool } from "@trycereal/sdk"

export default defineAgent({
  agent: {
    slug: "researcher",
    model: { provider: "gateway", model: "anthropic/claude-opus-5" },
    system: "Answer with current information and cite what you used.",
    tools: [
      providerTool({
        type: "web_search_20250305",
        name: "web_search",
        max_uses: 5,
        allowed_domains: ["arxiv.org"]
      }),
      providerTool(
        { type: "code_execution_20250825", name: "code_execution" },
        { betas: ["code-execution-2025-08-25"] }
      )
    ]
  }
})

Use the provider's documented tool fields and a model that supports them. Cereal passes the tool object through; invalid provider-specific fields can fail on the first model request even if the agent definition was accepted.

The four options beside the tool

OptionWhat it does
nameWhat the transcript, the trace and the usage counter call it. Defaults to the tool object's name, else its type.
providerPins the entry to one provider type. Unpinned, it is declared on every rung of the fallback ladder.
betasanthropic-beta values the tool needs. Anthropic only, and the one thing pass-through would otherwise lose.
requiredfalse lets a rung that cannot carry the tool run without it, recording a session.error. Defaults to true.

A cross-provider fallback ladder writes one pinned entry per provider, because Anthropic's web_search_20250305 is not OpenAI's web_search:

tools: [
  providerTool({ type: "web_search_20250305", name: "web_search" }, { provider: "anthropic" }),
  providerTool({ type: "web_search" }, { provider: "openai" })
]

When the provider says no

Cereal refuses two things at write time and nothing else: a tool object with no type, and a log name that collides with a tool Cereal itself runs. Everything else is the provider's answer, and it arrives in the transcript as a session.error naming the entry, so a rejected declaration does not read as a bad prompt.

Three shapes of that, all of them lines in the transcript rather than failures you have to go looking for:

  • The provider rejected the request, and the error names the tools this request declared.
  • The provider returned a result Cereal's model adapter cannot read, which happens for a genuinely new tool family rather than a new dated version of an existing one.
  • The tool ran and this build cannot replay its blocks into the next prompt, so the model will not see those results on the next step. Said once per session.

What you see on the stream

  • A tool.call.started / .completed pair carrying executedBy: "provider", under the provider's own tool name. The call and its result arrive together, because the provider already ran it.
  • The model's citations on agent.message.sources.
  • What the provider billed beyond tokens, in model.request.completed.usage.serverToolRequests. Consult the provider for current tool pricing.

Provider tools bypass Cereal approvals

policies.toolCalls is evaluated over the calls the model asks Cereal to run. A provider-executed call is already finished when it arrives, before any hook could have been asked, so it can never become a tool.call.pending. A capability the provider runs cannot be approved through Cereal's tool-call policy.

If a provider tool connects to an MCP server, that provider sends the requests. Cereal does not sign them or apply its approval policy. To have Cereal call the server with those controls, use an MCP tool declaration.

Some vendor tools expect *you* to run them

Anthropic's memory, bash, text_editor and computer, and OpenAI's shell and apply_patch, are client-executed: the vendor supplies the schema and the caller runs the tool. Declaring one as a provider-executed tool does not supply an implementation. Use a custom tool with a handler for code that your application must run.

Long turns

Anthropic can pause a turn mid-flight while a server-side tool runs. Cereal continues the paused request with the same provider. Model fallback is disabled during that continuation, and the provider's code-execution container is reused across steps.

Compatibility

The earlier string form, such as providerTool("web_search", { maxUses: 5 }), remains supported for its existing tool types. Use a provider tool object for new integrations.

On this page