Tools

Code Mode guest API

Guest runtime API

typescript
declare const catalog: ToolCatalog;declare const MCP: Record<string, unknown>;declare const namespaces: Record<string, unknown>; declare function setTimeout(  callback: (...args: unknown[]) => void,  delay?: number,  ...args: unknown[]): number;declare function clearTimeout(id: number): void;declare function text(value: unknown): void;declare function json(value: unknown): void;declare function yield_control(reason?: string): Promise<void>;

TextEncoder and TextDecoder are available for local text and byte transforms. Encoder and decoder instances survive wait snapshot restoration. They run inside the QuickJS sandbox and grant no filesystem, module, or network access. Returned values still use the JSON-only bridge; emit decoded text or an array of byte values rather than a binary attachment.

console.log, console.info, console.warn, console.error, and console.debug emit diagnostic text through the same ordered output channel as text and json, not through host logs. They return undefined; console.error does not throw. Non-log methods prefix their message with the level, such as [warn]. Arguments are separated by spaces; printf-style tokens like %s stay literal. Other Console API methods are not provided.

Console inspection is intentionally bounded rather than a Node/browser console emulation. Strings are unquoted; objects and arrays use compact JSON-shaped inspection of enumerable own properties, with an Error's own message included. It does not invoke getters or custom toJSON methods. Cycles, accessors, unawaited promises, and inspection failures get diagnostic placeholders. Each call visits at most 100 values, 50 properties per object, and four object levels. String values and keys retain up to 512 UTF-16 code units; a message retains up to 4096, plus a truncation suffix, without splitting surrogate pairs.

Across all resumes, console entries have an additional conservative limit of 16,384 serialized JSON UTF-16 code units (less than 49,152 UTF-8 bytes). Once full, a single [console output truncated] entry replaces the next message and further console calls are ignored. The existing cumulative maxOutputBytes and model-result caps still apply to console output together with text, json, and the final value or error. Use explicit text/json with narrower inputs when diagnostic inspection is insufficient.

Guest timers are bridged through the host, so they survive QuickJS snapshot/resume and remain bounded by the Code Mode execution and snapshot limits. clearTimeout also cancels a timer created before an earlier suspension; this applies to interactive Code Mode and headless automation scripts.

Every effective non-MCP tool is also installed as an async global function. The model-visible exec description includes a bounded, deterministic subset of final callable names, compact input hints, and trusted declared output hints. Descriptions remain deferred so adversarial catalog prose cannot steer the model. When that index omits a tool, call catalog.search(...); its results are callable functions.

The arrow in each quick-index line describes the callable function's value. -> Array<{ id: string }> is a declared output hint; -> ? is output unknown. For unknown outputs, return the value unchanged or return await results.save(value) for a bounded preview. Observe the raw value or preview before filtering or mapping in a later exec; do not feed guessed fields into dependent logic in the same program. This also applies when a declared-output read feeds a final -> ? call: return or save that final raw value without wrapping it in a guessed answer shape.

results.load(id) returns a detached JSON copy for later cells in the same agent run, and results.delete(id) frees capacity. Read results.d.ts through API.read for types, limits, and lifetime, or see Reuse data across cells. Oversized final objects and arrays may return an automatic value.reference instead of an unrecoverable display prefix; use its id with results.load. Larger previews show explicitly sampled paths, counts, and observed shapes. These samples are not schemas; load the original value before processing full data.

typescript
type ToolCatalogMetadata = {  callableName: string;  toolName: string;  label?: string;  description: string;  source: "openclaw" | "client" | "mcp";  apiPath?: string;  input?: string;  output?: string;}; type ToolCatalogHandle = ((input?: unknown) => Promise<unknown>) &  ToolCatalogMetadata & {    describe(): Promise<ToolCatalogDescription | McpCatalogDescription>;    toJSON(): ToolCatalogMetadata;  };

Returning await catalog.search(...) or catalog.all() serializes each callable handle to this bounded metadata. Serialization does not call describe() or start another bridge request; inside the same program, the handle remains callable.

input is a bounded TypeScript-style signature for the common case. Use the handle's describe() when the exact full schema is still needed. Client entries use input: "unknown" so their untrusted schemas stay deferred until describe(). output is present only for a complete compact hint derived from a trusted OpenClaw core or plugin outputSchema. MCP and client output-schema claims are not promoted into this trusted catalog hint.

Plugin tools use source: "openclaw"; there is no separate "plugin" source value. Search includes visible MCP tools using the same ranking and total result limit as native tools. An MCP handle has source: "mcp", a fully qualified callableName such as MCP.accounting.listInvoices, the original MCP toolName, and an apiPath such as mcp/accounting.d.ts. Its remote description is limited to 512 UTF-16 code units without splitting surrogate pairs; input and output hints remain absent. Returning or emitting MCP discovery metadata uses the normal untrusted-content wrapper, even if no MCP tool is called.

MCP handles invoke the existing namespace path with one object argument, including its input defaults, policy checks, approvals, and native MCP result projection. Their describe() returns the exact tool's $api(method, { schema: true }) header and schemas. A normalized method name takes precedence over a colliding original tool name when selecting a $api declaration. Use API.read(handle.apiPath) for the entire server's TypeScript declaration. Search also accepts the fully qualified callableName. catalog.all() continues to list only native and client handles; searching does not add remote tools to that list or to the trusted quick index.

Full schema is loaded only on demand:

typescript
type ToolCatalogDescription = Omit<ToolCatalogMetadata, "toolName"> & {  name: string;  parameters: unknown;  outputSchema?: unknown;};

MCP description shape:

typescript
type McpCatalogDescription = {  kind: "mcp_api";  scope: "tool";  server: { identifier: string; serverName: string };  header: string;  tools: unknown[];  schemas: Record<string, unknown>;  note: string;};

Catalog helpers:

typescript
type ToolCatalog = {  search(query: string, options?: { limit?: number }): Promise<ToolCatalogHandle[]>;  all(): readonly ToolCatalogHandle[];};

catalog.search(...) returns a frozen array of callable handles, or an empty array when no tools match. If the matching callable names exceed the available program-data inbox capacity, search rejects with guidance to narrow the request. It never silently substitutes an empty or partial match list. A narrower search remains available after the error.

Paired Gateway nodes are available through the nodes global:

typescript
const available = await nodes.list();const node = await nodes.get(available[0].id);const status = await node.invoke("device.status");

nodes.list() returns paired node ids, names, platforms, connection state, and advertised commands. TypeScript preflight knows these fields and the node handle methods. Command parameters and results remain unknown because each node command defines its own payload; check the result before composing it. nodes.get(idOrName) resolves an exact id before a display name and returns a handle with id, name, and invoke(command, params?). Invocation uses the normal nodes tool path, so pairing, command policy, scopes, approvals, timeouts, hooks, and telemetry are unchanged. A handle includes listDir(path) only when the node advertises fs.listDir. It does not include exec: the generic nodes surface reserves system.run for the normal shell exec tool with a node host.

Call quick-index globals directly, or use callable catalog handles when lookup is needed:

typescript
const content = await read({ path: "README.md" }); const [tool] = await catalog.search("...");const result = await tool({ query: "OpenClaw" }); const [search] = await catalog.search("search the web", { limit: 1 });const schema = await search.describe();const hits = await search({ query: "OpenClaw code mode" });

Calling a native global or native catalog handle returns the normal tool's JSON details value directly. MCP handles retain the native MCP result (content, optional structuredContent, and optional isError). Exact catalog ids and raw { tool, result } envelopes are not guest-visible.

The ls, find, and grep tools include their bounded listing or search text in content, including empty-result messages and truncation notices. Directory pages retain nextAfter; search results retain their existing limit and truncation metadata.

Reading paginated file data

For text file pages, read(...) returns file text in content; filename-resolution and pagination notices stay in the human-readable tool display, not the structured file data. Existing file redaction still applies. Check kind before parsing: "truncated" means more data is available at continuation. Read that next page with the same path and the returned offset, optional cursor, and optional limit. Join line continuations with "\n"; append cursor continuations directly. Do not strip display-notice patterns from file data: those strings may be actual file contents. Each call still honors its explicit limit; if more file data remains, the result is "truncated" and its continuation describes the next page.

Was this useful?
On this page

On this page