This tutorial will guide you through building a barebones Model Context Protocol (MCP) server from scratch. We'll create a server that exposes a simple "tool" and then build a basic LLM agent client to interact with it. Our demo will be a classic: a weather lookup tool.
Prerequisites
You'll need Python 3.7+ installed. We'll be using the FastAPI framework for our server and `requests` for our client.
# Install the necessary libraries
pip install "fastapi[all]" uvicorn python-multipart requests
Step-by-Step Guide
Step 1: Define the Tool Logic
First, let's write the actual function our server will expose. It's just a plain Python function. Create a file named `tools.py`.
# tools.py
import random
def get_weather(location: str):
"""A mock function to get the weather for a given location."""
if "tokyo" in location.lower():
return {"location": "Tokyo", "temperature": "15°C", "condition": "Cloudy"}
temps = ["25°C", "-5°C", "30°C"]
conditions = ["Sunny", "Snowing", "Rainy"]
return {
"location": location,
"temperature": random.choice(temps),
"condition": random.choice(conditions)
}
Step 2: Build the MCP Server
Now, create the server that exposes this tool via an API endpoint. This is our core MCP logic. Create a file named `server.py`.
# server.py
from fastapi import FastAPI
from pydantic import BaseModel
from typing import Dict, Any
from tools import get_weather
app = FastAPI()
# This defines the expected input for our endpoint
class ToolCallRequest(BaseModel):
tool_name: str
parameters: Dict[str, Any]
# Our "tool registry" is just a simple dictionary
AVAILABLE_TOOLS = {
"get_weather": get_weather
}
@app.post("/tools/call")
def call_tool(request: ToolCallRequest):
tool_function = AVAILABLE_TOOLS.get(request.tool_name)
if not tool_function:
return {"error": "Tool not found"}
# Call the actual tool function with its parameters
result = tool_function(**request.parameters)
return {"status": "success", "result": result}
Step 3: Create the LLM Agent Client
This agent will simulate an LLM deciding to use a tool. It takes a user prompt, determines which tool to call, and then makes a request to our MCP server. Create `agent.py`.
# agent.py
import requests
import json
MCP_SERVER_URL = "http://127.0.0.1:8000/tools/call"
def run_agent(prompt: str):
print(f"Agent received prompt: '{prompt}'")
# --- This part simulates the LLM's reasoning ---
tool_name = "get_weather"
parameters = {"location": "Tokyo"}
print(f"LLM decided to call tool '{tool_name}' with params {parameters}")
# --- End of simulation ---
# Construct the request to the MCP server
payload = {
"tool_name": tool_name,
"parameters": parameters
}
try:
response = requests.post(MCP_SERVER_URL, json=payload)
response.raise_for_status() # Raise an exception for bad status codes
result = response.json()
print("\n--- Server Response ---")
print(json.dumps(result, indent=2))
except requests.exceptions.RequestException as e:
print(f"\nError calling MCP server: {e}")
if __name__ == "__main__":
user_prompt = "What's the weather like in Tokyo today?"
run_agent(user_prompt)
Step 4: Run the Demo
Open two terminal windows. In the first, start the server.
# Terminal 1: Run the server
uvicorn server:app --reload
In the second terminal, run the agent client.
# Terminal 2: Run the agent
python agent.py
You should see the agent print its decision, and then display the JSON response it gets back from the server!
You've Built an MCP Server!
Congratulations! You've created the fundamental components of an agentic system. This simple pattern—an API server for tools and a client that calls it—is the core concept behind MCP. From here, you can add more tools, implement resource management, and build more sophisticated agent reasoning.