main.py•4.7 kB
from fastmcp import FastMCP
from pydantic import BaseModel, Field
from typing import Optional
# Create MCP server
mcp = FastMCP("User Profile MCP Server")
# Pydantic models for type safety and validation
class UserProfile(BaseModel):
"""User profile data model"""
age: int = Field(ge=0, le=150, description="User's age in years")
weight: int = Field(ge=20, le=300, description="User's weight in kilograms")
height: int = Field(ge=100, le=250, description="User's height in centimeters")
class UserInfoRequest(BaseModel):
"""Request model for user info lookup"""
name: str = Field(min_length=1, description="Name of the user to look up")
class UserInfoResponse(BaseModel):
"""Response model for user information"""
name: str = Field(description="User's name (capitalized)")
age: int = Field(description="User's age in years")
weight: int = Field(description="User's weight in kilograms")
height: int = Field(description="User's height in centimeters")
bmi: float = Field(description="Calculated Body Mass Index")
bmi_category: str = Field(description="BMI category (Underweight, Normal, Overweight, Obese)")
class ErrorResponse(BaseModel):
"""Error response model"""
error: str = Field(description="Error message")
# Stateless user profiles - moved to a function to make it stateless
def get_user_profiles() -> dict[str, dict]:
"""Get user profiles data - stateless function"""
return {
"alex": {"age": 20, "weight": 80, "height": 150},
"maria": {"age": 25, "weight": 65, "height": 165},
"john": {"age": 30, "weight": 90, "height": 180},
"sarah": {"age": 22, "weight": 55, "height": 160},
"mike": {"age": 28, "weight": 75, "height": 175},
"lisa": {"age": 24, "weight": 60, "height": 155},
"david": {"age": 32, "weight": 85, "height": 185},
"emma": {"age": 26, "weight": 58, "height": 162},
"tom": {"age": 29, "weight": 70, "height": 170},
"anna": {"age": 23, "weight": 62, "height": 158}
}
def calculate_bmi_category(bmi: float) -> str:
"""Calculate BMI category based on BMI value"""
if bmi < 18.5:
return "Underweight"
elif bmi < 25:
return "Normal"
elif bmi < 30:
return "Overweight"
else:
return "Obese"
@mcp.tool()
def get_user_info(request: UserInfoRequest) -> UserInfoResponse | ErrorResponse:
"""
Get user information including age, weight, height, and calculated BMI.
Args:
request: UserInfoRequest containing the user's name
Returns:
UserInfoResponse with user data and BMI calculation, or ErrorResponse if user not found
"""
name_lower = request.name.lower()
user_profiles = get_user_profiles() # Stateless - get data each time
if name_lower not in user_profiles:
return ErrorResponse(error="User not found")
user_data = user_profiles[name_lower]
age = user_data["age"]
weight = user_data["weight"]
height = user_data["height"]
# Calculate BMI: weight (kg) / height (m)^2
# Height is in cm, so we need to convert to meters
height_in_meters = height / 100
bmi = round(weight / (height_in_meters ** 2), 2)
return UserInfoResponse(
name=name_lower.capitalize(),
age=age,
weight=weight,
height=height,
bmi=bmi,
bmi_category=calculate_bmi_category(bmi)
)
@mcp.tool()
def list_users() -> list[str]:
"""
List all available users in the system.
Returns:
List of available user names
"""
user_profiles = get_user_profiles() # Stateless - get data each time
return list(user_profiles.keys())
@mcp.tool()
def add_user(name: str, age: int, weight: int, height: int) -> str:
"""
Add a new user to the system (for demonstration - in real app this would persist).
Args:
name: User's name
age: User's age
weight: User's weight in kg
height: User's height in cm
Returns:
Confirmation message
"""
# In a real application, this would persist to a database
# For this demo, we'll just validate and return a message
profile = UserProfile(age=age, weight=weight, height=height)
height_in_meters = height / 100
bmi = round(weight / (height_in_meters ** 2), 2)
category = calculate_bmi_category(bmi)
return f"User {name} added successfully. BMI: {bmi} ({category})"
if __name__ == "__main__":
# Run as HTTP MCP server with stateless configuration
mcp.run(
transport="http",
host="0.0.0.0",
port=8000,
stateless_http=True
)