Collect and consolidate personal health data from multiple EHR systems into a single queryable database
Project description
chartfold
Patient-facing tool for consolidating personal health data from multiple EHR (Electronic Health Record) systems into a single SQLite database. Query, analyze, and export your aggregated clinical data via CLI, MCP server (for LLM-assisted analysis), or export to Markdown, HTML, or Hugo static sites.
Goal: Patient empowerment through data ownership — enabling time-series analysis, intelligent querying with tools like Claude Code, and organized preparation for medical visits.
Features
- Multi-EHR data consolidation — Import from Epic MyChart, MEDITECH Expanse, and athenahealth
- SQLite database — 16 clinical tables with full audit trail
- MCP server — 22 tools for LLM-assisted analysis with Claude
- Export formats — Markdown, self-contained HTML with charts, Hugo static sites, JSON
- Personal notes — Tag and annotate any clinical record
- Visit preparation — Generate visit diffs and clinical summaries
Installation
pip install chartfold
# With MCP server support (for Claude integration)
pip install "chartfold[mcp]"
Development Setup
git clone https://github.com/queelius/chartfold.git
cd chartfold
pip install -e ".[dev,mcp]"
Quick Start
Load Data from EHR Exports
# Load from individual sources
chartfold load epic ~/exports/epic/
chartfold load meditech ~/exports/meditech/
chartfold load athena ~/exports/athena/
# Or load all at once
chartfold load all \
--epic-dir ~/exports/epic/ \
--meditech-dir ~/exports/meditech/ \
--athena-dir ~/exports/athena/
Query and Inspect
# View database summary
chartfold summary
# Run SQL queries
chartfold query "SELECT test_name, value, result_date FROM lab_results ORDER BY result_date DESC LIMIT 10"
# What's new since your last visit
chartfold diff 2025-01-01
Export Your Data
# Markdown summary for your doctor (last 6 months)
chartfold export markdown --output summary.md --lookback 6
# PDF via pandoc
chartfold export markdown --output summary.pdf --pdf
# Self-contained HTML with charts
chartfold export html --output summary.html --lookback 6
# Full HTML export (all data)
chartfold export html --full --output full.html
# JSON for backup/restore
chartfold export json --output data.json
# Hugo static site
chartfold export hugo --output ./site
Personal Notes
# List recent notes
chartfold notes list --limit 20
# Search by tag or query
chartfold notes search --tag oncology --query "CEA"
# Search by reference (notes linked to specific records)
chartfold notes search --ref-table lab_results
Supported EHR Sources
| Source | Format | Description |
|---|---|---|
| Epic MyChart | CDA R2 XML | IHE XDM exports from Epic MyChart |
| MEDITECH Expanse | CCDA XML + FHIR JSON | Dual-format bulk exports (merged and deduplicated) |
| athenahealth | FHIR R4 XML | Ambulatory summary exports |
Expected Input Directory Structures
Epic: input_dir/DOC0001.XML, DOC0002.XML, ...
MEDITECH: input_dir/US Core FHIR Resources.json
input_dir/CCDA/<uuid>.xml
athena: input_dir/Document_XML/*AmbulatorySummary*.xml
Database Schema
chartfold stores data in 16 clinical tables:
| Category | Tables |
|---|---|
| Core | patients, documents, encounters |
| Labs & Vitals | lab_results, vitals |
| Medications | medications, allergies |
| Conditions | conditions |
| Procedures | procedures, pathology_reports, imaging_reports |
| Notes | clinical_notes |
| History | immunizations, social_history, family_history, mental_status |
| System | load_log (audit), notes, note_tags (personal), source_assets |
All dates are stored as ISO YYYY-MM-DD strings. Every record carries a source field for provenance tracking.
MCP Server
chartfold includes an MCP (Model Context Protocol) server with 22 tools for LLM-assisted health data analysis:
chartfold serve-mcp --db chartfold.db
Available Tools
| Category | Tools |
|---|---|
| SQL & Schema | run_sql, get_schema |
| Labs | query_labs, get_lab_series_tool, get_available_tests_tool, get_abnormal_labs_tool |
| Medications | get_medications, reconcile_medications_tool |
| Clinical | get_timeline, search_notes, get_pathology_report |
| Analysis | get_visit_diff, get_visit_prep, get_surgical_timeline |
| Cross-source | match_cross_source_encounters, get_data_quality_report |
| Summary | get_database_summary |
| Personal Notes | save_note, get_note, search_notes_personal, delete_note |
Claude Desktop Configuration
Add to your claude_desktop_config.json:
{
"mcpServers": {
"chartfold": {
"command": "python",
"args": ["-m", "chartfold", "serve-mcp", "--db", "/path/to/chartfold.db"]
}
}
}
Configuration
Generate a personalized config from your data:
chartfold init-config
This creates chartfold.toml with lab tests to chart based on what's in your database:
[[lab_tests]]
name = "CEA"
match = ["CEA", "Carcinoembryonic Antigen"]
[[lab_tests]]
name = "Hemoglobin"
match = ["Hemoglobin", "Hgb", "HGB"]
[hugo]
dashboard_recent_labs = 10
Architecture
chartfold uses a three-stage pipeline for each EHR source:
Raw EHR files (XML/FHIR)
↓
[Source Parser] → source-specific dict
↓
[Adapter] → UnifiedRecords (normalized dataclasses)
↓
[DB Loader] → SQLite tables
Key Design Decisions
- Idempotent loading — Re-running
loadfor a source replaces its data - Cross-source deduplication — Adapters deduplicate records using composite keys
- Date normalization — All dates normalized to ISO format at adapter stage
- Provenance tracking — Every record tracks its source for cross-source analysis
Testing
# Run all tests (700+ tests)
python -m pytest tests/
# Run a single test file
python -m pytest tests/test_adapters.py
# Run with coverage
python -m pytest tests/ --cov=chartfold --cov-report=term-missing
Project Structure
src/chartfold/
├── sources/ # EHR-specific parsers (epic.py, meditech.py, athena.py)
├── adapters/ # Normalize to UnifiedRecords (epic_adapter.py, etc.)
├── analysis/ # Query helpers (lab_trends.py, medications.py, etc.)
├── extractors/ # Specialized parsers (labs.py, pathology.py)
├── core/ # Shared utilities (cda.py, fhir.py, utils.py)
├── formatters/ # Output formatters (markdown.py)
├── hugo/ # Hugo site generator (generate.py)
├── mcp/ # MCP server (server.py)
├── db.py # Database interface
├── models.py # Dataclass models
├── config.py # Configuration management
├── cli.py # Command-line interface
├── export.py # Markdown export
├── spa/ # HTML SPA export with embedded SQLite (sql.js)
└── export_full.py # Full JSON/markdown export
Adding a New EHR Source
- Create
sources/newsource.pywithprocess_*_export(input_dir)returning a dict - Create
adapters/newsource_adapter.pywith*_to_unified(data) -> UnifiedRecords - Add a
SourceConfiginsources/base.py - Wire into
cli.py(add subcommand) - Add tests in
tests/
Requirements
- Python 3.11+ (uses
tomllibfrom stdlib) - Dependencies:
lxml,mcp(optional) - Optional:
pandocfor PDF export,hugofor static site generation
License
MIT
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file chartfold-1.1.0.tar.gz.
File metadata
- Download URL: chartfold-1.1.0.tar.gz
- Upload date:
- Size: 4.0 MB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.12.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
45bbb6106d4fd4ba569155286dd75ad3d2a759a2e4e532250d745f99b2a98182
|
|
| MD5 |
2797349bcca12303993295054fbb19f3
|
|
| BLAKE2b-256 |
b1999fdc029aa94e63562dedb3421bc31046c9557129aef814863a3c2c32d1d5
|
File details
Details for the file chartfold-1.1.0-py3-none-any.whl.
File metadata
- Download URL: chartfold-1.1.0-py3-none-any.whl
- Upload date:
- Size: 497.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.12.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e2cfe0a75282c5c9b6eebf5310989db63d4f42859cc58933f5b44c20c4eaf23a
|
|
| MD5 |
e35073285e6d9763790c7325f41155c9
|
|
| BLAKE2b-256 |
5caee4bc71ee9045f1b94125a9ac105604af2298167393e5ba7718fcfcab2613
|