MCPServer quickstart
MCPServer is the SDK's server class: decorate a function with it and the SDK builds the name, description and schema you wrote by hand in the previous lesson.
Last updated: 29 Sep, 2026 · MCP 2.2
In Tools without MCP you wrote a JSON Schema next to your function. Here an MCPServer reads your function's type hints and docstring and builds that schema for you.
Porting the math server from the video
The video writes mathserver.py. FastMCP("Math") names the server, and each function under @mcp.tool() becomes a tool: add and multiple, the video's name for multiply. The docstring is what the LLM reads to decide which tool to call. At the bottom, mcp.run(transport="stdio") starts the server; stdio transport explains that transport.
The video imports FastMCP from mcp.server.fastmcp, the class's name in mcp 1.x. In mcp 2.2 that import raises ModuleNotFoundError: the class is MCPServer, imported from mcp.server, and the decorator and run() work as before. The video's file with only those two lines changed:
from mcp.server import MCPServer
mcp=MCPServer("Math")
@mcp.tool()
def add(a:int,b:int)->int:
"""_summary_
Add to numbers
"""
return a+b
@mcp.tool()
def multiple(a:int,b:int)-> int:
"""Multiply two numbers"""
return a*b
#The transport="stdio" argument tells the server to:
#Use standard input/output (stdin and stdout) to receive and respond to tool function calls.
if __name__=="__main__":
mcp.run(transport="stdio")
Ask the server what it offers. Save this in a second file next to mathserver.py, such as list_tools.py, and run it. The client code is the next lesson's subject; for now, read what it prints.
import asyncio
from mcp import Client
from mathserver import mcp
async def main():
async with Client(mcp) as client:
tools = await client.list_tools()
for tool in tools.tools:
print(tool.name, repr(tool.description))
asyncio.run(main())add '_summary_\n Add to numbers\n ' multiple 'Multiply two numbers'
multiple is described by its docstring, Multiply two numbers. add carries the video's leftover docstring template, _summary_, and that text is what a model is told about add. In your own servers, write a docstring that says what the tool does.
The shop server is built the same way, with a tool the support team needs.
The MCPServer and tool API
from mcp.server import MCPServer
mcp = MCPServer("Shop support") # create and name the server
@mcp.tool() # register the function below as a tool
def lookup_order(order_id: str) -> str:
"""Docstring becomes the tool description."""
...The server object
Create the server and give it a name. The order data is a dictionary for now.
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},
}The lookup_order tool
@mcp.tool() registers lookup_order as a tool, a function a model can call. The SDK reads three things from it: the name from the function name, the description from the docstring, and the arguments from the type hints.
@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']}."Listing the tool a client is told about
The same listing, pointed at shop.py, prints what the SDK built from order_id: str and the docstring.
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())lookup_order
Look up an order by its id and say where it is.
{'type': 'object', 'properties': {'order_id': {'title': 'Order Id', 'type': 'string'}}, 'required': ['order_id'], 'title': 'lookup_orderArguments'}What the SDK built for you
- The name
lookup_ordercame from the function name. - The description came from the docstring, word for word.
- The schema lists
order_idas a required string, built from the type hint. Change the function and the schema changes with it. - The
titlekeys come from Pydantic, which builds the schema; a model readsproperties, the types andrequired.
By hand vs @mcp.tool()
| By hand | @mcp.tool() | |
|---|---|---|
| Name | You type it into the schema | The function name |
| Description | A string in the schema | The docstring |
| Arguments | You write the JSON Schema | Read from the type hints |
| Stays in step | Only if you remember | Rebuilt from the function |
When to reach for MCPServer
- Any time more than one application, or a host you do not control, will call your function.
- As the single place your tools, resources and prompts live, which the rest of the course fills in.
from mcp.server import MCPServer; the client is from mcp import Client. There is no from mcp import MCPServer, and reaching for it is the first mistake most people make.Related
- Previous: Tools without MCP
- Next: MCP client
- Reference: MCP Python SDK repository
- Give
lookup_ordera second parameter,include_total: bool = False, and list the tools again. What changed inrequired? - Delete the docstring and look at
tool.description. - Add a second tool,
count_orders() -> int, and list the tools.
Slow is fine. Stopping is the only problem.