Lab: M16: build an MCP server and client
You'll need: your venv and one install: pip install mcp. No API key, no internet.
Time: ~45 minutes • Work in your breakout pair.
Heads up: MCP runs locally, the client starts the server as a subprocess and they talk over "stdio" (standard input/output). You usually run the client, which launches the server for you. Nothing here can harm your computer, and there's nothing to pay.
This lab has two parts: - Part A: run a server + client and watch tools get discovered and called. - Part B: add your own tool to the server.
flowchart LR
C["MCP client<br/>(your app)"] -->|"initialize"| S["MCP server<br/>(your tools)"]
C -->|"list_tools (discover)"| S
C -->|"call_tool (run it)"| S
S -->|"result"| C
Part A: run a server and a client
Step 1: Install and set up
pip install mcp
mcp_server.py, mcp_client.py (from solution/) and mcp_server_starter.py
(from starters/) in a folder. Activate your venv.
You should now see: Successfully installed mcp-…, and those files in the folder.
Step 2: Run the client (it starts the server)
python mcp_client.py
You should now see: the client list the server's tools, calculate, lookup_ioc,
search_logs (with their descriptions), then call two of them:
calculate('245 * 18 / 100') -> 44.1 and lookup_ioc(...) -> MALICIOUS …. You just ran a real MCP
server and client, end to end, on your own machine.
Step 3: See the two-step handshake
Open mcp_client.py. Find session.list_tools() (discover) and session.call_tool(...)
(call), after session.initialize() (handshake).
You should now see / say: "connect → discover what tools exist → call one by name." That discover-then-call dance is exactly what an app like Claude Desktop does for you.
Step 4: See how a tool is published
Open mcp_server.py. Look at a @mcp.tool() function: its name, docstring (becomes the
description), and type hints (become the input schema) are all the client needs.
You should now see / say: "decorate a function → it's a discoverable tool." Same tool-schema idea as M9/M6, now shared over the MCP standard so any app can use it.
Part B: add your own tool
Step 5: Add a tool to the starter server
Open mcp_server_starter.py. Uncomment the reverse_text example (or write your own
@mcp.tool(), a word counter, a tool from your capstone). Save.
You should now see: a new @mcp.tool() function with a docstring and type hints, that's all
it takes to expose a tool.
Step 6: Point the client at your server and run it
Either copy mcp_server_starter.py over mcp_server.py, or edit mcp_client.py's
args=["mcp_server.py"] to your filename. Then:
python mcp_client.py
You should now see: your new tool appear in the discovered list, and (if you call it) return a result. You added a capability to an AI-tool server with one function.
Step 7: Connect it to a real app (optional, take-home-ready)
Read the notes' "Claude Desktop / IDEs" tip: adding your server to an MCP app's config (a small JSON
entry pointing at python mcp_server.py) lets that finished app discover and use your tools.
You should now see / say: "a tool I wrote can be used by Claude Desktop or my agent with zero extra code." That portability is the whole point of MCP.
Stuck? Working server + client are in
../solution/.
Your win
You built a real MCP server exposing your tools and a client that discovers and calls them, turning M9's "tools trapped in one script" into tools any MCP-aware app can use.
Post it to the chat wins board: "Built an MCP server, my lookup_ioc tool is now discoverable
and callable by any MCP app. Tools that travel "
Take-home (optional)
Wrap a tool from your capstone (or your M9 agent) as an MCP server, and add it to Claude Desktop's MCP config. Watch Claude itself discover and call your tool, your code, used by a finished AI app, no glue. That's shipping a tool the whole ecosystem can use.