Chapter 2: Your First MCP Server


This chapter gets a minimal MCP server running from scratch. By the end you will have a working server you can test locally, before connecting it to any AI client.


Installing the Python SDK

The official MCP Python SDK is the mcp package. Install it with:

pip install mcp

For development, also install the CLI tool that ships with it:

pip install "mcp[cli]"

This gives you the mcp command, which includes the dev subcommand for running an interactive inspector against your server.

Python version: 3.11 or newer is required. The SDK makes heavy use of modern type annotations and async features.


FastMCP: The High-Level API

The SDK provides two APIs:

This book uses FastMCP for all examples. It covers the vast majority of use cases and produces clean, readable code. The low-level API is only needed for edge cases not covered here.


Hello World Server

Here is the simplest possible MCP server:

from mcp.server.fastmcp import FastMCP

mcp = FastMCP("hello-server")

@mcp.tool()
def greet(name: str) -> str:
    """Return a greeting for the given name."""
    return f"Hello, {name}!"

if __name__ == "__main__":
    mcp.run()

Full example: code/01_hello_server.py

That is the complete server. Let us break down what each part does.


Breaking Down the Code

Creating the server

mcp = FastMCP("hello-server")

FastMCP takes a name string. This name is reported to clients during the initialization handshake. Choose something descriptive.

Defining a tool

@mcp.tool()
def greet(name: str) -> str:
    """Return a greeting for the given name."""
    return f"Hello, {name}!"

The @mcp.tool() decorator registers the function as an MCP tool. The SDK automatically:

Running the server

if __name__ == "__main__":
    mcp.run()

mcp.run() starts the server using stdio transport by default. This means the server reads JSON-RPC messages from stdin and writes responses to stdout. The host process manages the connection.


Testing With the MCP Inspector

The mcp dev command launches a web-based inspector that lets you test your server interactively without a full AI client.

mcp dev code/01_hello_server.py

This starts your server and opens an inspector UI in your browser. From there you can:

The inspector is your primary development and debugging tool. Use it whenever you make changes to your server.


Running the Server Directly

You can also run the server directly:

python code/01_hello_server.py

The server starts and waits for JSON-RPC input on stdin. It will not produce any output until a client connects and sends messages. This is normal — the server is waiting for the host to initiate the connection.


Async Support

FastMCP supports both sync and async functions:

import httpx

@mcp.tool()
async def fetch_url(url: str) -> str:
    """Fetch the content of a URL."""
    async with httpx.AsyncClient() as client:
        response = await client.get(url)
        return response.text

Use async when your tool does I/O (HTTP requests, database queries, file reads). The FastMCP event loop handles the async plumbing.


Server Metadata and Dependencies

You can pass additional metadata when creating the server:

mcp = FastMCP(
    "my-server",
    version="1.0.0",
    dependencies=["httpx", "sqlalchemy"],
)

The dependencies list is used by the mcp install command (covered in Chapter 7) to automatically set up the required packages in Claude Desktop’s environment.


Project Structure

For anything beyond a single tool, organize your server as a proper Python project:

my-mcp-server/
├── pyproject.toml
├── README.md
└── src/
    └── my_server/
        ├── __init__.py
        ├── server.py       # FastMCP instance + tool/resource/prompt definitions
        └── tools/
            ├── __init__.py
            ├── search.py
            └── files.py

The server.py entry point creates the FastMCP instance and imports the tool modules. Each module registers its tools against the shared instance.


Key Takeaways


← Chapter 1: MCP Architecture Table of Contents Chapter 3: Defining Tools →