A tool gate: scopes, approval and deny by default
Nothing is really sent or deleted. The point is the decision, made in code, whatever the model asks for.
A model that can call tools can be steered into calling them. Give every feature only the tools it needs, run them with narrow credentials, make dangerous ones need a human yes, and decide in your code, never in the model's reply.
Change the code and press Run (Ctrl+Enter). Predict what will happen first, then break it on purpose and read what changes. Your edits are saved and match the lesson page.
Ready for more? This phase has 5 hands-on exercises with automatic checks.
Practice questions
Write the code in the editor, run it, then open the model answer to compare.
Write allowed_tools(feature) that returns the tools a feature may use from a dict like {"summarise": ["read_note"], "support": ["read_note", "send_email"]}; an unknown feature gets an empty list. Print the result for support and for unknown.
Write valid_recipient(address, allowed_domains) that returns True only when the address has exactly one @ and the text after it is in allowed_domains. Test it with ana@example.com, eve@evil.test, and ana@example.com@evil.test.
Explain it without notes
What is excessive agency and how do you reduce it?
Why is checking tool arguments necessary even when the tool name is allowed?
Describe a good human-approval design for an email-sending tool.
Why use narrow credentials in addition to a gate?
Expected output
read-only session:
read_note -> allowed
send_email -> denied: session has no write scope
delete_note -> denied: session has no admin scope
format_disk -> denied: unknown tool
support session:
read_note -> allowed
send_email -> waiting: needs human approval
delete_note -> denied: session has no admin scope
format_disk -> denied: unknown tool
support session, email approved:
read_note -> allowed
send_email -> allowed
delete_note -> denied: session has no admin scope
format_disk -> denied: unknown tool