Skip to main content
Glama
MayankKapgate

fitness-tracker-mcp

🏋️ Fitness Tracker — MCP Server

A fully offline Model Context Protocol (MCP) server that enables any MCP‑compatible AI client — such as Claude Code, Claude Desktop, or Cursor — to log workouts, track dietary macros, and retrieve daily health summaries, all backed by a local SQLite database with zero network dependency.


📖 Table of Contents


Related MCP server: Nutrition MCP

💡 Why This Project?

Large Language Models are great at conversation, but they can't natively persist user data across sessions. The Model Context Protocol bridges that gap by letting LLMs call external tools — turning the AI into a true assistant that can read, write, and query structured data on the user's behalf.

This project demonstrates a practical MCP integration: a fitness tracker that an AI assistant can operate hands‑free. Ask your AI "Log a 30‑minute run that burned 300 calories" and it will validate the data, store it in SQLite, and confirm — all without you ever opening a spreadsheet.


✨ Key Features

Feature

Description

Workout Logging

Record exercise sessions with type, duration, and calories burned

Macro Tracking

Log daily protein, carbs, and fat intake per meal or per day

Daily Summaries

Aggregated view of workouts + nutrition with calorie calculations

Fully Offline

Stdio transport — no network calls, no API keys, no cloud dependency

Strict Validation

Pydantic v2 schemas catch malformed LLM outputs before they reach the DB

SQL Injection Safe

Parameterised queries throughout — user input never touches raw SQL

Comprehensive Tests

22 Pytest cases covering schema validation, DB logic, and edge cases


🧱 Technology Stack

Layer

Technology

Purpose

MCP Framework

FastMCP

Exposes Python functions as MCP tools over stdio

Database

SQLite 3

Lightweight, zero‑config local persistence

Validation

Pydantic v2

Schema enforcement and type coercion for LLM inputs

Testing

Pytest

Isolated, per‑test in‑memory databases

Language

Python 3.10+

Core runtime


🏗️ Architecture Overview

The system follows a layered architecture with clear separation of concerns:

graph TB
    subgraph Client Layer
        A["🤖 MCP Client<br/>(Claude Code / Claude Desktop / Cursor)"]
    end

    subgraph Transport Layer
        B["📡 stdio<br/>(JSON-RPC over stdin/stdout)"]
    end

    subgraph MCP Server ["MCP Server (server.py)"]
        direction TB
        C["🔧 FastMCP Tool Router<br/>Routes tool calls to handlers"]
        D["📋 Pydantic Schemas<br/>WorkoutInput · MacrosInput · DailySummaryRequest"]
        E["⚙️ Core Business Logic<br/>insert_workout · insert_macros · fetch_daily_summary"]
        F["🗄️ Database Layer<br/>get_connection · init_db"]
    end

    subgraph Storage
        G[("💾 SQLite<br/>fitness_tracker.db")]
    end

    A <-->|"JSON-RPC"| B
    B <-->|"Tool calls & responses"| C
    C --> D
    D -->|"Validated data"| E
    E <--> F
    F <--> G

    style A fill:#4A90D9,stroke:#2C5F8A,color:#fff
    style B fill:#F5A623,stroke:#C77E1A,color:#fff
    style C fill:#7B68EE,stroke:#5A4DB2,color:#fff
    style D fill:#50C878,stroke:#3A9458,color:#fff
    style E fill:#FF6B6B,stroke:#CC5555,color:#fff
    style F fill:#DDA0DD,stroke:#AA70AA,color:#fff
    style G fill:#87CEEB,stroke:#5F9EAF,color:#000

Layer Responsibilities

Layer

Component

Responsibility

Client

Claude Code / Desktop

Sends natural language → MCP tool calls

Transport

stdio (JSON-RPC)

Serialises tool calls over stdin/stdout — no HTTP, no ports

Router

FastMCP

Matches incoming tool names to Python handler functions

Validation

Pydantic Schemas

Parses & validates every input field before DB access

Business Logic

Core Functions

Executes inserts, aggregations, and calorie calculations

Storage

SQLite via sqlite3

Persists data in a single fitness_tracker.db file


🔄 Data Flow

A step‑by‑step trace of what happens when a user says "Log a 30‑minute run":

sequenceDiagram
    participant User
    participant Client as MCP Client (Claude)
    participant Transport as stdio (JSON-RPC)
    participant Router as FastMCP Router
    participant Schema as Pydantic Validator
    participant Logic as Business Logic
    participant DB as SQLite DB

    User->>Client: "Log a 30-minute run that burned 300 calories"
    Client->>Transport: tool_call: log_workout(date, type, duration, calories)
    Transport->>Router: Deserialize JSON-RPC request
    Router->>Schema: WorkoutInput(date, type, duration, calories)

    alt Validation Fails
        Schema-->>Router: ❌ ValidationError (clear message)
        Router-->>Transport: Error response
        Transport-->>Client: Display error to user
    end

    Schema-->>Router: ✅ Validated WorkoutInput object
    Router->>Logic: insert_workout(validated_data)
    Logic->>DB: INSERT INTO workouts (date, type, duration, calories) VALUES (?, ?, ?, ?)
    DB-->>Logic: Row ID
    Logic-->>Router: {status: success, workout: {...}}
    Router-->>Transport: JSON-RPC response
    Transport-->>Client: "Logged: 30 min running — 300 kcal burned ✅"
    Client-->>User: Confirmation message

🗃️ Database Schema

The SQLite database (fitness_tracker.db) is auto‑created on first run and contains two tables:

erDiagram
    WORKOUTS {
        INTEGER id PK "Auto-increment"
        TEXT date "YYYY-MM-DD (NOT NULL)"
        TEXT type "e.g. running, cycling (NOT NULL)"
        REAL duration "Minutes, > 0 (NOT NULL)"
        REAL calories "kcal burned, >= 0 (NOT NULL)"
    }

    MACROS {
        INTEGER id PK "Auto-increment"
        TEXT date "YYYY-MM-DD (NOT NULL)"
        REAL protein "Grams, >= 0 (NOT NULL)"
        REAL carbs "Grams, >= 0 (NOT NULL)"
        REAL fat "Grams, >= 0 (NOT NULL)"
    }

Calorie Calculation

The daily summary computes estimated calories consumed from macros using standard Atwater factors:

$$\text{Calories} = (\text{Protein} \times 4) + (\text{Carbs} \times 4) + (\text{Fat} \times 9) ;\text{kcal}$$


📂 Project Structure

MCP_Project/
├── server.py              # MCP server — tools, schemas, DB helpers, entrypoint
├── test_server.py         # Pytest suite (22 tests across 6 test classes)
├── requirements.txt       # Python dependencies (fastmcp, pydantic, pytest)
├── fitness_tracker.db     # SQLite database (auto-created on first run)
├── .gitignore             # Ignores venv, __pycache__, .env
├── .env                   # Environment variables (git-ignored)
└── README.md              # This file

File Breakdown

File

Lines

Description

server.py

~322

Complete MCP server: DB init, Pydantic models, CRUD operations, FastMCP tool definitions, and stdio entrypoint

test_server.py

~265

22 tests in 6 classes — schema validation (valid + invalid inputs), DB inserts, daily aggregation, date isolation, and SQL injection safety

requirements.txt

3

fastmcp, pydantic, pytest


🚀 Getting Started

Prerequisites

  • Python 3.10+ installed

  • pip package manager

1. Clone the Repository

git clone https://github.com/MayankKapgate/fitness-tracker-mcp.git
cd MCP_Project
# Windows
python -m venv myvenv
myvenv\Scripts\activate

# macOS / Linux
python3 -m venv myvenv
source myvenv/bin/activate

3. Install Dependencies

pip install -r requirements.txt

4. Run the Test Suite

pytest test_server.py -v

You should see 22 tests pass

5. Start the Server (Standalone)

python server.py

Note: The server uses stdio transport — it reads JSON‑RPC from stdin and writes to stdout. You won't see a shell prompt; this is by design for MCP client consumption.


🔌 Connecting to MCP Clients

Claude Code

Register the server once from your terminal:

claude mcp add fitness-tracker --transport stdio -- python server.py

Tip: If Claude Code isn't launched from the project directory, use the full path:

claude mcp add fitness-tracker --transport stdio -- python "C:\Users\Mayan\OneDrive\Documents\MCP_Project\server.py"

Claude Desktop

Add the following to your claude_desktop_config.json:

{
  "mcpServers": {
    "fitness-tracker": {
      "command": "python",
      "args": ["C:\\Users\\Mayan\\OneDrive\\Documents\\MCP_Project\\server.py"],
      "transport": "stdio"
    }
  }
}

Other MCP Clients

Any MCP‑compatible client can connect using:

  • Transport: stdio

  • Command: python server.py (or full path to server.py)


🛠️ Tool Reference (API)

The server exposes 3 MCP tools:

1. log_workout

Log a single workout session.

Parameter

Type

Constraints

Example

date

string

ISO 8601 (YYYY-MM-DD)

"2026-08-04"

type

string

1–100 characters

"running"

duration

float

> 0 (minutes)

30.0

calories

float

≥ 0 (kcal)

300.0

Returns:

{
  "status": "success",
  "workout": {
    "id": 1,
    "date": "2026-08-04",
    "type": "running",
    "duration": 30.0,
    "calories": 300.0
  }
}

2. log_macros

Log dietary macronutrients for a meal or full day.

Parameter

Type

Constraints

Example

date

string

ISO 8601 (YYYY-MM-DD)

"2026-08-04"

protein

float

≥ 0 (grams)

150.0

carbs

float

≥ 0 (grams)

200.0

fat

float

≥ 0 (grams)

60.0

Returns:

{
  "status": "success",
  "macros": {
    "id": 1,
    "date": "2026-08-04",
    "protein": 150.0,
    "carbs": 200.0,
    "fat": 60.0
  }
}

3. get_daily_summary

Retrieve a combined workout and nutrition summary for a given date.

Parameter

Type

Constraints

Example

date

string

ISO 8601 (YYYY-MM-DD)

"2026-08-04"

Returns:

{
  "date": "2026-08-04",
  "workouts": {
    "count": 2,
    "entries": [
      {"id": 1, "date": "2026-08-04", "type": "running", "duration": 30.0, "calories": 300.0},
      {"id": 2, "date": "2026-08-04", "type": "weights", "duration": 45.0, "calories": 250.0}
    ],
    "total_duration_min": 75.0,
    "total_calories_burned": 550.0
  },
  "macros": {
    "count": 1,
    "entries": [
      {"id": 1, "date": "2026-08-04", "protein": 150.0, "carbs": 200.0, "fat": 60.0}
    ],
    "total_protein_g": 150.0,
    "total_carbs_g": 200.0,
    "total_fat_g": 60.0,
    "total_calories_consumed": 1940.0
  }
}

💬 Example Usage

Once connected, simply chat with your AI assistant naturally:

You Say

Tool Called

What Happens

"I did a 30‑minute run and burned 300 calories"

log_workout

Stores the workout for today's date

"Log my lunch: 40g protein, 60g carbs, 15g fat"

log_macros

Records one macro entry

"How did I do today?"

get_daily_summary

Returns aggregated totals for the current date

"What was my workout on August 4th?"

get_daily_summary

Fetches data for 2026-08-04


🧪 Testing

The test suite (test_server.py) contains 22 tests across 6 test classes, using isolated temporary SQLite databases per test:

Test Class

Tests

What It Covers

TestWorkoutSchema

10

Valid workout, bad dates, negative/zero duration, negative calories, empty/too-long type, missing fields, wrong types

TestMacrosSchema

6

Valid macros, invalid date, negative protein/carbs/fat, missing fields

TestDailySummarySchema

2

Valid request, garbage date

TestWorkoutDB

3

Insert & retrieve, multiple inserts, SQL injection safety

TestMacrosDB

2

Insert & retrieve, SQL injection via date field

TestDailySummary

3

Empty day, populated day with aggregation, cross-date isolation

Running Tests

# Run all tests with verbose output
pytest test_server.py -v

# Run a specific test class
pytest test_server.py::TestWorkoutSchema -v

# Run with coverage (requires pytest-cov)
pip install pytest-cov
pytest test_server.py --cov=server --cov-report=term-missing

🔒 Security & Safety

Concern

Mitigation

SQL Injection

All database queries use parameterised ? placeholders — user input is never interpolated into SQL strings

Malformed LLM Output

Every tool input passes through a Pydantic v2 schema with strict field validators before reaching the database

Date Validation

Custom @field_validator ensures ISO 8601 compliance; garbage strings like "yesterday" or "'; DROP TABLE" are rejected

Type Coercion

Pydantic's strict mode catches truly incompatible types (e.g., "slow" for a float field)

Network Exposure

stdio transport — zero network traffic, no open ports, no API keys required

Data Privacy

All data stays in a local fitness_tracker.db file on your machine — nothing leaves your system


📝 License

MIT — use freely.

F
license - not found
Not graded
quality - not tested
C
maintenance

Maintenance

Maintainers
Response time
Release cycle
Releases (12mo)
Commit activity

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Related MCP Servers

  • F
    license
    B
    quality
    D
    maintenance
    A personal fitness tracking server that enables logging and querying workouts, nutrition, and body metrics through a local SQLite database. Integrates with OpenNutrition MCP for food logging and supports exercise history tracking for workout progression.
    17
  • A
    license
    Not graded
    quality
    B
    maintenance
    A filesystem-based MCP server that turns any MCP-capable AI agent into a conversational calorie and protein tracker with natural-language estimates, confidence-aware logging, daily/weekly progress, food-history search, and export, working offline with local fallback data.
    20
    MIT
  • F
    license
    Not graded
    quality
    B
    maintenance
    Local-first nutrition tracker MCP server for Hermes, enabling food, alias, recipe, and meal log management with SQLite persistence.
  • F
    license
    A
    quality
    B
    maintenance
    Personal workout coach MCP server that logs exercises in natural language, tracks progress with SQLite, and provides coaching signals like estimated 1RM and volume trends.
    6

View all related MCP servers

Related MCP Connectors

  • MCP server for Withings health data — sleep, activity, heart, and body metrics.

  • GibsonAI MCP server: manage your databases with natural language

  • MCP server for Argo RPG Platform — connects AI assistants to campaign data via OAuth2

View all MCP Connectors

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/MayankKapgate/fitness-tracker-mcp'

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