← All Agent engineering lessons

Agent engineering / HANDS-ON LESSON

Validate a tool call before it runs

Build a tiny dispatcher that rejects unknown tools and malformed arguments before execution.

IntermediateAbout 20 min5 practice questionsReviewed 2026-10-05SaveMyToken editorial

WHAT YOU WILL BUILD

One permitted read operation and two rejected requests with no side effects.

A model may propose a tool name and arguments. The application decides whether that proposal is allowed, validates it, performs the operation, and returns its result. A function schema helps describe the interface but does not replace those checks.

Before you start

Use a text editor and Node.js 22 or newer ↗. Check your version with node --version. Save the downloaded file in an empty folder, then open a terminal in that folder.

All data is included. No packages, account, or API key are required. This is a local read-only tool dispatcher. It uses fixed policy text and does not connect to an agent or external account.

Download the lab (.mjs)

FOLLOW ALONG

Work through the example.

  1. 01

    Inspect the permitted operation

    lookup_policy accepts one category: returns or delivery. Read the dispatcher and identify the checks that happen before the tool function is called.

  2. 02

    Run the rejected requests

    The unknown tool and the extra admin argument both throw. The calls counter remains zero, demonstrating that rejected input never reaches this tool body.

  3. 03

    Run the permitted lookup

    The final request returns the example policy and increases calls to one. Keep a structured result in a real application so the controller can distinguish success from failure.

  4. 04

    Add another negative case

    Test a missing category, an array, and a category outside the allowlist. Verify the call count does not increase. Add authentication and per-resource authorization before adapting this pattern to user data.

THE COMPLETE LAB

Run it locally.

Run this command from the folder containing your downloaded file:

node agent-tool-validation.mjs

View or copy the complete JavaScript
// SaveMyToken local lab. Run with Node.js 22 or newer.
import assert from "node:assert/strict";

let calls = 0;
const tools = new Map([["lookup_policy", ({ category }) => {
  calls++;
  return category === "returns" ? "Unused items: 30 days." : "Delivery: 3 to 5 working days.";
}]]);
function dispatch(name, args) {
  if (!tools.has(name)) throw new Error("Unknown tool");
  if (!args || typeof args !== "object" || Array.isArray(args)
    || Object.keys(args).length !== 1
    || !["returns", "delivery"].includes(args.category)) throw new Error("Invalid arguments");
  return tools.get(name)(args);
}
assert.throws(() => dispatch("delete_data", {}), /Unknown tool/);
assert.throws(() => dispatch("lookup_policy", { category: "returns", admin: true }), /Invalid arguments/);
assert.equal(calls, 0);
console.log(dispatch("lookup_policy", { category: "returns" }));
console.log("Executed tool calls:", calls);
assert.equal(calls, 1);

Expected output for the unchanged example

Unused items: 30 days.
Executed tool calls: 1

The assertions also check the baseline behavior. After changing an input, predict the result and update the relevant assertion.

NOW CHANGE ONE THING

Make the example your own.

Add a delivery lookup and a second permitted category assertion. Then attempt an unexpected path argument and verify that it is rejected without execution.

You are done when…

All invalid requests fail before the tool body, while the permitted request executes exactly once.

If something goes wrong

Do not dispatch a supplied name through eval, a shell command, or arbitrary object properties. A real write tool also needs action-specific authorization and protection against duplicate execution.

EXPLAIN WHAT YOU LEARNED

Interview practice

Try answering aloud before opening the reference answer. These are original learning questions, not a record of any employer's interviews.

01Who executes a function call proposed by a model?

For a client-side tool, the application receives the proposal, validates and authorizes it, executes the operation, and returns the result to the model or controller.

Watch for: Generated arguments are a proposal, not proof of permission.

02What belongs in a tool schema?

Give the tool a specific purpose, clear argument names, types, required fields, and constraints. Define how failure is returned so the caller can react appropriately.

Watch for: A broad tool named do_anything makes both selection and permission checks harder.

03Where should argument validation happen?

At the execution boundary before side effects, even if upstream generation used a schema. Validate allowed values and resource scope, not just JSON syntax.

Watch for: A valid ID can still belong to a different user.

04When can a failed call be retried?

Only when the failure is plausibly temporary and the operation is safe to repeat or protected against duplicate effects. Apply attempt and time limits.

Watch for: A timeout does not prove that a remote write never happened.

05What should a tool result contain?

Return the data required for the next decision, an explicit status, and useful bounded error information. Preserve evidence references when the result supports a factual claim.

Watch for: Dumping entire logs or credentials into the model context is unnecessary.

Practice more agent engineering questions

Sources and further reading

The explanation and local exercises were written for SaveMyToken. These references support the underlying concepts; the sample outputs describe only the supplied examples.

Anthropic: tool use ↗JSON Schema: object validation ↗