AI Agents and Tool Use
When a model does more than answer: tool calling, the agent loop of planning, acting and observing, the Model Context Protocol, multi-agent setups, and the guardrails that keep agents safe.
From chatbot to agent
A chatbot answers. An agent acts. The Agentic AI section of the IT Service Management topic introduces the idea and the risks for service management; this page looks at how it works underneath. An AI agent is a system in which a language model pursues a goal by repeatedly deciding on an action, carrying it out through tools, and looking at the result, with a degree of independence that the builder chooses.
It helps to see a spectrum rather than a single switch:
- Chat: the model answers from the prompt. A person does everything else.
- Workflow: your code runs fixed steps and calls the model at chosen points ("classify this ticket", "draft a reply"). The path is predictable and testable.
- Agent: the model decides which step comes next, which tool to use and when it is done. The path is flexible, and therefore less predictable.
More autonomy is not automatically better. If you can write the steps down in advance, a workflow is cheaper, faster and easier to test. Agents earn their place when the path genuinely depends on what is found along the way, as in troubleshooting, and when mistakes can be caught and undone.
Tool calling
A language model only produces text. It cannot query a database, read a monitoring dashboard or open a ticket. Tool calling (also called function calling) is the mechanism that bridges that gap: the application describes the available tools to the model (name, purpose, parameters as a JSON schema), and when the model decides a tool is needed, it returns a structured request naming the tool and its arguments instead of a normal answer. The application runs the tool and sends the result back, and the model continues with that information.
Step 2 is the key to understanding agents. The model's "action" is a
piece of
structured output, for example
{"name": "check_service_status", "arguments": {"service":
"vpn"}}. Nothing happens until your code chooses to execute it. That gives
you a control point for every action: you can check the tool name
against an allow-list, validate the arguments, ask a person, log the
call, or refuse. The model can also request several tools at once,
or none if it can answer directly.
Because the model chooses tools from their descriptions, the descriptions are part of the prompt. Clear names, a sentence on when (and when not) to use each tool, and strict parameter schemas make the difference between an agent that picks the right tool and one that guesses.
The agent loop
An agent loop is the cycle that turns tool calling into an agent: the model plans the next step, acts by requesting a tool, observes the result, and repeats until it decides the goal is reached. The ITSM page shows the loop as a diagram; in code it is a plain loop around the tool calling sequence above.
Every loop needs stopping conditions that do not depend on the model's own judgment, because a confused model can keep trying forever:
- the model returns a final answer instead of a tool call;
- a maximum number of steps or tool calls is reached;
- a budget of tokens, money or time runs out;
- the same call fails repeatedly, or a guardrail blocks an action that the task cannot do without;
- a person stops it.
The example simulates the loop without a real model. A "scripted
model" returns predetermined decisions, one per step; a real model
would choose them from the goal and the observations so far. Two
tools are available,
check_service_status and create_ticket. In
step 2 the script asks for a tool that is not on the allow-list, to
show a guardrail at work.
# --- Tools: in reality these call monitoring and the ticketing system ---
SERVICES = {"email": "operational", "portal": "operational", "vpn": "degraded"}
created = []
def check_service_status(service):
return {"service": service, "status": SERVICES.get(service, "unknown")}
def create_ticket(summary, priority):
ticket_id = f"INC-{20417 + len(created)}"
created.append(ticket_id)
return {"ticket_id": ticket_id, "priority": priority}
TOOLS = {"check_service_status": check_service_status,
"create_ticket": create_ticket} # the allow-list
# --- A scripted stand-in for the model ---
# A real model would choose each step from the goal and the observations.
def scripted_model(history):
step = len(history)
if step == 0:
return {"tool": "check_service_status", "args": {"service": "vpn"}}
if step == 1:
return {"tool": "restart_server", "args": {"host": "vpn-gw-01"}}
if step == 2:
return {"tool": "create_ticket",
"args": {"summary": "VPN degraded, users report error 809",
"priority": 2}}
ticket = history[-1]["result"]["ticket_id"]
return {"final": f"The VPN is degraded. I logged {ticket} for the "
"network team."}
# --- The agent loop ---
MAX_STEPS = 5
history = []
for step in range(1, MAX_STEPS + 1):
decision = scripted_model(history)
if "final" in decision:
print(f"step {step}: final answer")
print(f" {decision['final']}")
break
name, args = decision["tool"], decision["args"]
if name in TOOLS:
result = TOOLS[name](**args)
else:
result = {"error": f"tool '{name}' is not allowed"}
print(f"step {step}: {name} {args}")
print(f" -> {result}")
history.append({"tool": name, "args": args, "result": result})
else:
print("stopped: step limit reached")
step 1: check_service_status {'service': 'vpn'}
-> {'service': 'vpn', 'status': 'degraded'}
step 2: restart_server {'host': 'vpn-gw-01'}
-> {'error': "tool 'restart_server' is not allowed"}
step 3: create_ticket {'summary': 'VPN degraded, users report error 809', 'priority': 2}
-> {'ticket_id': 'INC-20417', 'priority': 2}
step 4: final answer
The VPN is degraded. I logged INC-20417 for the network team.
Three details carry over to real agents. The history of calls and
results is what the model "observes"; in a real system it is sent
back with every request, so long agent runs get expensive (see
tokens and cost). The blocked call does not crash the loop: the error is returned
as an observation, and the model can pick another route. And the
step limit (range(1, MAX_STEPS + 1)) guarantees the
loop ends even if the model never says it is done; the
else branch reports when that happens. Real projects
usually let an SDK or agent framework run this loop, but it is the
same loop.
The Model Context Protocol
Every AI application used to need its own integration code for every tool and data source: one connector for the ticketing system in the chat app, another one in the code editor, a third one in the agent framework. The Model Context Protocol (MCP) is an open standard that replaces this with one interface: a tool or data source is wrapped once as an MCP server, and any application that speaks MCP can use it. Anthropic introduced MCP on 25 November 2024. On 9 December 2025 it was donated to the Agentic AI Foundation, a then newly formed fund under the Linux Foundation co-founded by Anthropic, Block and OpenAI, so it is now governed by a vendor-neutral body.
The architecture has three participants:
- Host: the AI application the user works in, such as a chat app, an IDE (integrated development environment) or an agent.
- Client: a component inside the host that keeps a dedicated connection to one server. A host with three servers runs three clients.
- Server: a program that exposes capabilities, locally on the same machine or remotely over the network.
A server can offer three kinds of things (primitives):
- Tools: functions the model can ask to execute, such as "create a ticket" or "query the CMDB".
- Resources: data the application can read and give to the model as context, such as a file or a knowledge article.
- Prompts: reusable prompt templates, such as "summarize this incident for the major-incident report".
Messages are JSON-RPC 2.0. Local servers usually talk over stdio (standard input and output of a child process); remote servers use Streamable HTTP, with OAuth recommended for authentication. A tool definition that a ticketing server might advertise looks like this:
{
"name": "create_ticket",
"description": "Create an incident ticket in the service desk system. Use only after checking that no open ticket exists for the same issue.",
"inputSchema": {
"type": "object",
"properties": {
"summary": { "type": "string", "maxLength": 120 },
"priority": { "type": "integer", "minimum": 1, "maximum": 4 },
"service": { "type": "string", "enum": ["email", "portal", "vpn"] }
},
"required": ["summary", "priority"]
}
}
MCP standardizes the connection, not the trust. An MCP server runs with whatever permissions you give it and its descriptions and results go straight into the model's context. Install servers only from sources you trust, pin their versions, and review what they can access, just as with any other software dependency (see the supply chain risks in AI Security).
Multi-agent systems
A multi-agent system splits a task over several agents, each with its own instructions, tools and context, that hand work to one another. The most common pattern is orchestrator and workers: one agent breaks a task into parts and delegates them, for example one worker that searches the logs, one that checks recent changes and one that reads the knowledge base, and then combines their findings. Other patterns are a pipeline (each agent does one stage) and a reviewer agent that checks another agent's output.
The benefits are real: each agent has a short, focused context, parts can run in parallel, and agents can use different models, a cheap one for searching and a stronger one for the conclusion. So are the costs. Token use multiplies, errors in one agent are passed on as "facts" to the next, and debugging means tracing a conversation between programs. Start with one agent and a good set of tools; split only when one context window or one set of instructions genuinely cannot cope.
Guardrails
A guardrail is a control outside the model that limits what an AI system can do or output, such as an allow-list of tools, a permission check, an approval step, a rate limit or a content filter. The phrase "outside the model" matters: an instruction in the system prompt ("never delete anything") is a request the model usually follows, while a guardrail is enforced by code and cannot be talked out of. The ones that matter most for agents:
- Allow-lists: the agent can use only the tools, commands, hosts or URLs explicitly listed, as in the example above.
- Least privilege: the agent's credentials allow only what the task needs, read-only where possible, scoped to specific systems. An agent that triages tickets does not need domain admin rights. This is the same principle as for people (see Least privilege).
- Approvals: actions above a risk threshold wait for a person (see the next section).
- Rate and budget limits: caps on calls per minute, tickets per hour, tokens and money per run, so a loop that goes wrong does limited damage.
- Sandboxing: code the agent writes or runs executes in an isolated container or virtual machine without access to production systems or the internal network.
- Validation and logging: arguments are checked like any untrusted input, and every call is recorded with who, what, when and the result, so actions can be audited and undone.
Human in the loop
Human in the loop means designing an AI system so that a person reviews, approves or corrects its decisions at defined points before they take effect. It is the most effective guardrail for actions that are hard to undo, and it can be graded by risk:
| Action | Example | Typical control |
|---|---|---|
| Read-only | Check service status, search logs | Automatic, logged |
| Low impact, reversible | Add a note to a ticket, set a category | Automatic, reviewed in samples |
| Customer-facing or costly | Send a reply, order a replacement laptop | Draft by agent, a person approves |
| High impact or irreversible | Restart production, delete data, change firewall rules | Not available to the agent, or two-person approval |
Approval only works if the approver actually looks. When an agent is right 99 times, people start clicking "approve" without reading, the trap known as automation bias (see Responsible AI). Show the approver what will happen and why, keep approvals for decisions that matter, and track how often approvers change or reject a proposal; a rate of zero is a warning sign, not a success.
Scheduled agents
A scheduled agent is an agent that starts on a timetable instead of on request, for example every working day at 07:30, and does its work while nobody is watching: it reads a few sources, decides what matters and writes the result to one place, such as a morning brief in a team chat channel. Because no one is present to approve a step or notice a mistake, the usual failures are quiet ones: a source that can no longer be read, a message posted twice, a brief that ignores what the reader asked for, or an item reported as still open when it was solved an hour ago.
Anthropic described a reference design for such an agent in October 2026 (built on its Managed Agents platform, a beta product as of October 2026). The design is product-specific, but its six rules apply to any scheduled agent:
- Read from a bookmark, not a time window. Store the timestamp of the newest item the last run read, per source, and continue from there. "The last 24 hours" leaves a gap when a run starts late and repeats items when it starts early.
- A failed read is not a quiet day. When a source cannot be read, keep its bookmark, write the result from the other sources and say so in one line ("pull requests unavailable this run"). An empty section that hides an error is worse than a visible gap.
- Check every item again just before posting. Things change between reading and writing. One stale "still waiting on you" costs more trust than several missing items, so an item is either confirmed or left out.
- A post only counts when the destination confirms it. Update the bookmarks and the record of what was reported only after the chat service returns success. If the answer is unclear, record the run as "maybe posted" and change nothing else, so the next run neither skips nor repeats it.
- Read the preferences every run, from a store the agent cannot edit. The reader's wishes live outside the prompt and outside the agent's reach; if they cannot be read, the agent stops and says so instead of running on defaults.
- Read-only access where the agent only reads, and a budget per run. Read-only tokens mean an instruction hidden in a message or an issue can change the brief but cannot change the source systems (see AI Security). A spending cap, started at a few times the cost of a normal run and then tightened, stops a run that loops.
The first rule is easy to test. Two runs read the same channel; the second one starts two and a half hours late, for example after a restart of the host:
from datetime import datetime, timedelta
# Messages in a team channel, oldest first (UTC)
messages = [
("2026-10-07T06:50", "db01 backup finished"),
("2026-10-07T08:15", "Pull request 481 needs a review"),
("2026-10-08T05:40", "Portal certificate renews in 7 days"),
("2026-10-08T06:55", "Pull request 481 still waiting"),
]
def ts(text):
return datetime.fromisoformat(text)
def read_window(run_at, hours=24):
"""The common mistake: read 'the last 24 hours'."""
start = ts(run_at) - timedelta(hours=hours)
return [text for when, text in messages if start < ts(when) <= ts(run_at)]
def read_from_bookmark(bookmark, run_at):
"""Read everything after the newest item of the previous run."""
new = [(when, text) for when, text in messages if ts(bookmark) < ts(when) <= ts(run_at)]
next_bookmark = new[-1][0] if new else bookmark
return [text for _, text in new], next_bookmark
# Wednesday's run starts on time; Thursday's starts late, at 09:30
runs = ["2026-10-07T07:00", "2026-10-08T09:30"]
print("Fixed 24-hour window:")
for run_at in runs:
print(" ", run_at, read_window(run_at))
print("Bookmark:")
bookmark = "2026-10-06T07:00"
for run_at in runs:
items, bookmark = read_from_bookmark(bookmark, run_at)
print(" ", run_at, items)
Fixed 24-hour window: 2026-10-07T07:00 ['db01 backup finished'] 2026-10-08T09:30 ['Portal certificate renews in 7 days', 'Pull request 481 still waiting'] Bookmark: 2026-10-07T07:00 ['db01 backup finished'] 2026-10-08T09:30 ['Pull request 481 needs a review', 'Portal certificate renews in 7 days', 'Pull request 481 still waiting']
The fixed window missed "Pull request 481 needs a review" because it arrived after Wednesday's run but more than 24 hours before Thursday's. The bookmark run reads every item exactly once, whatever time it starts. Two small details from the same design are worth copying too: give the agent an explicit time zone for dates (on a server in another zone, "this morning" can become "yesterday"), and keep secrets such as chat tokens outside the agent's own environment, where the platform adds them only for the hosts they belong to. Scheduled agents are monitored like any other scheduled job; see Scheduling Tasks.
Risks
Agents combine the weaknesses of language models with the power to act, which creates risks a chatbot does not have:
- Excessive agency: the agent has more functionality, permissions or autonomy than its task needs, so a mistake or a manipulation does real damage. OWASP ranks it third in its 2026 Top 10 for LLM applications; see AI Security.
- Prompt injection through tool results: a web page, email or ticket the agent reads contains instructions, and the agent follows them with its tools.
- Errors at machine speed: a wrong decision repeated across fifty servers in a minute. Rate limits and step limits contain this.
- Runaway cost: loops that never converge, resending a growing history every step.
- Unclear accountability: when an agent acts, a named person or team must still own the outcome (see Using AI at Work).