Using multiple tools
Your conversation loop handles one tool cleanly. The reminder project needs three. The good news, and the whole reason we built the loop the way we did, is that adding the remaining two tools is a local change — no loop rewrites, no architecture decisions, just a new function, a new schema, and one more line in run_tool. This lesson wires up the full reminder system and tests it end to end.
The remaining two tools
You already have get_current_datetime. The other two tools for the project:
add_duration_to_datetime(datetime_str, amount, unit)— takes a datetime string, an integer amount, and a unit ("days","weeks","hours", etc.) and returns the resulting datetime string. Usesdatetime.timedeltaunder the hood for deterministic arithmetic.set_reminder(datetime_str, message)— records a reminder at the given datetime with the given message. In the course project this writes to an in-memory dict; in production, you'd persist it to your database or queue a job.
Both follow the same shape as get_current_datetime: a plain Python function, a matching JSON schema, input validation, and informative error messages on failure.
Registering the new tools
Step one: include all three schemas when you call chat():
response = chat(messages, tools=[
get_current_datetime_schema,
add_duration_to_datetime_schema,
set_reminder_schema,
])
Claude now knows about all three tools and can choose any of them on any turn. The tools list is Claude's menu — whatever's on it is what the model can call.
Extending the router
Step two: teach run_tool how to dispatch to the new functions:
def run_tool(tool_name, tool_input):
if tool_name == "get_current_datetime":
return get_current_datetime(**tool_input)
elif tool_name == "add_duration_to_datetime":
return add_duration_to_datetime(**tool_input)
elif tool_name == "set_reminder":
return set_reminder(**tool_input)
raise ValueError(f"Unknown tool: {tool_name}")
That's the whole integration. Nothing in run_conversation or run_tools changed — the loop is agnostic to how many tools are available, because it just processes whatever tool_use blocks come back and looks up each one by name.
This is the payoff of the architecture from the last lesson. The conversation machinery is written once. Tools are added locally by writing the function, writing the schema, registering it in the tools list, and adding one branch to the router. Four local edits per new tool. No global rewiring.
Testing with a request that chains tools
Now for the fun part. Ask the assistant something that genuinely requires all three tools:
"Set a reminder for my doctor's appointment. It's 177 days after Jan 1st, 2050."
Run the conversation and watch what happens turn by turn:
Turn 1: Claude responds with some text ("I'll calculate the reminder date") and a tool_use block requesting add_duration_to_datetime with datetime_str="2050-01-01", amount=177, unit="days". It skipped get_current_datetime — we gave it a specific starting date, so it doesn't need today.
Turn 2: Your server computes 2050-06-27 and returns it as a tool result. Claude looks at the result and responds with another tool_use block, this time for set_reminder(datetime_str="2050-06-27", message="Doctor's appointment").
Turn 3: Your set_reminder records the reminder in your store and returns a confirmation. Claude sees stop_reason != "tool_use" is false one more time, generates its final text response — "Reminder set for June 27, 2050" — and the loop exits.
Three turns, two tool calls, one natural-language answer. Claude chained the tools autonomously — you didn't have to prompt it to "first compute the date, then set the reminder." The tool descriptions and the request were enough for the model to plan the sequence.
Examining the message history
If you print the full messages list after the conversation, you'll see the complete structure:
- User message with the original request.
- Assistant message containing a text block and a
tool_useblock foradd_duration_to_datetime. - User message containing a
tool_resultblock with the computed date. - Assistant message with a text block and a
tool_useblock forset_reminder. - User message with the tool_result from setting the reminder.
- Assistant message with the final confirmation text.
Every message preserves its full block structure. This is what the helper upgrades from the previous lesson were for — nothing in the history gets collapsed to plain text. Claude sees its own prior tool calls and your tool results in every subsequent request, and that complete context is what lets it plan multi-step sequences reliably.
The pattern for adding more tools later
Any new tool you want to add to your project follows the same four steps:
- Write the tool function in Python with proper validation and informative errors.
- Define a JSON schema describing its name, description, and arguments. Have Claude write the first draft if you want.
- Add the schema to the
toolslist in thechat()call. - Add a branch to
run_toolthat dispatches the tool name to its implementation.
That's it. The four steps per tool. No matter how sophisticated your assistant gets, tool integration stays this local. When your assistant has ten tools, the diff for adding the eleventh is the same four-step change. This is the kind of shape you want in a production codebase — linear scaling, no surprises.
Key Takeaways
- 1Adding new tools to an existing conversation loop is a local change: one new function, one schema, one entry in the tools list, one branch in run_tool.
- 2The full reminder project uses three tools — get_current_datetime, add_duration_to_datetime, and set_reminder — each bridging a specific gap in Claude's native capabilities.
- 3Claude chains tool calls autonomously when given multiple relevant tools: it decides which ones to call, in what order, based on the tool descriptions and the user request.
- 4The conversation history preserves every tool_use and tool_result block, giving Claude full context on each subsequent turn so it can plan multi-step sequences reliably.
- 5The four-step pattern — write function, write schema, register schema, extend router — scales linearly from one tool to dozens without any architectural changes.