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
Metadata
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 | Provenance | Actions |
|---|---|---|---|---|
| chartfold-1.1.0.tar.gz | 4.0 MB | Without provenance | Details for chartfold-1.1.0.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| chartfold-1.1.0-py3-none-any.whl | Python 3 | none | any | Details for chartfold-1.1.0-py3-none-any.whl |
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
|
Security for chartfold 1.1.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.