Our earlier article Why AI Agents Use JSON Schema, Function Calling, and MCP explained why these three layers exist. This piece watches the bytes that actually move in one real call — almost all of them JSON.
Users see natural language. Agents get work done by encoding intent as JSON parameters, encoding tool results as JSON messages, and encoding cross-process protocol as JSON-RPC. JSON is not decoration; it is the only mutually validatable language among the model, the host, and MCP Servers.
Three names, one JSON payload
Docs mix three terms. They sit on different layers, but the payload shape is almost the same:
| Name | Between | JSON's job |
|---|---|---|
| Function Calling | Model API ↔ host | tools definition + tool_calls.arguments |
| Tool Calling | Same (generic name) | The same messages / tools JSON |
| MCP | Host ↔ tool process | JSON-RPC methods + inputSchema |
One sentence: the model side uses JSON to pick a tool and fill parameters; the MCP side uses JSON to discover and execute tools. The host is the translator: MCP tools/list becomes the model tools array; model tool_calls become tools/call.
Why it has to be JSON
An Agent must satisfy three parties at once:
- The model: training data is full of JSON; emitting a valid object is far easier than protobuf bytes
- The program: mature parse, Schema validation, Diff, and JSONPath tooling
- The protocol: OpenAPI, JSON-RPC, and MCP inputSchema already share one type description
Plain language cannot fail-fast: brackets, quotes, and mixed languages break regex parsers. YAML is indent-fragile. Binary protocols are hostile to both humans and LLMs. JSON becomes the default wire format that is auditable, validatable, and versionable — which is why this site's tools all revolve around JSON: you are debugging that wire.
Hop 1: Schema in the tool definition
The flow starts by telling the model which tools exist. Whether you use OpenAI-style tools or MCP tools/list, the core is a JSON Schema (or a subset):
{
"name": "get_weather",
"description": "Look up current weather for a city, read-only",
"parameters": {
"type": "object",
"properties": {
"city": { "type": "string", "description": "City name, e.g. Shanghai" },
"unit": { "type": "string", "enum": ["celsius", "fahrenheit"] }
},
"required": ["city"]
}
}
In MCP the same constraint lives in inputSchema. Schema feeds two paths: the validator rejects illegal parameters; model context uses description to decide when to call. The more the field text reads like a product spec, the fewer mistaken calls.
Hop 2: Function Calling / Tool Calling
After the host sends the tool list with messages, the model does not run code. It returns a structured call. Typical shape (field names vary by vendor):
{
"role": "assistant",
"tool_calls": [
{
"id": "call_01",
"type": "function",
"function": {
"name": "get_weather",
"arguments": "{\"city\":\"Shanghai\",\"unit\":\"celsius\"}"
}
}
]
}
Note that arguments is often a stringified JSON object: JSON.parse first, validate against Schema, then execute. Results flow back as a tool-role message:
{
"role": "tool",
"tool_call_id": "call_01",
"content": "{\"city\":\"Shanghai\",\"temp_c\":31,\"condition\":\"sunny\"}"
}
This hop is how the model reaches out. With parallel tools, the array holds multiple tool_calls; the host may run them concurrently and match results by id.
Hop 3: MCP JSON-RPC
If the tool is not in the host process but an MCP Server (filesystem, GitHub, internal orders), host and Server speak JSON-RPC 2.0. A read-only query is roughly three steps:
{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"host","version":"1.0"}}}
{"jsonrpc":"2.0","id":2,"method":"tools/list","params":{}}
{"jsonrpc":"2.0","id":3,"method":"tools/call","params":{"name":"get_weather","arguments":{"city":"Shanghai"}}}
A successful Server response is JSON too: content often has type: "text" whose text is another JSON string. That is JSON wrapping JSON — outer envelope vs inner business payload. When debugging MCP, split those layers, then Schema-validate the inner one.
Transport may be stdio or Streamable HTTP; the payload is still JSON lines or a JSON body. For 2026 transport and whether Server code must change, see the MCP 2026 migration guide.
End-to-end trace of one call
The user asks: “How warm is it in Shanghai today?” End to end:
- Host → MCP Server:
tools/listreturns tools withinputSchema(JSON) - Host → model API: mapped to
tools[].parameters(still JSON Schema) - Model → Host:
tool_callswitharguments{"city":"Shanghai"} - Host validates: against Schema; missing fields or wrong types refuse execution and feed error JSON back to the model
- Host → MCP:
tools/callwithparams.argumentsas an object (not a string) - MCP → Host: weather result JSON
- Host → model:
role: toolcontent string - Model → user: natural language; if a downstream system wants structure only, constrain the final JSON with an output Schema
User natural language
│
▼
Host orchestration ──JSON Schema──► LLM Tool Calling
│ │
│ ▼
│ arguments JSON
│ │
▼ ▼
MCP JSON-RPC ◄──────────── validate, then execute
│
▼
Result JSON ──► tool message ──► model final reply
A small script may skip MCP and call local functions in the host. Enterprise agents almost always stack Tool Calling + MCP. For ecosystem picks see 2026 MCP Server rankings.
How validation failures flow back
JSON can act as the Agent's type system because failures can be structured too. Use at least two gates:
| Gate | What you validate | How failure flows back |
|---|---|---|
| Before execute | Model arguments | Do not call the real tool; write Schema errors as a tool result or system hint so the model refills |
| Before write-back | MCP / function return | Truncate, redact, or mark errors; do not dump raw stacks into the next turn |
In development, keep Schema plus two or three valid/invalid payloads in git and validate them locally in JSON Toolbox — the same idea as REST contract tests, except the consumer is a model.
FAQ
Are Tool Calling and Function Calling the same thing?
For developers they are almost the same data flow: the host sends tool Schema to the model, the model returns a call with JSON arguments, the host executes and writes JSON results back. Function Calling was OpenAI's early name; Tool Calling / Tools API is the later generic name.
Why are MCP messages JSON too?
MCP is JSON-RPC 2.0: initialize, tools/list, and tools/call requests and responses are JSON objects. Each tool's inputSchema is JSON Schema, so a Host can map MCP tools one-to-one onto the model API tools array.
Are model arguments a string or an object?
Most Chat Completions-style APIs put arguments in a JSON string; the host must JSON.parse then validate against Schema. Some newer APIs return an object. Either way, validate with the same Schema before execution.
Why not YAML or protobuf instead of JSON?
Tool implementations can use any format internally, but model context and cross-vendor protocols treat JSON as the de facto standard. YAML is indent-fragile; protobuf is unfriendly to models. Typical pattern: JSON at the boundary, convert inside.
Which layer should validate Schema?
At least two gates: after tool_calls and before executing the real tool; and after the MCP Server returns, before writing back to the model. The first blocks hallucinated parameters; the second blocks dirty data in the next turn.
How do I validate this JSON locally?
Save inputSchema, sample arguments, and sample tool results as JSON files. Use JSON Toolbox in the browser to check Schema against data. Nothing is uploaded.
Summary
AI Agents cannot live without JSON because every hop must be machine-readable: Schema describes tools, Tool Calling carries the call, MCP ships it out of process as JSON-RPC. Natural language only appears at the user-facing ends; the middle is validatable objects.
Start with one real tool: write the Schema → print and parse the model's arguments string → if the tool lives on an MCP Server, capture one tools/call. When those three JSON documents line up, the Agent is actually working. For the evolution story see the technical timeline. Validate Schema samples locally in JSON Toolbox before you ship.