Lab 2: Building a Custom MCP Server
In this lab, you'll learn how to build your own custom MCP server that provides specialized functionality to Claude.
Prerequisites
- Completion of Lab 1
- Docker and Docker Compose installed
- Basic knowledge of Python
- Understanding of RESTful APIs
Introduction
One of the most powerful aspects of MCP is the ability to create custom tools that extend Claude's capabilities. In this lab, you'll build a weather service that provides real-time weather information.
Step 1: Setting Up Your Project Structure
Create a new directory for your custom MCP server:
mkdir weather-mcp-server
cd weather-mcp-server
Create the following file structure:
weather-mcp-server/
├ Dockerfile
├ requirements.txt
├ server.py
├ docker-compose.yml
Step 2: Creating the Server Code
First, create the requirements.txt
file:
flask==2.0.1
requests==2.26.0
pyyaml==6.0
Next, create the server code in server.py
:
from flask import Flask, request, jsonify
import requests
import os
import logging
app = Flask(__name__)
logging.basicConfig(level=logging.INFO)
# In a production environment, you would use a secure method to store this
WEATHER_API_KEY = os.environ.get('WEATHER_API_KEY', 'your_weather_api_key')
WEATHER_API_URL = "https://api.openweathermap.org/data/2.5/weather"
@app.route('/weather', methods=['POST'])
def get_weather():
try:
data = request.json
location = data.get('location', 'San Francisco')
units = data.get('units', 'metric')
# Make request to weather API
params = {
'q': location,
'appid': WEATHER_API_KEY,
'units': units
}
response = requests.get(WEATHER_API_URL, params=params)
weather_data = response.json()
# Format the response
if response.status_code == 200:
result = {
'location': location,
'temperature': weather_data['main']['temp'],
'description': weather_data['weather'][0]['description'],
'humidity': weather_data['main']['humidity'],
'wind_speed': weather_data['wind']['speed'],
'units': units
}
return jsonify(result)
else:
return jsonify({
'error': f"Weather API returned: {weather_data.get('message', 'Unknown error')}"
}), 400
except Exception as e:
app.logger.error(f"Error processing request: {str(e)}")
return jsonify({'error': str(e)}), 500
@app.route('/health', methods=['GET'])
def health_check():
return jsonify({'status': 'healthy'}), 200
if __name__ == '__main__':
app.run(host='0.0.0.0', port=8000)
Step 3: Creating the Dockerfile
Create a Dockerfile
to package your server:
FROM python:3.9-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY server.py .
EXPOSE 8000
CMD ["python", "server.py"]
Step 4: Setting Up Docker Compose
Create a docker-compose.yml
file that includes both your custom server and the MCP proxy:
version: '3'
services:
weather-server:
build: .
ports:
- "8000:8000"
environment:
- WEATHER_API_KEY=${WEATHER_API_KEY}
mcp-proxy:
image: anthropic/mcp-proxy:latest
ports:
- "8080:8080"
environment:
- MCP_API_KEY=${MCP_API_KEY}
- CLAUDE_API_KEY=${CLAUDE_API_KEY}
volumes:
- ./config:/app/config
depends_on:
- weather-server
Step 5: Creating the MCP Tool Configuration
Create a directory for your config:
mkdir config
Create a config/tools.json
file:
{
"tools": [
{
"name": "weather",
"service": "weather-server:8000",
"description": "Gets current weather information for a location",
"schema": {
"type": "function",
"function": {
"name": "get_weather",
"description": "Get current weather information for a location",
"parameters": {
"type": "object",
"properties": {
"location": {
"type": "string",
"description": "City name or location (e.g., 'New York', 'Tokyo')"
},
"units": {
"type": "string",
"enum": ["metric", "imperial"],
"description": "Temperature units (metric for Celsius, imperial for Fahrenheit)"
}
},
"required": ["location"]
}
}
},
"path": "/weather"
}
]
}
Step 6: Running Your Custom MCP Server
Build and start the services:
export WEATHER_API_KEY=your_openweathermap_api_key
export MCP_API_KEY=your_mcp_api_key
export CLAUDE_API_KEY=your_claude_api_key
docker-compose up --build
Step 7: Testing Your Custom Server
Create a file called test_weather.py
:
import requests
import json
API_KEY = "your_claude_api_key"
MCP_URL = "http://localhost:8080/v1/messages"
headers = {
"x-api-key": API_KEY,
"Content-Type": "application/json"
}
conversation = {
"model": "claude-3-opus-20240229",
"messages": [
{"role": "user", "content": "What's the weather like in Tokyo right now?"}
],
"tools": [
{
"name": "weather",
"description": "Gets current weather information for a location"
}
]
}
response = requests.post(MCP_URL, headers=headers, json=conversation)
result = response.json()
print(json.dumps(result, indent=2))
Run the script:
python test_weather.py
You'll need to sign up for a free API key from OpenWeatherMap (openweathermap.org) to use the weather API in this example.
Next Steps
- Add more functionality to your weather server, such as forecasts
- Create additional custom MCP servers for other domains
- Explore ways to make your server more robust with error handling
- Try integrating multiple custom servers together
Troubleshooting
- If you see connection errors, check that all services are running
- Verify that your API keys are correctly set
- Check the server logs for detailed error messages