Plane docs
Guides

Provider-executed tools

Capabilities the model provider runs inside the turn — web search, code execution.

tool() declares code you write and Plane runs. 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.

import { definePlane, providerTool } from "@plane/sdk"

export default definePlane({
  agent: {
    slug: "researcher",
    model: { provider: "gateway", model: "anthropic/claude-opus-5" },
    system: "Answer with current information and cite what you used.",
    tools: [
      providerTool("web_search", { maxUses: 5, allowedDomains: ["arxiv.org"] }),
      providerTool("code_execution")
    ]
  }
})

The canonical names are web_search, web_fetch, code_execution, file_search, image_generation, computer_use and memory. The config is camelCase and Plane's own, so the same entry works on Anthropic and on OpenAI even though they spell the fields allowed_domains and filters.allowed_domains. It is narrowed by the name — providerTool("web_search", { vectorStoreIds: [] }) does not compile.

Refusal is at write time

Where a provider cannot honour an entry, Plane refuses the definition with ProviderToolUnsupported rather than dropping it — and it checks the primary model and every fallback rung. An agent that quietly stops searching returns confident stale facts.

Pass required: false to opt into degradation instead, in which case the tool is dropped for that rung and a session.error says so.

A provider type that cannot carry provider tools at all (openai-compatible, and the gateway's non-anthropic/* routes) is refused regardless of required: the adapter would declare the tool as an ordinary function the model expects Plane to run, which is the worst of both.

What you see on the stream

  • A tool.call.started / .completed pair carrying executedBy: "provider". 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 — web search is about $10 per 1,000 calls on both providers, which is real money on an agent that searches five times a turn.

The gate never sees one

policies.toolCalls is evaluated over the calls the model asks Plane 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 is one Plane does not sign for or gate. That is also why a provider-dialled MCP server is refused.

Long turns

Anthropic can pause a turn mid-flight while a server-side tool runs. Plane continues it rather than settling the turn, with fallback disabled while paused (the continuation carries blocks another provider cannot read), and a code-execution container is carried across steps. None of that needs anything from you.

On this page