Prerequisites
Before you begin, make sure you have the following installed:
Node.js 18+Runtime
npmIncluded with Node.js
Verify your Node.js version:
node --version
# v18.0.0 or higher
Install the CLI
Install the CLI globally so the cascade command is available anywhere:
npm install -g @the-cascade-protocol/cli
Verify the installation:
cascade --version
cascade --help
The CLI bundles the Cascade Protocol SHACL shapes and all conversion logic. It has no external network dependencies — every operation runs entirely on your machine.
Available commands
| Command | Description |
|---|---|
| cascade validate | Validate Turtle files against SHACL shapes |
| cascade convert | Convert between FHIR R4 JSON and Cascade Turtle |
| cascade pod init | Create a new Pod directory structure |
| cascade pod info | Show Pod metadata and record counts |
| cascade pod query | Query records from a Pod by type |
| cascade pod export | Export a Pod as a ZIP archive or directory |
| cascade pod import | Convert FHIR files and import them directly into a Pod, splitting records by type and updating type indexes v0.3.3 |
| cascade reconcile | Merge Cascade Turtle files from multiple EHR systems, resolving duplicates and conflicts |
| cascade conformance run | Run the conformance test suite against fixtures |
| cascade serve --mcp | Start a local MCP-compatible agent server |
| cascade capabilities | Print machine-readable tool descriptions as JSON |
Initialize a Pod
A Pod is a local directory that organizes your health data into a standard structure. Create one with pod init:
cascade pod init ./my-health-pod
Cascade Pod initialized at: ./my-health-pod
Created:
.well-known/solid
profile/card.ttl
settings/publicTypeIndex.ttl
settings/privateTypeIndex.ttl
clinical/
wellness/
index.ttl
README.md
Next steps:
1. Edit profile/card.ttl to set patient name and demographics
2. Add data files to clinical/ and wellness/ directories
3. Run: cascade pod info ./my-health-pod
View Pod metadata at any time with pod info:
cascade pod info ./my-health-pod
Convert FHIR data
Apple Health, Epic MyChart, and most EHR patient portals can export your records as FHIR R4 JSON.
Use convert to transform them into Cascade Protocol Turtle:
cascade convert patient-export.json --from fhir --to cascade
This outputs Turtle to stdout. Redirect it to a file or pipe it directly into your Pod:
# Save to a file
cascade convert patient-export.json --from fhir --to cascade \
> ./my-health-pod/clinical/medications.ttl
# Or read from stdin (for piping)
cat patient-export.json | cascade convert --from fhir --to cascade
@prefix cascade: <https://ns.cascadeprotocol.org/core/v1#> .
@prefix health: <https://ns.cascadeprotocol.org/health/v1#> .
@prefix clinical: <https://ns.cascadeprotocol.org/clinical/v1#> .
@prefix rxnorm: <http://www.nlm.nih.gov/research/umls/rxnorm/> .
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
<urn:uuid:3d441e1f-1578-46f3-b2fb-32f59ae808a5> a health:MedicationRecord ;
cascade:dataProvenance cascade:ClinicalGenerated ;
cascade:schemaVersion "1.3" ;
health:medicationName "Metformin HCl 500mg" ;
health:isActive true ;
health:rxNormCode rxnorm:860975 ;
health:dose "500mg twice daily" ;
health:startDate "2024-01-15T00:00:00Z"^^xsd:dateTime .
Converted 1 resource (fhir -> turtle)
The converter handles FHIR Bundles (multiple resources at once) as well as single resources. It maps FHIR resource types to the appropriate Cascade vocabularies and preserves coded values like RxNorm, SNOMED CT, LOINC, and ICD-10.
To get machine-readable JSON output instead of raw Turtle, use --json:
cascade convert patient-export.json --from fhir --to cascade --json
Import manifest
Add --manifest to write a sidecar JSON file that records exactly what was converted, what types were preserved as Layer 1 passthrough, and what was intentionally excluded and why.
cascade convert patient-export.json --from fhir --to cascade \
--source-system my-ehr --manifest \
> patient.ttl
# Also writes: patient-export-manifest.json
{
"sourceFile": "patient-export.json",
"sourceSystem": "my-ehr",
"convertedAt": "2026-03-12T10:00:00.000Z",
"summary": {
"total": 47,
"fullyMapped": 41,
"passthrough": 2,
"excluded": 4
},
"byType": {
"MedicationStatement": { "count": 8, "strategy": "mapped" },
"Condition": { "count": 12, "strategy": "mapped" },
"Encounter": { "count": 6, "strategy": "mapped" },
"Procedure": { "count": 7, "strategy": "mapped" },
"DiagnosticReport": { "count": 4, "strategy": "mapped" },
"Claim": { "count": 4, "strategy": "mapped" },
"QuestionnaireResponse": { "count": 2, "strategy": "passthrough" },
"CareTeam": { "count": 3, "strategy": "excluded",
"reason": "Personnel registry. Care team context is captured via encounter provenance." },
"Medication": { "count": 1, "strategy": "excluded",
"reason": "Standalone medication definitional resources. Clinical meaning is in MedicationRequest/Statement." }
}
}
You can specify a custom manifest path with --manifest path/to/my-manifest.json.
Exporting Cascade Turtle back to FHIR
The same convert command handles the reverse direction. Use --from cascade --to fhir to produce a FHIR R4 Bundle from a Cascade Turtle file:
cascade convert --from cascade --to fhir reconciled-patient.ttl \
> patient-fhir-export.json
Records converted with full Layer 2 mapping export with all original fields. Records preserved as Layer 1 passthrough (types without a full Cascade vocabulary mapping) are restored verbatim from the embedded cascade:fhirJson triple — the original FHIR JSON is preserved inline in the Turtle file for exactly this purpose.
Import FHIR Data into a Pod v0.3.3
cascade pod import is the recommended way to populate a Pod from FHIR data.
It combines conversion, type-based splitting, and Pod index registration into a single command —
replacing the manual convert-then-copy workflow for most use cases.
pod init → pod import → pod query.
The pod import command handles conversion, splitting records by type, and updating the type index in one step.
Single-source import
Pass a FHIR JSON file and the Pod directory. Use --source-system to tag all imported records with the originating system name (used later for reconciliation):
cascade pod import ./my-pod patient-records.json \
--source-system "mass-general"
Records are split by type into organized files under clinical/:
my-pod/
clinical/
patient-profile.ttl (1 record per patient)
medications.ttl
conditions.ttl
allergies.ttl
lab-results.ttl
immunizations.ttl
vital-signs.ttl
procedures.ttl
encounters.ttl
documents.ttl
lab-reports.ttl
imaging.ttl
claims.ttl
benefits.ttl
fhir-passthrough.ttl (unmapped FHIR types, preserved verbatim)
settings/
publicTypeIndex.ttl (updated with solid:TypeRegistration entries)
index.ttl (updated with ldp:contains references)
Preview before importing
Use --dry-run to see what would be written without making any changes:
cascade pod import ./my-pod patient-records.json --dry-run
Multi-source import with automatic reconciliation
Pass multiple FHIR files to import from several providers at once.
The command automatically reconciles duplicates across sources using the same logic as cascade reconcile.
Use --trust to set per-source trust scores, and --report to write a full import report:
cascade pod import ./my-pod \
hospital-records.json \
primary-care.json \
--trust hospital-records=0.95,primary-care=0.85 \
--report import-report.json
Use --no-reconcile to skip reconciliation even when multiple input files are provided.
Options reference
| Option | Description |
|---|---|
| --source-system <name> | Tag all imported records with this system name (used for reconciliation) |
| --no-reconcile | Skip reconciliation even when multiple input files are provided |
| --trust <scores> | Per-source trust scores for multi-source reconciliation, e.g. hospital=0.95,clinic=0.85 |
| --dry-run | Preview the import without writing any files |
| --report <file> | Write an import report JSON to the specified file |
| --passthrough full|minimal | Controls whether FHIR passthrough resources include the raw JSON blob (default: full) |
Validate your data
The validate command checks Turtle files against the bundled SHACL shapes for all Cascade Protocol vocabularies.
It catches missing required fields, type mismatches, and vocabulary violations.
Validate a single file:
cascade validate ./my-health-pod/clinical/medications.ttl
PASS ./my-health-pod/clinical/medications.ttl (192 triples)
Validate an entire directory at once:
cascade validate ./my-health-pod/clinical/
PASS clinical/allergies.ttl (29 triples)
PASS clinical/conditions.ttl (64 triples)
PASS clinical/immunizations.ttl (64 triples)
PASS clinical/lab-results.ttl (187 triples)
PASS clinical/medications.ttl (192 triples)
Validation Summary
Files: 5 total, 5 passed, 0 failed
Use --verbose to see which SHACL shapes were applied and which RDF types were detected:
cascade validate medications.ttl --verbose
Use --json for machine-readable output suitable for CI pipelines:
cascade validate ./my-health-pod/clinical/ --json
Exit codes
| Code | Meaning |
|---|---|
| 0 | All files pass validation |
| 1 | One or more SHACL violations |
| 2 | Error (file not found, malformed Turtle, etc.) |
Query a Pod
Read records out of a Pod by type using pod query:
# View all medications (human-readable)
cascade pod query ./my-health-pod --medications
# View all data types as JSON
cascade pod query ./my-health-pod --all --json
Available query filters:
| Flag | Returns |
|---|---|
| --medications | Prescription and OTC medications |
| --conditions | Diagnoses and active conditions |
| --allergies | Drug, food, and environmental allergies |
| --lab-results | Lab and diagnostic results |
| --immunizations | Vaccine records |
| --vital-signs | Clinical vital sign readings |
| --supplements | Supplements and OTC vitamins |
| --insurance | Insurance / coverage plans |
| --procedures | Surgical and clinical procedures |
| --encounters | Clinical encounters and visits |
| --documents | Clinical documents (e.g. discharge summaries) |
| --lab-reports | Laboratory reports (DiagnosticReport) |
| --medication-administrations | Inpatient medication administrations |
| --devices | Implanted medical devices |
| --imaging | Imaging studies (CT, MRI, X-ray) |
| --claims | Insurance claims |
| --benefits | Explanation of benefits |
| --fhir-passthrough | FHIR resource types without a full Cascade mapping |
| --all | All data types in the Pod |
The JSON output from --json is designed for consumption by AI agents and is the same format returned by the MCP cascade_pod_query tool.
Zero network calls
The CLI is designed for complete offline operation.
- No data leaves your machine. SHACL validation, FHIR conversion, and Pod management all run locally.
- No telemetry. There is no usage tracking, no error reporting, and no analytics.
- No account required. Install and use without signing up for anything.
- The Docker image adds an extra layer:
docker run --rm -v $(pwd):/data cascade-protocol/tools cascade validate /data/record.ttl
Read the Security & Compliance Guide for a full description of the trust model and data flow.
Reconciling Records from Multiple Systems v0.3.3
When the same patient has records in multiple EHR systems, use cascade reconcile to merge them into a single canonical Turtle file.
The reconciler removes exact duplicates, merges near-duplicates, and resolves conflicts using configurable trust scores.
cascade pod import with multiple FHIR files handles reconciliation automatically as part of the import step — no separate convert-then-reconcile workflow needed.
The manual cascade reconcile workflow below is still available for full control over the process or for working with pre-converted Turtle files.
Step 1: Tag records with their source system during conversion
Use the --source-system flag when converting each system's FHIR export:
cascade convert patient.json --from fhir --to cascade \
--source-system primary-care > primary-care.ttl
cascade convert patient.json --from fhir --to cascade \
--source-system hospital > hospital.ttl
Step 2: Reconcile
cascade reconcile primary-care.ttl specialist.ttl hospital.ttl \
--output merged.ttl \
--report reconciliation-report.json \
--trust primary-care=0.90,specialist=0.85,hospital=0.95
Reconciliation complete
Input records: 1,737 (across 3 systems)
Exact duplicates: -452 removed
Near-duplicates: ~9 merged
Conflicts resolved: 2 (trust_priority)
Final record count: 787
Output: merged.ttl
Report: reconciliation-report.json
How conflicts are resolved
| Match type | Strategy | Description |
|---|---|---|
exact_duplicate |
trust_priority | All values identical across systems — keep highest-trust source |
near_duplicate |
merge_values | Same record identity, minor differences — merge fields from all sources |
status_conflict |
trust_priority | Conflicting status (e.g. active vs resolved) — highest-trust system wins |
value_conflict |
trust_priority | Conflicting clinical value — highest-trust system wins |
unresolved |
flag_unresolved | Trust scores too similar (<3% gap) — flagged with cascade:reconciliationStatus "unresolved-conflict" for manual review |
Records identified as coded using RxNorm, SNOMED CT, LOINC, CVX, or ICD-10 are matched by code first; plain-text records fall back to normalized name matching. Reconciliation provenance is written using Core Vocabulary v2.4 reconciliation properties.
Conformance Testing
The CLI ships with a conformance test runner for verifying that your data producer or consumer correctly implements the Cascade Protocol. The conformance fixtures are published separately and cover all data types with both positive and negative test cases.
# Clone the fixtures
git clone https://github.com/the-cascade-protocol/cascadeprotocol.org.git
cd cascadeprotocol.org
# Run the full suite
cascade conformance run --suite ./conformance/fixtures --self
Cascade Protocol Conformance Test Suite
========================================
Suite: ./conformance/fixtures
Mode: self-conformance
Fixtures: 59
Medication (10 fixtures)
✓ med-001: Happy path: Active prescription medication (Lisinopril)
✓ med-002: Happy path: Self-reported OTC medication (Cetirizine)
✓ med-008: [negative] Negative: Medication missing required medicationName field
...
Results: 59 passed, 0 failed, 0 errors
Exit code: 0
Add --json to get a structured report you can parse in CI.
Agent Server (MCP)
The CLI includes a local Model Context Protocol server that exposes your Pod to AI assistants like Claude.
It provides six typed tools: cascade_pod_read, cascade_pod_query, cascade_validate, cascade_convert, cascade_write, and cascade_capabilities.
Start the server in stdio mode (for Claude Desktop or other MCP clients):
cascade serve --mcp --pod ./my-health-pod
To add it to your Claude Desktop configuration, edit ~/Library/Application Support/Claude/claude_desktop_config.json:
{
"mcpServers": {
"cascade": {
"command": "cascade",
"args": ["serve", "--mcp", "--pod", "/path/to/my-health-pod"]
}
}
}
All data read and written by the agent stays in your local Pod. Agent-written observations are tagged with cascade:AIGenerated provenance, keeping them distinct from clinical and patient-entered data. Every operation is logged to provenance/audit-log.ttl.
See the full list of available tools:
cascade capabilities --json