manifest.visibility sets the default for every tool the plugin ships. Individual tools can override it. Three values: 'always', 'on-demand' (default), 'silent'.
Set plugin-wide visibility on the manifest
The manifest's visibility flag is the default applied to every tool the plugin returns.
import type { PluginManifest } from '@ixo/oracle-runtime';
const manifest: PluginManifest = {
title: 'Weather',
summary: 'Current weather + forecast lookups for cities.',
whenToUse: ['Current weather', 'Forecasts', 'Outfit recommendations'],
visibility: 'on-demand',
};Three tiers: 'always' (bound to the agent at boot, listed in the Tier-1 prompt), 'on-demand' (default — discoverable via list_capabilities, loaded via load_capability), 'silent' (not advertised — see the warning below). Canonical source: weather.plugin.ts.
'silent' controls advertising, not access — it is not a security boundary. A silent plugin is left out of the Tier-1 capability block, excluded from list_capabilities (by default), and rejected by load_capability. But if a silent plugin returns tools, the capability gate passes them straight through to the model, exactly like 'always'-tier tools. Use 'silent' for transport/middleware/Nest-module plugins (e.g. Slack transport, credits/billing) that have nothing to advertise to the agent — never to "hide" a sensitive capability.
Override visibility per tool
Set visibility directly on the PluginTool when one tool needs a different tier from the rest of the plugin.
import { tool, z, type PluginTool } from '@ixo/oracle-runtime';
const currentTool: PluginTool = tool(handler, {
name: 'get_current_weather',
description: 'Get the current weather for a city.',
schema: z.object({ city: z.string() }),
});
currentTool.visibility = 'always';
const forecastTool: PluginTool = tool(handler, {
name: 'get_weather_forecast',
description: 'Get a daily forecast for a city.',
schema: z.object({ city: z.string(), days: z.number().optional() }),
});
forecastTool.visibility = 'on-demand';
return [currentTool, forecastTool];PluginTool shape: see types.ts.
Hold back rare tools in an always plugin (Workers runtime)
On @ixo/oracle-runtime-workers 0.39.0 and later, an always plugin can list tools in manifest.onDemandTools instead of setting visibility on each one. The plugin keeps its Tier-1 entry and its other tools; the listed tools wait until load_capability names the plugin. Names are matched at request time, so this works for tools that come from an MCP server too. Give the plugin a brief that says what the held-back tools do: that is its line in the prompt's on-demand catalogue.
import type { PluginManifest } from '@ixo/oracle-runtime-workers';
const manifest: PluginManifest = {
title: 'Weather',
summary: 'Current weather + forecast lookups for cities.',
whenToUse: ['Current weather', 'Forecasts', 'Outfit recommendations'],
visibility: 'always',
brief: 'week-long forecasts',
onDemandTools: ['get_weather_forecast'],
};A tool's own visibility still wins over the list. Bound tool definitions are paid on every model call, so this keeps the first call small without hiding the plugin.
Pick the right tier
Use this decision tree.
Promoting to 'always' costs ~80 tokens per plugin in the Tier-1 prompt plus the tool schemas on every turn. 'silent' simply stops advertising the plugin to the agent — it does not hide or disable any tools the plugin returns.
What to know before shipping
- Default is
'on-demand'. A plugin without an explicitmanifest.visibilityis treated as on-demand. 'on-demand'plugins live in a per-threadloadedPluginsset. On the Node.js runtime the set is monotonic — loading is forever for that thread; a new thread resets it. On the Workers runtime (0.39.0 and later) a cold turn unloads plugins the thread stopped using, and never a warm one, so the prompt cache is kept. See Loading and unloading on the Workers runtime.- Per-tool overrides let you ship a mixed plugin (one
'always'tool + several'on-demand'tools) without splitting it. On the Workers runtime,manifest.onDemandToolsdoes the same from the manifest. - On the Workers runtime (0.39.0 and later) the model reaches an
'on-demand'plugin from its line in the on-demand catalogue, the capability router can load it before the first model call, and a call to one of its hidden tools loads it. Setroutable: falseto keep the router from preloading it. - The bundled
sandboxandvfsplugins are wholly'on-demand'on the Workers runtime from 0.39.0, andmemory,skillsanduser-preferenceshold back their rare tools withonDemandTools. Undo any of it withmanifestOverrides, for examplesandbox: { visibility: 'always' }ormemory: { onDemandTools: [] }. 'silent'plugins still run their middleware and Nest modules, and any tools they return stay callable by the agent — they're just never advertised. Use it for instrumentation, rate limiting, billing, and transports. It is not a way to hide a capability for security.- A fork with 50
'on-demand'plugins can usually keep 3–5 loaded per thread — the budget stays bounded as the catalog grows.