Anatomy of an AI agent: from MCP to multi-agent
MCP is to AI tools what USB was to peripherals: before, every agent framework had its own way of defining and connecting tools; MCP standardises that.
MATTEO DI PAOLANTONIO
CTO Orbitant
This is the second of a three-part series on AI agent architecture. In the first post I explained how an agent works on the inside: the loop, the tools and the runtime. This installment walks through what lives around it: the protocol it connects to the world through, the reusable workflows that encapsulate work, the guardrails that keep it inside the frame you’ve given it, and the patterns for coordinating several agents when one isn’t enough.
This second post walks through the five building blocks that live around the model and the runtime: MCP as the connection protocol, skills as encapsulated capability, limits and guardrails, multi-agent systems, and where a use case fits on the spectrum between a simple LLM call and a fully autonomous agent. The third installment of the series will focus on operation: observability, evaluation, and continuous improvement.
MCP: the protocol that connects the agent to the world
MCP (Model Context Protocol) is an open standard created by Anthropic, announced in late 2024, that has been adopted over the course of 2025 and 2026. It defines a universal way for AI agents to connect to external tools and data sources.
Before MCP, every agent framework solved this its own way. Each connector was custom code.
The architecture
┌──────────┐ ┌────────────┐ ┌────────────────┐
│ AI Agent │←→│ MCP Client │←→│ MCP Server │
│ (Claude) │ │ (built-in) │ │ (provides │
└──────────┘ └────────────┘ │ tools/data) │
└────────────────┘
│
▼
┌────────────────┐
│ Database │
│ API │
│ Files │
└────────────────┘
- MCP server: a lightweight process that exposes capabilities (tools, resources, prompts) through a standardised JSON-RPC protocol. Anyone can write one.
- MCP client: embedded in the application. It discovers MCP servers and connects to them.
- Transport: communication travels over stdio (local) or over HTTP with Server-Sent Events (remote).
What an MCP server contains falls into three categories:
- Tools: functions the agent can call (for example,
query_database,create_jira_ticket). - Resources: data the agent can read (file contents, database schemas).
- Prompts: pre-built prompt templates for common tasks.
MCP vs native tools
From the LLM’s perspective there’s no difference between a native tool and a tool exposed via MCP. The model sees a tool called query_database with a schema, calls it and gets a result. It doesn’t know — and doesn’t care — whether the implementation is a hard-coded function or an MCP server.
The difference lives entirely in the plumbing: how tools are discovered, connected and executed.
| Native Tool | MCP (stdio, local) | MCP (HTTP, remote) | |
|---|---|---|---|
| Same process? | Yes | No | No |
| Same machine? | Yes | Yes | No |
| Same language? | Mandatory | Doesn’t matter | Doesn’t matter |
| Standardised interface? | No (framework-dependent) | Yes | Yes |
The real boundary MCP draws is process isolation, not necessarily network separation. It’s similar to the difference between calling a function directly and calling a sidecar container in Kubernetes: it may live on the same node, but it’s a separate process with its own lifecycle, permissions and failure domain.
The software-engineering analogy helps the concept stick:
- Native tools = helper functions inside your codebase (
utils/formatDate.ts). - MCP servers = REST APIs you call (
api.stripe.com/v1/charges).
Both provide capabilities. But the REST API is maintained by another team, is language-agnostic, deploys independently, is discoverable (via its schema), and lives in isolation. MCP is essentially OpenAPI/Swagger but designed specifically for AI agents.
The four properties that make it matter in production:
- Write once, use everywhere: a single MCP server works with Claude, Cursor, VS Code and any compatible agent.
- Ecosystem: as of early 2026 there are thousands of community-built MCP servers, from GitHub to Kubernetes to Figma.
- Security: MCP includes patterns for authentication, permission scoping and sandboxing.
- Composability: an agent can connect to several MCP servers at once, gaining access to multiple tool sets.
The protocol
MCP uses JSON-RPC (inspired by LSP, the protocol editors use for language servers). A conversation looks like this:
// 1. The client (agent) asks: what can you do?
→ { "method": "tools/list" }
// 2. The server responds with its tools' schemas
← { "result": { "tools": [
{
"name": "query_database",
"description": "Run a SQL query",
"inputSchema": {
"type": "object",
"properties": {
"sql": { "type": "string" }
}
}
}
]}}
// 3. The LLM decides to call the tool; the client forwards it to the server
→ { "method": "tools/call",
"params": { "name": "query_database",
"arguments": { "sql": "SELECT * FROM users LIMIT 5" }}}
// 4. The server executes and returns the result
← { "result": { "content": [{ "type": "text", "text": "..." }] }}
The agent’s runtime automatically converts the MCP tool definitions into the same format as native tools before sending them to the LLM. The model sees a flat list of callable functions.
Skills: reusable workflows
A skill is a reusable, higher-level capability an agent can invoke: a pre-packaged workflow, not a single tool call. It shows up when the same set of steps recurs often enough to be worth encapsulating into a single invocable unit.
Skills vs tools
| Aspect | Tool | Skill |
|---|---|---|
| Granularity | A single function call | Multi-step workflow |
| Example | read_file("main.py") | /commit (reviews changes, generates a message, commits) |
| Complexity | Atomic operation | Orchestrated sequence |
| State | Stateless | Can hold state across steps |
How skills work
When triggered, a skill:
- Loads specialised instructions (a prompt template with detailed steps).
- Defines a concrete workflow (for example, in
/commit: check the status, read the diff, draft the message, commit). - Can restrict which tools are available (
allowed-tools). - Invokes multiple tools in sequence.
- Returns a structured result.
Skills encapsulate the know-how of specific tasks so the agent doesn’t have to rebuild the workflow from scratch every time. They are, in essence, agent-level abstractions.
Follow our journey building AI agents from the inside
Execution limits and context management
The agentic loop runs “until it’s done or until it hits a limit”. Without some mechanism to stop it, an agent can fall into infinite loops, burn through its entire token budget, or get stuck waiting on a tool. To prevent this there are several types of limits that halt execution, plus strategies for managing the LLM’s context window, which is one of the loop’s most expensive resources.
Iteration limit
A cap on how many rounds the agent can run. It’s a safety mechanism to avoid infinite loops.
agent = Agent(
model="claude-sonnet-4-6",
tools=[...],
max_iterations=50 # after 50 observe-think-act cycles, stop
)
The reason: an agent can get stuck trying the same failing approach over and over, or disappear down a rabbit hole it never climbs out of. Without a limit, it runs forever, burning money without making progress.
Context window limit
The model can only “see” a finite amount of text at a time. As the agent iterates, the conversation grows:
Loop 1: [system prompt] + [user goal] + [tool call] + [tool result] → 5K tokens
Loop 5: ... + 5 more rounds of reasoning + tool calls + results → 30K tokens
Loop 20: ... enormous conversation history → 150K tokens
Loop 40: ... brushing against the ceiling → 200K tokens
When it runs out of context, the agent can’t keep thinking: it loses the start of the conversation, loses track of what it was doing, and quality degrades.
Other limits: cost, time and error
There are three other limits — shorter to explain but just as important in operation:
- Cost / token budget: every round costs money (API calls are billed per token). An agent that runs 50 rounds with large tool results can cost dollars per run.
- Time limit: a wall-clock timeout. If the agent has been running for 30 minutes, stop regardless of progress.
- Error limit: how many consecutive failures are tolerated before giving up. If the agent fails 5 tool calls in a row, something is fundamentally wrong.
Strategies for managing context
One of the hardest engineering problems when building agents is how to deal with the context-window limit. There are four common strategies:
- Conversation compression / summarisation: when the context grows, the old parts are summarised and the raw detail is discarded.
- Sliding window: keep only the last N messages. Simple but lossy: the agent forgets the initial context.
- Long-term memory / external storage: write the important findings to a file or database as you go, to re-read them later without needing to hold them in context.
- Smarter tool use: return less data. Instead of dumping a 10,000-line file into context, read only the 20 relevant lines.
The first one, compression, works like this:
Before (50K tokens):
[detailed tool result from loop 1]
[detailed tool result from loop 2]
[detailed tool result from loop 3]
…
...After compression (5K tokens):
[summary: "In loops 1-10 I explored the codebase and found
the bug in auth.py line 42. Key findings: …"]
[detailed tool result from loop 11] ← recent ones are kept intact
```
The trade-off
Tuning all these limits involves a balance: too low, and the agent gives up on problems it could have solved; too high, and it wastes resources on unsolvable problems or on loops.
Limits too low → the agent gives up before solving hard problems
Limits too high → the agent wastes resources on unsolvable problems or on loops
There’s no universally right answer, because it depends on the type of agent. A coding agent may need more than 100 iterations for a complex refactor, while a Q&A agent should finish in 3-5. Each is tuned to its use case.
Guardrails: human-in-the-loop and I/O validation
The limits in the previous block prevent the most obvious failure scenarios (infinite loops, runaway spending, saturated contexts), but they don’t cover everything that can go wrong when an agent with autonomy touches external systems. That’s what guardrails are for: controls placed around the agentic loop to stop the agent from doing something risky, irreversible, or unauthorised.
Human-in-the-loop checkpoints
The agent stops and asks a human before doing something risky:
- “I’m about to delete 200 files. Shall I proceed?”
- “I’m about to push to main. Do you confirm?”
- “This will send an email to 500 customers. Are you sure?”
It can be configured per action type:
read files → always allow (low risk)
edit files → ask the first time, then allow (medium risk)
run bash → always ask (high risk)
git push → always ask (irreversible)
Input/output validation
Checking what goes into and comes out of the agent, for safety:
- Input validation: is what the user is asking allowed? is the input well-formed? does the user have permission?
- Output validation: does the generated code have security vulnerabilities? is the agent trying to access files outside its sandbox? does the response contain hallucinated or leaked information?
These validations are usually implemented as smaller models or rule-based checks that run around the main agent:
User input → [input guardrail] → Agent → [output guardrail] → Action
↓ ↓
"blocked" "blocked"
Limits and guardrails at a glance
| Defence | What it prevents |
|---|---|
| Iteration limits | Infinite loops, runaway agents |
| Cost controls | Uncapped spending |
| Human-in-the-loop | Irreversible or high-risk actions |
| Input/output validation | Unsafe, unauthorised or incorrect behaviour |
All four work together as layers of defence. None on its own is enough.
Multi-agent systems
A single agent with too many tools and responsibilities can get confused or become unreliable. Multi-agent systems split the work across specialised agents, each with a narrower role, and coordinate them with one of the patterns that have crystallised in the ecosystem.
Orchestrator-Worker (hub-and-spoke)
A “manager” agent breaks down the task and delegates subtasks to specialised worker agents.
┌──────────────┐
│ Orchestrator │ ← Plans, delegates, synthesises
└──────────────┘
/ | \
/ | \
┌──────┐ ┌──────┐ ┌──────┐
│Coder │ │Tester│ │Writer│ ← Specialised workers
└──────┘ └──────┘ └──────┘
Handoff (sequential pipeline)
Each agent handles its part and passes control to the next. It’s the pattern used by OpenAI’s Agents SDK.
Agent A ──handoff──→ Agent B ──handoff──→ Agent C
(Triage) (Specialist) (QA)
Collaborative (peer-to-peer)
Agents communicate as peers, debating or reviewing each other’s work. It’s the pattern of CrewAI and AutoGen.
Agent A ←──messages──→ Agent B
▲ ▲
│ │
└──→ Agent C ←──┘
Supervisor pattern
A supervisor agent monitors the worker agents, steps in when they get stuck, and watches over quality.
Orchestration challenges
Coordinating several agents introduces problems that don’t exist with a single agent. It’s worth having them identified before adopting a multi-agent pattern:
- Sharing context: how do agents share information without blowing past context limits?
- Error propagation: if one agent fails, how does the system recover?
- Cost: every agent call costs tokens. Multi-agent multiplies the cost.
- Coordination overhead: more agents means more communication between them.
The spectrum: which AI agent architecture your use case needs
It’s not all “agent” or “not an agent”. There’s a spectrum between an isolated LLM call and a fully autonomous system, with several intermediate levels. This section walks through the two extremes and the gradation between them.
Simple LLM call (non-agentic)
A simple LLM call is the deterministic end of the spectrum: one request, one response, no loop and no tools. For example:
User: "Explain quicksort to me"
→ LLM processes the prompt
→ LLM returns the answer
Done. One round-trip.
- One request, one response.
- No tool use.
- No iteration.
- No interaction with the environment.
- Deterministic (more or less): the same input tends to give the same output.
Agentic workflow
An agentic workflow is the other extreme: the agent iterates, calls tools, and decides each step. For example:
User: "Find and fix the performance bug in my app"
→ The agent reads the codebase structure
→ The agent searches for performance-related code
→ The agent reads specific files
→ The agent reasons about potential bottlenecks
→ The agent runs a profiling tool
→ The agent identifies the problem
→ The agent edits the code
→ The agent runs the tests
→ The agent verifies the fix works
→ The agent reports back
Done. Many round-trips, autonomous decisions at every step.
- Multiple iterations (the loop).
- Dynamic tool use (the agent chooses which, when and with what arguments).
- Adaptive (it changes strategy based on the results).
- Non-deterministic (different runs can take different paths).
- It can fail, retry, self-correct.
The spectrum of agency
Between the two extremes there’s a gradation:
Simple LLM call
│
▼
LLM + structured output (function calling, no loop)
│
▼
LLM + a single tool call (RAG: retrieval-augmented generation)
│
▼
LLM + loop with tools (basic ReAct agent)
│
▼
LLM + loop with tools + planning + memory (advanced agent)
│
▼
Multi-agent system
│
▼
Fully autonomous agent (long-running, minimal human supervision)
Most production systems as of early 2026 live in the middle: agentic loops with tool use, but with human checkpoints for the important decisions.
With these five layers — connection protocol, skills, guardrails, multi-agent systems and position on the spectrum — you now have the complete framework of an AI agent architecture that can hold up in production. The third installment of the series will tackle how all of this is operated day to day: observability and tracing over what the agent does, evaluation when the output isn’t deterministic, and the continuous-improvement cycle that closes the loop.