# Using Tools

Course: [AI Agents](https://www.devtutorials.ai/learn/ai-agents) · Section: Tool Orchestration
Source: https://www.devtutorials.ai/learn/ai-agents/tools/using-tools

> How an agent finds the right tool, checks the call against a schema, makes sure it is allowed, runs it in sync, async or background mode, and checks what comes back.

Think about a plumber at a job.

The plumber has a toolbox. First, they look through it to see which tools are available. Then they choose the right tool for the problem.

Before using it, they check that it is the correct size and type. Some tools, such as a gas key, may require the owner's approval before they can be used.

Some tasks are quick, like tightening a nut. Others take longer, such as waiting for glue to dry.

While the glue dries, the plumber checks on it from time to time. At the end, they inspect the pipe to make sure the leak is actually fixed.

An agent uses tools in the same way.

**The main idea:** To use a tool correctly, the agent finds the right tool, validates the request, checks whether the action is allowed, runs the tool, and then examines the result.

## A Real-World Example

The Brightgrid agent wants to fix a file. Here is everything that happens around a `write_file` call:

1. **Find:** The registry contains `write_file`, and its description says it modifies a file.
2. **Check:** The request contains `path` and `content`, the two inputs required by the tool.
3. **Allow:** The path is inside `web/`, and the agent has permission to write there.
4. **Run:** The application saves the file.
5. **Process:** The result says `"Saved web/format.js (5 lines)"`. That result becomes the next observation.

If any of these checks fail, the tool does not run and the agent receives a clear error.

**In simple words:** find it, check it, allow it, run it, read it.

## What You Will Learn

In this lesson, you will learn:

1. How an agent discovers available tools and matches them to a task.
2. How to validate a tool call by checking its schema, parameters, and readiness.
3. How access controls determine which tools an agent is allowed to use.
4. How a tool call is built, dispatched, and completed.
5. The difference between synchronous, asynchronous, and background tools.
6. How to validate tool outputs and turn them into observations.

The **Agent Lab** includes three tool-related problems in this lesson: a call with incorrect argument names, a test run that executes in the background, and a read-only agent that has no write tool. The **Tools** tab always shows the registry for the current scenario.

## Tool Discovery

### Available Tools

The **available tools** are the tools listed in the registry for the current run. The agent can only use tools that appear in this list. The application sends the tool list, along with each tool's description, to the model.

### Tool Selection

**Tool selection** is the process of choosing one tool for the next step. The model makes this choice by reading the tool descriptions.

### Capability Matching

**Capability matching** means matching what the agent needs to do with what a tool can do. Clear tool descriptions make this easy. Vague descriptions force the model to guess.

```js runnable title="Match a need to a tool by its description"
const tools = [
  { name: "run_tests", description: "run all tests and report which fail" },
  { name: "read_file", description: "read the text of one file" },
  { name: "write_file", description: "change the text of one file" },
  { name: "search_docs", description: "search the help pages for a word" },
];

function match(need) {
  const words = need.toLowerCase().split(" ");
  const scored = tools.map((t) => ({ name: t.name, score: words.filter((w) => t.description.split(" ").includes(w)).length }));
  scored.sort((a, b) => b.score - a.score);
  return scored[0].score > 0 ? scored[0].name : "no tool fits";
}

console.log("which tests fail -> " + match("which tests fail"));
console.log("change the file text -> " + match("change the file text"));
console.log("send an email -> " + match("send an email"));
```

Expected output:

```text
which tests fail -> run_tests
change the file text -> write_file
send an email -> no tool fits
```

"Send an email" does not match any available tool, so the agent learns that it has no tool capable of performing that action. That is much safer than choosing a random tool.

## Tool Validation

**Validation** means checking a tool call before it is allowed to run.

### Schema Checks

A **schema** describes a tool's inputs: their names, types, and which ones are required. A **schema check** compares the requested tool call against that schema.

### Parameter Validation

**Parameter validation** checks each input value:

- Is the value present?
- Is it the correct type?
- Is it within a safe and valid range?

```js runnable title="Check a call against the tool's schema"
const schema = {
  path: { type: "string", required: true },
  content: { type: "string", required: true },
};

function validate(args) {
  const errors = [];
  for (const [name, rule] of Object.entries(schema)) {
    if (!(name in args)) { if (rule.required) errors.push("missing " + name); continue; }
    if (typeof args[name] !== rule.type) errors.push(name + " must be a " + rule.type);
  }
  for (const name of Object.keys(args)) if (!(name in schema)) errors.push("unknown input " + name);
  return errors.length ? "INVALID: " + errors.join("; ") : "valid";
}

console.log(validate({ path: "web/format.js", content: "..." }));
console.log(validate({ file: "web/format.js", text: "..." }));
console.log(validate({ path: "web/format.js", content: 42 }));
```

Expected output:

```text
valid
INVALID: missing path; missing content; unknown input file; unknown input text
INVALID: content must be a string
```

The second call used the wrong names: `file` and `text`. The error clearly lists what is wrong, so the agent can correct the request.

At step 3, the agent sends `file` and `text`. The registry rejects the call.

At step 4, its thought quotes the error and sends `path` and `content` instead. One clear error message is enough for the agent to fix the call.

### Readiness Verification

**Readiness** means the tool can run right now. For example, the test server is running and the file exists. Checking readiness first produces a clear error instead of failing halfway through execution.

## Access Controls

### Scope Restrictions

A **scope restriction** limits *where* a tool may act. You have used one since Lesson 1: `write_file` may change files only inside `web/`.

### Permission Checks

A **permission** controls *who* may use a tool. For example, a reviewer agent may read files but not write them. A developer agent may do both.

### Availability Rules

An **availability rule** controls *when* a tool is available. For example, in **read-only mode**, write tools are not included in the registry.

```js runnable title="Permissions by role, plus read-only mode"
const permissions = {
  reviewer: ["read_file", "run_tests"],
  developer: ["read_file", "run_tests", "write_file"],
};
const writeTools = ["write_file"];

function canUse(role, tool, readOnly) {
  if (readOnly && writeTools.includes(tool)) return "not available (read-only mode)";
  return permissions[role].includes(tool) ? "allowed" : "not permitted for " + role;
}

console.log("developer, write_file: " + canUse("developer", "write_file", false));
console.log("reviewer, write_file: " + canUse("reviewer", "write_file", false));
console.log("developer, write_file, read-only: " + canUse("developer", "write_file", true));
```

Expected output:

```text
developer, write_file: allowed
reviewer, write_file: not permitted for reviewer
developer, write_file, read-only: not available (read-only mode)
```

Read-only mode overrides every role. Even a developer cannot write when read-only mode is enabled.

The agent finds the fix and tries to write it. However, the registry does not contain `write_file`, so the call fails.

The agent then stops and includes the suggested fix in its final answer. A person can review the change and apply it manually.

## Dispatch Workflows

**Dispatch** means sending a tool call to the code that actually runs it.

### Request Construction

**Request construction** creates a clean request containing:

- an id
- the tool name
- the validated inputs

The id is used later to match the result to the original request.

### Runtime Dispatch

**Runtime dispatch** looks up the requested tool in the registry and runs it.

### Result Collection

**Result collection** gathers everything returned by the tool into a single result object. It contains:

- the request id
- whether the call succeeded
- the output

```js runnable title="Build, dispatch and collect one call"
const registry = {
  read_file: (args) => "text of " + args.path,
};
let nextId = 1;

function dispatch(tool, args) {
  const request = { id: nextId++, tool, args };
  const run = registry[request.tool];
  if (!run) return { id: request.id, ok: false, output: "no tool named " + tool };
  return { id: request.id, ok: true, output: run(request.args) };
}

console.log(JSON.stringify(dispatch("read_file", { path: "web/format.js" })));
console.log(JSON.stringify(dispatch("delete_file", { path: "web/format.js" })));
```

Expected output:

```text
{"id":1,"ok":true,"output":"text of web/format.js"}
{"id":2,"ok":false,"output":"no tool named delete_file"}
```

Both results have the same structure: an id, an ok value, and an output. Because of that, the loop can handle every result in the same way, whether the call succeeded or failed.

## Execution Modes

### Synchronous Calls

A **synchronous** (sync) call means the agent waits for the tool to finish before continuing. `read_file` is a sync tool. It is fast, so waiting is reasonable.

### Asynchronous Calls

An **asynchronous** (async) call starts the work and returns immediately. The result becomes available later, so the agent must come back and check for it.

### Background Operations

A **background operation** is a long-running async task, such as a large test run. The tool immediately returns a **job id**. The agent then checks the job status until the work is finished.

```js runnable title="Start a background job, then check it"
const jobs = {};

function startTests() {
  jobs[1] = { checksLeft: 2, result: "3 passed, 0 failed" };
  return "Job 1 started";
}

function checkJob(id) {
  const job = jobs[id];
  if (job.checksLeft > 0) { job.checksLeft--; return "Job " + id + ": still running"; }
  return "Job " + id + ": done. " + job.result;
}

console.log(startTests());
for (let i = 0; i < 4; i++) {
  const status = checkJob(1);
  console.log(status);
  if (status.includes("done")) break;
}
```

Expected output:

```text
Job 1 started
Job 1: still running
Job 1: still running
Job 1: done. 3 passed, 0 failed
```

The agent never assumes the job is finished. It keeps checking until the job reports that it is complete. The `for` loop also has a limit, so a job that never finishes cannot trap the agent forever.

The registry in this scenario provides `start_tests` and `check_job` instead of `run_tests`. Each test run takes three steps: start the job, check and see "still running", then receive the final result. The agent waits for the actual result before making any decision.

**Easy Way to Remember:** **sync** means "wait here". **Async** means "I'll call you back". **Background** means "here is a ticket, come back and check later".

## Response Processing

### Output Validation

**Output validation** checks whether a tool's result looks correct. For example, the result of a test run should contain "passed". If it does not, something may have gone wrong even though no error was reported.

### Error Detection

**Error detection** finds failures hidden inside apparently successful results. Many tools return messages such as "Error: timeout" as plain text instead of reporting a formal failure.

### Observation Creation

**Observation creation** turns the tool result into the clean observation that the agent reads in the next step. You learned this process in Lesson 9.

```js runnable title="Validate the output and catch hidden errors"
function process(result) {
  if (!result.ok) return "ERROR: " + result.output;
  if (/error|timeout|failed to/i.test(result.output) && !/passed/.test(result.output)) return "ERROR (hidden in the output): " + result.output;
  if (!/passed/.test(result.output)) return "ERROR: unexpected output: " + result.output;
  return result.output;
}

console.log(process({ ok: true, output: "3 passed, 0 failed" }));
console.log(process({ ok: true, output: "Error: test server timeout" }));
console.log(process({ ok: true, output: "" }));
```

Expected output:

```text
3 passed, 0 failed
ERROR (hidden in the output): Error: test server timeout
ERROR: unexpected output:
```

The second result says `ok`, but the output itself contains an error. The third result is empty. Both are detected before the agent can mistake them for successful results.

Never assume a tool worked just because it did not crash. Always validate the output itself.

## Common Mistakes

| Mistake | Wrong idea | Correct idea | What goes wrong |
|---|---|---|---|
| Vague tool descriptions | "The model will figure it out." | Clearly describe what each tool does. | The model chooses the wrong tool. |
| No schema validation | "The model always sends the correct inputs." | Validate input names and types before running the tool. | The tool crashes or behaves incorrectly. |
| Same tools for every agent | "More tools make the agent smarter." | Give each role only the tools it needs. | A reviewer agent modifies files it should only inspect. |
| Not waiting for async results | "I started the tests, so they must have passed." | Check the job until it reports completion. | The agent makes decisions without a real result. |
| Trusting `ok` without checking output | "No crash means success." | Validate the output text as well. | Hidden errors are treated as successful results. |

## Exercises

Each exercise ends with a value on the last line. Press **Check** to compare it with the expected answer.

## What to Remember

- Find it, check it, allow it, run it, read it.
- Clear tool descriptions help the agent choose the right tool.
- Validate every tool call against its schema and return clear error messages when validation fails.
- Control tool access using scope restrictions, permissions, and availability rules.
- Wait for asynchronous and background operations to complete, and always validate the output before using it.

Next: **Action Coordination**, how an agent runs several actions in the right order, or at the same time.
