Claude Architect WorkshopPDFEPUBlearnwithparam.comkaas.team

Foundations, lesson 14

Scoped tools

Each agent gets only the tools its job needs

A review agent must only read records, but it holds every one of the tools in the catalogue. Most of them change data. The answer comes out right, yet a wrong pick would have paid a refund or sent an email. This is the agent at Lumen Supply, an online electronics shop. You will watch an agent hold every tool. Then you fix it so each role holds only the few its job needs.

I doWatch the failure and its fix.

A tool you never give cannot be called

An agent picks from the tools it can see. Every extra tool is another wrong option, and a tool that changes data turns a wrong pick into a real mistake.

The code decides the list and the model picks inside it.

Three things go wrong as the list grows. The pick gets less accurate. A read-only role holds a tool that writes. Nothing fixes the order of steps, so a required lookup can be skipped.

A role holds only the tools its job needs.Jobreview, refund, shiptools_forthe role's toolsAgentsees only those toolsToolsthe few it holdsthe roles are written in roles.pyA role holds only the tools its job needs.Jobreview, refund, shiptools_forthe role's toolsAgentsees only those toolsToolsthe few it holdsthe roles are written in roles.py

A scoped agent has few tools and picks right

The reviewer holds four read tools. It calls three of them, lookup_order, list_refunds and get_policy, finds the refund and the limit, and answers.

Open the agent card to see the tools it held. None of them can change data.

  1. ColleagueRefund 5501 on order 1042 was $80. Is that within the refund limit? Answer from the…
    Show the result
    Refund 5501 on order 1042 was $80. Is that within the refund limit? Answer from the records and change nothing.
  2. Agentholds 4 tools, 0 that change data unasked
    Show the result
    lookup_order
    list_refunds
    get_policy
    get_customer
  3. Toolscalled: lookup_order, list_refunds, get_policy
    Show the result
    lookup_order
    list_refunds
    get_policy
  4. AgentYes, refund 5501 for $80 on order 1042 is within the limit. The refund policy sets a $500 limit, and the records show…
    Show the result
    Yes, refund 5501 for $80 on order 1042 is within the limit. The refund policy sets a $500 limit, and the records show only this one $80 refund against an $80 order. I made no changes.

4 tools held: only what the job needs.

The roles as written

tools_for returns the tools an agent in a role may call. The starting version returns the whole catalogue for every role, so a reviewer can pay a refund.

Each tool also costs context. A schema of about 150 tokens goes out with every request. The whole catalogue is thousands of tokens on each call, and four tools is a few hundred.

starter/roles.py
1"""Which tools each role may use, and how a step must choose among them."""23EVERYTHING = [4 "lookup_order",5 "get_customer",6 "list_refunds",7 "get_policy",8 "process_refund",9 "cancel_order",10 "update_address",11 "issue_coupon",12 "send_email",13 "create_label",14 "track_parcel",15 "check_stock",16 "reorder_stock",17 "price_history",18 "fraud_score",19 "block_customer",20 "add_note",21 "escalate_ticket",22]232425def tools_for(role: str) -> list[str]:26 """The tools an agent in `role` may call.27 TODO(workshop): give each role four or five tools, and an unknown role28 none. Roles: reviewer, refunder, shipper, stock, support.29 """30 return list(EVERYTHING)313233def choice_for(role: str, step: str) -> dict[str, str]:34 """The `tool_choice` for `step`: auto, any, or one named tool.35 TODO(workshop): step "read" must call a tool, and step "policy" must36 call get_policy first when the role holds it.37 """38 return {"type": "auto"}

Does a reviewer need the refund tool?

Take the review job. The agent must check a refund against the limit and change nothing. The starting tools_for gives it every tool.

Predict: What does the agent hold?

  1. AOnly the read tools, because the job says to change nothing.
  2. BEvery tool, including the ones that pay and send, because the code gave it all.
Show the answer

B. The job text is a request. The tool list is what the agent can do. A sentence that says "change nothing" does not remove a tool.

The agent holds every tool

This is the same job with the starting code. The agent answers correctly, but it held tools that change data and needed none of them. The result card is red.

This is a real Claude Haiku 5.5 run through the Agent SDK, recorded once and replayed here.

  1. ColleagueRefund 5501 on order 1042 was $80. Is that within the refund limit? Answer from the…
    Show the result
    Refund 5501 on order 1042 was $80. Is that within the refund limit? Answer from the records and change nothing.
  2. Agentholds 18 tools, 10 that change data unasked
    Show the result
    lookup_order
    get_customer
    list_refunds
    get_policy
    process_refund
    cancel_order
    update_address
    issue_coupon
    send_email
    create_label
    track_parcel
    check_stock
    reorder_stock
    price_history
    fraud_score
    block_customer
    add_note
    escalate_ticket
  3. Toolscalled: lookup_order, list_refunds, get_policy
    Show the result
    lookup_order
    list_refunds
    get_policy
  4. AgentYes. Refund 5501 for $80 on order 1042 is within the $500 refund limit, and I made no changes.
    Show the result
    Yes. Refund 5501 for $80 on order 1042 is within the $500 refund limit, and I made no changes.

18 tools held: more than the job needs.

Give each role only the few tools it needs

The fix maps each role to its own short list of no more than five tools. The reviewer gets four read tools, the refunder gets the refund tool with its lookups, and an unknown role gets none. A tool several roles need, such as the policy lookup, sits in each list.

A broad tool is the same problem in one piece. Swap a general run_query for a narrow tool that reads one named table, and a wrong pick stays small.

Tools can also come from an MCP server, a program that offers tools over a standard protocol. A server can also offer a resource, a catalogue the agent can list. A team shares servers in a .mcp.json file at the repository root, and a user-level file lives on one machine.

What the agent holds and calls.ColleagueAgentToolsjobtool callsresultsanswer
roles.pygreen lines are what the fix adds
1"""Which tools each role may use, and how a step must choose among them."""23ROLES = {4 "reviewer": ["lookup_order", "list_refunds", "get_policy", "get_customer"],5 "refunder": [6 "lookup_order",7 "get_customer",8 "get_policy",9 "process_refund",10 ],11 "shipper": ["lookup_order", "track_parcel", "get_customer"],12 "stock": ["check_stock", "reorder_stock", "price_history"],13 "support": ["lookup_order", "get_customer", "add_note", "escalate_ticket"],14}15FIXED = {"policy": "get_policy"}16MUST_CALL = {"read"}171819def tools_for(role: str) -> list[str]:20 """The tools an agent in `role` may call; an unknown role gets none."""21 return list(ROLES.get(role, []))222324def choice_for(role: str, step: str) -> dict[str, str]:25 """The `tool_choice` for `step`: auto, any, or one named tool."""26 held = tools_for(role)27 named = FIXED.get(step)28 if named in held:29 return {"type": "tool", "name": named}3031 if step in MUST_CALL and held:32 return {"type": "any"}3334 return {"type": "auto"}

auto, any or one named tool

A short tool list says what the agent may call. tool_choice says whether a step must call one. With auto the model may answer in words and call nothing. With any it must call some tool, and which one is still its pick. With one named tool the step is fixed.

A fourth value, none, stops every tool call. Claude Haiku 5.5, the model in these runs, accepts a forced choice. Some larger current models reject any and a named tool with a 400 error. There, keep auto and mark the tool strict, so its input still follows the schema.

choice_for(role, step) returns the choice for a step, and the tests assert it. Force a lookup first, then let the model choose for the rest. A line in the system prompt cannot set the choice, because it is a request field.

  1. Colleaguea review question
    Show the result
    review
  2. Agenttool_choice: any
    Show the result
    any
  3. Toolscalled: lookup_order, list_refunds, get_policy
    Show the result
    lookup_order
    list_refunds
    get_policy
  4. Agent(no words)
    Show the result

tool_choice any: lookup_order, list_refunds, get_policy.

  1. Colleaguea review question
    Show the result
    review
  2. Agenttool_choice: auto
    Show the result
    auto
  3. Toolscalled: no tools
    Show the result
  4. AgentTo check a refund on order 1042, I need the customer's ID so I can pull up their account and confirm the order belongs…
    Show the result
    To check a refund on order 1042, I need the customer's ID so I can pull up their account and confirm the order belongs to them.

tool_choice auto: no tools.

  1. Colleaguea review question
    Show the result
    review
  2. Agenttool_choice: tool get_policy
    Show the result
    tool get_policy
  3. Toolscalled: get_policy, lookup_order
    Show the result
    get_policy
    lookup_order
  4. Agent(no words)
    Show the result

tool_choice tool get_policy: get_policy, lookup_order.

We doChange one setting at a time and predict.

The same job, with a different tool list

Use the switch to run the job with every tool, then with the role's tools. The card marked "This is where the runs split" shows how many tools the agent held.

Try the refund and parcel jobs too. A refund job needs the refund tool, so scoping is not removing everything.

This part is interactive in the lab: run ./workshop watch scoped-tools.

Try it: tools and jobs

Change one setting at a time. Pick a job, then switch between every tool and the role's tools.

Every combination is a recorded run, so this works with no internet.

This part is interactive in the lab: run ./workshop watch scoped-tools.

Scoped tools in the Agent SDK

The harness serves only the tools tools_for returns from an in-process MCP server. It lists the same names as the allowed tools. role_agents writes the same table as one subagent per role.

The Agent SDK does not expose tool_choice, so the three choice rows above call the Messages API directly. This is a real Claude Haiku 5.5 run.

Every connected server's tools are available to the agent, though Claude Code may defer loading their definitions behind tool search when there are many.

app/sdk_agent.py
1"""The scoped tools on the Agent SDK: one server, one subagent per role."""23from types import ModuleType45from claude_agent_sdk import (6 AgentDefinition,7 McpSdkServerConfig,8 create_sdk_mcp_server,9 tool,10)1112from kit.api import Json13from .tools import TOOLS1415SERVER = "lumen"16PREFIX = f"mcp__{SERVER}__"17SYSTEM = (18 "You are a support agent at Lumen Supply. Use your tools to do the "19 "job, then answer in one or two sentences."20)212223def build_server(names: list[str]) -> McpSdkServerConfig:24 """Serve exactly the named tools from the catalogue."""25 served = []26 for name in names:27 described, result = TOOLS[name]2829 @tool(name, described, {"id": str})30 async def handler(args: Json, result: str = result) -> Json:31 return {"content": [{"type": "text", "text": result}]}3233 served.append(handler)3435 return create_sdk_mcp_server(SERVER, tools=served)363738def role_agents(roles: ModuleType) -> dict[str, AgentDefinition]:39 """The role table as subagents: each holds only its role's tools."""40 return {41 role: AgentDefinition(42 description=f"Does the {role} job.",43 prompt=SYSTEM,44 tools=[PREFIX + n for n in roles.tools_for(role)],45 model="haiku",46 )47 for role in ("reviewer", "refunder", "shipper")48 }
  1. ColleagueCustomer c-77 is verified. Refund order 1042 in full and give the refund id.
    Show the result
    Customer c-77 is verified. Refund order 1042 in full and give the refund id.
  2. Agentholds 4 tools, 0 that change data unasked
    Show the result
    lookup_order
    get_customer
    get_policy
    process_refund
  3. Toolscalled: lookup_order, get_customer, process_refund
    Show the result
    lookup_order
    get_customer
    process_refund
  4. AgentRefund r-901 has been processed for the full $80 on order 1042. The order record doesn't show which customer it belongs…
    Show the result
    Refund r-901 has been processed for the full $80 on order 1042. The order record doesn't show which customer it belongs to, so I didn't confirm that it's c-77's order before refunding.

4 tools held: only what the job needs.

You doFix the starting code, then test yourself.

You do: scope each role

Copy the starting code and open work/scoped-tools/roles.py. tools_for() gives every role all 18 tools, and choice_for() always returns auto.

Terminal
./workshop start scoped-tools
./workshop check scoped-tools
./workshop watch scoped-tools

The checks want five tools or fewer per role, no tool that changes data for a reviewer, and a refund tool for the refunder. An unknown role gets nothing, the read step must call a tool, and the policy step must call get_policy.

Check your understanding

Answer each question, then read why every option is right or wrong. The wrong options are the mistakes engineers make on the job.

1. A support agent holds 18 tools and picks the wrong one in 3 of every 10 calls. What is the most direct fix?

  1. ARewrite each of the 18 descriptions to be twice as long.
  2. BRaise the turn limit so a wrong pick can be tried again.
  3. CGive each role the 4 or 5 tools its job needs.
  4. DKeep all 18 so the agent can handle any request.
Show the answer

Answer: C

  • A Longer text adds to the crowd the model sorts through. The list is the problem.
  • B Extra turns give more chances to pick wrong, and a wrong write is not undone.
  • C Correct. A short list leaves few wrong picks, and the tools a job needs stay in reach.
  • D Every extra tool is another wrong option, and some of the 18 change data.

2. A read-only reviewer called the refund tool even though its prompt said never to refund. What fixes it for good?

  1. ARemove the refund tool from the reviewer's tool list.
  2. BRepeat the rule three times in the system prompt.
  3. CAsk the reviewer to confirm before each call.
  4. DRename the refund tool so it is harder to spot.
Show the answer

Answer: A

  • A Correct. A tool the role does not hold cannot be called, whatever the prompt says.
  • B A prompt is a request. The tool stays in the list and can still be picked.
  • C A confirmation still lets the call through once the model says yes.
  • D The model reads the description too. The tool is still held and callable.

3. Every review must start with the policy lookup, but in 2 of 10 runs the agent answers first. What makes the first step fixed?

  1. AAdd a line to the system prompt that says to look up the policy first.
  2. BGive the agent only the policy tool for every step.
  3. CSet tool_choice to auto so the model decides each step.
  4. DSet tool_choice to the policy tool on the first request.
Show the answer

Answer: D

  • A A prompt line is followed most of the time. The 2 misses show it does not fix the order.
  • B That removes the other tools the later steps need.
  • C With auto the model may answer in text and skip the lookup.
  • D Correct. A named tool makes the first call that tool, and the model picks freely after.

4. Which two statements about the values of tool_choice are true? Select two.

  1. AWith auto, the model may answer in text and call nothing.
  2. BWith any, the model picks the best tool for the question.
  3. CWith a named tool, the model calls that tool first.
  4. DA system prompt line can set the value for a step.
Show the answer

Answer: A, C

  • A Correct. Auto leaves the decision to the model, and text is a valid answer.
  • B Any only requires some call. Which tool comes back is still the model's pick.
  • C Correct. The named tool is forced, so the step cannot be skipped.
  • D The value is a request field. Prompt text cannot set it.

5. An agent with one broad run_query tool can read or delete any of 12 tables, and it once deleted rows. What is the better design?

  1. AKeep run_query and add a prompt line that forbids deletes.
  2. BSwap it for a few narrow tools, such as one that reads a named table.
  3. CAdd 12 more tools so the agent has a choice for each table.
  4. DForce run_query with tool_choice so it is always used the same way.
Show the answer

Answer: B

  • A The tool can still run a delete. A prompt line does not narrow it.
  • B Correct. A narrow tool can only do its one job, so a wrong pick stays small.
  • C Twelve more tools add twelve more wrong picks and keep run_query in reach.
  • D Forcing the broad tool makes the risk certain. It does not narrow it.

6. One developer wants a private notes server on their own machine, and the other 5 on the team must not get it. Where does its configuration go?

  1. AThe .mcp.json file at the root of the repository.
  2. BThe system prompt of the reviewer role, as an instruction.
  3. CA note in the README telling each person to opt in.
  4. DA user-level file on that developer's machine.
Show the answer

Answer: D

  • A That file is checked in, so all 6 people would get the server.
  • B A prompt cannot connect a server. Servers come from configuration.
  • C A README only describes a setup. It connects nothing, for anyone.
  • D Correct. A user-level file lives on one machine, so only that developer gets the server.

7. A team of 6 wants the same MCP servers for everyone who opens the repository. Where does the configuration go?

  1. AEach person's own user-level file.
  2. BThe system prompt of each role.
  3. CA .mcp.json at the repository root.
  4. DThe README, with steps for each person to follow.
Show the answer

Answer: C

  • A A user-level file lives on one machine. The other 5 never get it.
  • B A prompt cannot connect a server. Servers come from configuration.
  • C Correct. It is checked in, so everyone who opens the repository gets the same servers.
  • D A README is read once. Nothing connects until each person does it by hand.

8. Two MCP servers are connected, one with 5 tools and one with 7. Which two statements are true? Select two.

  1. AOnly the first server's tools load until it is disconnected.
  2. BAll 12 tools are available to the agent at once.
  3. CConnecting more servers makes the agent safer, since it has more options.
  4. DA resource can list what a server offers, so the agent sees a catalogue.
Show the answer

Answer: B, D

  • A Connected servers are available together, so all 12 tools are in reach at once.
  • B Correct. Each connected server's tools join the same list, even if their definitions load on demand.
  • C More tools are more wrong picks. Connect only what the job needs.
  • D Correct. A resource gives the agent a summary of what it can reach.

9. A connected server offers 9 tools, 3 of which change data, and the reviewer role must only read. What keeps the 3 out of its reach?

  1. AConnect the server and list only the read tools the role may call.
  2. BConnect the server and tell the reviewer in its prompt to skip 3 of them.
  3. CConnect a second server so the reviewer has a safer choice.
  4. DLeave the server unconnected and give the reviewer no tools.
Show the answer

Answer: A

  • A Correct. The role's list names the tools it may call, so the other 3 are never offered to it.
  • B A prompt is a request, and the 3 tools stay callable.
  • C A second server adds tools and leaves the 3 in reach.
  • D That also removes the read tools the review needs.

Play: spot the bug

This lookup falls back to the full list when a role is not found.

python
def tools_for(role: str) -> list[str]:
    return ROLES.get(role, ALL_TOOLS)

Predict: A config typo asks for the role "reviwer". Which tools does that agent get?

  1. ANone, since the role does not exist.
  2. BAll 18, process_refund included.
Show the answer

B. A fallback to everything turns a typo into full access. Fail closed: an unknown role gets no tools, so the mistake shows up as an agent that cannot act.

References

Conclusions

  • An agent can only call what it holds, so give each role the few tools its job needs, never more than five.
  • A prompt that says "do not use it" is weaker than removing the tool.
  • Use tool_choice to make a step call a tool, and name the tool when the order is fixed.

You can now write a role map and a choice for each step, so an agent's tools and its mistakes stay small.

Role

The job an agent does, which decides its tools

Scoped tools

A short list held by one agent, not the full set

Write tool

A tool that changes data or sends something

Default deny

A role that is not listed holds no tools

tool_choice

A request field set to auto, any or one named tool