Hi there!
In this post, I’ll show you how to create MCP servers easily with FastMCP. To know the basics of MCP, you can read my previous post here.
What is FastMCP?
FastMCP is a new open-source library that makes it easy to create MCP servers. It’s built on top of the FastAPI framework and uses the latest version of the MCP protocol.
How to install FastMCP?
pip install fastmcp
How to create an MCP server with FastMCP?
Creating an MCP server with FastMCP is straightforward. Let’s build a simple example that exposes a file system tool and a resource.
Basic Setup
First, create a new Python file for your MCP server:
from fastmcp import FastMCP
# Create a new MCP server instance
mcp = FastMCP("My MCP Server")
That’s it! You’ve created an MCP server. Now let’s add some functionality.
Adding Tools
Tools are functions that the MCP client (like an AI agent) can call. Let’s add a tool to read files:
from fastmcp import FastMCP
import os
mcp = FastMCP("File System Server")
@mcp.tool()
def read_file(file_path: str) -> str:
"""Read the contents of a file from the local filesystem.
Args:
file_path: The path to the file to read
Returns:
The contents of the file as a string
"""
if not os.path.exists(file_path):
return f"Error: File '{file_path}' not found"
with open(file_path, 'r', encoding='utf-8') as f:
return f.read()
Adding Resources
Resources are data sources that agents can access. Let’s add a resource that lists files in a directory:
@mcp.resource("file://directory/{directory}")
def list_directory(directory: str) -> str:
"""List all files in the specified directory.
Args:
directory: The directory path to list
Returns:
A formatted string listing all files
"""
if not os.path.isdir(directory):
return f"Error: '{directory}' is not a valid directory"
files = os.listdir(directory)
return "\n".join(f"- {f}" for f in files)
Adding Prompts
Prompts are reusable templates that help guide the agent’s behavior:
@mcp.prompt()
def analyze_codebase() -> str:
"""Prompt template for analyzing a codebase.
Returns:
A prompt string for code analysis
"""
return """Analyze the codebase structure:
1. Identify the main entry points
2. List all dependencies
3. Find potential security issues
4. Suggest improvements"""
Complete Example
Here’s a complete MCP server that combines all these features:
from fastmcp import FastMCP
import os
import json
from datetime import datetime
mcp = FastMCP("Development Tools Server")
@mcp.tool()
def read_file(file_path: str) -> str:
"""Read the contents of a file from the local filesystem."""
if not os.path.exists(file_path):
return f"Error: File '{file_path}' not found"
try:
with open(file_path, 'r', encoding='utf-8') as f:
return f.read()
except Exception as e:
return f"Error reading file: {str(e)}"
@mcp.tool()
def write_file(file_path: str, content: str) -> str:
"""Write content to a file on the local filesystem.
Args:
file_path: The path where to write the file
content: The content to write
"""
try:
os.makedirs(os.path.dirname(file_path), exist_ok=True)
with open(file_path, 'w', encoding='utf-8') as f:
f.write(content)
return f"Successfully wrote to '{file_path}'"
except Exception as e:
return f"Error writing file: {str(e)}"
@mcp.tool()
def get_file_info(file_path: str) -> str:
"""Get metadata about a file.
Args:
file_path: The path to the file
Returns:
JSON string with file information
"""
if not os.path.exists(file_path):
return json.dumps({"error": "File not found"})
stat = os.stat(file_path)
info = {
"path": file_path,
"size": stat.st_size,
"modified": datetime.fromtimestamp(stat.st_mtime).isoformat(),
"is_file": os.path.isfile(file_path),
"is_dir": os.path.isdir(file_path)
}
return json.dumps(info, indent=2)
@mcp.resource("file://directory/{directory}")
def list_directory(directory: str) -> str:
"""List all files and subdirectories in the specified directory."""
if not os.path.isdir(directory):
return f"Error: '{directory}' is not a valid directory"
items = []
for item in os.listdir(directory):
item_path = os.path.join(directory, item)
item_type = "directory" if os.path.isdir(item_path) else "file"
items.append(f"{item_type}: {item}")
return "\n".join(items) if items else "Directory is empty"
@mcp.prompt()
def code_review_prompt() -> str:
"""Generate a prompt for code review tasks."""
return """Review the provided code and check for:
1. Code quality and best practices
2. Potential bugs or errors
3. Security vulnerabilities
4. Performance optimizations
5. Documentation completeness
Provide specific, actionable feedback."""
Running Your Server
To run your MCP server, add this at the end of your file:
if __name__ == "__main__":
mcp.run()
Then run it with:
python your_server.py
Or use the FastMCP CLI:
fastmcp serve your_server.py
Connecting to an Agent
Once your server is running, you can connect it to MCP-compatible agents like:
- Claude Desktop: Update claude settings and add your server to the
claude_desktop_config.jsonfile - Amazon Q: Configure it through Q CLI
- Cursor IDE: Add it to your cursor IDE MCP settings
Best Practices
- Error Handling: Always wrap file operations in try-except blocks and return clear error messages
- Type Hints: Use Python type hints in your function signatures for better documentation
- Docstrings: Write clear docstrings that explain what each tool/resource does
- Security: Be careful about file paths - validate and sanitize inputs to prevent directory traversal attacks
- Resource Limits: Consider adding limits on file sizes or operation timeouts for production use
Advanced Features
FastMCP also supports:
- Async tools: Use
async deffor non-blocking operations - Streaming responses: For large data transfers
- Custom schemas: Define complex input/output types
- Middleware: Add authentication, logging, or rate limiting
Example: Database MCP Server
Here’s a more advanced example that connects to a database:
from fastmcp import FastMCP
import sqlite3
from typing import List, Dict
mcp = FastMCP("Database Server")
@mcp.tool()
def query_database(db_path: str, query: str) -> str:
"""Execute a SQL query on a SQLite database.
Args:
db_path: Path to the SQLite database file
query: SQL query to execute
Returns:
JSON string with query results
"""
try:
conn = sqlite3.connect(db_path)
conn.row_factory = sqlite3.Row
cursor = conn.cursor()
cursor.execute(query)
rows = cursor.fetchall()
results = [dict(row) for row in rows]
conn.close()
return json.dumps(results, indent=2)
except Exception as e:
return json.dumps({"error": str(e)})
@mcp.resource("db://tables/{db_path}")
def list_tables(db_path: str) -> str:
"""List all tables in a SQLite database."""
try:
conn = sqlite3.connect(db_path)
cursor = conn.cursor()
cursor.execute("SELECT name FROM sqlite_master WHERE type='table'")
tables = [row[0] for row in cursor.fetchall()]
conn.close()
return "\n".join(f"- {table}" for table in tables)
except Exception as e:
return f"Error: {str(e)}"
Wrapping Up
FastMCP makes it incredibly easy to build MCP servers. With just a few decorators, you can expose tools, resources, and prompts that AI agents can use to interact with your systems.
The key advantages of FastMCP:
- Simple API: Decorator-based design that’s intuitive to use
- FastAPI Foundation: Built on FastAPI, so you get async support and great performance
- Type Safety: Leverages Python type hints for better validation
- Active Development: Regularly updated with the latest MCP protocol features
Next Steps
- Build your own MCP server for a specific use case (database, API, file system, etc.)
- Test it with Claude Desktop or another MCP client
- Share your server with the community or use it in your own agent workflows
Thanks for reading! 🎉
If you build something cool with FastMCP, I’d love to hear about it. Happy building!