Handle
Connecting…
Back to Workshop
Module 03 ~15 min

Explore the Running System

Poke around the existing system with curl. Understand how MCP's tools/list and tools/call work in practice.

Goal

Before building anything, let's understand how the existing system works by talking directly to the MCP server and agent using curl. This will help you understand the exact protocol you'll implement in Modules 4 and 5.

Hands-On Exploration

Step 1

Health Checks

Both the MCP server and agent expose health endpoints. Check that they're running:

terminal
# MCP Server health
curl http://localhost:8000/health | python3 -m json.tool

# Agent health
curl http://localhost:8001/health | python3 -m json.tool

Both should return {"status": "ok"} with some metadata.

Step 2

Discover Available Tools

This is the MCP discovery call. The agent does this at startup to learn what tools are available:

terminal — MCP tools/list request
curl -X POST http://localhost:8000/message \
  -H "Content-Type: application/json" \
  -H "MCP-Protocol-Version: 2026-07-28" \
  -H "Mcp-Method: tools/list" \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{"_meta":{"io.modelcontextprotocol/protocolVersion":"2026-07-28","io.modelcontextprotocol/clientCapabilities":{}}}}' \
  | python3 -m json.tool

Study the response. You'll see a JSON-RPC response with a result.tools array. Each tool has:

  • name — unique identifier
  • description — what the tool does (the LLM reads this!)
  • inputSchema — JSON Schema defining expected arguments

Key insight: The agent never hardcodes tool definitions. It reads them from this endpoint. When you add a new tool to the MCP server, the agent discovers it automatically.

Step 3

Call a Tool Directly

Now call the weather tool directly, bypassing the agent. This is exactly what the agent does internally:

terminal — MCP tools/call request
curl -X POST http://localhost:8000/message \
  -H "Content-Type: application/json" \
  -H "MCP-Protocol-Version: 2026-07-28" \
  -H "Mcp-Method: tools/call" \
  -H "Mcp-Name: get_weather_forecast" \
  -d '{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"get_weather_forecast","arguments":{"location":"Bergen"},"_meta":{"io.modelcontextprotocol/protocolVersion":"2026-07-28","io.modelcontextprotocol/clientCapabilities":{}}}}' \
  | python3 -m json.tool

Study the response structure. The MCP response has:

  • content — array of content blocks (text, images, etc.)
  • structuredContent — machine-readable JSON data
  • isError — whether the tool execution failed
Step 4

Query Through the Agent

Now talk to the agent in natural language. The agent will decide which tools to use:

terminal
curl -X POST http://localhost:8001/query \
  -H "Content-Type: application/json" \
  -d '{"query": "What is the weather like in Oslo?"}' \
  | python3 -m json.tool

Behind the scenes, the agent: (1) sent your query to the LLM, (2) the LLM decided to call get_weather_forecast, (3) the agent called the MCP server, (4) the LLM synthesized the result into natural language.

Step 5

Break Things (On Purpose)

Understanding error cases is important. Try these and observe the responses:

terminal — error cases
# Call a tool that doesn't exist
curl -X POST http://localhost:8000/message \
  -H "Content-Type: application/json" \
  -H "MCP-Protocol-Version: 2026-07-28" \
  -H "Mcp-Method: tools/call" \
  -H "Mcp-Name: nonexistent_tool" \
  -d '{"jsonrpc":"2.0","id":3,"method":"tools/call","params":{"name":"nonexistent_tool","arguments":{},"_meta":{"io.modelcontextprotocol/protocolVersion":"2026-07-28","io.modelcontextprotocol/clientCapabilities":{}}}}'

# Call a method that doesn't exist
curl -X POST http://localhost:8000/message \
  -H "Content-Type: application/json" \
  -H "MCP-Protocol-Version: 2026-07-28" \
  -H "Mcp-Method: does/not/exist" \
  -d '{"jsonrpc":"2.0","id":4,"method":"does/not/exist","params":{"_meta":{"io.modelcontextprotocol/protocolVersion":"2026-07-28","io.modelcontextprotocol/clientCapabilities":{}}}}'

# Ask the agent something that doesn't need tools
curl -X POST http://localhost:8001/query \
  -H "Content-Type: application/json" \
  -d '{"query": "What is 2+2?"}'

Notice: The MCP server returns proper JSON-RPC error codes for invalid requests. The agent gracefully handles questions that don't need tools by just answering directly.

Step 6

Watch the Logs

Open a separate terminal and watch what happens under the hood:

terminal — in a separate tab
# Watch all logs
make logs

# Or just the MCP server
make logs-mcp

# Or just the agent
make logs-agent

Now send a query in another terminal and watch the logs. You'll see the JSON-RPC requests and responses flowing between agent and MCP server in real time.

What You've Learned

MCP uses two JSON-RPC methods: tools/list and tools/call
Tools are discovered dynamically — not hardcoded in the agent
Tool responses have content, structuredContent, and isError
The agent orchestrates: user → LLM → tool call → LLM → response

How was this module?

Your feedback helps us improve the workshop.

Submitting as anonymous

Your handle is sent with this feedback; leave it blank in the header and the submission stays anonymous. Please keep personal data out of the comment too — no name, e-mail address or employer, yours or anyone else's.

Setup Build the MCP Server