Building a Minimal MCP Server and Client with FastMCP 2.0¶
The Model Context Protocol (MCP) standardizes how AI assistants discover and safely call
external tools. FastMCP 2.0 is a batteries-included Python framework that streamlines both
server and client development while remaining fully compatible with the protocol. This note walks through creating a single
greet tool that runs inside a FastMCP server and exercising it from a FastMCP client.
Prerequisites¶
- Python 3.10+
pipx(recommended) orpip- The
fastmcppackage pinned to the 2.x series for both the server and the client
Install FastMCP globally with pipx:
pipx install "fastmcp==2.*"
or within a virtual environment that you manage manually:
python -m venv .venv
source .venv/bin/activate
pip install "fastmcp==2.*"
FastMCP bundles an async runtime, a schema-first tool decorator, and a modern client that can speak to any MCP-compliant server.
Project Layout¶
fastmcp-demo/
├── client.py
└── server.py
The server.py script exposes the greet tool, while client.py starts the server as a subprocess and issues requests through
the MCP transport.
Implementing the FastMCP Server¶
Create server.py with the following contents:
"""Minimal FastMCP 2.0 server exposing a single greeting tool."""
import asyncio
from dataclasses import dataclass
from fastmcp import FastMCP, tool
mcp = FastMCP("fast-greeter")
@dataclass
class GreetArgs:
"""Typed input schema for the greet tool."""
name: str
@tool(mcp)
async def greet(args: GreetArgs) -> str:
"""Return a friendly greeting for the provided name."""
person = args.name.strip() or "there"
return f"Hello, {person}!"
def main() -> None:
asyncio.run(mcp.run_stdio())
if __name__ == "__main__":
main()
Key FastMCP 2.0 features illustrated above:
FastMCP("fast-greeter")registers the server with a human-readable identifier that will surface in client handshakes.@tool(mcp)wires the coroutine into the server, automatically deriving the MCP JSON schema from thedataclassargument.mcp.run_stdio()launches the event loop and keeps the server alive on the standard input/output transport expected by most assistants.
Implementing the FastMCP Client¶
Create client.py next:
"""Minimal FastMCP 2.0 client that calls the greet tool."""
import asyncio
from fastmcp import FastMCPClient
async def main() -> None:
async with FastMCPClient.from_subprocess(["python", "server.py"]) as session:
tools = await session.list_tools()
print("Tools:", tools)
response = await session.call_tool("greet", {"name": "Ada"})
print("Tool response:", response)
if __name__ == "__main__":
asyncio.run(main())
Highlights:
FastMCPClient.from_subprocess()launches the server as a subprocess and negotiates the MCP handshake automatically.session.list_tools()returns the tool metadata exposed by the server.session.call_tool("greet", ...)invokes the server tool and returns the structured response that the assistant will see.
Running the Demo¶
- Start the server in one terminal tab (or leave it to the client to spawn automatically):
python server.py
The process waits for MCP requests while streaming logs to the console.
- In a second terminal, execute the client:
python client.py
Expected output resembles the following:
Tools: [{'name': 'greet', 'description': 'Return a friendly greeting', ...}]
Tool response: {'type': 'text', 'text': 'Hello, Ada!'}
- Stop the server with
Ctrl+Cwhen finished.
Extending the Example¶
- Add more tools by defining additional
@tool(mcp)coroutines and associated dataclasses for their inputs. - Switch to transports such as WebSocket or TCP by replacing
mcp.run_stdio()with the correspondingfastmcphelper. - Use FastMCP's dependency-injection hooks (for example,
@tool(mcp, inject=[...])) to share database handles or API clients across tools. - Integrate with the
mcpdeveloper CLI (mcp run ...) or other MCP-compliant clients by pointing them atserver.py.
FastMCP 2.0 keeps the boilerplate minimal while providing type-safe tooling definitions, making it an excellent starting point for more sophisticated assistants.