The Request LoopIntermediate7 min06 / 10

Anatomy of a Tool Call

Trace one plain-English question all the way through the MCP loop — from reasoning to a JSON-RPC tools/call, real SQL on the server, and a friendly answer back.

You've seen the pieces of MCP — the host, the client, the server, the wire protocol. Now let's watch them work together on one real question, following a single request end to end. Here's the whole story in one sentence, straight from the playground's own docs:

> A user asks a question in plain English, the agent reasons about which MCP tool to call, the MCP server runs a real SQL query against a demo database, rows come back, and the agent turns them into a friendly answer.

Five honest steps. Let's take them one at a time.

#Steps 1 & 2 — The question, and the agent's reasoning

No SQL. No API. No knowledge of your schema. Just a question a human would actually type — and the agent doesn't guess randomly, it looks at the tools the MCP server has advertised and picks the one that fits:

the user's question
Who are our top 3 customers by total spend?
Note

The agent's reasoning

"The user wants ranked customer revenue. I'll call the query_sales tool exposed by the MCP server and let it run a grouped SQL query against the orders table."

#Step 3 — The agent issues a JSON-RPC tools/call

This is the moment MCP kicks in. The agent doesn't run anything itself — it sends a structured tools/call request over the protocol, naming the tool (query_sales) and the arguments it decided on. The arguments are structured data, not raw SQL:

client → server: tools/call request
{
  "jsonrpc": "2.0",
  "id": 7,
  "method": "tools/call",
  "params": {
    "name": "query_sales",
    "arguments": { "metric": "revenue", "groupBy": "customer", "limit": 3 }
  }
}
Quick check

In step 3, what does the agent actually send to the MCP server?

#Step 4 — The server runs real SQL

Now the MCP server takes over. It receives the query_sales call, translates the structured arguments into a real query, and runs it against the demo database. This SQL lives on the server, written by whoever built the connector — the agent never sees or writes it.

SQL the MCP server runs (server-side)
SELECT c.name AS customer,
       SUM(o.amount) AS total_spend
FROM orders o
JOIN customers c ON c.id = o.customer_id
GROUP BY c.name
ORDER BY total_spend DESC
LIMIT 3;
Think of it like

The agent never touches the database

This is the whole point of MCP. The agent speaks the protocol to the server; the server is the one holding the database connection. The server is the safety boundary — it decides which tools exist, what arguments they accept, and exactly which SQL runs. Even if the model asked for something dangerous, it can only ever send a tools/call for a tool the server chose to expose.

#Step 5 — Rows come back, the agent answers

The query returns rows, and the server sends them back over the same protocol as the tool's result:

server → client: the rows
[
  { "customer": "Northwind Traders", "total_spend": 48210 },
  { "customer": "Globex Corp", "total_spend": 39560 },
  { "customer": "Initech", "total_spend": 27840 }
]

Finally the agent reads those rows and writes something a human actually wants to read:

Tip

The agent's final answer

Your top 3 customers by total spend are Northwind Traders ($48,210), Globex Corp ($39,560), and Initech ($27,840). Northwind alone accounts for roughly a third more revenue than Initech.

#The same loop, every question

This isn't a one-off. Every question flows through the identical five-step loop — only the tool and arguments change:

  • "How did revenue trend over the last 5 months?"query_sales with {metric: "revenue", groupBy: "month", limit: 5}
  • "Which products are running low on stock?"inventory_lookup with {below: 25}
  • "Which customers haven't ordered in over 60 days?"query_sales with {metric: "recency", inactiveDays: 60}

Ask → reason → tools/call → server runs it → rows back → friendly answer. Learn the loop once, and every MCP interaction reads the same way.

Quick check

Why is the MCP server, not the agent, described as the "safety boundary"?

Key takeaways

  • Every MCP interaction follows one loop: ask in plain English → agent reasons → JSON-RPC tools/call → server runs it → rows back → friendly answer.
  • The agent sends structured arguments (name + arguments), never raw SQL — it speaks the protocol, not the database.
  • The MCP server owns the database connection and writes the actual SQL; it is the safety boundary that decides which tools and arguments are allowed.
  • Only the tool name and arguments change from question to question — trend, low-stock, and churn-risk questions all map to the exact same loop.
Try it yourself · The full round-trip
One question → reasoning → tool call → SQL → rows → answer.
YouThe question

“Who are our top 3 customers by total spend?”

The agent never touches the database — it speaks MCP to your server, which is the safety boundary.

step 1 / 6
Practice challenges
Test yourself · earn XP
0/4
Reorder the lines#1

Put the five steps of the MCP request loop in the order they happen for the question "Who are our top 3 customers by total spend?"

1
The MCP server runs real SQL against the database and gets rows back
2
The agent turns the rows into a friendly answer for the user
3
User asks in plain English: "Who are our top 3 customers by total spend?"
4
Agent sends a JSON-RPC tools/call for query_sales with { metric: "revenue", groupBy: "customer", limit: 3 }
5
Agent reasons: it should call the query_sales tool the server exposes
Predict the output#2

The agent decides to answer "Who are our top 3 customers by total spend?". What does it send over the MCP protocol?

predict-output
// The user asked a plain-English question.
// The agent has reasoned about which tool to use.
// What goes on the wire next?
Fill in the blank#3

Complete the JSON-RPC request the agent sends to run the top-customers query.

{
  "jsonrpc": "2.0",
  "id": 7,
  "method": "tools/",
  "params": {
    "name": "",
    "arguments": { "metric": "revenue", "groupBy": "customer", "limit":  }
  }
}
Fix the bug#4

A teammate claims this is how the agent should query the database for the top customers. What's the fundamental problem?

fix-bug
// Agent code (WRONG for MCP)
const db = openDatabase("sales.db");
const rows = db.run(
  `SELECT c.name AS customer, SUM(o.amount) AS total_spend
   FROM orders o JOIN customers c ON c.id = o.customer_id
   GROUP BY c.name ORDER BY total_spend DESC LIMIT 3;`
);
Your turn
Practice exercise

Trace a brand-new question through all five steps of the loop. Take the question "Which products are running low on stock?" and write out: (1) the plain-English question, (2) one sentence of agent reasoning, (3) the JSON-RPC tools/call the agent would send, (4) a note on where the SQL runs, and (5) a friendly one-line answer. Then, in one sentence, explain who the safety boundary is and why.

Try it yourself — a starting point to build on:

starter.py
# Write your solution here