SevyDevy

Guide · AI and GenAI

Tool Design for AI Agents: Descriptions, Errors and Retries

24 min readUpdated By Saket Bhatnagar

Researched and drafted with AI, reviewed and edited by Saket Bhatnagar.

In short

  • Tool design for AI agents decides whether the model picks the right action. An agent is a program where a language model chooses each next step, and a tool is one action your code runs when the model asks. From your tool list, the model sees only each tool's name, description and input rules, so vague or overlapping tools make it guess.
  • Give each tool one job and a verb-plus-noun name, like get_today_price. Its description says what it does, when to use it, when not to, and what it returns.
  • Make inputs strict with a schema, a set of rules for the shape of the data: fixed lists of allowed values, units in names like quantity_kg, and one date format. Your code fills in what it already knows, such as the buyer's id.
  • Return errors as normal tool results that say what went wrong, whether trying again can help, and what to do next. Count repeated errors in your code, not in the model.
  • Beginners can stop after the testing section. Engineers then get runnable code where a request key, a unique id for one order, and a database rule turn a retried order into Already placed. A retry loop follows that only repeats tools that are safe to repeat.

What goes wrong when an AI agent has badly designed tools?

The agent picks the wrong tool, fills in an input wrongly or repeats an action that should run once. The model is not being careless. It can only choose from the text you wrote about each tool.

Picture a common setup. A produce wholesaler sells jeera (cumin), groundnut and onions in bulk to hotels, caterers and shops. Its team built an ordering agent: a chat program that answers buyers and can also take actions, such as booking an order. The setup is made up, but the problems are real.

In its first month, three things went wrong:

  • Wrong tool. A caterer asks for today's jeera rate and says to book 40 kg if it is under Rs 300 a kg. Today's price is Rs 285, but the agent calls a tool named search, finds an old list showing Rs 310 and books nothing.
  • Wrong input. A buyer asks for 4 quintal of onions, and a quintal is 100 kg, so she wants 400 kg. The order tool's input was called quantity with no unit, so the model sent 4 and the system booked 4 kg.
  • Repeated action. An order for 40 kg of groundnut times out, meaning the agent stops waiting for a reply. The model tries again through a second tool, order_v2. Both calls had worked, so the buyer gets two deliveries and two bills.

The logs, the saved records of every chat and tool call, showed the cause. The agent had nine tools with names like search, get_data, rates and price_lookup. Several did the same job, and none said when to use it.

Think of a kitchen with nine spice jars, where three are labelled masala. A new cook grabs one at random. The cook is not the problem; the labels are. Where the picture breaks: a cook can open a jar and smell it, but the model cannot open your code. It has only the labels.

The rest of this guide fixes the labels first, then the inputs, the errors and the retries, one failure at a time.

Which words do you need before designing tools for an AI agent?

You need five words: agent, tool, tool definition, schema and idempotent. Each one gets a plain meaning and an example from the ordering agent.

Agent. An AI agent is a program where a language model decides the next step. A language model is a program trained on large amounts of text to write the next words. Your code, called the agent loop, sends the chat to the model, runs the action it asks for and repeats until the job is done.

Tool. A tool is an action the model can ask for, which your code runs. Example: get_today_price looks up today's rate for one crop. The model only asks. Your code runs the tool and sends back the answer, called the tool result.

Tool definition. A tool definition is the short text the model reads about each tool: a name, a description and a list of inputs, called parameters. Unless your prompt adds more, it is all the model knows about the tool. Example: get_today_price, described as: Get today's price for one crop, in rupees per kg.

Schema. A schema is a set of rules for what shape the inputs must have. MCP and many model services describe tool inputs with JSON Schema, an open standard for describing data in JSON, a plain-text format for data. Example: crop must be one of onion, groundnut, jeera or cotton_seed.

Idempotent. A tool is idempotent when calling it twice with the same input has the same effect as calling it once. Reading a price is idempotent. Placing an order is not, unless you design it to be.

What is tool design for AI agents?

Tool design for AI agents is the work of naming, describing and shaping the actions a model can ask your program to run. It covers four parts: the tool list, the inputs, the errors and the retries.

It matters because the model cannot see your code. It chooses only from the names, descriptions and input rules you wrote, so those few lines of text are the whole interface between the model and your system.

How does an AI model choose which tool to call?

The model reads your tool definitions as text, together with the chat so far, and writes back a tool name plus values for its inputs. In the basic setup, none of your code matches the message to a tool; the model does.

So every tool definition is part of the prompt, the text sent to the model on each request. Two definitions that sound alike force the model to guess, and it may guess differently on two similar messages.

Definitions also cost money. Tool definitions are usually sent to the model as part of the prompt, so each one adds tokens, the small pieces of text a model reads and writes. All nine old ones were sent even with a simple hello.

How the agent picks and runs a tool

Notice that the choice happens inside the model, from text alone. Your code only checks the inputs and runs what it chose.

Diagram connections:

  • Buyer's message to Model picks a tool
  • Tool definitions to Model picks a tool
  • Model picks a tool to Inputs match the schema?
  • Inputs match the schema? to Your code runs the tool (Yes)
  • Inputs match the schema? to Error goes back (No)
  • Error goes back to Model picks a tool (Model tries again)
  • Your code runs the tool to Model reads the result
  • Buyer's message: Jeera rate today, then book 40 kg
  • Tool definitions: Name, description and input schema
  • Model picks a tool: Writes a tool name and input values
  • Inputs match the schema?: Your code checks before running
  • Your code runs the tool: get_today_price returns Rs 285 a kg
  • Error goes back: Says which input was wrong and why
  • Model reads the result: Then replies or picks the next tool

With the old tools, the model saw search next to rates and price_lookup. All three could answer a price question, and nothing told it which one to use.

How do you write tool names and descriptions an AI agent can follow?

Give each tool one job and a name that states that job as a verb and a noun. Then write a description that says what it does, when to use it and when not to. Write it for a reader who knows nothing about your system.

A name like get_today_price says the action and the thing. Names like search or get_data could mean almost anything. Avoid version numbers too: if order and order_v2 are both visible, the model cannot tell the first one is old.

A good description answers four questions, in this order:

  • What does it do? Get today's mandi (wholesale market) price for one crop, in rupees per kg.
  • When should the model use it? When the buyer asks what a crop costs today.
  • When should it not? Not for stock levels or past prices, which have their own tools.
  • What comes back? The price, the grade and the time the price was last updated.

The when-not-to-use-it line does the most work. Most wrong picks happen between two close neighbours, like price and stock. One sentence that points to the other tool settles the choice.

Here is what the team changed. The old list had nine tools and the new one has six.

The ordering agent's tool list before and after
Old toolsProblemNew tool
rates, price_lookupTwo tools for one job, so the model picked either oneget_today_price
searchToo vague: could mean prices, stock or ordersRemoved, split into check_stock and get_order_status
order, order_v2The old version was still visible next to the new oneplace_order
modifyCould change or cancel, so the model guessed whichcancel_order, other changes go to staff
get_dataSaid nothing about which data it returnsRemoved
notifyUnclear who gets told, or whyhand_to_staff
stockA noun with no verb and no descriptioncheck_stock

Names can also clash when one agent gathers tools from several MCP servers. The MCP specification gives the example of two servers that both expose a search tool. It says clients should handle this, for example by adding a prefix that names the server.

How should you design tool parameters for an AI agent?

Design parameters so the model has few ways to get them wrong: strict types, fixed lists of allowed values, units in names and one date format. Anything your code already knows should not be a parameter.

The quintal mix-up from the opening shows why. The old input was called quantity, with no unit, so 4 quintal became 4 kg. The team now follows these rules for every input:

  • Put the unit in the name: quantity_kg, not quantity. Say in the description that 1 quintal is 100 kg.
  • Use the real type: quantity_kg is a number, not text like forty, so the schema can reject anything else.
  • Use a fixed list of allowed values wherever you can. JSON Schema calls this an enum, short for enumeration.
  • Pick one date format, such as 2026-10-23 (year, month, day). Put today's date in the prompt, so the model can turn Thursday into a real date.
  • Mark which inputs are required. Give optional ones a clear default, like grade A when the buyer does not say.
  • Keep the list short. Three or four inputs are easier to fill correctly than ten.

Here is the new price tool as a Python dictionary. The input_schema part is plain JSON Schema. MCP calls the same field inputSchema, and model services use their own key names, so check your provider's documentation.

python
GET_TODAY_PRICE = {
    "name": "get_today_price",
    "description": (
        "Get today's mandi price for one crop, in rupees per kg. "
        "Use this when the buyer asks what a crop costs today. "
        "Do not use it for stock levels; call check_stock for that. "
        "Returns the price, the grade and when the price was updated."
    ),
    "input_schema": {
        "type": "object",
        "properties": {
            "crop": {"type": "string",
                     "enum": ["onion", "groundnut", "jeera", "cotton_seed"]},
            "grade": {"type": "string", "enum": ["A", "B"],
                      "description": "Quality grade. Use A if the buyer does not say."},
        },
        "required": ["crop"], "additionalProperties": False,
    },
}

Two lines matter most. The enum means crop can only be one of four words, so a buyer's cumin must become jeera. Setting additionalProperties to False rejects any input the schema does not list.

You can check a schema before any model sees it. Install the checker with pip install jsonschema==4.25.1. Put the dictionary above and this code in one file, then run it:

python
from jsonschema import Draft202012Validator

checker = Draft202012Validator(GET_TODAY_PRICE["input_schema"])
tries = [{"crop": "jeera"}, {"crop": "cumin"},
         {"crop": "jeera", "qty": 5}, {"grade": "A"}]
for args in tries:
    errors = [e.message for e in checker.iter_errors(args)]
    print(args, "->", errors or "OK")

The checker uses JSON Schema draft 2020-12, which is also the version MCP assumes when a schema does not name one. Expected output on Python 3.13 with jsonschema 4.25.1:

text
{'crop': 'jeera'} -> OK
{'crop': 'cumin'} -> ["'cumin' is not one of ['onion', 'groundnut', 'jeera', 'cotton_seed']"]
{'crop': 'jeera', 'qty': 5} -> ["Additional properties are not allowed ('qty' was unexpected)"]
{'grade': 'A'} -> ["'crop' is a required property"]

How should a tool return errors to an AI agent?

Return errors as normal tool results that say what went wrong, whether trying again can help, and what to do next. A clear error lets the model fix its own mistake instead of giving up.

Say a buyer asks for delivery on a date that has already passed. The old order tool returned Error 500, so the model apologised and stopped. The new tool returns a short structured error instead.

A useful error has four parts:

  • code: a short fixed label, like DATE_IN_PAST, that your program and your logs can count.
  • message: what went wrong in plain words, like delivery_date 2026-10-12 is before today, 2026-10-19.
  • retryable: true if the same call might work later, like after a timeout, and false if it never will.
  • fix: what to do next, like asking the buyer for a date from tomorrow onwards.

Web APIs, the services programs call over HTTP, already have a standard for this idea: Problem Details, in RFC 9457. An RFC is a numbered document in the series that publishes internet standards, and this one comes from the IETF, the group that writes them. RFC 9457 defines fields such as type, title, status and detail, and replaces the older RFC 7807.

It also warns against exposing a stack dump, the long listing a program prints when it crashes, because it can reveal how your server works. The same rule fits tool errors: tell the model what to fix, not how your server failed.

The Model Context Protocol makes a split worth copying. Errors the model can fix, like a date in the wrong format or a value out of range, go back inside the tool result with isError set to true. Its specification says clients should pass these to the model so it can correct itself.

Problems with the request itself, like an unknown tool name, go back as protocol errors instead. The specification says models are less likely to recover from those, so clients may pass them on but need not.

What the agent loop does with a tool error

Notice the two paths. Code retries timeouts only on safe tools, fixable errors go to the model, and unsafe timeouts, used-up retries and repeats go to a person.

Diagram connections:

  • Tool returns an error to Timeout or fixable error?
  • Timeout or fixable error? to Tool safe to repeat? (Timeout)
  • Timeout or fixable error? to Same error 3 times? (Fixable error)
  • Tool safe to repeat? to Code retries after a wait (Yes)
  • Tool safe to repeat? to Hand to staff (No)
  • Code retries after a wait to Tool runs again
  • Code retries after a wait to Hand to staff (Retries used up)
  • Same error 3 times? to Hand to staff (Yes)
  • Same error 3 times? to Model reads the fix (No)
  • Model reads the fix to Tool runs again
  • Tool runs again to Tool returns an error (Fails again)
  • Tool returns an error: Code, message, retryable flag and fix
  • Timeout or fixable error?: Read from the error code
  • Tool safe to repeat?: Set by your code, not the model
  • Code retries after a wait: Up to 3 retries: about 1, then 2, then 4 seconds
  • Same error 3 times?: Counted by your loop, not the model
  • Model reads the fix: Changes an input or asks the buyer
  • Tool runs again: Same call, or a call with new inputs
  • Hand to staff: A person on the team checks and takes over

Timeouts on tools that are safe to repeat do not reach the model at first, because your code retries them quietly and waits longer each time. Engineers call this backoff. The repeat count also lives in your code, so a confused model cannot loop forever.

How many tools should an AI agent have?

Give an agent as few tools as its job needs, and never two tools that do the same thing. There is no magic number, but every extra tool is one more wrong choice the model can make.

Two papers point the same way. A 2024 paper tested function calling on edge devices, small low-power computers that run a model locally. Offering fewer tools per request improved success rates and cut execution time by up to 70%.

A 2025 paper tested very large lists of MCP tools. A search step that picked the few relevant tools before the model chose raised correct picks from 13.62% to 43.13% in its tests. It also cut prompt tokens by more than half.

For the ordering agent, merging look-alike tools and deleting unused ones was enough. Teams with dozens of tools often use one of two patterns:

  • Route first: a small first step decides what the message is about, like orders or payments, and the agent then sees only that group's tools.
  • Search first: your code finds the few tools whose descriptions best match the message, and shows only those to the model.

How do you test whether an AI agent picks the right tool?

Build a small test set of real messages, each with the tool and key inputs it should lead to, and run it after every change. A test set is a fixed list of example inputs with the answers you expect.

Here the answer is not the final reply. It is which tool the model calls first, and with what inputs.

The team starts with 40 messages from old chats, with buyer names removed. They make sure the set includes:

  • Close neighbours, like a price question and a stock question about the same crop.
  • Unit traps, like 4 quintal, 400 kg and half a tonne.
  • Date words, like Thursday, tomorrow and day after tomorrow.
  • Messages that need no tool, like thank you, so the agent does not call one for no reason.
  • Messages that mix English with a local language, the way buyers really type.

They then run the set in five steps:

  1. Write the expected answer

    For each message, write the tool the model should call first and its key inputs. Book 4 quintal onions for Friday should call place_order with quantity_kg 400.

  2. Run every message a few times

    The model can answer differently on two runs. Three runs per message show which choices are shaky.

  3. Score the tool and the inputs separately

    The right tool with 4 kg instead of 400 kg is still a failure, so count both.

  4. Read every failure

    Ask which words in the tool list misled the model. The fix is usually a name, a description or an enum.

  5. Run it again after every change

    Rerun the set when you change a tool, the prompt or the model. A fix for one tool can break the choice between two others.

On the team's set, the old nine tools picked correctly on 29 of 40 messages and the six new tools on 38 of 40 (made-up numbers). Both misses were date words, so the team moved today's date to the top of the prompt and reran the set.

How do you make tool retries safe for an AI agent?

Make every tool that changes data idempotent, with a request key that your code builds and your database enforces. A repeat call with the same key then returns the first result instead of doing the work again.

Go back to the double groundnut order. The first call saved the order, but the reply came too late, so it looked like a failure. A timeout tells you only that you did not hear back, not whether the work was done.

HTTP's core standard, RFC 9110, draws the same line. It lets a client repeat an idempotent request automatically after a connection failure. It says a client should not automatically retry a non-idempotent request unless it can tell the request is actually idempotent or never took effect.

Reading tools like get_today_price are already idempotent: ask twice, get the same price, change nothing. Tools that change data, like place_order, need a request key, often called an idempotency key: a unique id for one intended action.

Your code builds the key, not the model

If the model invents the key, a retry may arrive with a new one, and the tool sees a fresh order. So the agent loop builds the key from the chat id, the buyer and the order details: crop, quantity and delivery date.

The same key comes out even when the model retries through a new tool call. If the buyer really wants a second, identical order, the agent asks them to confirm, and your code adds an order number to the key. A repeat order is then chosen, never guessed.

The place_order tool in code

Here is the new place_order tool. It runs as it is with Python's built-in sqlite3 module, which uses SQLite, a small database stored in one file or in memory. This form of ON CONFLICT, which SQLite calls UPSERT, needs SQLite 3.24.0 or later.

python
import sqlite3

db = sqlite3.connect(":memory:")
db.execute("""CREATE TABLE orders (
    id INTEGER PRIMARY KEY, buyer_id TEXT NOT NULL, crop TEXT NOT NULL,
    quantity_kg INTEGER NOT NULL, delivery_date TEXT NOT NULL,
    request_key TEXT NOT NULL UNIQUE)""")


def place_order(buyer_id, crop, quantity_kg, delivery_date, request_key):
    if not 10 <= quantity_kg <= 5000:
        return {"ok": False, "code": "BAD_QUANTITY", "retryable": False,
                "message": f"{quantity_kg} kg is outside 10 to 5000 kg",
                "fix": "Ask the buyer again. 1 quintal is 100 kg."}
    cur = db.execute(
        "INSERT INTO orders (buyer_id, crop, quantity_kg, delivery_date,"
        " request_key) VALUES (?, ?, ?, ?, ?)"
        " ON CONFLICT (request_key) DO NOTHING",
        (buyer_id, crop, quantity_kg, delivery_date, request_key))
    db.commit()
    order_id = db.execute("SELECT id FROM orders WHERE request_key = ?",
                          (request_key,)).fetchone()[0]
    note = "New order placed" if cur.rowcount == 1 else "Already placed"
    return {"ok": True, "order_id": order_id, "note": note}


key = "chat-8812|B-17|groundnut|40|2026-10-23"
print(place_order("B-17", "groundnut", 40, "2026-10-23", key))
print(place_order("B-17", "groundnut", 40, "2026-10-23", key))  # the retry
print(place_order("B-17", "onion", 4, "2026-10-23", "chat-8812|B-17|onion|4|2026-10-23"))
print(db.execute("SELECT COUNT(*) FROM orders").fetchone()[0], "order row")

Pause and predict: the second call is the retry after the timeout. Will the table hold one order or two? Expected output on Python 3.13 with SQLite 3.45.1:

text
{'ok': True, 'order_id': 1, 'note': 'New order placed'}
{'ok': True, 'order_id': 1, 'note': 'Already placed'}
{'ok': False, 'code': 'BAD_QUANTITY', 'retryable': False, 'message': '4 kg is outside 10 to 5000 kg', 'fix': 'Ask the buyer again. 1 quintal is 100 kg.'}
1 order row

What just happened? The first call saved order 1. The retry carried the same key, so the insert did nothing and the tool returned order 1 with the note Already placed. The 4 kg order failed the range check before anything was saved.

Three design choices matter. buyer_id and request_key come from your code, never from the model. The duplicate check and the save are one statement. And the UNIQUE rule on request_key lets the database, not your code, decide which call wins.

With the UNIQUE rule added, the second save in that test failed with an error instead, and one order stayed. With ON CONFLICT DO NOTHING, five retries at once left exactly one order in 10 of 10 trials. PostgreSQL has the same ON CONFLICT DO NOTHING clause.

A safe retry with a request key

Notice that the retry carries the same key, so the database returns the first order instead of saving a second.

Diagram connections:

  • Model asks to place order to Loop builds request key
  • Loop builds request key to place_order runs
  • place_order runs to Timeout after 10 seconds
  • Timeout after 10 seconds to Same call, same key
  • Same call, same key to Key already stored?
  • Key already stored? to Return the saved order (Yes)
  • Key already stored? to Save a new order (No)
  • Model asks to place order: Groundnut, 40 kg, Friday delivery
  • Loop builds request key: From the chat id, buyer and order details
  • place_order runs: Order saved, but the reply is slow
  • Timeout after 10 seconds: The loop cannot tell if it worked
  • Same call, same key: Sent again after a short wait
  • Key already stored?: The UNIQUE rule checks inside the insert
  • Return the saved order: No second order, no second bill
  • Save a new order: Key stored with the order

How should the agent loop retry a failed tool call?

Retry automatically only tools that are safe to repeat, wait longer after each failure, add a little randomness and stop after a fixed number of tries. Everything else goes back to the model or to a person.

Waits that double each time are called exponential backoff: here about 1, 2 and then 4 seconds. The small random extra on each wait is called jitter. It spreads the retries out, so calls that failed together do not all retry at the same moment.

python
import random
import time


class ToolTimeout(Exception):
    pass


def call_with_retry(tool, args, idempotent, retries=3):
    for attempt in range(retries + 1):
        try:
            return tool(**args)
        except ToolTimeout:
            if not idempotent or attempt == retries:
                return {"ok": False, "code": "NO_ANSWER", "retryable": False,
                        "message": "No reply. The action may or may not have run.",
                        "fix": "Hand to staff to check before trying again."}
            wait = 2 ** attempt + random.uniform(0, 0.5)  # 1, 2, 4 s + jitter
            print(f"timeout, retrying in {wait:.1f} s")
            time.sleep(wait)


calls = []


def slow_tool(**args):  # stands in for a tool that times out twice
    calls.append(args)
    if len(calls) < 3:
        raise ToolTimeout()
    return {"ok": True, "note": "done"}


print(call_with_retry(slow_tool, {}, idempotent=True), len(calls), "calls")
calls.clear()
print(call_with_retry(slow_tool, {}, idempotent=False), len(calls), "call")

Expected output: two timeout lines, the first wait between 1.0 and 1.5 seconds and the second between 2.0 and 2.5 seconds. Your numbers will differ because of the jitter, and the run takes between 3 and 4 seconds.

text
timeout, retrying in 1.2 s
timeout, retrying in 2.3 s
{'ok': True, 'note': 'done'} 3 calls
{'ok': False, 'code': 'NO_ANSWER', 'retryable': False, 'message': 'No reply. The action may or may not have run.', 'fix': 'Hand to staff to check before trying again.'} 1 call

The second run stops after one call. A tool not marked as safe to repeat is never retried blindly; a person checks first.

Where does the safe-to-repeat flag come from? For your own tools, you set it. MCP lets a server attach optional annotations to a tool, including idempotentHint, a hint that repeat calls with the same arguments have no extra effect. It is a claim by the server, not a promise.

The 2026-07-28 MCP specification says clients must treat annotations as untrusted unless they come from trusted servers. So never retry a write just because an outside server's hint says it is safe.

How do you explain tool design for AI agents in a system design interview?

Explain it as a contract between the model and your code: clear tools going in, structured results coming out, and retries that cannot repeat an action. Interviewers want to see that you design for the model's mistakes, not only the happy path.

  1. Start with how the model chooses

    The model picks from names, descriptions and schemas alone, so overlapping tools cause wrong picks.

  2. Design the tool list

    A few tools with one job each, verb_noun names, and descriptions that say when not to use them.

  3. Tighten the inputs

    Enums, units in names and one date format. Ids come from your code, never from the model.

  4. Shape the errors

    A code, a message, a retryable flag and a fix. Cap repeated errors in the loop and hand over to a person.

  5. Make writes idempotent

    A request key built by your code on every tool that changes data, enforced by a UNIQUE rule in the database.

  6. Show how you test

    The test set, the two scores you track, and when you rerun it.

Questions people ask

What is a tool in an AI agent?
A tool is an action the model can ask your program to run, such as looking up a price or placing an order. From the tool list, the model sees only the tool's name, description and input rules. Your code runs the action and sends the result back to the model.
How long should a tool description be?
Usually two to five short sentences. Say what the tool does, when to use it, when not to, and what it returns, plus any units and formats. Tool definitions are usually sent as part of the prompt, so every extra word adds tokens. Cut anything the model does not need.
Should tool errors be sent back to the model?
Yes, for errors the model can act on, like a bad date or an item that is out of stock. A short structured error with a suggested fix lets the model correct itself. The MCP specification says clients should pass such errors to the model. Keep stack dumps out, and let your code retry timeouts on safe tools first.
What is an idempotent tool?
An idempotent tool has the same effect whether you call it once or several times with the same input. Tools that only read data are idempotent by nature. Tools that change data, like placing an order, become idempotent when a request key and a UNIQUE rule make a repeat return the saved result.
Should the model create the idempotency key?
No. A model can write a fresh key on every attempt, so a retry looks like a new order. Build the key in your agent loop from the chat id, the buyer and the order details. Add an order number only after the buyer confirms a real second order.
How many tools is too many for an AI agent?
There is no fixed number, and trouble often starts as soon as two tools overlap. Merge look-alike tools and remove unused ones. For large tool sets, show the model only the few tools that fit each message.
Can JSON Schema stop every bad tool input?
No. A schema catches wrong types, missing inputs and values outside a fixed list. It cannot tell whether 40 kg is what the buyer really meant, or whether a delivery date falls on a holiday. Your tool still needs its own checks that return clear, structured errors.

Sources and further reading

Practise this with Lala

Practise