Skip to main content
Glama

log_workout

Record workout details including exercises, sets, and metrics to track fitness progress in a personal database.

Instructions

Log a complete workout with exercises and sets.

Returns the fully logged workout with all exercises and sets for confirmation.

Args: date_time: ISO datetime string (e.g., "2026-01-06T18:30:00") workout_type: Optional type/category for the workout tags: Optional list of tags (e.g., ["legs", "sprint"]) notes: Optional notes for the workout exercises: List of exercises with sets. Each exercise should have: - name: str (required) - category: Optional[str] - notes: Optional[str] - sets: List of sets with fields like reps, weight_kg, weight_lbs, distance_yards, side, etc.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
date_timeYes
workout_typeNo
tagsNo
notesNo
exercisesNo

Implementation Reference

  • The 'log_workout' tool implementation, which processes the input arguments, handles potential JSON strings for tags/exercises, inserts the workout and exercise/set data into the SQLite database, and returns the fully hydrated workout details.
    @app.tool()
    def log_workout(
        date_time: str,
        workout_type: Optional[str] = None,
        tags: Optional[Any] = None,
        notes: Optional[str] = None,
        exercises: Optional[Any] = None,
    ) -> dict[str, Any]:
        """Log a complete workout with exercises and sets.
    
        Returns the fully logged workout with all exercises and sets for confirmation.
    
        Args:
            date_time: ISO datetime string (e.g., "2026-01-06T18:30:00")
            workout_type: Optional type/category for the workout
            tags: Optional list of tags (e.g., ["legs", "sprint"])
            notes: Optional notes for the workout
            exercises: List of exercises with sets. Each exercise should have:
                - name: str (required)
                - category: Optional[str]
                - notes: Optional[str]
                - sets: List of sets with fields like reps, weight_kg, weight_lbs, distance_yards, side, etc.
        """
        # Handle JSON string inputs (for clients that serialize arrays as strings)
        tags = _parse_json_array(tags)
        exercises = _parse_json_array(exercises)
        
        # Validate types after parsing
        if tags is not None and not isinstance(tags, list):
            raise TypeError(f"tags must be a list, got {type(tags).__name__}")
        if exercises is not None and not isinstance(exercises, list):
            raise TypeError(f"exercises must be a list, got {type(exercises).__name__}")
        
        date_time = _ensure_iso_date(date_time)
        tags_json = serialize_tags(tags)
    
        conn = get_connection()
        cursor = conn.cursor()
        cursor.execute(
            "INSERT INTO workouts (date_time, workout_type, tags, notes) VALUES (?, ?, ?, ?)",
            (date_time, workout_type, tags_json, notes),
        )
        workout_id = cursor.lastrowid
    
        if exercises:
            for order_index, exercise in enumerate(exercises, start=1):
                exercise_name = exercise.get("name")
                if not exercise_name:
                    raise ValueError(f"Exercise at index {order_index} is missing required 'name' field")
                
                cursor.execute(
                    "INSERT INTO exercises (workout_id, order_index, name, category, notes) VALUES (?, ?, ?, ?, ?)",
                    (workout_id, order_index, exercise_name, exercise.get("category"), exercise.get("notes")),
                )
                exercise_id = cursor.lastrowid
    
                for set_index, set_payload in enumerate(exercise.get("sets", []), start=1):
                    cursor.execute(
                        """INSERT INTO sets (
                            exercise_id, set_index, reps, weight_kg, weight_lbs,
                            distance_m, distance_yards, duration_s,
                            side, rpe, rir, is_warmup
                        ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)""",
                        (
                            exercise_id,
                            set_payload.get("set_index") or set_index,
                            set_payload.get("reps"),
                            set_payload.get("weight_kg"),
                            set_payload.get("weight_lbs"),
                            set_payload.get("distance_m"),
                            set_payload.get("distance_yards"),
                            set_payload.get("duration_s"),
                            set_payload.get("side"),
                            set_payload.get("rpe"),
                            set_payload.get("rir"),
                            1 if set_payload.get("is_warmup") else 0,
                        ),
                    )
    
        conn.commit()
        
        # Return the fully hydrated workout for confirmation
        workout = cursor.execute("SELECT * FROM workouts WHERE id = ?", (workout_id,)).fetchone()
        result = _hydrate_workout(conn, _row_to_dict(workout))
        
        conn.close()
        return result

Latest Blog Posts

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/JohnZolton/MCP-logger'

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