Build an MCP Server and Client
Use the official `mcp` Python SDK to build a minimal MCP server exposing two real tools, then write a client that connects over stdio, lists the tool catalog, and invokes both tools end to end.
PremiumProblem
The Model Context Protocol (MCP) standardizes how an AI application discovers and calls tools exposed by a separate process — instead of every agent framework inventing its own bespoke way to wire up tools, an MCP **server** exposes a catalog of tools over a well-defined protocol, and any MCP-compatible **client** (a chat app, an IDE, an agent) can connect, list what's available, and invoke it, regardless of what language or framework either side is written in. Most people first encounter MCP as a consumer — installing someone else's server into Claude Desktop or an IDE. This lab has you build both halves yourself using the official `mcp` Python SDK: a server exposing two genuinely simple tools (`get_time` and `echo`), and a client that connects to it over the **stdio transport** — the same mechanism most local MCP integrations use, where the client launches the server as a subprocess and the two communicate over its stdin/stdout. By the end you'll have watched a full MCP round trip happen — tool discovery, then tool invocation — with nothing hidden inside a larger application, which is the fastest way to actually understand what an MCP client is doing when it "connects to a server" in any real tool you use later.
Objectives
By the end of this lab you will be able to:
- Explain what problem MCP solves and how it differs from a
provider-specific tool-calling API. - Build a minimal MCP server with the
mcpPython SDK'sFastMCP
high-level API, registering tools with the@mcp.tool()decorator. - Explain the stdio transport model: the client launches the server as
a subprocess and communicates over its stdin/stdout. - Write an MCP client that connects over stdio, calls
list_tools(),
and callscall_tool()for each discovered tool. - Read and interpret the tool results an MCP client receives back from
a server.
Prerequisites
To complete this lab you'll need:
- Python 3.10+ installed.
-
pip install mcp(the official Model Context Protocol Python SDK). - A terminal and basic familiarity with Python decorators and
async/await. - No prior MCP experience required.
The two halves you're building
┌─────────────┐ stdio (stdin/stdout) ┌─────────────┐
│ client.py │ ───────────────────────► │ server.py │
│ │ ◄─────────────────────── │ │
│ launches │ │ exposes: │
│ server.py │ │ - get_time │
│ as a │ │ - echo │
│ subprocess │ │ │
└─────────────┘ └─────────────┘
The server doesn't know or care what kind of client connects to it — it
just exposes tools over the protocol. The client doesn't need to know
the server is written in Python, or how get_time is implemented — it
just needs to speak MCP. That decoupling is the entire point.
Why stdio, specifically
MCP supports multiple transports (stdio, SSE/HTTP, and others), but
stdio is the simplest and most common for local tools: the client
process spawns the server process directly and talks to it over
ordinary pipes, with no network port, no auth handshake, and no
separate process to keep running. This is exactly how tools like Claude
Desktop or an MCP-aware IDE typically run a locally installed MCP
server.
How to work through this lab
Build the server first and sanity-check it standalone, then build the
client against it. The real proof this all works is Step 4: the client
programmatically discovering and calling tools it never had hardcoded
knowledge of beyond their names.