Anthropic's Model Context Protocol (MCP) is revolutionizing how we connect Large Language Models (LLMs) to external services. While existing MCP servers offer easy integrations, what happens when you need custom interactions or a service isn't supported? The answer is building your own MCP server. This guide is for developers and AI enthusiasts looking to gain full control over their LLM integrations. You'll learn the 'why' and 'how' of building custom MCP servers, explore essential resources, and get a ready-to-use Python template designed with best practices to kickstart your development.
The Power of MCP: Beyond Off-the-Shelf Tools
Everyone is starting to realize how significant Anthropic's Model Context Protocol (MCP) is. It's the first real standard for connecting Large Language Models (LLMs) to your services like Gmail, Slack, GitHub, web search—the list goes on. You can use it to give higher-level capabilities to your LLMs, like long-term memory, as we'll see shortly.
Many MCP servers are already available, allowing us to easily connect LLMs to various services. For example, adding the Brave Search MCP server lets an LLM access up-to-date web information, overcoming knowledge cutoffs. You simply add its configuration to an MCP client (like Windsurf Cursor, N8N, Claude Desktop, or your own custom agent) and grant permission.
Here's a peek at how simple the configuration can be in Claude Desktop:
// Example Cloud Desktop Config with Brave Search
{
"servers": [
{
"name": "brave-search",
"type": "sse", // or "stdio"
"url": "http://localhost:8000", // if using sse
// or "command": ["python", "path/to/brave_server.py"] // if using stdio
"config": {
"api_key": "YOUR_BRAVE_API_KEY"
}
}
// ... other servers ...
]
}
After adding the server and API key, Claude Desktop can now use Brave Search when needed.
The Catch: What if the existing server doesn't interact with the service exactly how you need it to? Or what if the capability you want doesn't have a pre-built MCP server?
That's where the true power lies: building your own MCP servers. This gives you complete customization and control to integrate any service, precisely tailoring your AI agent's abilities.
Essential Resources for Building MCP Servers
Before diving in, familiarize yourself with MCP. These resources were invaluable for learning and building:
- Official MCP Documentation: The primary source for understanding MCP concepts, including guides for building servers and clients.
- List of Existing MCP Servers on GitHub: Excellent reference points and examples for building your own servers.
- MCP Python SDK GitHub Repo: Guidance specifically for building servers using the most common language, Python.
Demo: A Custom Server for Long-Term Memory
To illustrate, let's consider a custom MCP server built using the template provided later in this guide. This server integrates with Mem0, a library for adding long-term memory to AI agents.
Here's how it looks in the Claude Desktop config:
// Example Cloud Desktop Config with Custom Mem0 Server
{
"servers": [
// ... other servers like Brave Search ...
{
"name": "mem0-mcp-server",
"type": "sse",
"url": "http://localhost:8070", // Example host/port
"config": {}
}
]
}
With this server running and configured, you can ask Claude Desktop, "What are my memories?" It will use the custom getAllMemories
tool provided by your server to retrieve information (e.g., "You currently have one memory: You like chicken.").
This single custom server can be used by multiple MCP clients simultaneously – Claude Desktop, a Pydantic AI agent, an N8N workflow – all accessing the same custom capability you built.
Your Starting Point: The Python MCP Server Template
Building servers correctly requires understanding MCP nuances. To save you headaches and ensure you follow best practices often missed even in official examples, I've created a Python MCP server template:
➡️ MCP Server Template (Mem0 MCP)
The template uses Mem0 integration as a concrete example, but it's structured so you can easily remove the Mem0 parts and implement your own tools. It provides a solid foundation supporting different MCP transport protocols.
💡 Pro Tip: AI coding assistants are helpful but can struggle with MCP specifics. Provide this template code as an example within your prompt (along with the official docs) for significantly better results when asking an AI to generate a custom server for you.
Core Concepts: Building with the Python SDK
Building an MCP server in Python is straightforward with the mcp
package.
- Installation:
pip install mcp
-
Initialization: Use
FastMCP
to quickly set up your server.from mcp import FastMCP mcp = FastMCP(name="my-server", description="My custom MCP server.")
- Adding Tools: Decorate your Python functions with
@mcp.tool
.@mcp.tool def calculate_bmi(weight: float, height: float) -> float: """Calculates Body Mass Index (BMI). Provide weight in kg and height in meters.""" if height <= 0: return "Height must be positive." bmi = weight / (height * height) return round(bmi, 2)
- The function becomes a tool callable by the LLM via the MCP client.
- The LLM uses the function signature and docstring to understand how and when to use the tool. Write clear, descriptive docstrings!
While MCP also supports adding prompts, images, and resources, tools are the primary way to add agentic capabilities.
Leveraging AI Assistants Effectively
The official MCP docs include a handy LLM-L.ext page with the full documentation in Markdown, perfect for pasting into an LLM prompt. Combine this with the template code for the best results:
Use the MCP docs attached here.
Also, use this Python MCP server as an example for the MCP server that you are about to build:
```python
# [Paste the template's main Python script content here]
Now I want to build my own MCP server to integrate with [Your Target Service/Tool, e.g., Your internal API, a specific database, etc.].
## Template Deep Dive: Code Walkthrough
Let's examine key parts of the [template code][8] and the best practices it implements:
### 1. Lifespan Management
This is crucial for managing resources like database connections or API clients.
```python
import contextlib
from mcp import MCPContext
from mem0 import Memory # Example import
@contextlib.asynccontextmanager
async def lifespan(context: MCPContext):
# Initialize client(s) ONCE when the server starts
print("Initializing resources...")
mem_client = Memory() # Example: Initialize Mem0 client
context["mem_client"] = mem_client # Store in context for tools
print("Mem0 client initialized.")
try:
yield # Server runs here
finally:
# Cleanup actions when the server shuts down
print("Closing resources...")
# Example: await mem_client.close() if needed
- The
@contextlib.asynccontextmanager
defines setup and teardown logic. - Clients are initialized once and stored in the
context
dictionary. - Tools access these shared clients via the
context
object, avoiding re-initialization on every call (singleton pattern).
2. Server Instantiation
Initialize FastMCP
, passing the lifespan
function and transport configurations (host/port often set via environment variables).
import os
mcp = FastMCP(
name="mem0-mcp-server",
description="MCP server for Mem0 long-term memory.",
lifespan=lifespan,
host=os.getenv("MCP_HOST", "0.0.0.0"),
port=int(os.getenv("MCP_PORT", 8070))
)
3. Tool Definitions
Define asynchronous functions decorated with @mcp.tool
.
@mcp.tool
async def save_memory(context: MCPContext, text: str) -> str:
"""Saves a piece of text as a memory."""
try:
mem_client = context["mem_client"] # Access client from context
response = await mem_client.add(text, user_id="mcp_user")
return f"Memory saved successfully: {response}"
except Exception as e:
return f"Error saving memory: {str(e)}" # Return informative errors
# ... (other tools like get_all_memories, search_memories) ...
- The first argument must be
context: MCPContext
to access shared resources. - Subsequent arguments are determined by the LLM based on the function signature and docstring.
- Implement robust error handling and return meaningful messages to the LLM.
4. Main Function & Transport Handling
This part runs the server and crucially supports both primary MCP transport protocols:
- Standard I/O (stdio): Client starts the server as a subprocess. Good for local-only setups.
- Server-Sent Events (SSE): Server runs independently via HTTP. Necessary for networked access or clients like N8N.
import os
import uvicorn
if __name__ == "__main__":
transport = os.getenv("MCP_TRANSPORT", "sse").lower()
if transport == "stdio":
print("Running MCP server with Standard I/O transport...")
mcp.run_stdio()
elif transport == "sse":
print(f"Running MCP server with SSE transport on {mcp.host}:{mcp.port}...")
uvicorn.run(mcp, host=mcp.host, port=mcp.port)
else:
print(f"Error: Unknown transport type '{transport}'. Use 'stdio' or 'sse'.")
- The script checks the
MCP_TRANSPORT
environment variable (defaulting tosse
). - It calls the appropriate run method (
mcp.run_stdio()
oruvicorn.run(mcp, ...)
). - Supporting both makes your server much more versatile.
Setting Up and Running the Template
The template's README provides detailed instructions for setup:
- Installation: Set up a Python environment or use the provided Dockerfile (recommended for consistency).
- Environment Variables: Configure
.env
forMCP_TRANSPORT
,MCP_HOST
,MCP_PORT
, and any API keys your tools require. - Running: Commands are provided for both direct Python execution and Docker, covering
stdio
andsse
transports.
Example: Running with Docker (SSE on port 8070)
# 1. Build the image
docker build -t mcp-mem0-server .
# 2. Run the container (ensure .env sets MCP_TRANSPORT=sse, MCP_PORT=8070)
docker run -p 8070:8070 --env-file .env mcp-mem0-server
Your server is now running and accessible at http://localhost:8070
(or your configured host/port).
Connect Your Clients
Once running, configure your MCP clients (Claude Desktop, N8N, custom agents) to point to your server's address (stdio
command or SSE url
). You can test it by asking questions that should trigger your custom tools, like asking the Mem0 server about memories or telling it to save new ones.
Conclusion
Building your own MCP server opens up a world of possibilities for customizing and enhancing your AI agents. By leveraging the Python SDK and following best practices (like those in the provided template), you can connect your LLMs to virtually any service or capability you need. Feel free to use and adapt the template – it's designed to be the perfect starting point for your MCP development journey.