# Tools

Offer host functions to the model as tools, from Rust with `#[tool]` and `FunctionTool`, or from TypeScript with `HostTool` definitions and one `tools.call` function.

## Overview

A tool is a name, a description, and a JSON Schema for its input. The model fills in the input, and the result returns to the model as JSON. The SDK records a receipt for each call, so a resumed turn does not run a committed call again.

In Rust, `#[nanocode::tool]` turns an async function into a tool, and `Tools::builder()` collects tools for the runtime builder. In TypeScript, each `HostTool` in `config.tools` declares a tool, and the `tools.call` runtime option runs every host tool by name.

#### Rust

```rust
use nanocode::Tools;

// The function's parameters become the tool's input schema
#[nanocode::tool(
    description = "Add two signed integers, reporting overflow"
)]
async fn checked_sum(a: i64, b: i64) -> std::io::Result<i64> {
    a.checked_add(b).ok_or_else(|| {
        std::io::Error::other("integer addition overflow")
    })
}

let tools = Tools::builder()
    .without_defaults()
    .tool(checked_sum)
    .build()?;
```

#### TypeScript

```ts
// Declare the tool in the configuration, then run it by name in the
// options
await runtime.open(
  {
    ...config,
    tools: [
      {
        description: 'Read the time.',
        name: 'now',
        parameters: { type: 'object' },
      },
    ],
  },
  {
    dispatch,
    tools: { call: async () => ({ now: new Date().toISOString() }) },
  },
)
```

## Walkthrough

The steps below declare one tool, register it, and run a turn that calls it. The TypeScript steps use the `runtime` and `dispatch` values from [Getting Started](/docs/introduction/getting-started), with `config` as its session configuration.

### Declare the Tool

The Rust macro requires an async function that returns `Result<T, E>` and a `description`. It also generates a unit struct with the function's name to register. A TypeScript `HostTool` has a `name`, a `description`, and `parameters`, a JSON Schema object.

#### Rust

```rust title="src/main.rs"
type Result<T> =
    std::result::Result<T, Box<dyn std::error::Error + Send + Sync>>;

// The Ok value returns to the model as JSON
#[nanocode::tool(
    description = "Add two signed integers, reporting overflow"
)]
async fn checked_sum(a: i64, b: i64) -> std::io::Result<i64> {
    a.checked_add(b).ok_or_else(|| {
        std::io::Error::other("integer addition overflow")
    })
}
```

#### TypeScript

```ts title="host.ts"
import type { HostTool } from 'nanocode-wasm'

// The model reads the description and fills in the parameters
const lookup: HostTool = {
  description: 'Look up the value stored under a key.',
  name: 'lookup',
  parameters: {
    additionalProperties: false,
    properties: { key: { type: 'string' } },
    required: ['key'],
    type: 'object',
  },
}

const values: Record<string, string> = { alpha: 'bravo' }

// Runs every host tool, and the name selects the tool
async function call(
  name: string,
  input: unknown,
  signal: AbortSignal,
): Promise<unknown> {
  if (name !== 'lookup') throw new Error(`unknown tool ${name}`)
  const { key } = input as { key: string }
  const value = values[key]
  if (value === undefined)
    throw new Error(`no value stored under ${key}`)
  return { key, value }
}
```

### Register the Tool

In Rust, `without_defaults()` turns off the built-in shell, file, web search, and image tools, and every agent receives the tools. In TypeScript, pass the definitions in `config.tools` and the function in the `tools` option. If a session declares tools without the option, each call fails with a tool error.

#### Rust

```rust title="src/main.rs"
use nanocode::{Nanocode, OpenAi, Tools};

#[tokio::main]
async fn main() -> Result<()> {
    // Build a runtime that offers only checked_sum
    let runtime = Nanocode::builder()
        .openai(OpenAi::default())
        .tools(
            Tools::builder()
                .without_defaults()
                .tool(checked_sum)
                .build()?,
        )
        .without_cua()
        .runner_management(false)
        .build_async()
        .await?;
```

#### TypeScript

```ts title="host.ts"
// Offer lookup to the model and run it with call
await runtime.open(
  { ...config, tools: [lookup] },
  { dispatch, tools: { call } },
)
```

### Run a Turn

Send a prompt that needs the tool. A native Rust tool is callable only inside [Code Mode](/docs/guides/code-mode) by default, so the prompt asks for Code Mode. The TypeScript session logs a `tool.call` event when the model calls the tool, and a `tool.result` event when `call` settles.

#### Rust

```rust title="src/main.rs"
    use nanocode::{Codex, Model};

    // Ask a Codex agent to call the tool from Code Mode
    let (agent, _events) = runtime
        .spawn()
        .backend::<Codex>()
        .model(Model::Luna)
        .build()
        .await?;
    let turn = agent
        .prompt("Use Code Mode to add 19 and 23 with checked_sum.")
        .await?;
    println!("{}", turn.result().await?.final_message());

    runtime.shutdown().await?;
    Ok(())
}
```

#### TypeScript

```ts title="host.ts"
// 1. Queue a prompt that needs the tool
await runtime.submit('owner', {
  metadata: {},
  prompt: 'Look up alpha.',
  request_id: 'turn-1',
})

// 2. Drive it until nothing is pending, as in Getting Started
const next = (await runtime.pending('owner'))[0]
if (next) await runtime.drive(next.report.request_id)
```

If the process stops during a call, the resumed turn reports the call's outcome to the model as unknown and does not run the tool again. Design tools with side effects for that case. The [Events](/docs/guides/events) guide shows how to read `tool.call` and `tool.result` events.

## Recipes

### Return Errors

An error reaches the model as a tool error. The turn continues, and the model reads the message as the tool's failed output. In Rust, return `Err`. In TypeScript, throw or reject, and the error's `message` becomes the tool error.

#### Rust

```rust
// The model reads "integer addition overflow" as the failed output
Err(std::io::Error::other("integer addition overflow"))
```

#### TypeScript

```ts
// Logs a tool.result with status "failed" and result "no value stored
// under zulu"
throw new Error('no value stored under zulu')
```

### Choose the Exposure

Exposure controls where the model can call a tool. A native Rust runtime offers a registered tool only inside Code Mode by default. nanocode-wasm offers each host tool directly and inside Code Mode, on both backends, and has no exposure option.

#### Rust

* **`ToolExposure::CodeModeOnly`:** The default. The tool is callable only inside Code Mode cells.
* **`ToolExposure::DirectAndCodeMode`:** The tool is also a direct tool call.
* **`ToolExposure::DirectOnly`:** The tool is only a direct tool call.
* **`ToolExposure::Hidden`:** The tool is registered but not offered to the model.

```rust
use nanocode::Tools;
use nanocode::tools::ToolExposure;

// exposure(...) sets every tool, and tool_with_exposure(...) sets one
let tools = Tools::builder()
    .without_defaults()
    .tool_with_exposure(checked_sum, ToolExposure::DirectAndCodeMode)
    .build()?;
```

#### TypeScript

```ts
// No option: lookup is a direct tool call and tools.lookup(...) in Code
// Mode
await runtime.open(
  { ...config, tools: [lookup] },
  { dispatch, tools: { call } },
)
```

### Run in Parallel

A tool that allows parallel calls lets the model request several calls in one response. Rust tools stay sequential unless they opt in. Every TypeScript host tool allows parallel calls, so `call` can run again before an earlier call settles.

#### Rust

```rust
// name sets the tool name that the model sees, and parallel opts in
#[nanocode::tool(
    description = "Look up an order by ID",
    name = "lookup_order",
    parallel = true
)]
async fn order(id: String) -> std::io::Result<String> {
    Ok(format!("order {id} shipped"))
}
```

#### TypeScript

```ts
// Each call reads only its own input, so concurrent calls do not
// interfere
async function call(name: string, input: unknown) {
  return { value: values[(input as { key: string }).key] ?? null }
}
```

### Stop on Cancel

The `signal` argument is TypeScript only. It aborts when the SDK cancels the call, such as when the turn is cancelled or the runtime is disposed. Pass it to any work the tool starts.

```ts
// Cancels the request with the tool call
async function call(name: string, input: unknown, signal: AbortSignal) {
  const response = await fetch(
    `https://example.com/items/${(input as { key: string }).key}`,
    { signal },
  )
  return await response.json()
}
```

### Declare at Run Time

`FunctionTool` is Rust only, since every TypeScript host tool already takes its schema as data. `FunctionTool::new(name, description, schema, handler)` declares a tool whose schema is known only at run time. The handler receives the tool name, the decoded JSON input, and the call's `ToolContext`.

```rust
use std::sync::Arc;

use nanocode::tool_contract::ToolContext;
use nanocode::tools::embedded::HostFuture;
use nanocode::{FunctionHandler, FunctionTool};
use serde_json::{Value, json};

struct Inventory;

impl FunctionHandler for Inventory {
    // An Ok value becomes the tool output, and an Err string becomes a
    // tool error
    fn call<'a>(
        &'a self,
        _name: &'a str,
        input: Value,
        _context: ToolContext<'a>,
    ) -> HostFuture<'a, Result<Value, String>> {
        Box::pin(async move {
            let sku =
                input["sku"].as_str().ok_or("sku must be a string")?;
            Ok(json!({ "sku": sku, "in_stock": 12 }))
        })
    }
}

// Parallel calls stay off unless parallel_tool_calls(true) is set
let inventory = FunctionTool::new(
    "inventory",
    "Report how many units of a SKU are in stock",
    json!({
        "type": "object",
        "properties": { "sku": { "type": "string" } },
        "required": ["sku"]
    }),
    Arc::new(Inventory),
)
.parallel_tool_calls(true);
```

### Keep Some Defaults

The built-in tool groups are Rust only. nanocode-wasm starts without them and offers only Code Mode and the host tools. `workspace`, `web_search`, and `image_generation` turn each group on or off.

```rust
use nanocode::Tools;

// Keep the shell and file tools, drop web search and image generation
let tools = Tools::builder()
    .web_search(false)
    .image_generation(false)
    .tool(checked_sum)
    .build()?;
```

### Connect MCP Servers

MCP servers are Rust only, on native targets. `Mcp::builder()` collects servers, and `add` registers them with the tools. `McpServer::stdio` starts a local command, and `McpServer::http` connects to a Streamable HTTP endpoint.

```rust
use nanocode::Tools;
use nanocode::tools::mcp::{Mcp, McpServer};

// 1. One stdio server and one HTTP server with a bearer token from the
// environment
let mcp = Mcp::builder()
    .server(
        "files",
        McpServer::stdio("npx").args([
            "-y",
            "@modelcontextprotocol/server-filesystem",
            ".",
        ]),
    )
    .server(
        "docs",
        McpServer::http("https://mcp.example.com/mcp")
            .bearer_token_env("DOCS_TOKEN"),
    )
    .build()?;

// 2. Register the servers beside the Rust tools
let tools = Tools::builder()
    .without_defaults()
    .add(mcp)
    .tool(checked_sum)
    .build()?;
```

By default, MCP tools are found through `tool_search` and are callable in Code Mode (`McpToolExposure::DeferredAndCodeMode`). A server waits 30 seconds to start and 5 minutes per call unless `startup_timeout` and `tool_timeout` change the limits.

## More

- [Code Mode](/docs/guides/code-mode): Call registered tools from JavaScript cells.

- [Events](/docs/guides/events): Read tool calls and results as they happen.

- [MPP Payments](/docs/guides/mpp-payments): Pay for MCP tool calls with a payment provider.

- [Tools](/docs/api/rust/Tools): Reference for the Rust tool collection and its builder.

- [#[tool]](/docs/api/rust/tool-macro): Reference for the tool attribute and its arguments.

- [FunctionTool](/docs/api/rust/FunctionTool): Reference for Rust tools declared at run time.

- [HostTool](/docs/api/wasm/HostTool): Reference for the TypeScript tool definition.
