Picture by Writer# Introduction
The Mannequin Context Protocol (MCP) has modified how giant language fashions (LLMs) work together with exterior instruments, knowledge sources, and companies. Nonetheless, constructing MCP servers from scratch historically required navigating complicated boilerplate code and detailed protocol specs. FastMCP eliminates this roadblock, offering a decorator-based, Pythonic framework that allows builders to construct production-ready MCP servers and purchasers with minimal code.
On this tutorial, you will learn to construct MCP servers and purchasers utilizing FastMCP, which is complete and full with error dealing with, making it ultimate for each freshmen and intermediate builders.
// Stipulations
Earlier than beginning this tutorial, be sure to have:
- Python 3.10 or larger (3.11+ really useful for higher async efficiency)
- pip or uv (uv is really useful for FastMCP deployment and is required for the CLI instruments)
- A code editor (I’m utilizing VS Code, however you need to use any editor of your alternative)
- Terminal/Command Line familiarity for working Python scripts
Additionally it is useful to have good Python programming information (features, decorators, kind hints), some understanding of async/await syntax (non-obligatory, however useful for superior examples), familiarity with JSON and REST API ideas, and primary command-line terminal utilization.
Earlier than FastMCP, constructing MCP servers required you to have a deep understanding of the MCP JSON-RPC specification, intensive boilerplate code for protocol dealing with, guide connection and transport administration, and complicated error dealing with and validation logic.
FastMCP addresses these points with intuitive decorators and a easy, Pythonic API, enabling you to concentrate on enterprise logic moderately than protocol implementation.
# What’s the Mannequin Context Protocol?
The Mannequin Context Protocol (MCP) is an open normal created by Anthropic. It supplies a common interface for AI purposes to securely join with exterior instruments, knowledge sources, and companies. MCP standardizes how LLMs work together with exterior methods, very like how net APIs standardized net service communication.
// Key Traits of MCP
- Standardized Communication: Makes use of JSON-RPC 2.0 for dependable, structured messaging
- Bidirectional: Helps each requests from purchasers to servers and responses again
- Safety: Constructed-in help for authentication and authorization patterns
- Versatile Transport: Works with any transport mechanism (stdio, HTTP, WebSocket, SSE)
// MCP Structure: Servers and Shoppers
MCP follows a transparent client-server structure:

Picture by Writer
- MCP Server: Exposes capabilities (instruments, assets, prompts) that exterior purposes can use. Consider it as a backend API particularly designed for LLM integration.
- MCP Consumer: Embedded in AI purposes (like Claude Desktop, Cursor IDE, or customized purposes) that connect with MCP servers to entry their assets.
// Core Parts of MCP
MCP servers expose three main varieties of capabilities:
- Instruments: Executable features that LLMs can name to carry out actions. Instruments can question databases, name APIs, carry out calculations, or set off workflows.
- Sources: Learn-only knowledge that MCP purchasers can fetch and use as context. Sources could be file contents, configuration knowledge, or dynamically generated content material.
- Prompts: Reusable message templates that information LLM conduct. Prompts present constant directions for multi-step operations or specialised reasoning.
# What’s FastMCP?
FastMCP is a high-level Python framework that simplifies the method of constructing each MCP servers and purchasers. Created to cut back improvement complications, FastMCP possesses the next traits:
- Decorator-Primarily based API: Python decorators (
@mcp.device,@mcp.useful resource,@mcp.immediate) remove boilerplate - Sort Security: Full kind hints and validation utilizing Python’s kind system
- Async/Await Assist: Fashionable async Python for high-performance operations
- A number of Transports: Assist for stdio, HTTP, WebSocket, and SSE
- Constructed-in Testing: Straightforward client-server testing with out subprocess complexity
- Manufacturing Prepared: Options like error dealing with, logging, and configuration for manufacturing deployments
// FastMCP Philosophy
FastMCP depends on three core rules:
- Excessive-level abstractions: Much less code and sooner improvement cycles
- Easy: Minimal boilerplate permits concentrate on performance over protocol particulars
- Pythonic: Pure Python idioms make it acquainted to Python builders
# Set up
Begin by putting in FastMCP and the required dependencies. I like to recommend utilizing uv.
Should you don’t have uv, set up it with pip:
Or set up FastMCP straight with pip:
Confirm that FastMCP is put in:
python -c "from fastmcp import FastMCP; print('FastMCP put in efficiently')"
# Constructing Your First MCP Server
We’ll create a sensible MCP server that demonstrates instruments, assets, and prompts. We’ll construct a Calculator Server that gives mathematical operations, configuration assets, and instruction prompts.
// Step 1: Setting Up the Venture Construction
We first have to create a challenge listing and initialize your setting. Create a folder on your challenge:
Then navigate into your challenge folder:
Initialize your challenge with the required information:
// Step 2: Creating the MCP Server
Our Calculator MCP Server is a straightforward MCP server demonstrating instruments, assets, and prompts. Inside your challenge folder, create a file named calculator_server.py and add the next code.
import logging
import sys
from typing import Dict
from fastmcp import FastMCP
# Configure logging to stderr (important for MCP protocol integrity)
logging.basicConfig(
stage=logging.DEBUG,
format="%(asctime)s - %(title)s - %(levelname)s - %(message)s",
stream=sys.stderr
)
logger = logging.getLogger(__name__)
# Create the FastMCP server occasion
mcp = FastMCP(title="CalculatorServer")
The server imports FastMCP and configures logging to stderr. The MCP protocol requires that each one output, besides protocol messages, be directed to stderr to keep away from corrupting communication. The FastMCP(title="CalculatorServer") name creates the server occasion. This handles all protocol administration routinely.
Now, let’s create our instruments.
@mcp.device
def add(a: float, b: float) -> float:
"""
Add two numbers collectively.
Args:
a: First quantity
b: Second quantity
Returns:
Sum of a and b
"""
strive:
consequence = a + b
logger.data(f"Addition carried out: {a} + {b} = {consequence}")
return consequence
besides TypeError as e:
logger.error(f"Sort error in add: {e}")
increase ValueError(f"Invalid enter varieties: {e}")
@mcp.device
def subtract(a: float, b: float) -> float:
"""
Subtract b from a.
Args:
a: First quantity (minuend)
b: Second quantity (subtrahend)
Returns:
Distinction of a and b
"""
strive:
consequence = a - b
logger.data(f"Subtraction carried out: {a} - {b} = {consequence}")
return consequence
besides TypeError as e:
logger.error(f"Sort error in subtract: {e}")
increase ValueError(f"Invalid enter varieties: {e}")
We have now outlined features for addition and subtraction. Each are wrapped in a try-catch block to lift worth errors, log the knowledge, and return the consequence.
@mcp.device
def multiply(a: float, b: float) -> float:
"""
Multiply two numbers.
Args:
a: First quantity
b: Second quantity
Returns:
Product of a and b
"""
strive:
consequence = a * b
logger.data(f"Multiplication carried out: {a} * {b} = {consequence}")
return consequence
besides TypeError as e:
logger.error(f"Sort error in multiply: {e}")
increase ValueError(f"Invalid enter varieties: {e}")
@mcp.device
def divide(a: float, b: float) -> float:
"""
Divide a by b.
Args:
a: Dividend (numerator)
b: Divisor (denominator)
Returns:
Quotient of a divided by b
Raises:
ValueError: If trying to divide by zero
"""
strive:
if b == 0:
logger.warning(f"Division by zero tried: {a} / {b}")
increase ValueError("Can not divide by zero")
consequence = a / b
logger.data(f"Division carried out: {a} / {b} = {consequence}")
return consequence
besides (TypeError, ZeroDivisionError) as e:
logger.error(f"Error in divide: {e}")
increase ValueError(f"Division error: {e}")
4 adorned features (@mcp.device) expose mathematical operations. Every device consists of:
- Sort hints for parameters and return values
- Complete docstrings (MCP makes use of these as device descriptions)
- Error dealing with with try-except blocks
- Logging for debugging and monitoring
- Enter validation
Let’s transfer on to constructing assets.
@mcp.useful resource("config://calculator/settings")
def get_settings() -> Dict:
"""
Supplies calculator configuration and out there operations.
Returns:
Dictionary containing calculator settings and metadata
"""
logger.debug("Fetching calculator settings")
return {
"model": "1.0.0",
"operations": ["add", "subtract", "multiply", "divide"],
"precision": "IEEE 754 double precision",
"max_value": 1.7976931348623157e+308,
"min_value": -1.7976931348623157e+308,
"supports_negative": True,
"supports_decimals": True
}
@mcp.useful resource("docs://calculator/information")
def get_guide() -> str:
"""
Supplies a person information for the calculator server.
Returns:
String containing utilization information and examples
"""
logger.debug("Retrieving calculator information")
information = """
1. **add(a, b)**: Returns a + b
Instance: add(5, 3) = 8
2. **subtract(a, b)**: Returns a - b
Instance: subtract(10, 4) = 6
3. **multiply(a, b)**: Returns a * b
Instance: multiply(7, 6) = 42
4. **divide(a, b)**: Returns a / b
Instance: divide(20, 4) = 5.0
## Error Dealing with
- Division by zero will increase a ValueError
- Non-numeric inputs will increase a ValueError
- All inputs ought to be legitimate numbers (int or float)
## Precision
The calculator makes use of IEEE 754 double precision floating-point arithmetic.
Outcomes could include minor rounding errors for some operations.
"""
return information
Two adorned features (@mcp.useful resource) present static and dynamic knowledge:
config://calculator/settings: Returns metadata concerning the calculatordocs://calculator/information: Returns a formatted person information- URI format distinguishes useful resource varieties (conference:
kind://class/useful resource)
Let’s construct our prompts.
@mcp.immediate
def calculate_expression(expression: str) -> str:
"""
Supplies directions for evaluating a mathematical expression.
Args:
expression: A mathematical expression to judge
Returns:
Formatted immediate instructing the LLM the best way to consider the expression
"""
logger.debug(f"Producing calculation immediate for: {expression}")
immediate = f"""
Please consider the next mathematical expression step-by-step:
Expression: {expression}
Directions:
1. Break down the expression into particular person operations
2. Use the suitable calculator device for every operation
3. Observe order of operations (parentheses, multiplication/division, addition/subtraction)
4. Present all intermediate steps
5. Present the ultimate consequence
Accessible instruments: add, subtract, multiply, divide
"""
return immediate.strip()
Lastly, add the server startup script.
if __name__ == "__main__":
logger.data("Beginning Calculator MCP Server...")
strive:
# Run the server with stdio transport (default for Claude Desktop)
mcp.run(transport="stdio")
besides KeyboardInterrupt:
logger.data("Server interrupted by person")
sys.exit(0)
besides Exception as e:
logger.error(f"Deadly error: {e}", exc_info=True)
sys.exit(1)
The @mcp.immediate decorator creates instruction templates that information LLM conduct for complicated duties.
Error dealing with greatest practices included listed below are:
- Particular exception catching (TypeError, ZeroDivisionError)
- Significant error messages for customers
- Detailed logging for debugging
- Swish error propagation
// Step 3: Constructing the MCP Consumer
On this step, we are going to display the best way to work together with the Calculator MCP Server that we created above. Create a brand new file named calculator_client.py.
import asyncio
import logging
import sys
from typing import Any
from fastmcp import Consumer, FastMCP
logging.basicConfig(
stage=logging.INFO,
format="%(asctime)s - %(title)s - %(levelname)s - %(message)s",
stream=sys.stderr
)
logger = logging.getLogger(__name__)
async def primary():
"""
Fundamental shopper operate demonstrating server interplay.
"""
from calculator_server import mcp as server
logger.data("Initializing Calculator Consumer...")
strive:
async with Consumer(server) as shopper:
logger.data("✓ Linked to Calculator Server")
# DISCOVER CAPABILITIEs
print("n" + "="*60)
print("1. DISCOVERING SERVER CAPABILITIES")
print("="*60)
# Record out there instruments
instruments = await shopper.list_tools()
print(f"nAvailable Instruments ({len(instruments)}):")
for device in instruments:
print(f" • {device.title}: {device.description}")
# Record out there assets
assets = await shopper.list_resources()
print(f"nAvailable Sources ({len(assets)}):")
for useful resource in assets:
print(f" • {useful resource.uri}: {useful resource.title or useful resource.uri}")
# Record out there prompts
prompts = await shopper.list_prompts()
print(f"nAvailable Prompts ({len(prompts)}):")
for immediate in prompts:
print(f" • {immediate.title}: {immediate.description}")
# CALL TOOLS
print("n" + "="*60)
print("2. CALLING TOOLS")
print("="*60)
# Easy addition
print("nTest 1: Including 15 + 27")
consequence = await shopper.call_tool("add", {"a": 15, "b": 27})
result_value = extract_tool_result(consequence)
print(f" Consequence: 15 + 27 = {result_value}")
# Division with error dealing with
print("nTest 2: Dividing 100 / 5")
consequence = await shopper.call_tool("divide", {"a": 100, "b": 5})
result_value = extract_tool_result(consequence)
print(f" Consequence: 100 / 5 = {result_value}")
# Error case: division by zero
print("nTest 3: Division by Zero (Error Dealing with)")
strive:
consequence = await shopper.call_tool("divide", {"a": 10, "b": 0})
print(f" Surprising success: {consequence}")
besides Exception as e:
print(f" ✓ Error caught accurately: {str(e)}")
# READ RESOURCES
print("n" + "="*60)
print("3. READING RESOURCES")
print("="*60)
# Learn settings useful resource
print("nFetching Calculator Settings...")
settings_resource = await shopper.read_resource("config://calculator/settings")
print(f" Model: {settings_resource[0].textual content}")
# Learn information useful resource
print("nFetching Calculator Information...")
guide_resource = await shopper.read_resource("docs://calculator/information")
# Print first 200 characters of information
guide_text = guide_resource[0].textual content[:200] + "..."
print(f" {guide_text}")
# CHAINING OPERATIONS
print("n" + "="*60)
print("4. CHAINING MULTIPLE OPERATIONS")
print("="*60)
# Calculate: (10 + 5) * 3 - 7
print("nCalculating: (10 + 5) * 3 - 7")
# Step 1: Add
print(" Step 1: Add 10 + 5")
add_result = await shopper.call_tool("add", {"a": 10, "b": 5})
step1 = extract_tool_result(add_result)
print(f" Consequence: {step1}")
# Step 2: Multiply
print(" Step 2: Multiply 15 * 3")
mult_result = await shopper.call_tool("multiply", {"a": step1, "b": 3})
step2 = extract_tool_result(mult_result)
print(f" Consequence: {step2}")
# Step 3: Subtract
print(" Step 3: Subtract 45 - 7")
final_result = await shopper.call_tool("subtract", {"a": step2, "b": 7})
last = extract_tool_result(final_result)
print(f" Ultimate Consequence: {last}")
# GET PROMPT TEMPLATE
print("n" + "="*60)
print("5. USING PROMPT TEMPLATES")
print("="*60)
expression = "25 * 4 + 10 / 2"
print(f"nPrompt Template for: {expression}")
prompt_response = await shopper.get_prompt(
"calculate_expression",
{"expression": expression}
)
print(f" Template:n{prompt_response.messages[0].content material.textual content}")
logger.data("✓ Consumer operations accomplished efficiently")
besides Exception as e:
logger.error(f"Consumer error: {e}", exc_info=True)
sys.exit(1)
From the code above, the shopper makes use of async with Consumer(server) for protected connection administration. This routinely handles connection setup and cleanup.
We additionally want a helper operate to deal with the outcomes.
def extract_tool_result(response: Any) -> Any:
"""
Extract the precise consequence worth from a device response.
MCP wraps leads to content material objects, this helper unwraps them.
"""
strive:
if hasattr(response, 'content material') and response.content material:
content material = response.content material[0]
# Favor specific textual content content material when out there (TextContent)
if hasattr(content material, 'textual content') and content material.textual content isn't None:
# If the textual content is JSON, attempt to parse and extract a `consequence` area
import json as _json
text_val = content material.textual content
strive:
parsed_text = _json.masses(text_val)
# If JSON accommodates a consequence area, return it
if isinstance(parsed_text, dict) and 'consequence' in parsed_text:
return parsed_text.get('consequence')
return parsed_text
besides _json.JSONDecodeError:
# Attempt to convert plain textual content to quantity
strive:
if '.' in text_val:
return float(text_val)
return int(text_val)
besides Exception:
return text_val
# Attempt to extract JSON consequence through mannequin `.json()` or dict-like `.json`
if hasattr(content material, 'json'):
strive:
if callable(content material.json):
json_str = content material.json()
import json as _json
strive:
parsed = _json.masses(json_str)
besides _json.JSONDecodeError:
return json_str
else:
parsed = content material.json
# If parsed is a dict, strive frequent shapes
if isinstance(parsed, dict):
# If nested consequence exists
if 'consequence' in parsed:
res = parsed.get('consequence')
elif 'textual content' in parsed:
res = parsed.get('textual content')
else:
res = parsed
# If res is str that appears like a quantity, convert
if isinstance(res, str):
strive:
if '.' in res:
return float(res)
return int(res)
besides Exception:
return res
return res
return parsed
besides Exception:
cross
return response
besides Exception as e:
logger.warning(f"Couldn't extract consequence: {e}")
return response
if __name__ == "__main__":
logger.data("Calculator Consumer Beginning...")
asyncio.run(primary())
Trying on the above code, earlier than utilizing instruments, the shopper lists out there capabilities. The await shopper.list_tools() will get all device metadata, together with descriptions. The await shopper.list_resources() discovers out there assets. Lastly, the await shopper.list_prompts() will discover out there immediate templates.
The await shopper.call_tool() technique does the next:
- Takes the device title and parameters as a dictionary
- Returns a wrapped response object containing the consequence
- Integrates with error dealing with for device failures
On the consequence extraction, the extract_tool_result() helper operate unwraps MCP’s response format to get the precise worth, dealing with each JSON and textual content responses.
The chaining operations you see above display the best way to use output from one device as enter to a different, enabling complicated calculations throughout a number of device calls.
Lastly, the error dealing with catches device errors (like division by zero) and logs them gracefully with out crashing.
// Step 4: Working the Server and Consumer
You’ll open two terminals. On terminal 1, you’ll begin the server:
python calculator_server.py
You need to see:

Picture by Writer
On terminal 2 run the shopper:
python calculator_client.py
Output will present:

Picture by Writer
# Superior Patterns with FastMCP
Whereas our calculator instance makes use of primary logic, FastMCP is designed to deal with complicated, production-ready situations. As you scale your MCP servers, you may leverage:
- Asynchronous Operations: Use
async deffor instruments that carry out I/O-bound duties like database queries or API calls - Dynamic Sources: Sources can settle for arguments (e.g.,
useful resource://customers/{user_id}) to fetch particular knowledge factors on the fly - Advanced Sort Validation: Use Pydantic fashions or complicated Python kind hints to make sure the LLM sends knowledge within the precise format your backend requires
- Customized Transports: Whereas we used
stdio, FastMCP additionally helps SSE (Server-Despatched Occasions) for web-based integrations and customized UI instruments
# Conclusion
FastMCP bridges the hole between the complicated Mannequin Context Protocol and the clear, decorator-based developer expertise Python programmers anticipate. By eradicating the boilerplate related to JSON-RPC 2.0 and guide transport administration, it lets you concentrate on what issues: constructing the instruments that make LLMs extra succesful.
On this tutorial, we coated:
- The core structure of MCP (Servers vs. Shoppers)
- Easy methods to outline Instruments for actions, Sources for knowledge, and Prompts for directions
- Easy methods to construct a practical shopper to check and chain your server logic
Whether or not you’re constructing a easy utility or a posh knowledge orchestration layer, FastMCP supplies probably the most “Pythonic” path to a production-ready agentic ecosystem.
What is going to you construct subsequent? Take a look at the FastMCP documentation to discover extra superior deployment methods and UI integrations.
Shittu Olumide is a software program engineer and technical author captivated with leveraging cutting-edge applied sciences to craft compelling narratives, with a eager eye for element and a knack for simplifying complicated ideas. It’s also possible to discover Shittu on Twitter.















