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:
Who are our top 3 customers by total spend?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:
{
"jsonrpc": "2.0",
"id": 7,
"method": "tools/call",
"params": {
"name": "query_sales",
"arguments": { "metric": "revenue", "groupBy": "customer", "limit": 3 }
}
}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.
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;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:
[
{ "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:
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_saleswith{metric: "revenue", groupBy: "month", limit: 5} - "Which products are running low on stock?" →
inventory_lookupwith{below: 25} - "Which customers haven't ordered in over 60 days?" →
query_saleswith{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.
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.
“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.
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?"
The MCP server runs real SQL against the database and gets rows back
The agent turns the rows into a friendly answer for the user
User asks in plain English: "Who are our top 3 customers by total spend?"
Agent sends a JSON-RPC tools/call for query_sales with { metric: "revenue", groupBy: "customer", limit: 3 }Agent reasons: it should call the query_sales tool the server exposes
The agent decides to answer "Who are our top 3 customers by total spend?". What does it send over the MCP protocol?
// The user asked a plain-English question.
// The agent has reasoned about which tool to use.
// What goes on the wire next?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": } } }
A teammate claims this is how the agent should query the database for the top customers. What's the fundamental problem?
// 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;`
);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:
# Write your solution here