Part 1's tools were a hardcoded Python dict baked into one process. The Model Context Protocol turns "what tools do you have and how do I call them" into a wire protocol. A SERVER advertises capabilities; a HOST discovers and calls them over JSON-RPC. Step through the exact frames, then connect two servers and watch their tools merge into one run-time palette.
server.handle(request) directly, but every frame below is a real JSON-RPC message printed verbatim, the exact bytes that would cross a socket. The real stdio subprocess path (two OS processes, no network) is shown at the bottom as an illustrative transcript, not a frozen run.
inputSchema Part 1 hand-wrote now arrives over the wire from tools/list, and feeds the same validator and controller unchanged. One host MULTIPLEXES many servers: the agent's action space is the union of support-server and catalog-server, assembled at run time, not at author time. A multi-hop task draws search_products from catalog and process_refund from support transparently, off one palette.
$ python mcp_server.py # process A: reads JSON-RPC from stdin host -> server (stdin): {"jsonrpc":"2.0","id":1,"method":"initialize",...} server -> host (stdout): {"jsonrpc":"2.0","id":1,"result":{"protocolVersion":...}} host -> server (stdin): {"jsonrpc":"2.0","id":2,"method":"tools/list"} server -> host (stdout): {"jsonrpc":"2.0","id":2,"result":{"tools":[...]}} (same frames as above, now crossing a pipe between two OS processes)
protocolVersion string and SDK shapes move fast; only the transport and version string would need edits for a live run. The real subprocess-over-stdio path, like the generate() LLM swap in earlier parts, is one flag away. The three primitives (tools, resources, prompts) are shown in full in the companion figures.