Every earlier lesson in this course treated MCP from the outside: you called tools someone else's server exposed, or read about the protocol that makes that possible. This lesson flips the seat. You will build tasks_mcp, a complete MCP server for a small task tracker, from an empty file to something you can point the MCP Inspector at and drive by hand. The backing store is a plain in-memory dictionary, so nothing here depends on a database, an API key, or a network call — every piece of logic runs and asserts on its own, with the mcp package's decorators layered on only in the final assembled file.
The design decisions in a real MCP server are not really about Python syntax. They are about what a model needs from a tool call that a human caller would not: a name that cannot collide with another server's tool, a parameter contract strict enough that a malformed call fails before it touches your data, a response shaped for reading rather than for parsing, and an error that tells the model what to try next instead of just failing. Every one of those decisions traces back to a lesson you have already done — lesson 03's JSON Schema, lesson 08's response shaping, lesson 09's actionable errors — and this project is where they stop being separate ideas and become one file.
By the end you will have five verified, independently runnable code blocks: the input models, the handlers that use them, the pagination logic, the assembled server with the FastMCP decorators wired on, and a test harness that exercises all of it. The decorators require pip install mcp to import; everything else runs with nothing but pydantic.