Tools: giving the model hands
A tool is any button the model is allowed to press. Click each part of the diagram to see where trust is won and lost.
In 60 seconds
Tools: giving the model hands
A tool is any button the model is allowed to press. Click each part of the diagram to see where trust is won and lost.
| Part | Example | Who writes it |
|---|---|---|
| Name | send_email | Developer |
| Description | "Sends an email. Use when the user asks to contact someone." | Developer β but see Module 15 |
| Arguments | to, subject, body | The model, at run time |
| Result | "Sent, message id 8812" | The outside world β untrusted |
Tap any box in the diagram
You set the goal and, in a well-built system, you approve the risky steps. You are the only part of this picture that can be held responsible. Never design a system that quietly removes you from it.
Tools are ranked by how sorry you will be
| Risk | Tools | Rule of thumb |
|---|---|---|
| Low | Read a public page, do maths, check the time | Let it run freely |
| Medium | Read private files, query a database, search internal docs | Log everything, limit scope |
| High | Send email, post publicly, write to a database, run code | Ask a human first |
| Extreme | Move money, delete data, change permissions, deploy | Ask a human, every single time, with the details shown |
Watch and read more
Lab
A tool registry with real argument validation, and the injection it stops.
The problem
send_email with an address you choose, and confirm your validation blocks it.from dataclasses import dataclass
from typing import Callable
@dataclass
class Tool:
name: str
run: Callable
validate: Callable[[dict], str | None] # returns an error, or None
def refund_validate(args):
if not isinstance(args.get("amount"), (int, float)):
return "amount must be a number"
if not 0 < args["amount"] <= 5000:
return "amount must be between 0 and 5000"
if not args.get("order_id", "").startswith("ORD-"):
return "order_id must look like ORD-xxxx"
return NoneYou are done when
Hard questions
Try to answer before you reveal. If you can answer these, you understood the lesson.
Q1Your validation caps refunds at βΉ5,000. An injected agent issues 400 refunds of βΉ4,999 in one hour. Was your control useless?Reveal
Q2Why validate arguments in the tool runner rather than asking the model to produce valid arguments?Reveal
{"amount": 999999} while explaining that it is following your rules. Validation in ordinary code cannot be argued with, and it is testable.Questions people ask
Can the model invent a tool that does not exist?
It can try. It will write a call to transfer_funds even if you never built one. A correct tool runner rejects unknown tool names outright. If yours passes them through to some generic executor, you have a serious hole.
What if the model puts nonsense in the arguments?
Then your tool must reject it. Validate every argument like it came from a stranger on the internet β because functionally, it did. Type checks, ranges, allow-lists, maximum amounts.
Is MCP a tool?
MCP (Model Context Protocol) is a standard way to plug whole sets of tools into an agent, like a USB port for capabilities. Very useful, and a real supply-chain risk, because you are now trusting someone else's tool descriptions. Module 15.
Should tool results be shown to the user?
Ideally yes, at least in a log. Hidden tool output is how bad things stay hidden. If a human never sees what came back, nobody notices the poisoned instruction that arrived with it.
Lesson test
5 questions. Get 3 right (60%) to pass and complete this lesson.
Sign in with your phone number to take the test and save your progress