Generic MCP server for tracking any entity type with schema-less JSON Hybrid storage.
Track body weight, daily scorecards, fitness sessions, books, or any custom entity without defining rigid schemas. Auto-discovery, self-documenting, and SQL-queryable.
- Schema-less Design: Track any entity type (weight, scorecard, fitness, books, custom) without ALTER TABLE
- Auto-Discovery: Entity types automatically registered on first use
- Self-Documenting: MCP Resources expose schema examples and usage guides
- SQL-Queryable: Use
json_extract()for advanced analytics - Local-First: Privacy-friendly, zero external dependencies
- Hybrid Storage: SQLite with JSON columns for flexibility + performance
- CRUD Operations: Insert, update, query, delete via MCP Tools
- Built-in Prompts: Pre-configured templates for common tracking scenarios
# Run directly without installation
uvx tracking-mcp
# Or install globally
pip install tracking-mcpgit clone https://github.com/mindfullabai/tracking-mcp.git
cd tracking-mcp
pip install -e .Database is auto-created on first use at the path specified in DB_PATH environment variable (defaults to ~/tracking.db).
Add to your Claude Desktop MCP settings (.mcp.json or ~/Library/Application Support/Claude/claude_desktop_config.json on macOS):
{
"mcpServers": {
"tracking-mcp": {
"type": "stdio",
"command": "uvx",
"args": ["tracking-mcp"],
"env": {
"DB_PATH": "/path/to/your/data/tracking.db"
}
}
}
}Alternative (with pip install):
{
"mcpServers": {
"tracking-mcp": {
"type": "stdio",
"command": "tracking-mcp",
"env": {
"DB_PATH": "/path/to/your/data/tracking.db"
}
}
}
}From Claude Desktop, you can now:
Track my weight: 72.5kg today
Show me my weight trend for the last 30 days
Log workout: HYROX for 45 minutes today
Insert or update tracking event for any entity type.
Parameters:
entity_type(string, required): Entity type (e.g., 'weight', 'scorecard', 'fitness', 'book', or custom)date(string, required): Event date in YYYY-MM-DD formatdata(object, required): Entity-specific data (schema-free JSON)entity_id(string, optional): Unique ID for entity instance (e.g., 'book_atomic_habits')
Example:
track_event(
entity_type="weight",
date="2026-01-14",
data={"weight_kg": 72.8, "day_type": "MAR", "source": "manual"}
)Query tracking events with filters.
Parameters:
entity_type(string, optional): Filter by entity typeentity_id(string, optional): Filter by entity IDstart_date(string, optional): Start date (inclusive)end_date(string, optional): End date (inclusive)limit(integer, optional): Maximum results (default: 100)
Example:
query_events(
entity_type="weight",
start_date="2025-12-15",
end_date="2026-01-14",
limit=30
)Delete tracking event by ID.
Parameters:
event_id(integer, required): Event ID to delete
Get all registered entity types with schema examples.
Returns: JSON array of entity types with descriptions and schema examples.
List of all registered entity types with schema examples (JSON).
Usage guide for tracking new entity types dynamically (Markdown).
Current statistics: total events, entity types, date range, events by type (JSON).
Template for tracking body weight.
Arguments: weight_kg, date
Template for logging workout session.
Arguments: workout_type, duration_min, date
Get trend data for entity type over date range.
Arguments: entity_type, days (default: 30)
| Column | Type | Description |
|---|---|---|
id |
INTEGER PRIMARY KEY | Auto-increment ID |
entity_type |
TEXT | Entity type ('weight', 'scorecard', etc.) |
entity_id |
TEXT | Optional unique ID for entity instances |
date |
DATE | Event date (YYYY-MM-DD) |
data |
JSON | Schema-free JSON data |
created_at |
TIMESTAMP | Auto-generated creation timestamp |
updated_at |
TIMESTAMP | Auto-updated modification timestamp |
Indexes: entity_type, date, entity_id
| Column | Type | Description |
|---|---|---|
entity_type |
TEXT PRIMARY KEY | Entity type name |
description |
TEXT | Human-readable description |
schema_example |
JSON | Example JSON schema |
created_at |
TIMESTAMP | Registration timestamp |
updated_at |
TIMESTAMP | Last update timestamp |
Pre-seeded entity types: weight, scorecard, fitness, book
# Sleep quality tracking (auto-registered)
track_event(
entity_type="sleep_quality",
date="2026-01-14",
data={
"hours": 7.5,
"quality_score": 8,
"dreams": True,
"interruptions": 2,
"notes": "Felt refreshed"
}
)# Reading progress for specific book
track_event(
entity_type="book",
entity_id="book_atomic_habits",
date="2026-01-14",
data={
"title": "Atomic Habits",
"author": "James Clear",
"current_page": 150,
"total_pages": 320,
"rating": 5
}
)# Get all weight entries for January 2026
query_events(
entity_type="weight",
start_date="2026-01-01",
end_date="2026-01-31"
)
# Get all entries for specific book
query_events(
entity_type="book",
entity_id="book_atomic_habits"
)To update an event, call track_event() with the same entity_type + date (+ entity_id if used). The tool will automatically UPDATE instead of INSERT.
Since data is stored in SQLite with JSON columns, you can run advanced analytics:
SELECT
date,
json_extract(data, '$.weight_kg') as weight,
json_extract(data, '$.delta_kg') as delta
FROM tracking_events
WHERE entity_type = 'weight'
AND date >= date('now', '-30 days')
ORDER BY date DESC;SELECT
strftime('%Y-W%W', date) as week,
AVG(CAST(json_extract(data, '$.total_score') AS INTEGER)) as avg_score,
COUNT(*) as days
FROM tracking_events
WHERE entity_type = 'scorecard'
AND date >= date('now', 'weekday 0', '-7 days')
GROUP BY week;SELECT
json_extract(data, '$.workout_type') as type,
COUNT(*) as sessions,
SUM(CAST(json_extract(data, '$.duration_min') AS INTEGER)) as total_minutes,
AVG(CAST(json_extract(data, '$.duration_min') AS INTEGER)) as avg_minutes
FROM tracking_events
WHERE entity_type = 'fitness'
AND date >= date('now', 'start of month')
GROUP BY type;tracking-mcp/
├── data/
│ ├── tracking.db # SQLite database
│ └── schema.sql # Database schema
├── tracking_mcp/
│ ├── tracking_server.py # MCP server implementation
│ └── __init__.py
├── tests/
│ └── test_server.py
├── pyproject.toml
├── LICENSE
├── CHANGELOG.md
└── README.md
- ✅ Flexibility: Add new entity types without schema migrations
- ✅ Performance: SQLite indexes +
json_extract()for fast queries - ✅ SQL-queryable: Standard SQL for analytics
- ❌ EAV alternative: Too many JOINs, poor performance for analytics
- ✅ Auto-discovery: New entity types registered automatically
- ✅ Self-documenting: Resources expose schemas and usage
- ✅ Dynamic: No rigid schema required
- ❌ Official SQLite MCP: Requires predefined schema
- ✅ Zero setup: File-based, no server required
- ✅ Local-first: Privacy-friendly for personal tracking
- ✅ Sufficient: Perfect for single-user personal use
- ❌ PostgreSQL: Unnecessary overhead for personal tracking
pytest# Format code
black mcp_server/
# Lint
ruff check mcp_server/pip install -e ".[dev]"If you see this error when running the server:
<coroutine object main at 0x...>
RuntimeWarning: coroutine 'main' was never awaited
This was fixed in version 1.0.1. Update to the latest version:
pip install --upgrade tracking-mcp
# or with uvx
uvx --refresh tracking-mcpRoot cause: Python CLI entry points from setuptools expect synchronous main() functions. Version 1.0.1+ includes a sync wrapper that properly handles the async MCP server.
See CHANGELOG.md for version history.
Current version: 1.0.1 (Async entry point fix)
- viz-mcp: Companion MCP server for auto-generating data visualizations from tracking data
- work-hub: Personal productivity system using tracking-mcp for daily scorecard and habit tracking
MIT License - see LICENSE file for details.
Mario Mosca - GitHub
Contributions welcome! Please open an issue or pull request.
For issues, questions, or feature requests, please open an issue on GitHub: https://github.com/mariomosca/tracking-mcp/issues