ATracker MCP Server
by braveva
README.md
# ATracker MCP Server
MCP server + standalone CLI for syncing [ATracker](https://atracker.pro) time tracking data to a local SQLite database. Works with Claude Desktop, Claude Code, or any MCP client.
## What it does
1. Calls the ATracker cloud API (reverse-engineered web sync endpoint)
2. Stores tasks, entries, tags, and task-tag relationships in a local SQLite DB
3. Supports incremental sync (only fetches changes since last sync) and full sync
4. Exposes 7 MCP tools for querying/exporting the data
5. Also works as a standalone CLI script (`atracker_sync.py`)
## Files
```
├── src/atracker_mcp/
│ ├── __init__.py # Package init, version
│ └── server.py # MCP server (7 tools: sync, timeline, summary, etc.)
├── atracker_sync.py # Standalone sync CLI (no MCP dependency needed)
├── ecosystem.config.js # PM2 config (optional, for auto-restart)
├── .env.example # Template for credentials
├── .gitignore # Excludes .env, *.db, venv, exports
├── pyproject.toml # Python package config (hatchling)
├── requirements.txt # Minimal deps: mcp, requests, python-dotenv
└── data/ # Created at runtime
├── atracker.db # SQLite database (gitignored)
└── .sync_state.json # Last sync timestamp (gitignored)
```
## Setup
### 1. Install dependencies
```bash
python3 -m venv venv
source venv/bin/activate
pip install -r requirements.txt
```
### 2. Get credentials
1. Open https://atracker.pro/Web/ and log in
2. Open browser DevTools (F12) → **Network** tab → filter "Fetch/XHR"
3. Do any action in the app (or refresh the page)
4. Click the `UpdateFromIOS` request
5. From **Headers**: copy the `ARRAffinity` cookie value
6. From **Payload**: copy the `uuid` value
### 3. Configure
```bash
cp .env.example .env
```
Edit `.env`:
```env
ATRACKER_COOKIE=your_arraffinity_cookie_value
ATRACKER_UUID=your-uuid-from-payload
```
## Running
### Standalone CLI (simplest)
```bash
# First sync — pulls all historical data
python3 atracker_sync.py --full
# Subsequent syncs — only new changes
python3 atracker_sync.py
# Export to CSV
python3 atracker_sync.py --export timeline.csv
# Export last 7 days to JSON
python3 atracker_sync.py --export week.json --days 7
# Export today only
python3 atracker_sync.py --export today.csv --today
```
### Cron (production setup)
Add to crontab for automatic sync every 15 minutes:
```bash
crontab -e
```
```cron
*/15 * * * * cd /path/to/ATracker-MCP && source venv/bin/activate && python3 atracker_sync.py >> /var/log/atracker-sync.log 2>&1
```
### PM2 (alternative)
```bash
pm2 start ecosystem.config.js
```
This runs the sync script and restarts it every 5 minutes (via `restart_delay`).
### MCP Server (for Claude Desktop / Claude Code)
Add to Claude Desktop config (`~/Library/Application Support/Claude/claude_desktop_config.json`):
```json
{
"mcpServers": {
"atracker": {
"command": "python3",
"args": ["-m", "atracker_mcp.server"],
"cwd": "/path/to/ATracker-MCP",
"env": {
"PYTHONPATH": "/path/to/ATracker-MCP/src"
}
}
}
}
```
## MCP Tools
| Tool | Description |
|------|-------------|
| `atracker_sync` | Sync from ATracker cloud. `full=true` for all data, default incremental |
| `atracker_current_task` | Get the currently running/active task |
| `atracker_timeline` | Query entries. Filters: `limit`, `date` (YYYY-MM-DD), `days`, `task` (name search) |
| `atracker_summary` | Time totals grouped by task. Filters: `date`, `days` |
| `atracker_tasks` | List all tasks with entry counts |
| `atracker_export` | Export to CSV/JSON. Filters: `format`, `days`, `output` filename |
| `atracker_stats` | DB stats: task/entry counts, date range, last sync time |
## Database Schema
SQLite with 4 tables:
```sql
tasks (task_id TEXT PK, name TEXT, category TEXT, color TEXT, icon TEXT, tag_id TEXT)
entries (entry_id TEXT PK, task_id TEXT FK, start_time REAL, end_time REAL, notes TEXT)
tags (tag_id TEXT PK, name TEXT, color TEXT)
task_tags (task_id TEXT, tag_id TEXT, PK(task_id, tag_id))
```
- `start_time` / `end_time` are Unix timestamps (seconds)
- Running tasks have `end_time = 0` or `end_time < start_time`
- Indexes on `entries(start_time)` and `entries(task_id)`
## Querying Tags
Tags are linked to tasks via the `task_tags` junction table. Here's how to query them:
### Get all tasks with their tags
```sql
SELECT
t.task_id,
t.name AS task_name,
GROUP_CONCAT(g.name) AS tags
FROM tasks t
LEFT JOIN task_tags tt ON t.task_id = tt.task_id
LEFT JOIN tags g ON tt.tag_id = g.tag_id
GROUP BY t.task_id, t.name;
```
### Get timeline entries with tags
```sql
SELECT
e.entry_id,
t.name AS task_name,
datetime(e.start_time, 'unixepoch') AS started,
datetime(e.end_time, 'unixepoch') AS ended,
GROUP_CONCAT(g.name) AS tags
FROM entries e
JOIN tasks t ON e.task_id = t.task_id
LEFT JOIN task_tags tt ON t.task_id = tt.task_id
LEFT JOIN tags g ON tt.tag_id = g.tag_id
GROUP BY e.entry_id
ORDER BY e.start_time DESC
LIMIT 20;
```
### Filter entries by tag
```sql
SELECT
t.name AS task_name,
SUM(e.end_time - e.start_time) / 3600.0 AS hours
FROM entries e
JOIN tasks t ON e.task_id = t.task_id
JOIN task_tags tt ON t.task_id = tt.task_id
JOIN tags g ON tt.tag_id = g.tag_id
WHERE g.name = 'Work'
AND e.start_time >= strftime('%s', 'now', '-7 days')
GROUP BY t.name
ORDER BY hours DESC;
```
### List all tags with task counts
```sql
SELECT
g.name AS tag,
g.color,
COUNT(DISTINCT tt.task_id) AS task_count
FROM tags g
LEFT JOIN task_tags tt ON g.tag_id = tt.tag_id
GROUP BY g.tag_id
ORDER BY task_count DESC;
```
## API Details
The sync uses ATracker's web sync endpoint (`PUT /api/services/app/ATUpdateFromIOS/UpdateFromIOS`). Key points:
- Auth via `ARRAffinity` cookie (same value in both `ARRAffinity` and `ARRAffinitySameSite`)
- Header `Abp.TenantId: 1` is required
- `lastSyncTimeStampInApp` controls incremental sync (0 = full sync, Unix timestamp = changes since)
- Response contains `result.updateTask`, `result.updateTaskEntry`, `result.updateTag`, `result.updateTaskTag` arrays
- Cookie typically valid ~1 week; re-login at web app to refresh
## Privacy
- Credentials in `.env` (gitignored)
- All data stored locally in SQLite
- Only communicates with `atracker.pro` official API
This server cannot be deployed
Maintenance
ActivityInactive
ResponsivenessSyncing