Skip to main content
Glama

JEFit MCP Server

server.py9.54 kB
""" JEFit MCP Server - Workout tracking and analysis This MCP server connects to the JEFit API to retrieve workout history and detailed workout information. Provides tools for listing workout dates and analyzing individual workout sessions. MCP Server Configuration Examples: === Local/stdio Configuration === { "mcpServers": { "jefitWorkouts": { "type": "stdio", "command": "uv", "args": ["run", "--directory", "/path/to/jefit-mcp", "python", "server.py"] } } } === Local/stdio Configuration with Environment === { "mcpServers": { "jefitWorkouts": { "type": "stdio", "command": "uv", "args": ["run", "--directory", "/path/to/jefit-mcp", "python", "server.py"], "env": { "JEFIT_USERNAME": "your_username", "JEFIT_PASSWORD": "your_password", "JEFIT_TIMEZONE": "-04:00" } } } } === Remote/HTTP Configuration === { "mcpServers": { "jefitWorkouts": { "type": "http", "url": "http://localhost:8000/mcp/" } } } Environment Variables Required: - JEFIT_USERNAME: Your JEFit username - JEFIT_PASSWORD: Your JEFit password - JEFIT_TIMEZONE: Your timezone offset (e.g., "-04:00" for EDT) Place this configuration in: - VS Code: .vscode/mcp.json (project) or user settings - Claude Desktop: claude_desktop_config.json - Cursor: .cursor/mcp.json (project) or ~/.cursor/mcp.json (user) - LM Studio: ~/.lmstudio/mcp.json """ import os from datetime import datetime, date from fastmcp import FastMCP from fastmcp.tools.tool import ToolResult from mcp.types import TextContent from history import get_workout_history from workout_info import get_workout_for_date, load_exercise_db # Initialize FastMCP server mcp = FastMCP( name="JEFitWorkouts", instructions=""" Analyzes JEFit workout data and provides workout history. Use for tracking workout dates, analyzing exercise routines, and viewing detailed session information. All dates use YYYY-MM-DD format (ISO 8601). """ ) # Load exercise database once at startup EXERCISE_DB = load_exercise_db() @mcp.tool def list_workout_dates(start_date: str, end_date: str | None = None) -> list[str]: """ List all workout dates within a date range. Args: start_date: Start date in YYYY-MM-DD format (required) end_date: End date in YYYY-MM-DD format (optional, defaults to today) Returns: List of workout dates as strings in YYYY-MM-DD format """ # Default end_date to today if not provided if end_date is None: end_date = date.today().isoformat() # Validate date formats try: start = datetime.strptime(start_date, "%Y-%m-%d").date() end = datetime.strptime(end_date, "%Y-%m-%d").date() except ValueError as e: raise ValueError(f"Invalid date format. Use YYYY-MM-DD format: {e}") if start > end: raise ValueError("start_date must be before or equal to end_date") # Get all workout dates from API all_dates = get_workout_history() # Filter to date range filtered_dates = [] for workout_date_str in all_dates: workout_date = datetime.strptime(workout_date_str, "%Y-%m-%d").date() if start <= workout_date <= end: filtered_dates.append(workout_date_str) return sorted(filtered_dates) @mcp.tool def get_workout_info(date: str) -> ToolResult: """ Get detailed workout information for a specific date. Args: date: Date in YYYY-MM-DD format Returns: Markdown-formatted workout details including exercises, sets, reps, and weights """ # Validate date format try: datetime.strptime(date, "%Y-%m-%d") except ValueError as e: raise ValueError(f"Invalid date format. Use YYYY-MM-DD format: {e}") # Get workout data from API workout_data = get_workout_for_date(date) # Build markdown output output_lines = [] output_lines.append(f"# Workout for {date}\n") if 'data' not in workout_data or not workout_data['data']: output_lines.append("No workout found for this date.") markdown_text = "\n".join(output_lines) return ToolResult(content=[TextContent(type="text", text=markdown_text)]) for session in workout_data['data']: session_date = datetime.fromtimestamp(session['date']).strftime('%Y-%m-%d %H:%M:%S') duration_minutes = session['total_time'] // 60 duration_seconds = session['total_time'] % 60 total_weight = session['total_weight'] output_lines.append(f"**Started:** {session_date}") output_lines.append(f"**Duration:** {duration_minutes}m {duration_seconds}s") output_lines.append(f"**Weight Lifted:** {total_weight} lbs\n") output_lines.append("## Exercises\n") for i, log in enumerate(session['logs'], 1): exercise_id = log['exercise_id'] exercise = EXERCISE_DB.get(exercise_id, {}) name = exercise.get('name', f'Unknown Exercise ({exercise_id})') muscle_groups = ', '.join(exercise.get('body_parts', ['Unknown'])) equipment = ', '.join(exercise.get('equipment', ['Unknown'])) output_lines.append(f"### {i}. {name}") output_lines.append(f"- **Muscle Groups:** {muscle_groups}") output_lines.append(f"- **Equipment:** {equipment}") output_lines.append("") # Add sets information for j, s in enumerate(log['log_sets'], 1): weight = s.get('weight', 0) reps = s.get('reps', 0) output_lines.append(f" - Set {j}: {weight} lbs × {reps} reps") output_lines.append("") # Blank line between exercises markdown_text = "\n".join(output_lines) return ToolResult(content=[TextContent(type="text", text=markdown_text)]) @mcp.tool def get_batch_workouts(dates: list[str]) -> ToolResult: """ Get detailed workout information for multiple dates in a single call. Args: dates: List of dates in YYYY-MM-DD format Returns: Markdown-formatted workout details for all requested dates """ if not dates: raise ValueError("dates list cannot be empty") # Validate all date formats first for date_str in dates: try: datetime.strptime(date_str, "%Y-%m-%d") except ValueError as e: raise ValueError(f"Invalid date format '{date_str}'. Use YYYY-MM-DD format: {e}") # Build combined markdown output all_workouts = [] for date_str in sorted(dates): # Get workout data from API workout_data = get_workout_for_date(date_str) output_lines = [] output_lines.append(f"# Workout for {date_str}\n") if 'data' not in workout_data or not workout_data['data']: output_lines.append("No workout found for this date.\n") else: for session in workout_data['data']: session_date = datetime.fromtimestamp(session['date']).strftime('%Y-%m-%d %H:%M:%S') duration_minutes = session['total_time'] // 60 duration_seconds = session['total_time'] % 60 total_weight = session['total_weight'] output_lines.append(f"**Started:** {session_date}") output_lines.append(f"**Duration:** {duration_minutes}m {duration_seconds}s") output_lines.append(f"**Weight Lifted:** {total_weight} lbs\n") output_lines.append("## Exercises\n") for i, log in enumerate(session['logs'], 1): exercise_id = log['exercise_id'] exercise = EXERCISE_DB.get(exercise_id, {}) name = exercise.get('name', f'Unknown Exercise ({exercise_id})') muscle_groups = ', '.join(exercise.get('body_parts', ['Unknown'])) equipment = ', '.join(exercise.get('equipment', ['Unknown'])) output_lines.append(f"### {i}. {name}") output_lines.append(f"- **Muscle Groups:** {muscle_groups}") output_lines.append(f"- **Equipment:** {equipment}") output_lines.append("") # Add sets information for j, s in enumerate(log['log_sets'], 1): weight = s.get('weight', 0) reps = s.get('reps', 0) output_lines.append(f" - Set {j}: {weight} lbs × {reps} reps") output_lines.append("") # Blank line between exercises all_workouts.append("\n".join(output_lines)) # Join all workouts with separator markdown_text = "\n---\n\n".join(all_workouts) return ToolResult(content=[TextContent(type="text", text=markdown_text)]) def main(): """Main entry point for the MCP server""" mcp_host = os.getenv("HOST", "127.0.0.1") mcp_port = os.getenv("PORT", None) if mcp_port: # Run with HTTP transport mcp.run(port=int(mcp_port), host=mcp_host, transport="streamable-http") else: # Run with stdio transport (default) mcp.run() if __name__ == "__main__": main()

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/ai-mcp-garage/jefit-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server