Building MCP Servers

MCP Server Basics

5 min read

Architecture out of the way, here is the part you actually write.

Server Lifecycle

Because the protocol is stateless, a server's lifecycle is mostly process lifecycle — not protocol lifecycle. Knowing which stage you're stuck in is most of debugging, and the first two look identical from the outside.

Server lifecycle — where things break

Process start

Imports run, handlers register. An import error kills the server here and the host just shows nothing at all

Transport attach

Client connects over stdio or Streamable HTTP. Wrong path or wrong interpreter stops you here

Request served

Each request arrives self-contained, is validated against its own _meta, and is dispatched. Repeat, independently, forever

Shutdown

Release file handles and connections. A client may drop the transport at any point without warning

Notice what is not a stage: there is no capability-exchange step. Capabilities arrive with each request rather than being agreed once up front, which is why the connection carries no memory and your handler must be able to serve a cold request at any moment.

Creating a Server Instance

from mcp.server import MCPServer

# Create server with a unique name
mcp = MCPServer(name="my-awesome-server")

The server name appears in logs and helps identify your server when multiple MCP servers are configured.

MCPServer is the high-level API, and it is what the SDK's own quickstart uses. There is also a low-level Server underneath it, which you reach for when you need to shape raw protocol responses yourself — the error-handling lesson uses it for exactly that. Everywhere else in this course, MCPServer is the right default.

Registering Tools

Tools are the primary way AI interacts with your server. Each tool needs:

ComponentWhere it comes from
nameThe function name, unless you override it
descriptionThe docstring — this is the part the model reads
inputSchemaGenerated from your type hints and defaults
from typing import Literal


@mcp.tool()
def get_weather(city: str, units: Literal["celsius", "fahrenheit"] = "celsius") -> str:
    """Get current weather for a city, e.g. 'London' or 'Tokyo'."""
    return fetch_weather(city, units)

The Literal is doing real work: it becomes an enum in the generated schema, so the model is constrained to the two values your code actually handles rather than being trusted to guess them. Anything you can express in a type hint, you get to stop validating by hand.

Handling Tool Calls

The function is the handler. The arguments arrive as ordinary parameters, already validated against the schema your type hints produced:

@mcp.tool()
async def get_weather(city: str, units: Literal["celsius", "fahrenheit"] = "celsius") -> str:
    """Get current weather for a city, e.g. 'London' or 'Tokyo'."""
    weather = await fetch_weather(city, units)
    return f"Weather in {city}: {weather}"

There is no dispatch table and no if name == ... ladder, because the SDK routes the call to the function you decorated. Handlers can be def or async def; use async def the moment you touch the network, which for most real tools is immediately.

Tool Descriptions Matter

The description is not documentation. It is the only thing the model reads when deciding whether this tool is the right one for what the user just asked — and it is reading a list of candidates, cold, before it has any idea what the conversation will be about.

The same tool, described two ways

What gets ignored
name: search_kb
description: "Search KB"

Three things changed, and each one fixes a specific failure:

ChangeFailure it prevents
Spelled out knowledge_base instead of kbThe model guessing what the abbreviation covers
Named what's inside — policies, runbooks, post-mortemsThe tool getting skipped for questions it could have answered
Named what's not inside — no code, no ticketsThe tool getting called for questions it will fail at

That last line is the one people leave out. Telling the model where a tool stops is worth as much as telling it where the tool starts.

Next, we'll add resources to expose data that the AI can read. :::

Quiz

Module 2 Quiz: Building MCP Servers

Take Quiz
Was this lesson helpful?

Sign in to rate