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
Release files for chartfold 1.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| chartfold-1.1.0.tar.gz | 4.0 MB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| chartfold-1.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 4.5 MB
Release files / chartfold-1.1.0.tar.gz
| Download URL | chartfold-1.1.0.tar.gz |
|---|---|
| Size | 4.0 MB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
45bbb6106d4fd4ba569155286dd75ad3d2a759a2e4e532250d745f99b2a98182
|
|
BLAKE2b-256 checksum How to use checksums |
b1999fdc029aa94e63562dedb3421bc31046c9557129aef814863a3c2c32d1d5
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.1.0 CPython/3.12.3
|
Release files / chartfold-1.1.0-py3-none-any.whl
| Download URL | chartfold-1.1.0-py3-none-any.whl |
|---|---|
| Size | 497.4 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
e2cfe0a75282c5c9b6eebf5310989db63d4f42859cc58933f5b44c20c4eaf23a
|
|
BLAKE2b-256 checksum How to use checksums |
5caee4bc71ee9045f1b94125a9ac105604af2298167393e5ba7718fcfcab2613
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.1.0 CPython/3.12.3
|