All lessons
AIAdvanced13 min readWorth knowing

AI agents

LLMs that use tools in a loop to finish multi-step tasks — and how to keep them safe and useful.

  • Tools
  • Planning
  • MCP
  • Guardrails
  • Evals
Step 1/6
An AI agent gets a goal, not a script: “find me a cheap flight”.

In 30 seconds

An AI agent is a language model running in a loop: it looks at a goal, decides which tool to call, reads the result, and repeats until the task is done. Agents can search, edit files, call APIs or run code. They are powerful for open-ended work, but they need clear tools, limits, permissions and evaluation to be trustworthy.

Not in my daily stack — I know the concepts and would learn the details when a project needs it.

Why it matters

  • Agents handle tasks that cannot be scripted in advance — investigating a bug, researching across sources, triaging tickets — because the model chooses the next step based on what it finds.
  • Coding agents have already changed how software teams work, so engineers need to understand how the loop, tools and context really work, not just use them.
  • Standards such as the Model Context Protocol (MCP) make it cheap to connect agents to company systems, which also makes security design urgent.
  • A team builds an agent only when a task is multi-step, valuable enough to justify higher cost and latency, and has errors that can be caught — otherwise a single LLM call or a fixed workflow is better.

How it works

01The idea: a model in a loop with tools

A chatbot answers once. An agent works like a junior colleague given a goal and a toolbox: it thinks about the next step, uses a tool, looks at the result, and keeps going until it is done or stuck. Technically, it is the tool-use API in a loop. You send the goal plus tool definitions; the model replies with a tool call; your code runs it and sends back the result; repeat until the model ends its turn. 'Planning' is mostly the model reasoning between steps, sometimes helped by asking it to write a plan or a to-do list first. The agent's quality depends on four things: the model, the tools you give it, the context it can see, and the guardrails around it. Anthropic's guidance is worth repeating: start with the simplest solution, and add agentic complexity only when it clearly improves results.

  • Workflow: your code decides the steps; the LLM fills in parts
  • Agent: the LLM decides the steps; your code provides tools and limits
  • Most production systems mix both

02A minimal agent loop

Here is the core loop with the Claude Messages API in TypeScript. It keeps calling the model while it asks for tools, runs each requested tool, and returns all results in one user message. Important details: a maximum number of steps so a confused agent cannot loop forever; errors are returned to the model as tool results with is_error set, so it can adapt instead of crashing; and the whole content of each assistant reply is appended to history, not just the text. In real projects you can use the SDK's tool runner helper (which drives this loop for you), the Claude Agent SDK (the Claude Code harness as a library, with built-in file, shell and web tools), or hosted options such as Claude Managed Agents. Other ecosystems offer similar SDKs; the loop underneath is the same.

ts
const messages: Anthropic.MessageParam[] = [{ role: "user", content: goal }];
for (let step = 0; step < 20; step++) {
  const res = await client.messages.create({ model: "claude-opus-5-5", max_tokens: 8000, tools, messages });
  messages.push({ role: "assistant", content: res.content });
  if (res.stop_reason !== "tool_use") return res;
 
  const results: Anthropic.ToolResultBlockParam[] = [];
  for (const block of res.content) {
    if (block.type !== "tool_use") continue;
    try {
      const out = await runTool(block.name, block.input, currentUser); // your code, your auth
      results.push({ type: "tool_result", tool_use_id: block.id, content: JSON.stringify(out) });
    } catch (e) {
      results.push({ type: "tool_result", tool_use_id: block.id, content: String(e), is_error: true });
    }
  }
  messages.push({ role: "user", content: results });
}
throw new Error("Agent hit the step limit");

03Designing tools the model can use well

Tools are the agent's hands, and most agent failures are tool-design failures. Give each tool one clear job, a name that says what it does, and a description that explains when to use it, what it returns and common pitfalls — as if writing docs for a new teammate. Prefer a few high-level tools ('search_orders' with filters) over dozens of thin wrappers around every endpoint. Return compact, relevant results: an agent that receives a 5 MB JSON dump wastes context and gets confused, so paginate, summarise and include ids for follow-up calls. Make errors helpful ('customer_id not found; use search_customers first'). Make write tools idempotent where possible, and separate read tools from write tools so you can permission them differently. Test tools by watching real transcripts: where the model misuses a tool, the description usually needs work.

04MCP: plugging agents into systems

The Model Context Protocol (MCP) is an open standard, introduced by Anthropic and now supported across many AI tools and providers, for connecting AI applications to tools and data. An MCP server exposes tools (actions), resources (readable data) and prompts (templates) over a standard JSON-RPC protocol, either locally via stdio or remotely over HTTP. Any MCP client — Claude Code, Claude apps, IDEs and other agent hosts — can then use it without custom glue code. That means you write a 'tickets' or 'database' integration once and reuse it across agents. Remote MCP servers use OAuth for authorisation, so the agent acts with a user's delegated, scoped permission. Treat third-party MCP servers like any dependency you install: review what they can access, pin versions, and prefer servers from trusted publishers.

json
{
  "mcpServers": {
    "tickets": {
      "command": "node",
      "args": ["./mcp/tickets-server.js"],
      "env": { "TICKETS_API_URL": "https://api.example.com" }
    }
  }
}

05Context: the agent's working memory

Everything the agent knows during a task lives in its context window: instructions, tool definitions, the history of calls and results. Long tasks fill it up, and quality drops as the context gets noisy. Good harnesses manage context actively. Keep the system prompt focused on goals, constraints and how to verify work. Load tools on demand when there are many (tool search). Clear old, bulky tool results once they are no longer needed (context editing) or summarise earlier history (compaction). Give the agent a scratchpad or memory file for notes that must survive — a plan, decisions, progress. For big tasks, split work across subagents: each explores in its own fresh context and returns only a short result to the main agent. This is how coding agents keep working on large repositories without drowning in file contents.

06Guardrails and human-in-the-loop

An agent can take real actions, so you design limits like you would for a new employee with production access. Least privilege: give only the tools the task needs, with credentials scoped to the user or a narrow service account. Permission tiers: read-only tools run freely; writes need confirmation or run in a sandbox; destructive actions (payments, deletes, emails to customers) always need a human. Budgets: step limits, token or cost budgets, and wall-clock timeouts. Sandboxing: run code and shell commands in isolated containers with no secrets and restricted network. Verification: tell the agent how to check its work — run tests, re-read the result — and check it yourself in code where possible. Audit: log every tool call with inputs, outputs and the identity it acted for.

  • Read: allow · Write: confirm or sandbox · Irreversible: human approval
  • Step, token and time budgets on every run
  • Secrets stay in the tool layer, never in the prompt

Use it when

  • Open-ended tasks where the steps depend on what you find: debugging, research, data investigation, codebase changes.
  • Tasks with a built-in way to verify success, such as tests passing, a schema validating or a reviewer approving.
  • Internal operations that combine several systems (tickets, logs, docs) where a human would otherwise click through many tools.
  • Long-running background work where a few minutes and some extra cost are acceptable for a better outcome.

Skip it when

  • Tasks with fixed, known steps — a plain workflow with one or two LLM calls is cheaper, faster and easier to test.
  • High-stakes irreversible actions without a human approval step or a reliable automatic check.
  • Latency-critical user interactions that must answer in under a second.

Alternatives

  • Single LLM call

    One request with good context handles classification, extraction and summarising — start here.

  • Fixed workflow (prompt chaining, routing)

    Your code runs the steps in order and calls the model for parts; predictable and testable.

  • Classic automation

    Scripts, cron jobs and rules engines when the logic is known and deterministic.

  • Hosted agent platforms

    Managed agent services (including Anthropic's Managed Agents) run the loop and sandbox for you, trading control for less infrastructure.

Best practices

  1. Start with the simplest design (single call or workflow) and move to an agent only when evals show it helps.
  2. Write tool descriptions like documentation, return compact results and helpful error messages.
  3. Enforce authorisation inside tools with the real user's identity; never trust ids proposed by the model.
  4. Set step, token and time budgets; stop cleanly and report progress when a limit is hit.
  5. Require human approval for irreversible or external actions; sandbox code execution.
  6. Give the agent a way to verify its own work (tests, validators) and tell it to use it.
  7. Log full transcripts and tool calls, and turn failures into evaluation cases.

Common mistakes

  • Building a complex multi-agent system for a task a single prompt could handle.
  • Exposing dozens of raw API endpoints as tools with one-line descriptions.
  • Putting API keys or database passwords into the prompt or tool results.
  • No step limit, so a confused agent loops and burns budget overnight.
  • Evaluating agents only by reading a few nice demos instead of running a task suite with clear success criteria.

Deep dive

07Context engineering for long-running agents

A long agent run is a context-budget problem. Each step adds the model's reasoning, the tool call and its result; after a few dozen steps, much of the window is stale logs. Techniques, roughly in order of use: keep tool outputs small by design (summaries, pagination, ids instead of full objects); clear old tool results the model no longer needs while keeping the fact that the call happened (context editing); periodically compact history into a summary the agent continues from; persist durable state outside the window — a progress file, a task list, a memory store — so a fresh context can resume; and delegate exploration to subagents that return condensed findings. Prompt caching interacts with all of this: cache hits need a stable prefix, so append to history instead of rewriting it, and edit context through the API's supported mechanisms rather than by mutating earlier messages, which can also invalidate the model's preserved reasoning on current models.

08Prompt injection and the lethal trifecta

Security researchers describe a dangerous combination for agents: access to private data, exposure to untrusted content, and the ability to send data out (an email tool, an HTTP request, even rendering an image URL). With all three, a malicious web page or document can instruct the agent to read secrets and exfiltrate them. Since no model is fully immune to injection, break the combination structurally. Remove one leg where possible: no outbound network in the sandbox, or no access to private data while browsing. Apply allow-lists for domains and recipients. Require confirmation for any action that sends data externally. Keep credentials in the tool layer, so the model never sees them, and use per-user scoped tokens so damage is limited to what that user could do. Log and alert on unusual tool sequences, and red-team your agent with injected documents as part of the eval suite.

09Building an MCP server

An MCP server is a small program that declares tools with JSON Schema inputs and handles calls. Official SDKs exist for TypeScript, Python and other languages. Local servers communicate over stdio and inherit the user's machine permissions; remote servers use Streamable HTTP and should implement OAuth so each user grants scoped access. Design tools for agents, not for API completeness: few tools, clear descriptions, compact results. Validate every input and enforce authorisation server-side. Version your server and document breaking changes, because agents depend on tool names and shapes. Check the current MCP specification and SDK docs before building, since the protocol evolves.

ts
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
 
const server = new McpServer({ name: "tickets", version: "1.0.0" });
 
server.registerTool(
  "get_ticket",
  {
    description: "Fetch one support ticket by id, including status and last 5 messages.",
    inputSchema: { id: z.string().describe("Ticket id, e.g. T-1042") },
  },
  async ({ id }) => {
    const t = await fetchTicket(id); // your API, your auth
    return { content: [{ type: "text", text: JSON.stringify(t) }] };
  },
);
 
await server.connect(new StdioServerTransport());

10Cost and reliability engineering

Agent cost is tokens per step times steps per task, and both grow quietly. Measure cost and success per completed task, not per request. Levers: prompt caching on the stable prefix (system prompt and tools), which matters a lot because every step resends the history; lower effort or a smaller model for subagents and simple steps; fewer, better tools so the agent needs fewer calls; and task budgets that let the model pace itself instead of being cut off mid-step. For reliability, make tools idempotent and retries safe, checkpoint progress so a crashed run can resume, set wall-clock limits, and handle non-happy stop reasons (max_tokens, refusal, step limit) with a clear outcome rather than silence. Run long tasks as background jobs with status updates, not inside an HTTP request.

Real-world scenarios

01

An internal ops agent for support triage

Problem

Support wants an assistant that reads a new ticket, checks the customer's recent orders and logs, and proposes a resolution. It must never issue refunds on its own.

How to solve it

Start as a workflow: classify the ticket, then run an agentic investigation step with three read-only tools — get_ticket, search_orders (by the ticket's verified customer id), get_recent_errors. Tools enforce the support agent's permissions and return compact summaries. The agent outputs a structured proposal (diagnosis, evidence ids, suggested action). A 'propose_refund' tool only creates a draft that a human approves in the admin UI. Set a 15-step limit and a token budget, log transcripts, and build an eval suite from 50 past tickets with known resolutions before rollout.

02

The agent loops and burns budget

Problem

A research agent sometimes runs for an hour, calling search again and again with slightly different queries, and never produces a final answer.

How to solve it

Read the transcripts. Usually the success criteria are unclear and results are noisy. Rewrite the system prompt with a concrete deliverable and a stopping rule ('after you have three independent sources, write the answer'). Make the search tool return fewer, deduplicated results with snippets. Add a step limit and a token budget; when hit, the harness asks the model for a best-effort summary instead of failing silently. Delegate deep reading to subagents that return short findings. Add these runaway tasks to the eval suite and track steps per task.

03

Connecting a coding agent to company tools

Problem

Developers want their coding agent to read tickets and query a staging database. Security worries about credentials and data leaks.

How to solve it

Build two small MCP servers: a ticket server using each developer's OAuth token with read-only scopes, and a database server that connects with a read-only role to staging only, runs parameterised queries with row limits and blocks DDL. Credentials live in the MCP server's environment or a secret manager, never in prompts. Pin server versions in the shared project config, document what each tool can access, and log queries. Production data stays out of reach entirely; if needed later, it gets a separate reviewed server with masking.

Cheat sheet

Core loop condition
while (res.stop_reason === "tool_use") { run tools; send tool_result }
Return a tool failure to the model
{ type: "tool_result", tool_use_id, content: msg, is_error: true }
Parallel tool calls
Return all tool_result blocks in one user message
Install the TypeScript MCP SDK
npm install @modelcontextprotocol/sdk zod
Add an MCP server to Claude Code
claude mcp add tickets -- node ./mcp/tickets-server.js
Install the Claude Agent SDK (TS)
npm install @anthropic-ai/claude-agent-sdk
Safe default budgets
max steps ≈ 20, token budget per task, wall-clock timeout
Permission tiers
read → allow; write → confirm/sandbox; irreversible → human approval
Success metric
success rate per task, plus cost, steps and time per completed task

Questions & answers

Q1What is the difference between a workflow and an agent?

In a workflow, your code defines the steps: for example, classify the ticket, then pick a template, then draft a reply — the LLM fills in each step, but the path is fixed. In an agent, the model decides the path: it chooses which tool to call next based on results so far, and how many steps to take. Workflows are predictable, cheaper and easier to test; agents are flexible and handle tasks you cannot fully specify in advance. Many production systems are workflows with one agentic step where flexibility is really needed.

Q2What is MCP and why does it matter?

The Model Context Protocol is an open standard for connecting AI applications to external tools and data. An MCP server exposes tools, resources and prompts in a standard format, and any MCP-compatible client can use it. Before MCP, every agent framework needed its own integration for every system; now you build one server for, say, your ticketing system and reuse it across coding agents, chat apps and IDEs. It also standardises remote authorisation with OAuth. The flip side is that installing an MCP server grants an agent real capabilities, so it needs the same review as any dependency.

Q3How do you stop an agent from doing something dangerous?

Design for containment rather than trust. Give the agent only the tools the task needs, with credentials scoped to the acting user. Enforce authorisation inside each tool. Split tools by risk: reads run automatically, writes go to a sandbox or need confirmation, irreversible actions need human approval. Run code in isolated containers without secrets and with restricted network. Set budgets on steps, tokens and time. Treat everything the agent reads — web pages, files, emails — as potentially containing prompt injection. And log every action so you can audit and roll back.

Q4Why do agents fail, most commonly?

Reviews of real agent transcripts usually point to the same causes: unclear or overlapping tools, so the model picks the wrong one; tool results that are too large or noisy, which floods the context; missing information in the instructions, such as what 'done' means or how to verify; no feedback signal, so mistakes are never noticed; and context growing so long that early instructions are effectively forgotten. Fixes are mostly engineering: better tool descriptions and outputs, explicit success criteria, verification steps, context management, and evaluation suites that catch regressions.

Q5How do you evaluate an agent?

With a suite of realistic tasks that each have a clear, ideally automatic success check — tests pass, the right record changed, the answer matches. Run each task several times because agents are non-deterministic, and track the success rate, cost, steps and time per task. Also check the path, not just the outcome: forbidden tool calls, unnecessary writes or ignored instructions are failures even if the result looks right. Grade open-ended outputs with a rubric, calibrated against human judgement. Run the suite on every change to prompts, tools or models.

Q6When do you use multiple agents instead of one?

When work splits naturally into parts that would overload a single context: researching many sources, reviewing many files, or independent subtasks that can run in parallel. A lead agent delegates to subagents, each with a fresh context and a narrow brief, and receives short summaries back. This keeps the lead's context clean and can speed things up. The costs are real: more tokens in total, coordination overhead, and harder debugging. For sequential tasks that share a lot of context, one well-equipped agent is usually better.

Senior-level questions

S1How would you architect an agent platform for a company with many teams?

I would separate three layers. A shared tool layer: MCP servers per system (tickets, CRM, data warehouse), each owned by the system's team, using OAuth or per-user scoped credentials, with central logging. A harness layer: one or two supported options — an SDK for teams embedding agents in products and a hosted or internal runner for background agents — with standard budgets, sandboxing, approval flows and transcript storage. A governance layer: an allow-list of servers, data-classification rules (which tools may combine private data with outbound actions), evaluation requirements before production, and cost dashboards per team. The trade-off is speed versus control: too much central process and teams build shadow agents; too little and you get leaked credentials and unbounded spend. I would make the safe path the easiest path rather than relying on reviews alone.

S2Single agent with many tools or multiple specialised agents — what are the trade-offs?

A single agent keeps all context in one place, which is ideal when steps depend on each other — a code change touching related files, for example. Its limits are context size and tool overload: past a few dozen tools, selection quality drops, which tool search can mitigate. Multiple agents help when work is parallel or reading-heavy: subagents explore in clean contexts and return condensed results, so the lead sees only what matters, and wall-clock time drops. Costs: more total tokens, information lost in handoffs, and harder debugging, since failures hide in briefs and summaries. My rule: start with one agent; add subagents for clearly separable, reading-heavy subtasks; avoid elaborate role-play hierarchies unless evaluation proves they help.

S3How do you make agent behaviour reproducible enough to debug and audit?

Full reproducibility is impossible with sampling, but you can get close enough. Store the complete transcript of each run: model id, effort settings, system prompt version, tool definitions version, every tool input and output, and timestamps. Make tools deterministic where you can and record external responses so a run can be replayed against recorded results. Version prompts and tools like code and tie each run to a commit. Run evaluation tasks multiple times and report success rates, not single outcomes. For audits, log the identity each tool call acted under and every approval decision. The trade-off is storage and privacy: transcripts contain sensitive data, so apply retention limits, access control and redaction just as you would for application logs.

Glossary

Agent
An LLM that chooses and calls tools in a loop until a goal is reached.
Harness
The code around the model that runs the loop, executes tools, manages context and enforces limits.
Tool
A function the model may request, described by a name, description and input schema.
MCP
Model Context Protocol: an open standard for exposing tools and data to AI applications.
Subagent
A separate agent run with its own fresh context, given a narrow task by a lead agent.
Compaction
Summarising earlier conversation so a long-running agent can continue within its context window.
Human-in-the-loop
A design where a person approves or edits certain agent actions before they take effect.
Sandbox
An isolated environment where agent code runs without secrets or access to production systems.
Indirect prompt injection
Malicious instructions hidden in content the agent reads, such as web pages, files or emails.

Have a product in mind?Let's build it.

Tell me about your idea, your product or your team. I usually reply within one working day.