Tools

Tool Search

Tool Search is an experimental OpenClaw agent runtime feature. It gives agents one compact way to discover and call large tool catalogs. It is useful when the run has many available tools but the model is likely to need only a few of them.

This page documents OpenClaw Tool Search. It is not the Codex-native tool search or dynamic-tools surface. Codex-native code mode, tool search, deferred dynamic tools, and nested tool calls are stable Codex harness surfaces and do not depend on tools.toolSearch.

For the generic OpenClaw runtime that exposes a QuickJS-WASI exec/wait surface instead of Tool Search controls, see Code Mode.

Local inference routes use structured Tool Search automatically when tools.toolSearch is unset. This defers tool schemas while keeping the policy-approved capabilities available. It does not enable lean mode or remove optional tools. The default follows the active model for each run, including model switches and fallbacks, without changing another agent's settings.

When enabled for OpenClaw runs, the model automatically receives a bounded directory of the available trusted tool names and descriptions. Explicitly setting tools.toolSearch: true selects one tool_search_code tool, plus any direct-only tools whose structured results cannot cross the compact bridge. The code tool runs a short JavaScript body in an isolated Node subprocess with an openclaw.tools bridge:

js
const hits = await openclaw.tools.search("create a GitHub issue");const tool = await openclaw.tools.describe(hits[0].id);return await openclaw.tools.call(tool.id, {  title: "Crash on startup",  body: "Steps to reproduce...",});

The directory scales with the active model's context window. When space is tight, descriptions shorten before tool names are omitted; every authorized catalog entry remains searchable and callable. Invalid arguments for OpenClaw-owned tools include a bounded expected input signature when one can be rendered, so the model can correct the call without another schema lookup. If a call mistakes an admitted skill name for a tool ID, the error points back to the skill’s complete instructions instead of sending the model through tool search.

The deferred directory omits tools already exposed directly. They remain searchable, so discovery can still return their complete schemas without duplicating native guidance.

The catalog can include catalog-eligible OpenClaw tools, plugin tools, MCP tools, and client-provided tools. The directory gives the model an idea of which trusted capabilities it can discover without exposing every cataloged schema up front. It also explains that policy-approved MCP and client tools may be discoverable. Their untrusted names and descriptions are not copied into the system prompt. Instead, the model searches compact descriptors, describes one selected tool when it needs the exact schema, and calls that tool through OpenClaw. Direct-only tools remain model-visible and are not added to the catalog.

Codex harness runs do not receive these experimental OpenClaw Tool Search controls. OpenClaw passes product capabilities to Codex as dynamic tools, and Codex owns the stable native code mode, native tool search, deferred dynamic tools, and nested tool calls.

How a turn runs

At planning time the OpenClaw embedded runner builds the effective catalog for the run:

  1. Resolve the active tool policy for the agent, profile, sandbox, and session.
  2. List eligible OpenClaw and plugin tools.
  3. List eligible MCP tools through the session MCP runtime.
  4. Add eligible client tools supplied for the current run.
  5. Keep core coding primitives and direct-only tools model-visible and index compact descriptors for the remaining catalog-eligible tools.
  6. Add a deterministic, bounded, policy-filtered capability directory to the cache-stable system-prompt prefix.
  7. Expose the OpenClaw code bridge, the structured fallback tools, or the compact directory surface alongside those stable, directly callable tools.

At execution time every real tool call returns to OpenClaw. The isolated Node runtime does not hold plugin implementations, MCP client objects, or secrets. openclaw.tools.call(...) crosses the bridge back into the Gateway, where the normal policy, approval, hook, logging, and result handling still apply.

Modes

tools.toolSearch has three model-facing modes:

  • code: exposes tool_search_code, the default compact JavaScript bridge, alongside the capability directory and direct-only tools.
  • tools: exposes tool_search, tool_describe, and tool_call as plain structured tools for providers that should not receive code, alongside the capability directory and direct-only tools.
  • directory: exposes tool_search, tool_describe, and tool_call plus a bounded, cache-stable prompt directory. Core coding primitives, direct-only tools, and tools required by the run's delivery policy remain visible; other schemas stay deferred.

All modes use the same policy-filtered catalog and normal OpenClaw execution path. Tools marked catalogMode: "direct-only" stay outside that catalog and remain model-visible. If the current runtime cannot launch the isolated Node code-mode child process, the default code mode falls back to tools before catalog compaction. In directory mode, client-provided tools stay directly visible for the current run while OpenClaw tools, plugin tools, and MCP tools can be compacted behind the directory catalog. A direct call to an exact hidden directory name is hydrated from that same authorized catalog before execution.

All modes are experimental. Local inference defaults to structured tools mode; other routes keep direct tool exposure unless configured otherwise. Codex harness runs use their native surfaces.

There is no separate source-selection config. When Tool Search is enabled, the catalog includes catalog-eligible OpenClaw, MCP, and client tools after normal policy filtering; direct-only tools are retained separately.

Why this exists

Large catalogs are useful but expensive. Sending every tool schema to the model makes the request larger, slows planning, and increases accidental tool selection.

Tool Search changes the shape:

  • direct tools: the model sees every selected schema before the first token
  • Tool Search code mode: the model sees one compact code tool, a bounded capability directory, a short API contract, and any direct-only tools
  • Tool Search tools mode: the model sees three compact structured fallback tools, the same capability directory, and any direct-only tools
  • Tool Search directory mode: the model sees a bounded directory plus search/describe/call controls, policy-required direct tools, and any direct-only tools
  • during the turn: the model can load remaining schemas as needed

Tool Search is useful when one run can see many tools, especially from MCP servers or client-provided app tools. Local inference uses it by default to reduce the prompt that the model must process before responding.

The capability directory is sorted by tool name, limited to 18,000 characters, and built from the already policy-filtered catalog. OpenClaw reuses the rendered directory for an unchanged catalog snapshot and places it above the system-prompt cache boundary. User messages, per-turn tool guesses, session identifiers, and untrusted MCP or client metadata do not enter the directory. This keeps repeated turns eligible for prompt KV-cache reuse. When the authorized catalog changes, OpenClaw builds a new directory for the new snapshot.

API

openclaw.tools.search(query, options?)

Searches the effective catalog for the current run.

Queries must be written in English. Ranking is lexical (Okapi BM25 over tool names, descriptions, and first-party parameter names and descriptions), with light English stemming so scheduling reaches a tool described as Schedule a recurring task, and a small intent expansion so look up the price reaches one described as Search the web. Tool names and descriptions are written in English, so a query in another language will usually match nothing. It is not rejected — a catalog may legitimately describe a tool in another script — but a query with no usable terms returns no ranked results rather than an arbitrary slice of the catalog. An exact tool name is still honored even when it tokenizes to nothing. Both tool_search and the code-mode bridge state this requirement in their model-facing descriptions.

Untrusted parameter schemas are never indexed. MCP and client tools are matched on name and description only, which is the same boundary that defers their input signatures as input: "unknown".

Results are compact and safe to put back into prompt context. Each hit includes a bounded TypeScript-style input signature, such as { id: string; mode?: "drip" | "flood" }, so the model can skip describe when that signature is sufficient. A trusted OpenClaw core or plugin tool may also include a compact output hint, such as Array<{ id: string; paid: boolean }>. MCP and client output-schema claims are not promoted into this trusted hint. Their untrusted input schemas are also deferred as input: "unknown"; use describe before calling them. Open, oversized, or otherwise partial output schemas omit the hint and remain available through describe instead.

js
const hits = await openclaw.tools.search("calendar event", { limit: 5 });

openclaw.tools.describe(id)

Loads full metadata for one search result, including the exact input schema and the trusted full outputSchema when the tool declares one.

js
const calendarCreate = await openclaw.tools.describe("mcp:calendar:create_event");

openclaw.tools.call(id, args)

Calls a selected tool through OpenClaw and returns the raw { tool, result } envelope. JSON-returning tools normally place their value in result.details. OpenClaw validates a trusted core or plugin tool's declared input schema before execution. Missing required arguments, incorrect types, and forbidden properties return actionable tool errors instead of executing the tool; misspelled properties include a suggested parameter when available. If a trusted tool also declares outputSchema, OpenClaw compiles that schema before execution and validates final details after normal tool hooks before returning the catalog call. MCP and client-owned schemas remain deferred to their owning execution boundary.

In structured mode, tool_call also repairs flattened target arguments from local models. It preserves target fields such as id and name, and rejects ambiguous tool selectors instead of calling the wrong tool. Nest target arguments under args when a target field matches another cataloged tool.

The structured control's model-facing text includes only the tool's id, name, and source alongside the unchanged target result; it does not repeat the description and input signature. Its structured details retain the full call envelope for runtime consumers. Use tool_describe for full tool metadata.

js
await openclaw.tools.call(calendarCreate.id, {  summary: "Planning",  start: "2026-05-09T14:00:00Z",});

Tool authors declare output contracts on the tool's outputSchema property. It describes AgentToolResult.details, not rendered content blocks. Include all non-throwing variants or omit it for unstable results. See Code Mode output contracts and Tool plugins.

The structured fallback mode exposes the same operations as tools:

  • tool_search
  • tool_describe
  • tool_call

Deferred names are catalog entries, not directly callable functions in this mode. Put the result ID or name in tool_call.id and all target parameters in tool_call.args, including when other instructions refer to the deferred tool by name. A compact search signature may be enough to call it; use tool_describe when the full schema is needed.

tool_search accepts either the existing single-query shape or a batch of independent queries:

json
{  "query": "today's calendar events",  "limit": 3}
json
{  "queries": [    { "query": "today's calendar events", "limit": 3 },    { "query": "Slack messages needing attention", "limit": 3 }  ]}

Single-query calls continue to return the compact candidate array directly. When both shapes contain searches, the non-empty query runs first, with the top-level limit scoped to it. Batch entries follow in request order, including repeated query text; each occurrence keeps its own limit and counts toward the batch budgets.

Beside a non-empty batch, an omitted, null, empty, or whitespace-only query is ignored. In that case, omit the top-level limit or set it to null; a non-null top-level limit is rejected rather than applied to the batch. An omitted or null queries retains scalar behavior, and queries: [] also falls back to the scalar shape when query is non-empty. A blank scalar query without a batch still returns an empty candidate array. A missing or null scalar with no batch, or an empty batch with no non-empty scalar, is rejected. A scalar limit: null uses the default limit, just like an omitted limit. Invalid query shapes, invalid limits, and over-budget batches still fail.

Batch calls return { results: [{ query, candidates }] } in request order. Each query uses the same effective catalog, ranking, filtering, and per-query limit as an ordinary search; a candidate may appear in more than one result group. Descriptions are compacted before output. If the complete batch would exceed the 4,000-character response budget, lower-ranked candidates are removed and the response includes truncated: true. A result group that lost candidates also includes truncated: true, so an empty truncated group cannot be mistaken for a query that had no matches. Omitted per-query limits use searchDefaultLimit. The effective limits in one batch may request at most 50 candidates in total. A batch accepts at most 16 queries, with at most 512 characters per query and 512 UTF-8 bytes across the serialized query list. Invalid batches fail as one request, while a valid query with no matches returns an empty candidates array.

Directory mode exposes:

  • tool_search
  • tool_describe
  • tool_call

It also keeps core file and shell primitives, client-provided tools, direct-only tools, and policy-required delivery tools directly visible. Other authorized tool schemas stay deferred rather than changing with each user prompt. MCP tools cannot impersonate a directly visible core or policy-required delivery tool. If the bounded directory omits entries, use tool_search to find them and tool_describe to retrieve their full schemas. If the model requests an exact hidden directory tool name directly, OpenClaw resolves it from the authorized catalog before normal execution. Directory-mode client tool names must not collide with OpenClaw, plugin, or MCP tool names because exact deferred dispatch uses those names.

Runtime boundary

The code bridge runs in a short-lived Node subprocess. The subprocess starts with Node permission mode enabled, an empty environment, no filesystem or network grants, and no child-process or worker grants. OpenClaw enforces a parent-process wall-clock timeout and kills the subprocess on timeout, including after async continuations.

Outstanding bridged tool calls are canceled when the child settles, including fatal exits and final results. Failed exits wait for stderr to drain before rendering a bounded diagnostic. The error separately reports bytes discarded from the 64 KiB retained tail and bytes omitted from its final text preview.

The runtime exposes only:

  • console.log, console.warn, and console.error
  • openclaw.tools.search
  • openclaw.tools.describe
  • openclaw.tools.call

Normal OpenClaw behavior still applies to final calls:

  • tool allow and deny policies
  • per-agent and per-sandbox tool restrictions
  • channel/runtime tool policy
  • approval hooks
  • plugin before_tool_call hooks
  • tool executionMode: sequential calls run exclusively with other calls in the same catalog, including calls from other Tool Search or Code Mode cells
  • session identity, logs, and telemetry

Config

With tools.toolSearch unset, local Ollama models, LM Studio, and managed local services use structured tools mode with a default search limit of 5 and a maximum of 10. Known hosted Ollama routes (cloud model tags, the cloud provider, or the hosted endpoint) are excluded. An untagged alias served by an Ollama daemon inherits the daemon's Tool Search default even if that alias forwards to a hosted model. Other providers are not classified from model names or a loopback URL alone.

An explicit tools.toolSearch value takes precedence, including false. Setting agents.defaults.experimental.localModelLean: false restores optional tools but does not turn off automatic Tool Search.

Enable Tool Search explicitly for OpenClaw runs with the default code bridge:

bash
openclaw config set tools.toolSearch true

Equivalent JSON:

json5
{  tools: {    toolSearch: true,  },}

Use the structured fallback tools instead for OpenClaw runs:

json5
{  tools: {    toolSearch: {      mode: "tools",    },  },}

Use the compact directory surface instead for OpenClaw runs:

json5
{  tools: {    toolSearch: {      mode: "directory",    },  },}

Tune code-mode timeout and search result limits (values shown are the defaults):

json5
{  tools: {    toolSearch: {      mode: "code",      codeTimeoutMs: 10000,      searchDefaultLimit: 8,      maxSearchLimit: 20,    },  },}

The runtime clamps codeTimeoutMs to 1000-60000, maxSearchLimit to 1-50, and searchDefaultLimit to 1..maxSearchLimit.

Disable it:

json5
{  tools: {    toolSearch: false,  },}

Prompt and telemetry

Code mode attaches a telemetry object to every tool_search_code result:

  • catalogSize: number of catalog entries the runtime resolved
  • sources: catalog entry counts split into openclaw, mcp, and client
  • counterScope: opaque identifier for the counter lifetime; it stays stable when tools are appended or prompt policy narrows the catalog, and changes when the catalog is replaced or restored
  • searchCount, describeCount, callCount: running totals for the catalog session, carried across calls rather than reset per call

tools and directory mode emit no telemetry object; their tool_search, tool_describe, and tool_call results carry only the catalog data for that operation. OpenClaw does not record serialized tool or prompt byte counts. The E2E scenario measures provider payload bytes separately from the mock provider lane, not from the runtime.

Regardless of mode, completed target calls persist as bounded, redacted display activity in session history without adding synthetic model turns to replay. Search, describe, and call results carry each tool's id and source. Session logs therefore still answer:

  • how many tool schemas the model saw up front
  • how many search and describe operations it performed
  • which final tool was called
  • whether the result came from OpenClaw, MCP, or a client tool

E2E validation

The QA Lab gateway scenario proves all three paths with the OpenClaw runtime:

bash
pnpm openclaw qa suite --provider-mode mock-openai --scenario tool-search-gateway-e2e

It creates a temporary fake plugin with a large tool catalog, starts the mock OpenAI provider, then runs the Gateway in direct, code-mode Tool Search, and structured Tool Search modes. It compares provider request payloads for direct and code mode, then verifies session logs and tool flow across all three lanes.

The regression proves:

  1. Direct mode can call the fake plugin tool.
  2. Tool Search can call the same fake plugin tool.
  3. Direct mode exposes the fake plugin tool schemas directly to the provider.
  4. Tool Search exposes only the compact bridge plus any direct-only tools.
  5. The Tool Search request payload is smaller for the large fake catalog.
  6. Session logs show the expected tool-call counts and bridged call telemetry.
  7. Structured mode resolves two queries with one tool_search call before the selected plugin tool runs through tool_call.

Failure behavior

Tool Search should fail closed:

  • if a tool is not in the effective policy, search should not return it
  • if a selected tool becomes unavailable, tool_call should fail
  • if policy or approval blocks execution, the call result should report that block instead of bypassing it
  • if the code bridge cannot create an isolated runtime, use mode: "tools" or disable Tool Search for that deployment
Was this useful?
On this page

On this page