timeline-mcp
# timeline-mcp
**MCP Server for Timeline-Aware Conversational Memory**
A lightweight, deterministic [MCP (Model Context Protocol)](https://modelcontextprotocol.io) server that gives AI assistants a temporal memory layer — extracting events from conversation, normalizing relative time expressions, and maintaining structured timeline state so LLMs can answer consistently across multi-session conversations.
---
## Project Overview
Conversational AI systems today have no native sense of time. They treat every message as a fresh interaction, with no awareness of how many days have passed since a user last reported an event. **timeline-mcp** solves this by sitting between the LLM and the conversation history as a dedicated temporal reasoning tool.
The server exposes five MCP tools that any MCP-compatible client (Claude, Continue, Cursor, etc.) can call:
| # | Tool | Purpose |
|---|------|---------|
| 1 | `extract_timeline_events` | Scan messages for events and normalize dates |
| 2 | `build_timeline_state` | Aggregate events into a coherent timeline |
| 3 | `summarize_timeline_context` | Generate an LLM-ready natural-language summary |
| 4 | `days_since_event` | Compute elapsed days since any tracked event |
| 5 | `upsert_message_into_timeline` | Incrementally update the timeline with new messages |
Zero external dependencies beyond `python-dateutil`. Pure rule-based extraction — no ML, no randomness, fully deterministic.
---
## Problem Statement
**LLMs lose track of time across conversations.**
This isn't a model quality issue — it's an architectural one. LLMs have no internal clock, no persistent state between sessions, and no mechanism to compute "how long ago" something happened relative to *now*. When a conversation spans days or weeks, the assistant's temporal reasoning collapses.
### Concrete Failure Mode
```
Day 1 User: "I had appendicitis surgery two weeks ago." → surgery = May 8
Day 3 User: "Still have some pain, but it's improving."
Day 7 User: "Feeling much better today."
Day 14 User: "Should I remove my stitches now?"
Assistant: "You should remove them two weeks after surgery."
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
WRONG. Two weeks have ALREADY passed.
The correct answer accounts for elapsed time.
```
The assistant interpreted "two weeks" as a *future* interval from today, rather than understanding that two weeks have already elapsed since the original surgery date. This class of error is systematic — it happens whenever relative time anchors drift across session boundaries.
---
## Why Timeline-Aware Memory Matters
This problem is acute in any domain where time-sensitive follow-up matters:
| Domain | Example Failure |
|--------|----------------|
| **Healthcare** | Misjudging post-surgical recovery milestones, medication tapering schedules |
| **Legal** | Miscalculating filing deadlines relative to prior events |
| **Project Management** | Losing track of sprint days elapsed vs. original estimates |
| **Habit Tracking** | Recommending actions that don't account for days already passed |
| **Customer Support** | Failing to escalate based on how long an issue has been open |
**timeline-mcp** provides a reusable, protocol-compliant solution. Instead of every assistant developer building their own temporal logic (or ignoring the problem), they can point their MCP client at this server and get structured timeline state for free.
---
## Installation
**Prerequisites:** Python 3.11+
### Option 1: System package (Debian/Ubuntu)
```bash
sudo apt install python3-dateutil
git clone https://github.com/your-org/timeline-mcp.git
cd timeline-mcp
```
### Option 2: Virtual environment
```bash
git clone https://github.com/your-org/timeline-mcp.git
cd timeline-mcp
python3 -m venv .venv
.venv/bin/pip install python-dateutil
```
The only runtime dependency is `python-dateutil` (for ISO-8601 date parsing). Everything else uses the Python standard library.
---
## Quick Start
Run the included medical follow-up example to see the full scenario:
```bash
PYTHONPATH=src python3 examples/medical_followup.py
```
Run all tests:
```bash
PYTHONPATH=src python3 -m unittest discover tests -v
```
Expected: **33 tests pass.**
---
## Running as an MCP Server
The server communicates via JSON-RPC 2.0 over stdio — no network ports, no configuration files. The client launches `timeline-mcp` as a child process and communicates over its stdin/stdout pipes.
### How the command works
```
python3 -m timeline_mcp.server
^
Runs the package as a Python module. The server loop
reads JSON-RPC requests from stdin, processes them,
and writes JSON-RPC responses to stdout.
```
The `PYTHONPATH` environment variable tells Python where to find the `timeline_mcp` package. If installed as a pip package, `PYTHONPATH` is not needed — the client can run `timeline-mcp` directly via the console script entry point.
### Start the server manually (for testing)
```bash
cd timeline-mcp
PYTHONPATH=src python3 -m timeline_mcp.server
# Server is now waiting for JSON-RPC on stdin. Type a JSON-RPC
# request and press Enter, or pipe one in:
echo '{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{}}' | PYTHONPATH=src python3 -m timeline_mcp.server
```
---
## MCP Client Configuration
All MCP clients that support stdio transport use the same pattern: a `command`, an `args` array, and optionally an `env` map. Pre-built config snippets are available in [`examples/configs/`](examples/configs/).
### Common pattern (all clients)
```json
{
"mcpServers": {
"timeline-mcp": {
"command": "python3",
"args": ["-m", "timeline_mcp.server"],
"env": {
"PYTHONPATH": "/absolute/path/to/timeline-mcp/src"
}
}
}
}
```
**Replace `/absolute/path/to/timeline-mcp/src`** with the actual path on your system. Use an absolute path — relative paths may resolve differently depending on how the client launches the process.
> **Virtual environment users:** If you installed dependencies in a venv, point `command` to the venv's Python binary:
> ```json
> "command": "/absolute/path/to/timeline-mcp/.venv/bin/python3"
> ```
---
### Claude Desktop
File: `claude_desktop_config.json` (see [Anthropic docs](https://docs.anthropic.com/en/docs/claude-desktop/mcp))
```json
{
"mcpServers": {
"timeline-mcp": {
"command": "python3",
"args": ["-m", "timeline_mcp.server"],
"env": {
"PYTHONPATH": "/absolute/path/to/timeline-mcp/src"
}
}
}
}
```
After editing the config, restart Claude Desktop. The five `timeline-mcp` tools will appear in the tool list automatically.
---
### Claude Code (CLI)
File: `.claude/settings.json` in your project, or `~/.claude/settings.json` for all projects.
```json
{
"mcpServers": {
"timeline-mcp": {
"type": "stdio",
"command": "python3",
"args": ["-m", "timeline_mcp.server"],
"env": {
"PYTHONPATH": "/absolute/path/to/timeline-mcp/src"
}
}
}
}
```
Claude Code requires the explicit `"type": "stdio"` field. Restart Claude Code or run `/mcp reload` to pick up the change.
---
### Continue.dev (VS Code / JetBrains)
File: `.continue/config.json` (project) or `~/.continue/config.json` (global)
```json
{
"experimental": {
"mcpServers": {
"timeline-mcp": {
"command": "python3",
"args": ["-m", "timeline_mcp.server"],
"env": {
"PYTHONPATH": "/absolute/path/to/timeline-mcp/src"
}
}
}
}
}
```
Note: Continue wraps MCP servers under the `experimental` key. Restart Continue or reload the config from the extension menu.
---
### Cursor IDE
File: `.cursor/mcp.json` (project) or `~/.cursor/mcp.json` (global)
```json
{
"mcpServers": {
"timeline-mcp": {
"command": "python3",
"args": ["-m", "timeline_mcp.server"],
"env": {
"PYTHONPATH": "/absolute/path/to/timeline-mcp/src"
}
}
}
}
```
Cursor discovers MCP servers on launch. Use the MCP panel (`Ctrl+Shift+P` → "MCP: Manage Servers") to verify.
---
### Generic / Other Clients
Any MCP-compatible client that supports stdio transport uses the same structure. The three required fields are:
| Field | Purpose |
|-------|---------|
| `command` | The executable to launch (e.g., `python3`) |
| `args` | Arguments passed to the command (e.g., `["-m", "timeline_mcp.server"]`) |
| `env` | Environment variables set for the child process (minimum: `PYTHONPATH`) |
The server implements the MCP 2024-11-05 protocol version and supports `initialize`, `tools/list`, and `tools/call` methods.
---
## MCP Tools — Full Reference
### 1. `extract_timeline_events`
Parse a list of conversation messages and extract timeline-relevant events with normalized dates.
**Input:**
```json
{
"messages": [
{
"id": "msg_1",
"text": "I had appendicitis surgery two weeks ago.",
"timestamp": "2026-05-22T10:00:00Z"
}
]
}
```
**Output:**
```json
{
"events": [
{
"message_id": "msg_1",
"event_type": "surgery",
"time_expression": "two weeks ago",
"normalized_date": "2026-05-08",
"confidence": 0.89
}
]
}
```
**Supported event types:** `surgery`, `stitch_removal`, `symptom_start`, `medication_start`, `medication_stop`, `followup_visit`
**Supported time expressions:** `today`, `yesterday`, `tomorrow`, `N days ago`, `N weeks ago`, `last week`, `next week`, `in N days`, `N days/weeks after <event>`, word numbers (`two weeks ago`)
---
### 2. `build_timeline_state`
Aggregate extracted events into a normalized timeline with anchor dates, derived fields, and recovery phase.
**Input:** List of events + reference datetime
**Output:** `TimelineState` with `anchor_events`, `event_log`, `derived` (days_since_*), `active_context` (phase), `conflicts`
---
### 3. `summarize_timeline_context`
Generate a concise natural-language summary for injection into an LLM system prompt.
**Output example:** `"The user had surgery 14 days ago. They are in the Post Op Recovery phase. No stitch removal event has been recorded yet."`
---
### 4. `days_since_event`
Compute days since a named anchor event (e.g., `surgery_date`, `stitch_removal_date`).
**Input:** Timeline state + event name + reference time
**Output:** `{"event_name": "surgery_date", "days_since": 14}`
---
### 5. `upsert_message_into_timeline`
The primary runtime tool — feed in one new message and the existing timeline state; get back the updated state.
**Key behavior:**
- Detects new events in the message
- Resolves relative time expressions using existing anchor dates
- Merges new events with existing log (conflict resolution: prefers higher confidence, logs conflicts)
- Recomputes all derived fields against the new message timestamp
---
## Example Usage: Medical Follow-Up
```
Message 1 (May 8): "I had appendicitis surgery today."
→ surgery_date: 2026-05-08, days_since: 0, phase: immediate_post_op
Message 2 (May 15): "I still have some pain."
→ symptom_start: 2026-05-15, days_since_surgery: 7, phase: early_recovery
Message 3 (May 22): "Should I remove my stitches now?"
→ days_since_surgery: 14, stitch_removal_date: null, phase: post_op_recovery
→ Summary: "The user had surgery 14 days ago. No stitch removal recorded."
Message 4 (May 23): "I removed my stitches yesterday."
→ stitch_removal_date: 2026-05-22, days_since_surgery: 15, phase: scar_care_early
```
The LLM receiving this context at Message 3 can correctly reason that the user is already at day 14 post-surgery and that stitch removal is appropriate *now* — rather than naively computing "two weeks from today."
---
## Project Structure
```
timeline-mcp/
├── README.md
├── pyproject.toml
├── requirements.txt
├── src/
│ └── timeline_mcp/
│ ├── __init__.py
│ ├── server.py # MCP JSON-RPC 2.0 server (stdio transport)
│ ├── schemas.py # Dataclass models with dict serialization
│ ├── temporal.py # Time expression parser (10+ patterns)
│ ├── extractors.py # Event extraction via regex trigger phrases
│ ├── state.py # Timeline construction, upsert, conflict resolution
│ ├── summarizer.py # Template-based NL summary generation
│ └── constants.py # EventType enum, trigger phrases, recovery phases
├── tests/
│ ├── test_temporal.py # 13 tests — time expression parsing
│ ├── test_extractors.py # 7 tests — event detection
│ ├── test_state.py # 8 tests — state construction & updates
│ ├── test_summarizer.py # 4 tests — summary generation
│ └── test_integration.py # 1 test — full 15-day scenario
└── examples/
├── medical_followup.py # End-to-end demonstration script
└── configs/
├── generic-stdio.json # Generic MCP client config
├── claude-desktop.json # Claude Desktop config
├── claude-code.json # Claude Code (CLI) config
├── continue-dev.json # Continue.dev (VS Code) config
└── cursor.json # Cursor IDE config
```
---
## Limitations
- **Rule-based extraction only.** Uses regex patterns and keyword matching — no ML. Covers common phrasings well but will miss implicit, idiomatic, or highly variable event mentions.
- **English only.** All trigger phrases, time expressions, and summaries target English text. Other languages would require separate pattern modules.
- **Single conversation scope.** Each timeline state is self-contained. No cross-conversation aggregation, no user identity layer (MVP scope).
- **No persistence.** State lives in memory only. Server restarts lose all timeline data. Persistent storage is a planned enhancement.
- **Confidence scores are heuristic.** Based on match length, not statistical calibration. Useful for relative ranking within a session, not as absolute probabilities.
- **Recovery phases are illustrative.** The phase labels (`post_op_recovery`, `scar_care_early`) are example mappings for the medical domain. They are not clinical guidance.
---
## Safety Disclaimer
> **timeline-mcp is NOT a medical device. It does not provide medical advice, diagnosis, prognosis, or treatment recommendations.**
This tool:
- Tracks **dates** and computes **elapsed time intervals**
- Detects **mentions** of medical events in conversation text
- Labels **recovery phases** using a static day-range lookup table
This tool does **not:**
- Make clinical decisions or recommendations
- Replace professional medical judgment
- Store, transmit, or process Protected Health Information (PHI) in a HIPAA-compliant manner
- Validate the accuracy of user-reported medical events
Any medical-domain examples in this repository are for **illustration only**. The core capability is **temporal reasoning** — it applies equally to project management, legal workflows, habit tracking, customer support, and any other domain where elapsed time matters.
**If you are building a healthcare application, consult with qualified medical and legal professionals.**
---
## Roadmap
### v0.2 (next)
- [ ] Persistent timeline storage (SQLite)
- [ ] Multi-user / multi-session timeline isolation
- [ ] Confidence scoring based on explicit vs. implicit date resolution
- [ ] Additional event types: `lab_result`, `imaging`, `physical_therapy`, `diet_change`
### v0.3
- [ ] Persian (Farsi) language module — separate trigger phrase and time expression tables
- [ ] Calendar-aware date computation (skip weekends/holidays for business-day intervals)
- [ ] Timeline visualization as structured JSON for rendering
### v1.0
- [ ] Conflict resolution strategies (user-prompted, majority-vote, source-weighted)
- [ ] Adapter examples for Claude Desktop, Continue.dev, Cursor, and OpenAI
- [ ] Comprehensive benchmark dataset for temporal extraction accuracy
- [ ] Full internationalization framework for trigger phrases
---
## Sponsorship & Support
timeline-mcp is an open-source project built to solve a real problem in conversational AI. It is maintained by independent developers who believe LLMs should be able to track time reliably.
**If this project is useful to you or your organization, please consider supporting it:**
- **GitHub Sponsors:** [github.com/sponsors/your-org](https://github.com/sponsors)
- **Open Collective:** [opencollective.com/timeline-mcp](https://opencollective.com)
- **Contribute:** PRs, issues, and documentation improvements are always welcome
**Commercial support and custom integrations are available.** Contact `timeline-mcp@example.com` for inquiries about:
- Priority feature development
- Custom event type and domain modeling
- On-premises deployment support
- HIPAA-compliant configuration guidance
---
## License
MIT — see [LICENSE](LICENSE) for details.
TDQS
Scored across 5 tools
Most tools are clearly separated by their role in the pipeline: extraction, state building, summarization, and querying. There is minor overlap between build_timeline_state and upsert_message_into_timeline since both aggregate events and recompute derived fields, but the incremental vs bulk distinction is clear enough.
The naming mostly follows a verb_noun pattern: extract_timeline_events, build_timeline_state, summarize_timeline_context, upsert_message_into_timeline. The exception is days_since_event, which is not a verb-led action name and breaks the pattern slightly.
Five tools is well-scoped for a focused timeline-management server. Each tool covers a distinct stage of the workflow without redundancy or excessive granularity.
The core lifecycle is covered: extraction, state construction, incremental updates, summarization, and queries. Minor gaps include lack of an explicit reset/clear operation or a way to remove individual events, but these are not critical for the intended use case.