Volley / Templates / MCP tool call
AI agents template
MCP tool call sequence diagram
An agent connects to a remote MCP server, lists its tools and calls one when the model asks for it. Each JSON-RPC request and response is on its arrow, along with the session header the server hands out.
Click any arrow in the editor to see its request and response as a table or the raw body. Change the hosts, ids and fields to match your integration, then share a link or embed it in your README.
Step by step
- The agent sends
initializeas a JSON-RPC 2.0 POST to/mcp, withprotocolVersion, itscapabilitiesandclientInfo. - The server answers with its own capabilities and an
Mcp-Session-Idheader. The agent sends that header andMCP-Protocol-Versionon every request after this. - The agent sends
notifications/initialized. It is a notification with noid, so the server answers 202 with no body. - The agent calls
tools/listand gets each tool's name, description andinputSchema. - The model picks a tool. The agent passes the user's question and the tool definitions to the model, which asks for
search_issueswith arguments. - The agent sends
tools/call, gets backcontentwith text, and gives it to the model for the final answer.
Easy to get wrong
- Send
Accept: application/json, text/event-stream. The server may answer any request as a stream of server-sent events instead of plain JSON. - A tool that fails returns a normal result with
isError: true, not a JSON-RPC error. Check both. - If the server answers 404 to a request with your
Mcp-Session-Id, the session is gone. Start again withinitialize.
The source
The whole diagram is this plain text. Copy it into your repo, or open it in the editor.
mcp-tool-call.volley
title: MCP tool call over Streamable HTTP
order: User, Agent, Model, MCP Server
note Agent, MCP Server: The agent is the MCP client. Every message to the server is a JSON-RPC 2.0 POST to one endpoint, here `/mcp`.
=: {fas-plug} Connecting
Agent -> MCP Server: POST /mcp (initialize)
in: Request
```http
POST /mcp HTTP/1.1
Content-Type: application/json
Accept: application/json, text/event-stream
Authorization: Bearer <redacted>
{
"jsonrpc": "2.0",
"id": 1,
"method": "initialize",
"params": {
"protocolVersion": "2025-06-18",
"capabilities": { "roots": { "listChanged": true } },
"clientInfo": { "name": "example-agent", "version": "1.4.0" }
}
}
```
MCP Server --> Agent: 200 OK
out: Response
```http
HTTP/1.1 200 OK
Content-Type: application/json
Mcp-Session-Id: 1868a90c-4f2b-4c6e-9d3a-7b5e2f8c1a04
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"protocolVersion": "2025-06-18",
"capabilities": { "tools": { "listChanged": true } },
"serverInfo": { "name": "issue-tracker", "version": "2.1.0" }
}
}
```
note: Send `Mcp-Session-Id` and `MCP-Protocol-Version: 2025-06-18` on every request after this one.
Agent -> MCP Server: POST /mcp (notifications/initialized)
in: Request
```http
POST /mcp HTTP/1.1
Content-Type: application/json
Accept: application/json, text/event-stream
Mcp-Session-Id: 1868a90c-4f2b-4c6e-9d3a-7b5e2f8c1a04
MCP-Protocol-Version: 2025-06-18
{ "jsonrpc": "2.0", "method": "notifications/initialized" }
```
MCP Server --> Agent: 202 Accepted
note: A notification has no `id`, so there is nothing to answer and the body is empty.
Agent -> MCP Server: POST /mcp (tools/list)
in: Request
```json
{ "jsonrpc": "2.0", "id": 2, "method": "tools/list" }
```
MCP Server --> Agent: 200 OK
out: Tools
```json
{
"jsonrpc": "2.0",
"id": 2,
"result": {
"tools": [
{
"name": "search_issues",
"description": "Search issues by text and state",
"inputSchema": {
"type": "object",
"properties": {
"query": { "type": "string" },
"state": { "type": "string", "enum": ["open", "closed"] }
},
"required": ["query"]
}
}
]
}
}
```
=: {fas-robot} The model picks a tool
User -> Agent: Which open issues mention the login timeout?
Agent -> Model: The question and the tool definitions
in: Tools offered
search_issues: Search issues by text and state
Model --> Agent: Call search_issues
out: Tool call
```json
{ "name": "search_issues", "arguments": { "query": "login timeout", "state": "open" } }
```
note: The model only asks for the call. The agent decides whether to run it, and may ask the user first.
Agent -> MCP Server: POST /mcp (tools/call)
in: Request
```json
{
"jsonrpc": "2.0",
"id": 3,
"method": "tools/call",
"params": { "name": "search_issues", "arguments": { "query": "login timeout", "state": "open" } }
}
```
MCP Server --> Agent: 200 OK
out: Result
```json
{
"jsonrpc": "2.0",
"id": 3,
"result": {
"content": [
{ "type": "text", "text": "2 open issues: #812 Login times out after 30 seconds on Safari. #837 Session timeout ignores Remember me." }
],
"isError": false
}
}
```
Agent -> Model: Tool result
Model --> Agent: Final answer
Agent --> User: Two open issues mention it: #812 and #837