> ## Documentation Index
> Fetch the complete documentation index at: https://docs.agentfront.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Tool System

> Integrating tools with enclave for script-to-system communication

The tool system allows scripts to interact with external systems through controlled `callTool()` invocations. Tools are the primary way for sandboxed code to perform actions and retrieve data.

## Basic Tool Handler

```ts theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
import { Enclave } from '@enclave-vm/core';

const enclave = new Enclave({
  timeout: 10000,
  toolHandler: async (toolName, args) => {
    console.log(`Tool called: ${toolName}`, args);

    switch (toolName) {
      case 'users:list':
        return { items: [{ id: 1, name: 'Alice' }] };
      case 'users:get':
        return { id: args.id, name: 'Alice' };
      default:
        throw new Error(`Unknown tool: ${toolName}`);
    }
  },
});

const result = await enclave.run(`
  const users = await callTool('users:list', {});
  return users.items.length;
`);
```

## Tool Handler Signature

```ts theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
type ToolHandler = (
  toolName: string,  // Tool identifier (e.g., 'users:list')
  args: unknown      // Arguments passed from the script
) => Promise<unknown>;
```

The script's `callTool` options (below) are applied inside the sandbox and are not passed to the handler.

## Tool Namespaces

Expose tools as namespaced functions, the natural API for scripts:

```ts theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
const enclave = new Enclave({
  toolHandler: async (name, args) => callMyTool(name, args),
  toolNamespaces: {
    mail: ['list', 'send'],       // mail.list(args) calls the tool 'mail.list'
    users: { get: 'users:get' },  // users.get(args) calls the tool 'users:get'
  },
});

const result = await enclave.run(`
  const inbox = await mail.list({ unread: true });
  return await users.get({ id: inbox[0].from });
`);
```

`mail.list(args, options)` is exactly `callTool('mail.list', args, options)`: it counts toward `maxToolCalls`, is rate-limited and checked by the suspicious-sequence detectors, its result is sanitized, and it reaches your `toolHandler`. Each namespace is a frozen, null-prototype object created inside the sandbox, so aliasing (`const m = mail`) and destructuring work.

Unsafe or unusable names are refused when the enclave is constructed (`TypeError: Invalid toolNamespaces: ...`): non-identifiers, `__proto__`/`constructor`/`prototype`, names starting with `__`, reserved words and sandbox globals as namespaces, identifiers the AgentScript validator refuses, collisions with a custom global, duplicated methods, and tool names every adapter cannot carry (letters, digits, `:`, `.`, `_` and `-`, starting with a letter; this includes the generated `<namespace>.<method>` name, so a namespace such as `_util` needs explicit tool names).

<Warning>
  Do not expose tools as functions in `globals`. Those calls are capped, rate-limited and sanitized, but the suspicious-sequence detectors only see tool calls. Use `toolNamespaces` or `callTool()`.
</Warning>

## callTool Options

```ts theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
const r = await callTool('users:get', { id }, { throwOnError: false });
if (r.success) return r.data;
return { failed: r.error.message }; // r.error: { name, message, code?, toolName }
```

| Option | Default | Description |
| - | - | - |
| `throwOnError` | `true` | `false`: resolve to `{ success: true, data }`, or `{ success: false, error }` when the tool fails, instead of throwing |

Only failures of the tool itself (the handler threw or rejected, or its result could not be delivered) are returned. Refusals by the enclave (abort, `maxToolCalls`, rate limit, operation-name and suspicious-sequence checks, invalid arguments) always throw. Namespace methods take the same options as their second argument.

## Integrating with a Tool Registry

Connect enclave to your existing tool system:

```ts theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
import { Enclave } from '@enclave-vm/core';
import { ToolRegistry } from './tools';

const enclave = new Enclave({
  timeout: 10000,
  toolHandler: async (toolName, args) => {
    // Look up tool in registry
    const tool = ToolRegistry.get(toolName);
    if (!tool) {
      throw new Error(`Unknown tool: ${toolName}`);
    }

    // Validate input schema
    const validatedArgs = tool.inputSchema.parse(args);

    // Execute with your pipeline (auth, logging, etc.)
    return tool.execute(validatedArgs);
  },
});
```

## Tool Call Limits

Prevent runaway scripts with tool call limits:

```ts theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
const enclave = new Enclave({
  maxToolCalls: 50, // Maximum tool calls per execution
  toolHandler: async (name, args) => { /* ... */ },
});
```

When the limit is exceeded, execution fails with error code `MAX_TOOL_CALLS`.

## Custom Globals

Provide custom globals for scripts to access:

```ts theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
const enclave = new Enclave({
  toolHandler: async (name, args) => { /* ... */ },
  globals: {
    // Custom read-only context
    context: {
      userId: 'user-123',
      tenantId: 'tenant-456',
    },
    // Custom utility function (requires allowFunctionsInGlobals)
    formatDate: (date: Date) => date.toISOString(),
  },
  allowFunctionsInGlobals: true,
});

const code = `
  const userId = context.userId;
  const timestamp = formatDate(new Date());
  return { userId, timestamp };
`;

const result = await enclave.run(code);
```

Every call into a function from `globals` goes through the enclave's gate: it is refused after an abort, counts toward `maxGlobalFunctionCalls` (default 10 × `maxToolCalls`), shares the double VM's rate limit with tool calls, and returns a sanitized, plain-data copy of the result (functions and symbols are refused). Errors it throws reach the script without the host stack, and `new` is refused. The `worker_threads` adapter does not pass functions into the sandbox.

Functions (and objects with methods) are refused inside arrays, Maps and Sets, because the built-in methods of those collections hand elements to the script directly; keep them as properties of plain objects. Promises, WeakMaps, iterators and generators are refused anywhere in `globals`.

## One-Shot Execution

For simple cases without a persistent enclave instance:

```ts theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
import { runAgentScript } from '@enclave-vm/core';

const result = await runAgentScript(`
  return Math.max(1, 2, 3);
`, {
  timeout: 1000,
});

console.log(result.value); // 3
```

## Tool Error Handling

Handle tool errors gracefully:

```ts theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
const enclave = new Enclave({
  toolHandler: async (name, args) => {
    try {
      return await executeToolSafely(name, args);
    } catch (error) {
      // Return structured error that script can handle
      return {
        error: true,
        code: error.code || 'TOOL_ERROR',
        message: error.message,
      };
    }
  },
});

const code = `
  const result = await callTool('risky:operation', {});
  if (result.error) {
    return { success: false, reason: result.message };
  }
  return { success: true, data: result };
`;
```

## Securing Tool Access

Limit which tools are available based on context:

```ts theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
const enclave = new Enclave({
  toolHandler: async (name, args) => {
    // Allowlist of permitted tools
    const allowedTools = ['users:list', 'users:get', 'data:read'];

    if (!allowedTools.includes(name)) {
      throw new Error(`Tool not allowed: ${name}`);
    }

    return ToolRegistry.execute(name, args);
  },
});
```

## Async Tool Calls

Tools are always async, allowing for external API calls:

```ts theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
const enclave = new Enclave({
  toolHandler: async (name, args) => {
    if (name === 'api:fetch') {
      const response = await fetch(args.url);
      return response.json();
    }
    // ...
  },
});
```

## Tool Call Statistics

Track tool usage in execution stats:

```ts theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
const result = await enclave.run(code);

console.log('Tool calls made:', result.stats.toolCallCount);
console.log('Execution time:', result.stats.duration);
```

## Related

* [Overview](/enclave/core-libraries/enclave-vm/overview) - Getting started with enclave
* [Reference Sidecar](/enclave/core-libraries/enclave-vm/reference-sidecar) - Handling large tool responses
* [Configuration](/enclave/core-libraries/enclave-vm/configuration) - All configuration options


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.