# Introduction
The Mannequin Context Protocol (MCP) has modified how massive language fashions (LLMs) work together with exterior instruments, knowledge sources, and providers. Nevertheless, 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 may discover ways to construct MCP servers and purchasers utilizing FastMCP, which is complete and full with error dealing with, making it supreme for each freshmen and intermediate builders.
// Stipulations
Earlier than beginning this tutorial, be sure you 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 operating Python scripts
It is usually useful to have good Python programming information (capabilities, decorators, sort 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, in depth boilerplate code for protocol dealing with, handbook 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 give attention to enterprise logic reasonably than protocol implementation.
# What’s the Mannequin Context Protocol?
The Mannequin Context Protocol (MCP) is an open normal created by Anthropic. It gives a common interface for AI purposes to securely join with exterior instruments, knowledge sources, and providers. MCP standardizes how LLMs work together with exterior methods, very similar to 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 assist 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, sources, prompts) that exterior purposes can use. Consider it as a backend API particularly designed for LLM integration.
- MCP Shopper: Embedded in AI purposes (like Claude Desktop, Cursor IDE, or customized purposes) that hook up with MCP servers to entry their sources.
// Core Elements of MCP
MCP servers expose three major kinds of capabilities:
- Instruments: Executable capabilities 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 is likely to be file contents, configuration knowledge, or dynamically generated content material.
- Prompts: Reusable message templates that information LLM habits. 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.instrument,@mcp.useful resource,@mcp.immediate) remove boilerplate - Kind Security: Full sort hints and validation utilizing Python’s sort system
- Async/Await Help: Fashionable async Python for high-performance operations
- A number of Transports: Help 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 ideas:
- Excessive-level abstractions: Much less code and sooner improvement cycles
- Easy: Minimal boilerplate permits give attention to performance over protocol particulars
- Pythonic: Pure Python idioms make it acquainted to Python builders
# Set up
Begin by putting in FastMCP and the mandatory dependencies. I like to recommend utilizing uv.
If you happen to 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, sources, and prompts. We’ll construct a Calculator Server that gives mathematical operations, configuration sources, and instruction prompts.
// Step 1: Setting Up the Challenge Construction
We first have to create a venture listing and initialize your surroundings. Create a folder on your venture:
Then navigate into your venture folder:
Initialize your venture with the mandatory information:
// Step 2: Creating the MCP Server
Our Calculator MCP Server is an easy MCP server demonstrating instruments, sources, and prompts. Inside your venture 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 (essential for MCP protocol integrity)
logging.basicConfig(
degree=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 every 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.instrument
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"Kind error in add: {e}")
increase ValueError(f"Invalid enter varieties: {e}")
@mcp.instrument
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"Kind error in subtract: {e}")
increase ValueError(f"Invalid enter varieties: {e}")
We’ve outlined capabilities for addition and subtraction. Each are wrapped in a try-catch block to boost worth errors, log the data, and return the consequence.
@mcp.instrument
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"Kind error in multiply: {e}")
increase ValueError(f"Invalid enter varieties: {e}")
@mcp.instrument
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 making an attempt 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 embellished capabilities (@mcp.instrument) expose mathematical operations. Every instrument consists of:
- Kind hints for parameters and return values
- Complete docstrings (MCP makes use of these as instrument descriptions)
- Error dealing with with try-except blocks
- Logging for debugging and monitoring
- Enter validation
Let’s transfer on to constructing sources.
@mcp.useful resource("config://calculator/settings")
def get_settings() -> Dict:
"""
Offers calculator configuration and obtainable 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:
"""
Offers a consumer 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 needs to be legitimate numbers (int or float)
## Precision
The calculator makes use of IEEE 754 double precision floating-point arithmetic.
Outcomes might comprise minor rounding errors for some operations.
"""
return information
Two embellished capabilities (@mcp.useful resource) present static and dynamic knowledge:
config://calculator/settings: Returns metadata in regards to the calculatordocs://calculator/information: Returns a formatted consumer information- URI format distinguishes useful resource varieties (conference:
sort://class/useful resource)
Let’s construct our prompts.
@mcp.immediate
def calculate_expression(expression: str) -> str:
"""
Offers directions for evaluating a mathematical expression.
Args:
expression: A mathematical expression to guage
Returns:
Formatted immediate instructing the LLM learn how 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 instrument for every operation
3. Comply with order of operations (parentheses, multiplication/division, addition/subtraction)
4. Present all intermediate steps
5. Present the ultimate consequence
Obtainable 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 consumer")
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 habits 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 Shopper
On this step, we’ll display learn how 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 Shopper, FastMCP
logging.basicConfig(
degree=logging.INFO,
format="%(asctime)s - %(title)s - %(levelname)s - %(message)s",
stream=sys.stderr
)
logger = logging.getLogger(__name__)
async def essential():
"""
Most important shopper operate demonstrating server interplay.
"""
from calculator_server import mcp as server
logger.data("Initializing Calculator Shopper...")
strive:
async with Shopper(server) as shopper:
logger.data("✓ Related to Calculator Server")
# DISCOVER CAPABILITIEs
print("n" + "="*60)
print("1. DISCOVERING SERVER CAPABILITIES")
print("="*60)
# Listing obtainable instruments
instruments = await shopper.list_tools()
print(f"nAvailable Instruments ({len(instruments)}):")
for instrument in instruments:
print(f" • {instrument.title}: {instrument.description}")
# Listing obtainable sources
sources = await shopper.list_resources()
print(f"nAvailable Sources ({len(sources)}):")
for useful resource in sources:
print(f" • {useful resource.uri}: {useful resource.title or useful resource.uri}")
# Listing obtainable 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" Sudden 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})
ultimate = extract_tool_result(final_result)
print(f" Closing Consequence: {ultimate}")
# 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("✓ Shopper operations accomplished efficiently")
besides Exception as e:
logger.error(f"Shopper error: {e}", exc_info=True)
sys.exit(1)
From the code above, the shopper makes use of async with Shopper(server) for secure 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 instrument 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]
# Desire specific textual content content material when obtainable (TextContent)
if hasattr(content material, 'textual content') and content material.textual content is just not None:
# If the textual content is JSON, attempt to parse and extract a `consequence` subject
import json as _json
text_val = content material.textual content
strive:
parsed_text = _json.hundreds(text_val)
# If JSON accommodates a consequence subject, 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.hundreds(json_str)
besides _json.JSONDecodeError:
return json_str
else:
parsed = content material.json
# If parsed is a dict, strive widespread 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:
go
return response
besides Exception as e:
logger.warning(f"Couldn't extract consequence: {e}")
return response
if __name__ == "__main__":
logger.data("Calculator Shopper Beginning...")
asyncio.run(essential())
Wanting on the above code, earlier than utilizing instruments, the shopper lists obtainable capabilities. The await shopper.list_tools() will get all instrument metadata, together with descriptions. The await shopper.list_resources() discovers obtainable sources. Lastly, the await shopper.list_prompts() will discover obtainable immediate templates.
The await shopper.call_tool() technique does the next:
- Takes the instrument title and parameters as a dictionary
- Returns a wrapped response object containing the consequence
- Integrates with error dealing with for instrument 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 learn how to use output from one instrument as enter to a different, enabling complicated calculations throughout a number of instrument calls.
Lastly, the error dealing with catches instrument errors (like division by zero) and logs them gracefully with out crashing.
// Step 4: Working the Server and Shopper
You’ll open two terminals. On terminal 1, you’ll begin the server:
python calculator_server.py
It’s best 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 eventualities. 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 - Complicated Kind Validation: Use Pydantic fashions or complicated Python sort 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 count on. By eradicating the boilerplate related to JSON-RPC 2.0 and handbook transport administration, it means that you can give attention to what issues: constructing the instruments that make LLMs extra succesful.
On this tutorial, we lined:
- The core structure of MCP (Servers vs. Shoppers)
- How one can outline Instruments for actions, Sources for knowledge, and Prompts for directions
- How one can construct a practical shopper to check and chain your server logic
Whether or not you might be constructing a easy utility or a fancy knowledge orchestration layer, FastMCP gives probably the most “Pythonic” path to a production-ready agentic ecosystem.
What’s going to you construct subsequent? Try the FastMCP documentation to discover extra superior deployment methods and UI integrations.
Shittu Olumide is a software program engineer and technical author obsessed with leveraging cutting-edge applied sciences to craft compelling narratives, with a eager eye for element and a knack for simplifying complicated ideas. You too can discover Shittu on Twitter.

