tools array on an agent is simple and static: every tool in it is sent to the model on every turn. That works well when you have a handful of tools that are always relevant.
Real applications are messier. You might have admin tools that only certain users can access. You might have tools that depend on which phase of a workflow the agent is in. You might be integrating tools from an external MCP server. You might want to add logging or rate-limiting across a whole group of tools without touching each one individually.
Toolsets solve all of these. A toolset is an object with a single method, tools(ctx), that returns a list of tool definitions. Because it receives the RunContext, it can decide at the start of each turn which tools to expose. Agents accept any number of toolsets via the toolsets option, and Vibes merges them with the static tools array before each model call.
FunctionToolset — the basic collection
FunctionToolset is the simplest toolset: a plain wrapper around an array of tool definitions. Use it to logically bundle related tools and pass them as a unit.
FunctionToolsettakes an optional array in the constructor.addTool()lets you add tools incrementally — useful when building a toolset dynamically.- Toolsets are passed to
toolsets, nottools.
FilteredToolset — all-or-nothing gating
FilteredToolset wraps another toolset and hides it entirely when a predicate returns false. Use it when a whole category of tools should be invisible to the model based on the caller’s context — user role, feature flags, environment, and so on.
- The wrapped
adminToolsis either shown in full or hidden entirely — there’s no partial exposure. - The predicate receives the
RunContextand can be async. Returntrueto expose the inner toolset,falseto hide it.
false, the model won’t see any of these tools. It won’t try to call them and won’t know they exist.
FilteredToolset is all-or-nothing at the toolset level. If you need to filter individual tools within a toolset based on state, use PreparedToolset instead.PreparedToolset — per-tool control per turn
PreparedToolset gives you fine-grained control over which tools inside a toolset are exposed each turn. The prepare function receives the full RunContext and the complete list of tools from the inner toolset, and returns whichever tools to expose.
- The
toolsparameter is the full resolved list from the inner toolset — you return a subset. - You can apply multiple conditions in sequence. The function just returns an array.
FilteredToolset vs PreparedToolset at a glance:
PrefixedToolset — namespacing tool names
When you combine tools from multiple sources, name collisions become a real risk.PrefixedToolset prepends a string to every tool name in the wrapped toolset, giving you a clean namespace.
- The prefix is prepended to every tool name.
"search_repos"becomes"github_search_repos". The model’s tool call will use the prefixed name, and Vibes routes it correctly.
RenamedToolset:
WrapperToolset — middleware for tool execution
WrapperToolset is an abstract base class that intercepts every tool call in the wrapped toolset. Subclass it and implement callTool to add cross-cutting behavior — logging, metrics, rate limiting, error transformation, argument sanitization — without touching the underlying tools.
- Provide the deps type as a generic parameter so
ctx.depsis properly typed insidecallTool. nextis a function that invokes the original tool’sexecute. You must call it (unless you want to short-circuit).- You can modify
argsbefore callingnext, or transform the result it returns. - Re-throwing preserves normal error handling. You can also wrap in a custom error type here.
CombinedToolset — merging multiple toolsets
CombinedToolset merges any number of toolsets into one. The resolved tools from all member toolsets are combined into a single flat list. If two toolsets expose a tool with the same name, the last one wins.
CombinedToolset is especially useful when you want to aggregate several independently-defined toolsets and expose them as a single unit — for example, packaging a plugin or feature module.
ExternalToolset — tools that execute outside the agent
Sometimes a tool must run in a different environment than the agent’s process. A browser extension needs to manipulate the DOM. A desktop app needs to read local files. A sandboxed service needs to call a restricted API.ExternalToolset handles this. Each tool is described with a JSON Schema instead of Zod. When the model calls any of these tools, the run pauses and throws an ApprovalRequiredError containing the pending DeferredToolRequests. The caller executes the tools externally, collects the results, and calls agent.resume() to continue.
- Tool definitions use raw JSON Schema objects — no Zod dependency in the external environment.
- The run pauses immediately when an external tool is called.
err.deferred.requestsis an array of{ toolName, args, toolCallId }objects describing what the model wants to do.agent.resume()feeds the results back and continues the run. The run may pause again if more external tool calls follow.
See Human-in-the-Loop for the full
DeferredToolRequests / DeferredToolResults API and patterns for multi-step external execution.MCPToolset — tools from MCP servers
The Model Context Protocol (MCP) is an open standard for exposing tools from external servers.MCPToolset connects to an MCP server and exposes all of its tools as a Vibes toolset. Tools are discovered lazily on the first call and cached for 60 seconds by default.
MCPStdioClientspawns the MCP server as a child process. UseMCPHttpClientfor HTTP-based servers.MCPToolsetwraps the client and implements theToolsetinterface. Tools are fetched from the server and cached.- Disconnect the client when the agent is done to avoid leaving zombie processes.
MCPManager:
Composing toolsets
All toolset classes implement the sameToolset<TDeps> interface, so they compose freely. You can nest them to build precisely the behavior you need:
LoggingToolset wraps a CombinedToolset that merges coreTools with namespacedAdmin. Each layer adds a single concern. None of the layers need to know about the others.
Tools
Build individual tools with the tool() factory
Human-in-the-Loop
Pause and resume runs for external tool execution and approval