A Deep Dive into Model Context Protocol
Introduction
This post provides a deep dive into the Model Context Protocol (MCP), a popular approach to implementing advanced RAG and function-calling capabilities. It was created by Anthropic and released on November 25, 2024.
It is a protocol with a standardized structure for both client and server sides, designed to be flexible and extensible.
What is MCP?
According to Anthropic:
“Just as USB-C provides a standardized way to connect your devices to various peripherals and accessories, MCP provides a standardized way to connect AI models to different data sources and tools.”
While RAG (Retrieval-Augmented Generation) enhances model capabilities, and function calling enables complex interactions with external systems by using prompts to give instructions and by parsing model responses to parameterize and invoke functions—
MCP merges both concepts into a unified, standardized way of connecting AI models to different data sources and tools.
MCP has two main components:
-
MCP Server
-
MCP Client
Let’s start with the MCP Server.
MCP Server
The current components of MCP servers include:
- Tools: Perform actions and make computations, such as scheduling a meeting or dinner, handling calendar functionalities, etc.
- Resources: Provide contextual data, such as contact lists, task lists, or other static and semi-static data.
- Prompts: Help contextualize how functionalities are used.
Let’s see a simple server I built
Set up your MCP server instance with a descriptive name and detailed instructions.
# servers/tools/__init__.py
from mcp.server.fastmcp import FastMCP
mcp = FastMCP(
"Task Manager Agent",
instructions="""
You are the Task Manager Agent. Your job is to help users efficiently manage their tasks and schedules. You can create, edit, and delete tasks, as well as provide summaries and reminders. Your capabilities include:
1. Scheduling Tasks: Add new tasks with specific titles, start times, and end times. Ensure the time format is clear (e.g., YYYY-MM-DDTHH:MM:SS).
2. Editing Tasks: Update the title, start time, or end time of existing tasks. Always confirm the changes with the user.
3. Deleting Tasks: Remove tasks as requested by the user, confirming the task details before deletion.
4. Listing Tasks: Provide a list of all scheduled tasks, including their titles and scheduled times.
5. Task Summaries: Summarize upcoming tasks or provide daily/weekly overviews as requested.
- Always confirm task details (title, date, and time) with the user before making changes.
- Use clear and concise language when communicating with users.
- If a user request is ambiguous, ask for clarification.
- Respect user privacy and do not share task information unless explicitly requested.
- If you encounter an error or cannot complete a request, politely inform the user and suggest possible solutions.
"""
)
from servers.tools import calendar, task_manager, weather
__all__ = ['mcp', 'calendar', 'task_manager', 'weather']
Let’s implement our MCP server entry point.
# main.py
from servers.tools import mcp
import logging
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
logger = logging.getLogger(__name__)
if __name__ == "__main__":
logger.info("Starting MCP server...")
mcp.run('stdio')
It’s a basic task-manager server with some tools and resources:
# calendar.py
import json
from datetime import datetime, timedelta
from typing import Optional
from dateutil import parser
import pytz
from servers.tools import mcp
@mcp.tool()
def get_current_datetime(timezone_str: str = "UTC") -> str:
"""
Gets the current date and time for a given timezone and returns it as a JSON string.
:param timezone_str: The timezone to use, e.g., "America/Sao_Paulo".
:return: A JSON string with the current date and time details.
"""
timezone = pytz.timezone(timezone_str)
now = datetime.now(timezone)
output = {
"datetime": now.strftime("%Y-%m-%d %H:%M:%S"),
"timezone": timezone_str,
"iso_format": now.isoformat(),
"weekday": now.strftime("%A"),
}
return json.dumps(output, indent=4)
@mcp.tool()
def get_future_datetime(days: int, date_str: Optional[str] = None) -> str:
"""
Calculates a future date and returns it as a JSON string.
:param days: The number of days to add.
:param date_str: The base date as a string. If None, the current date is used.
:return: A JSON string with the future date details.
"""
base_date = parser.parse(date_str) if date_str else datetime.now()
future_date = base_date + timedelta(days=days)
output = {
"base_date": base_date.isoformat(),
"days_added": days,
"future_datetime": future_date.strftime("%Y-%m-%d %H:%M:%S"),
"iso_format": future_date.isoformat(),
}
return json.dumps(output, indent=4)
@mcp.tool()
def get_past_datetime(days: int, date_str: Optional[str] = None) -> str:
"""
Calculates a past date and returns it as a JSON string.
:param days: The number of days to subtract.
:param date_str: The base date as a string. If None, the current date is used.
:return: A JSON string with the past date details.
"""
base_date = parser.parse(date_str) if date_str else datetime.now()
past_date = base_date - timedelta(days=days)
output = {
"base_date": base_date.isoformat(),
"days_subtracted": days,
"past_datetime": past_date.strftime("%Y-%m-%d %H:%M:%S"),
"iso_format": past_date.isoformat(),
}
return json.dumps(output, indent=4)
Notice that this file includes some calendar-related functionalities. It provides information about date and time to help the model generate better results for the next actions. See another example of a resource below.
# weather.py
"""
This module simulates a weather forecast tool.
It generates fictional but plausible weather data for a given location.
"""
import json
import random
from typing import Dict, Any
from servers.tools import mcp
@mcp.tool()
def get_weather_forecast(location: str) -> str:
"""
Generates a simulated weather forecast for a specific location.
:param location: The location to get the forecast for (e.g., "São Paulo, Brazil").
:return: A JSON string with the simulated weather forecast details.
"""
# Simulate weather conditions
conditions = ["Sunny", "Cloudy", "Partly Cloudy", "Rainy", "Stormy", "Windy"]
selected_condition = random.choice(conditions)
# Simulate temperature based on condition
if "Sunny" in selected_condition:
temp_celsius = random.uniform(25.0, 35.0)
elif "Cloudy" in selected_condition:
temp_celsius = random.uniform(18.0, 24.0)
elif "Rainy" in selected_condition or "Stormy" in selected_condition:
temp_celsius = random.uniform(15.0, 20.0)
else:
temp_celsius = random.uniform(20.0, 28.0)
# Simulate other weather data
humidity = random.uniform(40.0, 90.0)
wind_speed_kmh = random.uniform(5.0, 25.0)
precipitation_chance = random.uniform(0.0, 100.0) if "Rainy" in selected_condition or "Stormy" in selected_condition else random.uniform(0.0, 20.0)
forecast = {
"location": location,
"condition": selected_condition,
"temperature_celsius": f"{temp_celsius:.1f}",
"humidity_percent": f"{humidity:.1f}",
"wind_speed_kmh": f"{wind_speed_kmh:.1f}",
"precipitation_chance_percent": f"{precipitation_chance:.1f}"
}
response = {
"status": "success",
"message": f"Simulated weather forecast for {location}.",
"forecast": forecast
}
return json.dumps(response, indent=4)
Amazing! Now, let’s see an example of some tools:
# task_manager.py
"""
This module simulates a task scheduler.
Tasks are stored in memory and will be lost on server restart.
"""
import json
import uuid
from typing import Dict, Any, Optional
from servers.tools import mcp
_scheduled_tasks: Dict[str, Dict[str, Any]] = {}
@mcp.tool()
def schedule_task(title: str, start_time: str, end_time: str) -> str:
"""
Schedules a new task in the calendar.
:param title: The title or description of the task.
:param start_time: The start time for the task (e.g., "2025-12-01T10:00:00").
:param end_time: The end time for the task (e.g., "2025-12-01T11:00:00").
:return: A JSON string confirming the creation and the task details.
"""
task_id = str(uuid.uuid4())
task = {
"id": task_id,
"title": title,
"start_time": start_time,
"end_time": end_time,
"status": "scheduled"
}
_scheduled_tasks[task_id] = task
response = {
"status": "success",
"message": "Task scheduled successfully.",
"task": task
}
return json.dumps(response, indent=4)
@mcp.tool()
def delete_task(task_id: str) -> str:
"""
Deletes a scheduled task.
:param task_id: The unique ID of the task to delete.
:return: A JSON string confirming the deletion or reporting an error.
"""
if task_id in _scheduled_tasks:
del _scheduled_tasks[task_id]
response = {"status": "success", "message": f"Task {task_id} deleted."}
else:
response = {"status": "error", "message": f"Task {task_id} not found."}
return json.dumps(response, indent=4)
@mcp.tool()
def edit_task(task_id: str, title: Optional[str] = None, start_time:
Optional[str] = None, end_time: Optional[str] = None) -> str:
"""
Edits an existing scheduled task.
:param task_id: The unique ID of the task to edit.
:param title: The new title for the task (optional).
:param start_time: The new start time for the task (optional).
:param end_time: The new end time for the task (optional).
:return: A JSON string confirming the update or reporting an error.
"""
if task_id not in _scheduled_tasks:
response = {"status": "error", "message": f"Task {task_id} not found."}
return json.dumps(response, indent=4)
task = _scheduled_tasks[task_id]
if title is not None:
task["title"] = title
if start_time is not None:
task["start_time"] = start_time
if end_time is not None:
task["end_time"] = end_time
_scheduled_tasks[task_id] = task
response = {
"status": "success",
"message": f"Task {task_id} updated.",
"task": task
}
return json.dumps(response, indent=4)
@mcp.resource("tasks://list")
def list_tasks() -> str:
"""
Lists all currently scheduled tasks.
:return: A JSON string containing a list of all tasks.
"""
response = {
"status": "success",
"count": len(_scheduled_tasks),
"tasks": list(_scheduled_tasks.values())
}
return json.dumps(response, indent=4)
Usage
I’m using Claude Code, but you can use any editor, like VS Code with Copilot. Just search for it.
You can learn about Claude Code mcp setup at official documentation
An example of weather tool:
Another example of full use of mcp server as assistant and task-manager:
GG!
here’s the repository from example above
MCP Client
Workflow from official documentation
The client can be your IDE if it has support for it, like vscode, zed, etc.., or any other tool that supports the MCP protocol.
But we’ll explore the MCP Client another day.
Bye!