android-timeline-mcp
Provides tools for querying timeline data collected from Android devices, including raw events, hourly and daily features, coverage, and gaps.
Click on "Install Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@android-timeline-mcpAny gaps in yesterday's phone timeline?"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
android-timeline-home-assistant
Status: experimental. The server side is exercised end to end in CI against the real collector, but no physical Android phone has ever fed it. See Current limitations.
A Home Assistant app (formerly "add-on") that receives raw timestamped
events from the
android-timeline-termux
collector, stores them immutably, derives versioned hourly and daily
features, measures how much of each day was actually observed, publishes a
handful of summary entities, and exposes a read-only MCP server so
Claude can ask questions about the data.
What it does
Authenticated ingestion. Device-specific bearer tokens, stored only as keyed hashes. Idempotent on
event_id, so a replay is always a no-op.Immutable raw events. Features are derived and versioned; recomputing them never rewrites history.
Explicit missingness. Every hour is a row, including the empty ones. A flat line and a dead collector are different things and are reported differently.
Read-only MCP. No SQL, no shell, no filesystem, no mutation, no phone control. Bounded ranges, pagination, and redaction that cannot be turned off.
Minimal privileges. Ingress only, no published port, no Supervisor role, no Home Assistant directory mounted.
Related MCP server: airspeed-mcp
Architecture
flowchart TD
phone["Android phone<br/><i>android-timeline-termux</i>"]
subgraph app["Home Assistant app (this repository)"]
direction TB
api["FastAPI ingestion<br/>POST /api/v1/events/batch"]
auth["Device token auth<br/>+ rate limiting"]
store[("SQLite event store<br/>append-only")]
features["Feature pipeline<br/>hourly + daily, versioned"]
cov["Coverage and gap detection"]
tl["Timeline API<br/>GET /api/v1/timeline/yesterday"]
mcp["Read-only MCP server<br/>/mcp"]
ent["Summary entity publisher"]
api --> auth --> store
store --> features --> cov
cov --> tl
cov --> mcp
features --> ent
end
ha["Home Assistant Core<br/>sensor.* / binary_sensor.*"]
claude["Claude / MCP client"]
phone -->|"HTTPS, Bearer token,<br/>X-Batch-ID"| api
api -->|"per-event acknowledgement"| phone
ent -->|"Supervisor API proxy"| ha
mcp --> claudeInstallation
In Home Assistant: Settings -> Apps -> App store -> ⋮ -> Repositories
Add
https://github.com/resace3/android-timeline-home-assistantInstall Android Timeline from the store, then Start it.
Open the app's web UI (ingress) to reach the diagnostic page.
Enroll your phone:
curl -X POST http://<app>/api/v1/admin/devices \ -H 'Content-Type: application/json' \ -d '{"device_id": "device-my-pixel-001"}'The response contains the device token once. Copy it into the collector's
~/.config/android-timeline/token. Only a keyed hash is stored here; there is no way to recover it later, only to rotate.
Full details, including running behind ingress and the optional custom integration: docs/installation.md.
Configuration
Option | Default | Notes |
|
| Where a "day" starts. Events are always stored in UTC. |
|
| Turning it off stops the pipeline; nothing is deleted. |
|
| Summary entities only, never one per event. |
|
| Read-only MCP endpoint, ingress-protected. |
|
| Safe because no port is published. |
| (empty) | Only needed outside ingress. |
|
| Raw events are kept forever by default. |
API
Endpoint | Auth | Purpose |
| none | liveness; no user data |
| device token | ingestion |
| device token (own device) | queue and sync state |
| admin | the day timeline |
| admin | a specific local date |
| admin | stored hourly features |
| admin | coverage and gaps |
| admin | enroll, returns a token once |
| admin | rotate, revoking the old token |
| admin | revoke |
MCP
Twelve read-only tools: list_devices, list_phone_sources,
get_phone_latest, query_phone_events, get_day_timeline,
get_hourly_features, get_daily_features, get_data_coverage,
find_data_gaps, get_collector_status, export_phone_window and
describe_server.
Guaranteed absent: arbitrary SQL, shell execution, filesystem access, mutation of any kind, and phone control. Date ranges are capped at 31 days, pages at 1000 rows, and precise coordinates, message bodies and contact names are stripped with no parameter to re-enable them.
Client configuration (placeholders only): docs/mcp.md.
Testing
Layer | Status | What it proves |
Unit | required | database, auth, ingestion, features, coverage, timeline |
API + MCP | required | the HTTP surface and the MCP protocol, in-process |
Contract | required | Pydantic models and published JSON Schemas agree |
E2E (in-process) | required | synthetic day -> ingest -> features -> timeline -> MCP, across a restart |
Container (Layer A) | required | the real image: build, boot, drive, restart |
Mock Supervisor (Layer B) | required | the one Supervisor endpoint this app calls |
App image build (Layer D) | required | current official HA builder actions, amd64 + aarch64 |
Cross-repository E2E | required | the real collector uploading to the real image |
Home Assistant Core (Layer C) | non-blocking | the custom integration loads in a real HA container |
Supervised HA (Layer E) | not attempted | see below |
The cross-repository workflow installs the actual collector from
android-timeline-termux at a pinned revision, runs its own outbox and
uploader against a container built from this repository, then asserts on
storage, idempotency, features, coverage and MCP. Details:
docs/testing.md.
Current limitations
Stated plainly:
No physical phone has ever fed this. Every event CI has ever ingested is synthetic.
Supervisor is not tested in CI. A real Supervisor needs privileged Docker-in-Docker or a full OS image; a flaky green result would be worse than an honest gap. Layer B tests the one endpoint this app actually calls, and nothing more.
Ingress trust is a design assumption. Treating an ingress request as an authenticated admin is only safe because no port is published. If you publish one, set
admin_tokenand turntrust_ingress_adminoff.SQLite, single writer. Fine for a personal deployment; not a fleet.
Features are approximations.
charging_minutesandwifi_connected_minutesare inferred from periodic samples, so a state change between samples is invisible. Every value ships with a coverage proportion for exactly this reason.The timeline has no polished UI. The
/page is a diagnostic table.
Privacy considerations
Raw events are stored as the phone sent them; the phone is where redaction happens, and it defaults to metadata only.
The MCP server redacts precise coordinates, message bodies and contact names again on the way out, with no override.
Home Assistant entities carry aggregates only, so the recorder database never accumulates behavioural detail.
No fixture, test or CI artifact contains real personal data, and CI fails if a credential-shaped string appears anywhere in history.
Relationship to the other repository
runs on the phone: collection, the outbox, the upload client | |
This repository | runs in Home Assistant: ingestion, storage, features, coverage, MCP |
This repository is the source of truth for the protocol schemas in
schemas/. The collector vendors a byte-identical copy; the
cross-repository workflow diffs them directly and fails on any difference.
Both speak protocol_version 1. Compatibility policy:
docs/data-model.md.
Development
python -m pip install -e ".[dev]"
ruff format . && ruff check . && mypy
pytestRun the whole pipeline locally without Home Assistant:
docker compose -f test-harness/docker-compose.yml up --buildRoadmap
Ingest from a real phone (blocking for 1.0)
QR-code device enrollment
A real timeline UI
Optional Postgres backend for larger deployments
Intervention annotations feeding the causal-inference use case
Feature definitions v2 with dwell-time and place transitions
License
This server cannot be installed
Maintenance
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
- AlicenseAqualityBmaintenanceMCP server to read daily activity, sleep, heart rate, and body metrics from Google Health API, allowing AI assistants like Claude to access your health data. Optionally syncs health metrics to an Obsidian vault.Last updated5MIT
- Flicense-qualityCmaintenanceRemote MCP server that exposes Airspeed/Glyphic call data to Claude web, enabling tools to list, retrieve, and query calls, transcripts, snippets, and playbooks.Last updated
- AlicenseAqualityBmaintenanceRead-only MCP server exposing daily health facts, 7/28-day baselines, and deterministic daily pulse from Google Health API data.Last updated72MIT
- AlicenseAqualityCmaintenanceRead-only MCP server enabling natural language querying of personal health and cultural activity scores from the health.ojimpo.com dashboard via Claude.Last updated520MIT
Related MCP Connectors
Read-only MCP server for ClassQuill, a tutoring-business-management platform.
Agent-native MCP server over the public saagarpatel.dev corpus. Read-only, stateless.
A paid remote MCP for AI SDK data query MCP, built to return verdicts, receipts, usage logs, and aud
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
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/resace3/android-timeline-home-assistant'
If you have feedback or need assistance with the MCP directory API, please join our Discord server