# 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 permits 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 best for each rookies and intermediate builders.
// Conditions
Earlier than beginning this tutorial, be sure you have:
- Python 3.10 or larger (3.11+ really helpful for higher async efficiency)
- pip or uv (uv is really helpful for FastMCP deployment and is required for the CLI instruments)
- A code editor (I’m utilizing VS Code, however you should utilize any editor of your alternative)
- Terminal/Command Line familiarity for working Python scripts
Additionally it is useful to have good Python programming information (capabilities, decorators, sort hints), some understanding of async/await syntax (non-compulsory, 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, 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 deal with enterprise logic somewhat than protocol implementation.
# What’s the Mannequin Context Protocol?
The Mannequin Context Protocol (MCP) is an open normal created by Anthropic. It offers a common interface for AI functions to securely join with exterior instruments, knowledge sources, and providers. MCP standardizes how LLMs work together with exterior programs, 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 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 Creator
- MCP Server: Exposes capabilities (instruments, assets, prompts) that exterior functions can use. Consider it as a backend API particularly designed for LLM integration.
- MCP Shopper: Embedded in AI functions (like Claude Desktop, Cursor IDE, or customized functions) that connect with MCP servers to entry their assets.
// Core Parts of MCP
MCP servers expose three main forms 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.
- Assets: Learn-only knowledge that MCP purchasers can fetch and use as context. Assets 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 scale back growth complications, FastMCP possesses the next traits:
- Decorator-Primarily based API: Python decorators (
@mcp.device,@mcp.useful resource,@mcp.immediate) get rid of boilerplate - Kind Security: Full sort hints and validation utilizing Python’s sort system
- Async/Await Help: Trendy async Python for high-performance operations
- A number of Transports: Help for stdio, HTTP, WebSocket, and SSE
- Constructed-in Testing: Simple 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 growth cycles
- Easy: Minimal boilerplate permits deal with 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.
For those who 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 installed successfully')"
# 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 must create a undertaking listing and initialize your atmosphere. Create a folder to your undertaking:
Then navigate into your undertaking folder:
Initialize your undertaking with the mandatory recordsdata:
// Step 2: Creating the MCP Server
Our Calculator MCP Server is a straightforward MCP server demonstrating instruments, assets, and prompts. Inside your undertaking 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 (vital for MCP protocol integrity)
logging.basicConfig(
stage=logging.DEBUG,
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
stream=sys.stderr
)
logger = logging.getLogger(__name__)
# Create the FastMCP server occasion
mcp = FastMCP(identify="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(identify="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
"""
attempt:
outcome = a + b
logger.data(f"Addition performed: {a} + {b} = {result}")
return outcome
besides TypeError as e:
logger.error(f"Type error in add: {e}")
increase ValueError(f"Invalid input types: {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
"""
attempt:
outcome = a - b
logger.data(f"Subtraction performed: {a} - {b} = {result}")
return outcome
besides TypeError as e:
logger.error(f"Type error in subtract: {e}")
increase ValueError(f"Invalid input types: {e}")
We now have outlined capabilities for addition and subtraction. Each are wrapped in a try-catch block to boost worth errors, log the knowledge, and return the outcome.
@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
"""
attempt:
outcome = a * b
logger.data(f"Multiplication performed: {a} * {b} = {result}")
return outcome
besides TypeError as e:
logger.error(f"Type error in multiply: {e}")
increase ValueError(f"Invalid input types: {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 making an attempt to divide by zero
"""
attempt:
if b == 0:
logger.warning(f"Division by zero attempted: {a} / {b}")
increase ValueError("Cannot divide by zero")
outcome = a / b
logger.data(f"Division performed: {a} / {b} = {result}")
return outcome
besides (TypeError, ZeroDivisionError) as e:
logger.error(f"Error in divide: {e}")
increase ValueError(f"Division error: {e}")
4 embellished capabilities (@mcp.device) expose mathematical operations. Every device contains:
- Kind 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:
"""
Offers calculator configuration and obtainable operations.
Returns:
Dictionary containing calculator settings and metadata
"""
logger.debug("Fetching calculator settings")
return {
"version": "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/guide")
def get_guide() -> str:
"""
Offers a person information for the calculator server.
Returns:
String containing utilization information and examples
"""
logger.debug("Retrieving calculator guide")
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 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 person 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 how you can consider the expression
"""
logger.debug(f"Generating calculation prompt 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. Comply with order of operations (parentheses, multiplication/division, addition/subtraction)
4. Present all intermediate steps
5. Present the ultimate outcome
Out there instruments: add, subtract, multiply, divide
"""
return immediate.strip()
Lastly, add the server startup script.
if __name__ == "__main__":
logger.data("Starting Calculator MCP Server...")
attempt:
# Run the server with stdio transport (default for Claude Desktop)
mcp.run(transport="stdio")
besides KeyboardInterrupt:
logger.data("Server interrupted by user")
sys.exit(0)
besides Exception as e:
logger.error(f"Fatal 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 finest 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 exhibit how you can 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(
stage=logging.INFO,
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
stream=sys.stderr
)
logger = logging.getLogger(__name__)
async def principal():
"""
Most important consumer perform demonstrating server interplay.
"""
from calculator_server import mcp as server
logger.data("Initializing Calculator Client...")
attempt:
async with Shopper(server) as consumer:
logger.data("✓ Connected to Calculator Server")
# DISCOVER CAPABILITIEs
print("n" + "="*60)
print("1. DISCOVERING SERVER CAPABILITIES")
print("="*60)
# Listing obtainable instruments
instruments = await consumer.list_tools()
print(f"nAvailable Tools ({len(tools)}):")
for device in instruments:
print(f" • {tool.name}: {tool.description}")
# Listing obtainable assets
assets = await consumer.list_resources()
print(f"nAvailable Resources ({len(resources)}):")
for useful resource in assets:
print(f" • {resource.uri}: {resource.name or resource.uri}")
# Listing obtainable prompts
prompts = await consumer.list_prompts()
print(f"nAvailable Prompts ({len(prompts)}):")
for immediate in prompts:
print(f" • {prompt.name}: {prompt.description}")
# CALL TOOLS
print("n" + "="*60)
print("2. CALLING TOOLS")
print("="*60)
# Easy addition
print("nTest 1: Adding 15 + 27")
outcome = await consumer.call_tool("add", {"a": 15, "b": 27})
result_value = extract_tool_result(outcome)
print(f" Result: 15 + 27 = {result_value}")
# Division with error dealing with
print("nTest 2: Dividing 100 / 5")
outcome = await consumer.call_tool("divide", {"a": 100, "b": 5})
result_value = extract_tool_result(outcome)
print(f" Result: 100 / 5 = {result_value}")
# Error case: division by zero
print("nTest 3: Division by Zero (Error Handling)")
attempt:
outcome = await consumer.call_tool("divide", {"a": 10, "b": 0})
print(f" Unexpected success: {result}")
besides Exception as e:
print(f" ✓ Error caught correctly: {str(e)}")
# READ RESOURCES
print("n" + "="*60)
print("3. READING RESOURCES")
print("="*60)
# Learn settings useful resource
print("nFetching Calculator Settings...")
settings_resource = await consumer.read_resource("config://calculator/settings")
print(f" Version: {settings_resource[0].text}")
# Learn information useful resource
print("nFetching Calculator Guide...")
guide_resource = await consumer.read_resource("docs://calculator/guide")
# 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 consumer.call_tool("add", {"a": 10, "b": 5})
step1 = extract_tool_result(add_result)
print(f" Result: {step1}")
# Step 2: Multiply
print(" Step 2: Multiply 15 * 3")
mult_result = await consumer.call_tool("multiply", {"a": step1, "b": 3})
step2 = extract_tool_result(mult_result)
print(f" Result: {step2}")
# Step 3: Subtract
print(" Step 3: Subtract 45 - 7")
final_result = await consumer.call_tool("subtract", {"a": step2, "b": 7})
remaining = extract_tool_result(final_result)
print(f" Final Result: {final}")
# 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 consumer.get_prompt(
"calculate_expression",
{"expression": expression}
)
print(f" Template:n{prompt_response.messages[0].content.text}")
logger.data("✓ Client operations completed successfully")
besides Exception as e:
logger.error(f"Client error: {e}", exc_info=True)
sys.exit(1)
From the code above, the consumer makes use of async with Shopper(server) for secure connection administration. This routinely handles connection setup and cleanup.
We additionally want a helper perform to deal with the outcomes.
def extract_tool_result(response: Any) -> Any:
"""
Extract the precise outcome worth from a device response.
MCP wraps leads to content material objects, this helper unwraps them.
"""
attempt:
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 shouldn't be None:
# If the textual content is JSON, attempt to parse and extract a `outcome` subject
import json as _json
text_val = content material.textual content
attempt:
parsed_text = _json.masses(text_val)
# If JSON accommodates a outcome subject, return it
if isinstance(parsed_text, dict) and 'outcome' in parsed_text:
return parsed_text.get('outcome')
return parsed_text
besides _json.JSONDecodeError:
# Attempt to convert plain textual content to quantity
attempt:
if '.' in text_val:
return float(text_val)
return int(text_val)
besides Exception:
return text_val
# Attempt to extract JSON outcome by way of mannequin `.json()` or dict-like `.json`
if hasattr(content material, 'json'):
attempt:
if callable(content material.json):
json_str = content material.json()
import json as _json
attempt:
parsed = _json.masses(json_str)
besides _json.JSONDecodeError:
return json_str
else:
parsed = content material.json
# If parsed is a dict, attempt widespread shapes
if isinstance(parsed, dict):
# If nested outcome exists
if 'outcome' in parsed:
res = parsed.get('outcome')
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):
attempt:
if '.' in res:
return float(res)
return int(res)
besides Exception:
return res
return res
return parsed
besides Exception:
move
return response
besides Exception as e:
logger.warning(f"Could not extract result: {e}")
return response
if __name__ == "__main__":
logger.data("Calculator Client Starting...")
asyncio.run(principal())
Wanting on the above code, earlier than utilizing instruments, the consumer lists obtainable capabilities. The await consumer.list_tools() will get all device metadata, together with descriptions. The await consumer.list_resources() discovers obtainable assets. Lastly, the await consumer.list_prompts() will discover obtainable immediate templates.
The await consumer.call_tool() technique does the next:
- Takes the device identify and parameters as a dictionary
- Returns a wrapped response object containing the outcome
- Integrates with error dealing with for device failures
On the outcome extraction, the extract_tool_result() helper perform unwraps MCP’s response format to get the precise worth, dealing with each JSON and textual content responses.
The chaining operations you see above exhibit how you can 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: Operating the Server and Shopper
You’ll open two terminals. On terminal 1, you’ll begin the server:
python calculator_server.py
It is best to see:

Picture by Creator
On terminal 2 run the consumer:
python calculator_client.py
Output will present:

Picture by Creator
# 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’ll be able to leverage:
- Asynchronous Operations: Use
async deffor instruments that carry out I/O-bound duties like database queries or API calls - Dynamic Assets: Assets 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 actual 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 lets you deal with what issues: constructing the instruments that make LLMs extra succesful.
On this tutorial, we lined:
- The core structure of MCP (Servers vs. Shoppers)
- Learn how to outline Instruments for actions, Assets for knowledge, and Prompts for directions
- Learn how to construct a practical consumer to check and chain your server logic
Whether or not you’re constructing a easy utility or a posh knowledge orchestration layer, FastMCP offers essentially the most “Pythonic” path to a production-ready agentic ecosystem.
What’s 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 keen about leveraging cutting-edge applied sciences to craft compelling narratives, with a eager eye for element and a knack for simplifying complicated ideas. You may as well discover Shittu on Twitter.



