MCPMCP Python SDK 2.2 · Python 3.10+
0%
1
Curious builder0 XP earned · 300 to level 2
0 daysFinish a lesson to begin
Badge collection0 of 6 unlocked
24 small wins to finish your pathNext lesson

MCPServer: your first tool

An MCP server is a Python file. Put a decorator on a function and the SDK builds the name, description and schema that lesson 1 wrote by hand.

Exampleshop.py
from mcp.server import MCPServer

mcp = MCPServer("Shop support")

ORDERS = {
    "A17": {"item": "blue mug", "status": "shipped", "total": 12.50},
    "B42": {"item": "desk lamp", "status": "processing", "total": 48.00},
}


@mcp.tool()
def lookup_order(order_id: str) -> str:
    """Look up an order by its id and say where it is."""
    order = ORDERS[order_id]
    return f"Order {order_id}: {order['item']}, {order['status']}."

MCPServer("Shop support") creates the server and names it. @mcp.tool() registers lookup_order as a tool, a function a model can call. The order data is a dictionary for now.

The SDK reads three things from the function: the name from the function name, the description from the docstring, and the arguments from the type hints.

What a client is told

Example
import asyncio

from mcp import Client
from shop import mcp


async def main():
    async with Client(mcp) as client:
        tools = await client.list_tools()
        for tool in tools.tools:
            print(tool.name)
            print(tool.description)
            print(tool.input_schema)


asyncio.run(main())

Ignore the client code for a moment; lesson 3 explains it. Look at what it printed: the same name, description and schema you wrote by hand in lesson 1, now built from order_id: str and the docstring. Change the function and the schema changes with it.

The title keys come from Pydantic, which builds the schema. The parts that matter to a model are properties, the types and required.

Two import paths

The SDK has two halves. The server is from mcp.server import MCPServer; the client is from mcp import Client. There is no from mcp import MCPServer.

Try it yourself
  • Give lookup_order a second parameter, include_total: bool = False, and list the tools again. What changed in required?
  • Delete the docstring and look at tool.description.
  • Add a second tool, count_orders() -> int, and list the tools.

You understood something today that you didn't yesterday.