Large Language Models (LLMs) are powerful, but they can’t access your local files, databases, or APIs on their own. That’s where the Model Context Protocol (MCP) comes in. You can think of MCP as the USB-C of AI. Instead of creating a new integration for every tool, you build an MCP server once, and any MCP-compatible AI (like Claude Desktop or Cursor) can use it right away. In this article, I’ll show you how to build your first MCP server in Python from the ground up.
How MCP Actually Works
Before we start coding, it’s important to understand how MCP works. MCP uses a straightforward Client-Server model. It includes:
- The MCP Host: This is the application the user interacts with (e.g., Claude Desktop, Zed, or a custom LangChain agent).
- The MCP Client: This lives inside the Host. It translates the LLM’s requests and manages the connection to your server.
- The MCP Server (what we’ll build): This is a simple app that safely shares your local data or functions with the Client.
When you create an MCP Server, you usually make three types of features available to the AI:
- Tools: These are functions the model can run, like query_database() or add_task().
- Resources: This is read-only data the model can use for context, such as a local log file or an API response.
- Prompts: These are templates that help guide the AI on how to use the server.
In this tutorial, we’ll mainly focus on Tools.
Building the MCP Server in Python
To make this project free and simple, we’ll build an in-memory Task Manager MCP server. An LLM will be able to connect to this server to add tasks, see what’s pending, and help organize your day.
Step 1: Setting Up the Environment
You’ll need Python 3.10 or newer. Open your terminal and run:
# Create a new directory for our project
mkdir mcp-tutorial
cd mcp-tutorial
# Create and activate a virtual environment
python -m venv .venv
source .venv/bin/activate # On Windows: .venv\Scripts\activate
# Install the official MCP Python SDK
pip install "mcp[cli]"
Step 2: Writing the Server Code
Create a file called server.py in your project folder. The Python MCP SDK comes with a great framework called FastMCP, which takes care of the complex JSON-RPC communication for you. It works a lot like FastAPI and uses simple Python decorators.
Here is the complete code for our Task Manager:
from mcp.server.fastmcp import FastMCP
# 1. Initialize the FastMCP server with a descriptive name
mcp = FastMCP("LocalTaskManager")
# 2. Set up a simple in-memory database
# In a real-world scenario, you'd replace this with SQLite or PostgreSQL.
tasks = []
current_id = 1
# 3. Define our first Tool: Adding a task
@mcp.tool()
def add_task(title: str, description: str = "") -> str:
"""
Add a new task to the user's task list.
The AI will read this docstring to understand what the tool does.
"""
global current_id
tasks.append({
"id": current_id,
"title": title,
"description": description,
"status": "pending"
})
current_id += 1
return f"Success! Task '{title}' was added to the tracker."
# 4. Define our second Tool: Listing pending tasks
@mcp.tool()
def get_pending_tasks() -> str:
"""Retrieve all pending tasks so the user knows what to do next."""
pending = [t for t in tasks if t["status"] == "pending"]
if not pending:
return "You're all caught up! No pending tasks right now."
output = "Here are your pending tasks:\n"
for t in pending:
output += f"- [ID: {t['id']}] {t['title']} (Details: {t['description']})\n"
return output
# 5. Run the server
if __name__ == "__main__":
# We use "stdio" transport. This means the AI client will communicate
# with this Python script via standard input and output streams.
mcp.run(transport="stdio")Here are the main parts of the code you should know about:
- The @mcp.tool() decorator: This is important. When you add it to a regular Python function, FastMCP checks the function name, type hints (like title: str), and the docstring. It then puts all this into a JSON schema and gives it to the LLM.
- The Docstrings: Notice how descriptive the docstrings are. Don’t skip these. The LLM reads your docstrings to decide when and how to use the tool. If your docstring is vague, the AI might make up parameters or not call the tool at all.
- STDIO Transport: mcp.run(transport= “stdio”) tells the script to use your terminal’s standard input and output instead of opening a web port. This is great for local agents on your machine because it avoids network firewall issues.
Step 3: Testing Your Server
You can test this right from your terminal using the MCP CLI we installed earlier. Run this command:
mcp dev server.py
Make sure your settings look like this before you click to connect:

This starts the MCP Inspector, a local web interface where you can click around and test your tools just like an LLM would.

Try calling add_task with a few test strings, then call get_pending_tasks to see your local Python state update in real time.
You can learn more about working with MCP Servers here.
Closing Thoughts
That’s how you build your first MCP server from scratch.
When you build standalone MCP servers, you turn your data and infrastructure into modular Lego bricks. Your Python server doesn’t care if the AI using it is Claude 3.5 Sonnet, GPT-4o, or a local Llama model. It simply exposes the tools and lets the intelligence layer handle the rest. This is how enterprise AI is built today.
If you found this article helpful, you can follow me on Instagram for daily AI tips and practical resources. You may also be interested in my latest book, Hands-On GenAI, LLMs & AI Agents, a step-by-step guide to prepare you for careers in today’s AI industry.





