{
  "name": "@the-cascade-protocol/cli",
  "version": "0.27.0",
  "description": "Cascade Protocol CLI - Validate, convert, and manage health data. Local-first: every command operates on the local filesystem except the two named in securityModel.networkCalls.",
  "protocol": "https://cascadeprotocol.org",
  "parameterConventions": "In tools[].parameters: \"required\": true means the ARGUMENT PARSER rejects the command without it, and the key is absent otherwise. Absent is not a promise that the command will run: several commands accept a parse-legal invocation and then refuse it, and every one of those states its real requirement in that command's \"notes\" — read notes before composing a call. \"type\": \"boolean\" is a flag that takes no value; \"string[]\" is a variadic argument. \"default\" appears only when a default is registered. mcpTools[].parameters state \"required\" explicitly on every entry.",
  "globalOptions": {
    "position": "Declared on the root command, not on any subcommand, and accepted in any position: `cascade --json pod query <pod-dir> --all` and `cascade pod query <pod-dir> --all --json` are equivalent. They are listed here once and are NOT repeated in each command below.",
    "options": [
      {
        "name": "--version",
        "type": "boolean",
        "description": "output the version number",
        "short": "-V"
      },
      {
        "name": "--verbose",
        "type": "boolean",
        "description": "Verbose output"
      },
      {
        "name": "--json",
        "type": "boolean",
        "description": "Output results as JSON (machine-readable)"
      }
    ]
  },
  "securityModel": {
    "networkCalls": "none by default — no command contacts a network to validate, convert, query, import or write. Two exceptions, both only when you invoke them: `advisory feed pull` fetches the feed URL you pass it (any host you choose), and `pod extract` posts narrative text to the cascade-agent server at --agent-url (http://127.0.0.1:8765 by default, i.e. your machine). No telemetry, no analytics, no implicit calls.",
    "dataStorage": "local filesystem only",
    "provenance": "all agent-written data tagged with AIExtracted provenance",
    "auditLog": "all MCP operations logged to provenance/audit-log.ttl"
  },
  "tools": [
    {
      "name": "validate",
      "description": "Validate Cascade data against SHACL shapes",
      "usage": "cascade validate <file-or-dir> [options]",
      "parameters": [
        {
          "name": "file-or-dir",
          "type": "string",
          "required": true,
          "description": "Turtle file or directory to validate"
        },
        {
          "name": "--shapes",
          "type": "string",
          "description": "Path to custom SHACL shapes directory"
        }
      ],
      "examples": [
        "cascade validate record.ttl",
        "cascade --json validate ./data/"
      ],
      "status": "implemented"
    },
    {
      "name": "convert",
      "description": "Convert between health data formats",
      "usage": "cascade convert [file] [options]",
      "parameters": [
        {
          "name": "file",
          "type": "string",
          "description": "Input file (reads from stdin if omitted)"
        },
        {
          "name": "--from",
          "type": "string",
          "required": true,
          "description": "Source format (fhir-genomics|phenopacket|vrs|fhir|clinvar|c-cda|vcf|cascade)"
        },
        {
          "name": "--to",
          "type": "string",
          "required": true,
          "description": "Target format (turtle|jsonld|fhir|cascade)"
        },
        {
          "name": "--format",
          "type": "string",
          "description": "Output serialization format (turtle|jsonld)",
          "default": "turtle"
        },
        {
          "name": "--source-system",
          "type": "string",
          "description": "Tag all records with a source system name (adds cascade:sourceSystem for reconciliation)"
        },
        {
          "name": "--passthrough",
          "type": "string",
          "description": "Passthrough mode for unmapped FHIR types: full (store fhirJson, round-trip supported) or minimal (omit fhirJson, smaller output)",
          "default": "full"
        },
        {
          "name": "--allow-vrs-hash-mismatch",
          "type": "boolean",
          "description": "Accept a VRS Allele whose declared id does not match cascade-cli's simple canonical-form hash. Required for vrs-python-generated alleles (whose recursive-digest canonicalization the CLI does not reproduce). Default: false (strict reject)."
        },
        {
          "name": "--manifest",
          "type": "string",
          "description": "Write import manifest JSON alongside output (default: {input}-manifest.json). Only meaningful when --from fhir."
        },
        {
          "name": "--extract-narratives",
          "type": "boolean",
          "description": "Extract narrative text blocks from C-CDA sections and write a JSON sidecar <file>.narratives.json. Only meaningful when --from c-cda."
        }
      ],
      "examples": [
        "cascade convert patient.json --from fhir --to turtle",
        "cat data.json | cascade convert --from fhir --to turtle"
      ],
      "status": "implemented"
    },
    {
      "name": "reconcile",
      "description": "Reconcile Cascade RDF from multiple sources into a normalized record set",
      "usage": "cascade reconcile <files...> [options]",
      "parameters": [
        {
          "name": "files",
          "type": "string[]",
          "required": true,
          "description": "Cascade Turtle files to reconcile (2 or more)"
        },
        {
          "name": "--output",
          "type": "string",
          "description": "Write merged Turtle output to file (default: stdout)"
        },
        {
          "name": "--report",
          "type": "string",
          "description": "Write JSON transformation report to file"
        },
        {
          "name": "--trust",
          "type": "string",
          "description": "Source trust scores: system1=0.9,system2=0.85"
        },
        {
          "name": "--lab-tolerance",
          "type": "string",
          "description": "Lab value match tolerance as fraction (default: 0.05)",
          "default": "0.05"
        }
      ],
      "examples": [
        "cascade reconcile system-a.ttl system-b.ttl --output merged.ttl --report report.json",
        "cascade reconcile vm.ttl swedish.ttl --trust 0.9,0.7 --output merged.ttl"
      ],
      "status": "implemented"
    },
    {
      "name": "pod",
      "description": "Manage Cascade Pod structures",
      "usage": "cascade pod <subcommand>",
      "parameters": [],
      "subcommands": [
        "init",
        "query",
        "export",
        "info",
        "profile",
        "import",
        "conflicts",
        "resolve",
        "extract",
        "encrypt",
        "decrypt",
        "passphrase",
        "amend",
        "annotate",
        "add-record",
        "retract",
        "erase",
        "doctor",
        "reconcile"
      ],
      "status": "implemented"
    },
    {
      "name": "pod init",
      "description": "Initialize a new Cascade Pod",
      "usage": "cascade pod init <directory> [options]",
      "parameters": [
        {
          "name": "directory",
          "type": "string",
          "required": true,
          "description": "Directory to initialize as a Cascade Pod"
        },
        {
          "name": "--encrypt",
          "type": "boolean",
          "description": "Encrypt pod resources at rest (AES-256-GCM, passphrase-wrapped). Passphrase is read from CASCADE_POD_PASSPHRASE or a hidden prompt."
        },
        {
          "name": "--owner-name",
          "type": "string",
          "description": "Set the pod owner's display name (foaf:name) in profile/card.ttl."
        }
      ],
      "examples": [
        "cascade pod init ./my-pod"
      ],
      "status": "implemented"
    },
    {
      "name": "pod query",
      "description": "Query data within a pod",
      "usage": "cascade pod query <pod-dir> [options]",
      "parameters": [
        {
          "name": "pod-dir",
          "type": "string",
          "required": true,
          "description": "Path to the Cascade Pod"
        },
        {
          "name": "--medications",
          "type": "boolean",
          "description": "Query medications"
        },
        {
          "name": "--conditions",
          "type": "boolean",
          "description": "Query conditions"
        },
        {
          "name": "--allergies",
          "type": "boolean",
          "description": "Query allergies"
        },
        {
          "name": "--lab-results",
          "type": "boolean",
          "description": "Query lab results"
        },
        {
          "name": "--immunizations",
          "type": "boolean",
          "description": "Query immunizations"
        },
        {
          "name": "--vital-signs",
          "type": "boolean",
          "description": "Query vital signs"
        },
        {
          "name": "--supplements",
          "type": "boolean",
          "description": "Query supplements"
        },
        {
          "name": "--insurance",
          "type": "boolean",
          "description": "Query insurance / coverage plans"
        },
        {
          "name": "--procedures",
          "type": "boolean",
          "description": "Query procedures"
        },
        {
          "name": "--encounters",
          "type": "boolean",
          "description": "Query encounters"
        },
        {
          "name": "--documents",
          "type": "boolean",
          "description": "Query clinical documents"
        },
        {
          "name": "--lab-reports",
          "type": "boolean",
          "description": "Query laboratory reports (DiagnosticReport)"
        },
        {
          "name": "--medication-administrations",
          "type": "boolean",
          "description": "Query medication administrations"
        },
        {
          "name": "--devices",
          "type": "boolean",
          "description": "Query implanted devices"
        },
        {
          "name": "--imaging",
          "type": "boolean",
          "description": "Query imaging studies"
        },
        {
          "name": "--claims",
          "type": "boolean",
          "description": "Query insurance claims"
        },
        {
          "name": "--benefits",
          "type": "boolean",
          "description": "Query explanation of benefits"
        },
        {
          "name": "--fhir-passthrough",
          "type": "boolean",
          "description": "Query FHIR passthrough records (unmapped types)"
        },
        {
          "name": "--all",
          "type": "boolean",
          "description": "Query all data"
        },
        {
          "name": "--neighbors",
          "type": "string",
          "description": "Return the typed neighborhood of a record (traverses stored edges both directions)"
        },
        {
          "name": "--hops",
          "type": "string",
          "description": "Traversal depth for --neighbors (default 1, capped at 3)"
        },
        {
          "name": "--edge",
          "type": "string",
          "description": "Restrict --neighbors traversal to this edge predicate (repeatable; full IRI or prefix:local CURIE)"
        },
        {
          "name": "--edges",
          "type": "boolean",
          "description": "With --all, add a record-to-record edge projection to the output"
        },
        {
          "name": "--exclude-data-type",
          "type": "string",
          "description": "Leave out one data type: its file is never read, decrypted or parsed, by the record sweep, --edges or --neighbors (repeatable). A key is a data type (heart-rate, sleep, ...), wellness-samples (the retained-sample descriptors), or wellness: every file under wellness/ except supplements, plus wellness-samples, the cheap way to ask a clinical question. A key excludes its whole FILE: heart-rate and wellness also drop health:VitalSignReading records coded with a heart-rate LOINC code, clinical ones included; body-measurements and wellness drop VO2 max; wellness keeps supplements. An unknown key is a usage error that lists the keys"
        },
        {
          "name": "--wellness-series",
          "type": "boolean",
          "description": "Add the pod's stored daily wellness series (one reading per reading type, statistic and local day, chosen by source priority, each citing its record) and its per-source summary, under `wellnessDailySeries` (null when the pod holds none). Reads one small file and hashes (never parses) the files it was built from: a series they no longer match comes back with `stale: true` and the reasons. Alone, or beside any filter except --neighbors"
        },
        {
          "name": "--include-bookkeeping",
          "type": "boolean",
          "description": "Also return the pod's own bookkeeping subjects (cascade:PendingConflict, cascade:UserResolution, solid:TypeIndex, solid:TypeRegistration, pim:ConfigurationFile). These are notes ABOUT records, not records, and are excluded by default; ask for them when building a conflict queue"
        }
      ],
      "examples": [
        "cascade pod query ./my-pod --medications --json",
        "cascade pod query ./my-pod --encounters --conditions --json",
        "cascade pod query ./my-pod --all --json",
        "cascade pod query ./my-pod --all --exclude-data-type wellness --wellness-series --json"
      ],
      "outputSchema": {
        "description": "JSON output structure for --json flag",
        "shape": "{ pod: string, dataTypes: { [type]: { count: number, file: string, records: Record[] } } }",
        "recordShape": "{ id: string, type: string, properties: { [prefixed-property]: string } }",
        "propertyPrefixes": {
          "health:": "wellness/device data — health:testName, health:resultValue, health:resultUnit, health:performedDate, health:testCode (LOINC URI), health:conditionName, health:conditionCategory (FHIR category: problem-list-item|encounter-diagnosis|social-history), health:snomedSemanticTag (semantic type from SNOMED display name: disorder|finding|situation|procedure|observable entity), health:status (active/inactive/resolved), health:onsetDate, health:medicationName, health:isActive (true/false string), health:rxNormCode",
          "clinical:": "EHR-imported clinical data — clinical:encounterDate, clinical:encounterType, clinical:procedureName, clinical:procedureDate, clinical:drugCode, clinical:clinicalIntent",
          "core:": "provenance — core:sourceSystem, core:dataProvenance, core:schemaVersion, core:reconciliationStatus, core:mergedSources"
        },
        "jqExamples": [
          "# Clinical conditions only (excludes social findings): cascade pod query <pod> --conditions --json | jq '[.dataTypes.conditions.records[] | select(.properties[\"health:status\"] == \"active\" and .properties[\"health:snomedSemanticTag\"] == \"disorder\") | .properties[\"health:conditionName\"]]'",
          "# All active conditions including findings: cascade pod query <pod> --conditions --json | jq '[.dataTypes.conditions.records[] | select(.properties[\"health:status\"] == \"active\") | {name: .properties[\"health:conditionName\"], type: .properties[\"health:snomedSemanticTag\"]}]'",
          "# HbA1c trend (most recent first): cascade pod query <pod> --lab-results --json | jq '[.dataTypes[\"lab-results\"].records[] | select(.properties[\"health:testName\"] | ascii_downcase | test(\"a1c\")) | {date: .properties[\"health:performedDate\"], value: .properties[\"health:resultValue\"], unit: .properties[\"health:resultUnit\"]}] | sort_by(.date) | reverse'",
          "# Active medications: cascade pod query <pod> --medications --json | jq '[.dataTypes.medications.records[] | select(.properties[\"health:isActive\"] == \"true\") | .properties[\"health:medicationName\"]]'",
          "# Medications with source provenance: cascade pod query <pod> --medications --json | jq '[.dataTypes.medications.records[] | {name: .properties[\"health:medicationName\"], active: .properties[\"health:isActive\"], sources: .properties[\"core:mergedSources\"]}]'"
        ],
        "shellQuotingNote": "Always pipe pod query output through jq rather than reading raw JSON (output can be very large). Use [\"key\"] bracket notation in jq filters to avoid shell quoting issues. If a filter is complex, write it to /tmp/filter.jq and run: cascade pod query <pod> --TYPE --json | jq -f /tmp/filter.jq"
      },
      "status": "implemented"
    },
    {
      "name": "pod export",
      "description": "Export pod data",
      "usage": "cascade pod export <pod-dir> [options]",
      "parameters": [
        {
          "name": "pod-dir",
          "type": "string",
          "required": true,
          "description": "Path to the Cascade Pod"
        },
        {
          "name": "--format",
          "type": "string",
          "description": "Export format (zip|directory)",
          "default": "zip"
        },
        {
          "name": "--output",
          "type": "string",
          "description": "Output path for export"
        },
        {
          "name": "--allow-encrypted",
          "type": "boolean",
          "description": "Export an encrypted pod as ciphertext (the export is stamped with a note explaining it)"
        }
      ],
      "examples": [
        "cascade pod export ./my-pod",
        "cascade pod export ./my-pod --format directory"
      ],
      "notes": [
        "Conditional refusal, enforced at run time: against an ENCRYPTED pod this exits 1 unless --allow-encrypted is passed, because the archive would otherwise contain ciphertext that looks like a working export. Decrypt the pod first, or pass the flag to export the sealed bytes deliberately — an export made that way carries a README explaining what the files are."
      ],
      "status": "implemented"
    },
    {
      "name": "pod info",
      "description": "Show pod metadata and statistics",
      "usage": "cascade pod info <pod-dir>",
      "parameters": [
        {
          "name": "pod-dir",
          "type": "string",
          "required": true,
          "description": "Path to the Cascade Pod"
        }
      ],
      "examples": [
        "cascade pod info ./my-pod",
        "cascade --json pod info ./my-pod"
      ],
      "status": "implemented"
    },
    {
      "name": "pod profile",
      "description": "Manage a pod owner's profile identity",
      "usage": "cascade pod profile <subcommand>",
      "parameters": [],
      "subcommands": [
        "set-name"
      ],
      "status": "implemented"
    },
    {
      "name": "pod profile set-name",
      "description": "Set the pod owner's display name (foaf:name) in profile/card.ttl",
      "usage": "cascade pod profile set-name <dir> <name>",
      "parameters": [
        {
          "name": "dir",
          "type": "string",
          "required": true,
          "description": "Path to the Cascade Pod"
        },
        {
          "name": "name",
          "type": "string",
          "required": true,
          "description": "Owner display name (e.g. \"Jane Doe\")"
        }
      ],
      "status": "implemented"
    },
    {
      "name": "pod import",
      "description": "Import FHIR JSON or Cascade Turtle files into a pod",
      "usage": "cascade pod import <pod-dir> <files...> [options]",
      "parameters": [
        {
          "name": "pod-dir",
          "type": "string",
          "required": true,
          "description": "Path to the Cascade Pod directory"
        },
        {
          "name": "files",
          "type": "string[]",
          "required": true,
          "description": "Files or folders to import (FHIR JSON, Cascade Turtle, C-CDA XML/zip, or a folder / Apple Health export)"
        },
        {
          "name": "--source-system",
          "type": "string",
          "description": "Tag all imported records with this system name"
        },
        {
          "name": "--no-reconcile",
          "type": "boolean",
          "description": "Skip reconciliation even when importing multiple files"
        },
        {
          "name": "--reconcile-existing",
          "type": "boolean",
          "description": "Include existing pod records in reconciliation pass (cross-batch dedup, on by default; disable with --no-reconcile-existing)",
          "default": true
        },
        {
          "name": "--no-reconcile-existing",
          "type": "boolean",
          "description": "Skip loading existing pod records (additive import only)"
        },
        {
          "name": "--trust",
          "type": "string",
          "description": "Trust scores e.g. hospital=0.95,clinic=0.85"
        },
        {
          "name": "--dry-run",
          "type": "boolean",
          "description": "Preview the import without writing any files"
        },
        {
          "name": "--report",
          "type": "string",
          "description": "Write import report JSON to this file"
        },
        {
          "name": "--passthrough",
          "type": "string",
          "description": "Passthrough mode: full or minimal (default: full)",
          "default": "full"
        }
      ],
      "examples": [
        "cascade pod import ./my-pod patient.json --source-system \"Virginia Mason\"",
        "cascade pod import ./my-pod vm.json swedish.json --source-system \"primary,specialist\" --report report.json",
        "cascade pod import ./my-pod records.ttl --no-reconcile"
      ],
      "status": "implemented"
    },
    {
      "name": "pod conflicts",
      "description": "List unresolved conflicts in a pod (--resolved for the decision log)",
      "usage": "cascade pod conflicts <pod-dir> [options]",
      "parameters": [
        {
          "name": "pod-dir",
          "type": "string",
          "required": true,
          "description": "Path to the Cascade Pod directory"
        },
        {
          "name": "--format",
          "type": "string",
          "description": "Output format: text or json",
          "default": "text"
        },
        {
          "name": "--resolved",
          "type": "boolean",
          "description": "List recorded decisions instead of the unanswered queue"
        }
      ],
      "status": "implemented"
    },
    {
      "name": "pod resolve",
      "description": "Record a conflict resolution decision in the pod (carried out by the next pod reconcile --apply)",
      "usage": "cascade pod resolve <pod-dir> [options]",
      "parameters": [
        {
          "name": "pod-dir",
          "type": "string",
          "required": true,
          "description": "Path to the Cascade Pod directory"
        },
        {
          "name": "--conflict",
          "type": "string",
          "required": true,
          "description": "Conflict ID to resolve (from cascade pod conflicts)"
        },
        {
          "name": "--keep",
          "type": "string",
          "required": true,
          "description": "Which source to keep: source-a, source-b, both"
        },
        {
          "name": "--note",
          "type": "string",
          "description": "Optional note about your decision"
        },
        {
          "name": "--by",
          "type": "string",
          "description": "Optional actor IRI (prov:wasAttributedTo)"
        }
      ],
      "status": "implemented"
    },
    {
      "name": "pod extract",
      "description": "Send narrative text from imported C-CDA records to cascade-agent for AI extraction",
      "usage": "cascade pod extract <pod-dir> [options]",
      "parameters": [
        {
          "name": "pod-dir",
          "type": "string",
          "required": true,
          "description": ""
        },
        {
          "name": "--agent-url",
          "type": "string",
          "description": "cascade-agent base URL",
          "default": "http://127.0.0.1:8765"
        },
        {
          "name": "--dry-run",
          "type": "boolean",
          "description": "Show what would be extracted without calling the agent"
        },
        {
          "name": "--section",
          "type": "string",
          "description": "Only extract a specific section (medications|conditions|labResults|...)"
        }
      ],
      "status": "implemented"
    },
    {
      "name": "pod encrypt",
      "description": "Encrypt an existing plaintext pod at rest (AES-256-GCM)",
      "usage": "cascade pod encrypt <dir>",
      "parameters": [
        {
          "name": "dir",
          "type": "string",
          "required": true,
          "description": "Path to the Cascade Pod directory"
        }
      ],
      "status": "implemented"
    },
    {
      "name": "pod decrypt",
      "description": "Decrypt an encrypted pod back to plaintext at rest",
      "usage": "cascade pod decrypt <dir> [options]",
      "parameters": [
        {
          "name": "dir",
          "type": "string",
          "required": true,
          "description": "Path to the Cascade Pod directory"
        },
        {
          "name": "--force",
          "type": "boolean",
          "description": "Proceed even when some files cannot be decrypted with this pod key. They are LEFT UNCHANGED and the encryption manifest is still removed."
        }
      ],
      "status": "implemented"
    },
    {
      "name": "pod passphrase",
      "description": "Manage the passphrase that opens an encrypted pod",
      "usage": "cascade pod passphrase <subcommand>",
      "parameters": [],
      "subcommands": [
        "set"
      ],
      "status": "implemented"
    },
    {
      "name": "pod passphrase set",
      "description": "Change the passphrase of an encrypted pod by re-wrapping its key. A copy of the pod made before the change still opens with the old passphrase.",
      "usage": "cascade pod passphrase set <pod-dir> [options]",
      "parameters": [
        {
          "name": "pod-dir",
          "type": "string",
          "required": true,
          "description": "Path to the encrypted Cascade Pod directory"
        },
        {
          "name": "--rotate-dek",
          "type": "boolean",
          "description": "Also replace the data key: re-encrypt every file under a new key and keep only the new passphrase"
        }
      ],
      "status": "implemented"
    },
    {
      "name": "pod amend",
      "description": "Override one property value on a record via an append-only Amendment overlay",
      "usage": "cascade pod amend <pod-dir> [options]",
      "parameters": [
        {
          "name": "pod-dir",
          "type": "string",
          "required": true,
          "description": "Path to the Cascade Pod directory"
        },
        {
          "name": "--record",
          "type": "string",
          "required": true,
          "description": "IRI of the record to amend"
        },
        {
          "name": "--property",
          "type": "string",
          "required": true,
          "description": "Predicate CURIE being overridden, e.g. clinical:dosage"
        },
        {
          "name": "--value",
          "type": "string",
          "required": true,
          "description": "The new value that supersedes the original"
        },
        {
          "name": "--reason",
          "type": "string",
          "description": "Optional rationale for the amendment"
        },
        {
          "name": "--by",
          "type": "string",
          "description": "Optional actor IRI (prov:wasAttributedTo)"
        }
      ],
      "status": "implemented"
    },
    {
      "name": "pod annotate",
      "description": "Add a note or extra attribute to a record via an append-only Annotation overlay",
      "usage": "cascade pod annotate <pod-dir> [options]",
      "parameters": [
        {
          "name": "pod-dir",
          "type": "string",
          "required": true,
          "description": "Path to the Cascade Pod directory"
        },
        {
          "name": "--record",
          "type": "string",
          "required": true,
          "description": "IRI of the record to annotate"
        },
        {
          "name": "--text",
          "type": "string",
          "description": "Free-text note to attach to the record"
        },
        {
          "name": "--property",
          "type": "string",
          "description": "Predicate CURIE of an extra attribute (paired with --value)"
        },
        {
          "name": "--value",
          "type": "string",
          "description": "Value of the extra attribute named by --property"
        },
        {
          "name": "--by",
          "type": "string",
          "description": "Optional actor IRI (prov:wasAttributedTo)"
        }
      ],
      "notes": [
        "Enforced at run time, not by the parser: the annotation needs content. Pass --text, or --property together with --value. With neither, the command exits 1 with \"Provide at least one of --text or --value (with --property).\""
      ],
      "status": "implemented"
    },
    {
      "name": "pod add-record",
      "description": "Add a new self-reported record to its canonical bucket file",
      "usage": "cascade pod add-record <pod-dir> [propsJson] [options]",
      "parameters": [
        {
          "name": "pod-dir",
          "type": "string",
          "required": true,
          "description": "Path to the Cascade Pod directory"
        },
        {
          "name": "propsJson",
          "type": "string",
          "description": "JSON object of { \"<curie>\": \"<value>\" } properties"
        },
        {
          "name": "--type",
          "type": "string",
          "required": true,
          "description": "rdf:type CURIE of the new record, e.g. clinical:Medication"
        },
        {
          "name": "--by",
          "type": "string",
          "description": "Optional actor IRI (prov:wasAttributedTo)"
        }
      ],
      "examples": [
        "cascade pod add-record ./my-pod '{\"health:conditionName\":\"Iron deficiency\",\"health:status\":\"active\"}' --type health:ConditionRecord",
        "CASCADE_RECORD_JSON='{\"health:medicationName\":\"Aspirin\"}' cascade pod add-record ./my-pod --type clinical:Medication"
      ],
      "notes": [
        "Enforced at run time, not by the parser: the record properties are mandatory. Pass them as the propsJson positional argument — a JSON object of { \"<curie>\": \"<value>\" } — or set CASCADE_RECORD_JSON, which is the better route for a large payload. With neither, the command exits 1 with \"No properties provided.\"",
        "--type takes a CURIE whose prefix is one of cascade/core, health, clinical, coverage, checkup, pots, workbench, fhir, and whose class must map to a known bucket file (e.g. health:ConditionRecord, clinical:Medication). An unmapped type is refused rather than guessed.",
        "The record is written as cascade:SelfReported and workbench:Unverified, attributed to --by if given and otherwise to the pod owner's WebID.",
        "Property values are given as strings, and each is written as the datatype the bundled SHACL shapes declare for that property: a checkup:supplementIsActive of \"true\" becomes an xsd:boolean, a checkup:supplementStartDate an xsd:date, a checkup:patientCost an xsd:decimal. A property no shape declares stays a plain string literal. A value that cannot be its declared datatype (a boolean given as \"maybe\", a date given as \"2026-02-30\") is refused and nothing is written."
      ],
      "status": "implemented"
    },
    {
      "name": "pod retract",
      "description": "Soft-delete / supersede a record via an append-only Retraction overlay",
      "usage": "cascade pod retract <pod-dir> [options]",
      "parameters": [
        {
          "name": "pod-dir",
          "type": "string",
          "required": true,
          "description": "Path to the Cascade Pod directory"
        },
        {
          "name": "--record",
          "type": "string",
          "required": true,
          "description": "IRI of the record to retract"
        },
        {
          "name": "--reason",
          "type": "string",
          "description": "Optional rationale (e.g. \"entered in error\")"
        },
        {
          "name": "--superseded-by",
          "type": "string",
          "description": "Optional IRI of the kept record when merging duplicates"
        },
        {
          "name": "--by",
          "type": "string",
          "description": "Optional actor IRI (prov:wasAttributedTo)"
        }
      ],
      "status": "implemented"
    },
    {
      "name": "pod erase",
      "description": "Hard-delete a record from its bucket file and write a Tombstone audit marker",
      "usage": "cascade pod erase <pod-dir> [options]",
      "parameters": [
        {
          "name": "pod-dir",
          "type": "string",
          "required": true,
          "description": "Path to the Cascade Pod directory"
        },
        {
          "name": "--record",
          "type": "string",
          "required": true,
          "description": "IRI of a record to erase (repeatable: several records are erased in one command, and the stored daily wellness series is rebuilt once, after the last)"
        },
        {
          "name": "--confirm",
          "type": "boolean",
          "description": "Required confirmation for the destructive hard delete"
        },
        {
          "name": "--reason",
          "type": "string",
          "description": "Optional rationale for the erasure"
        },
        {
          "name": "--by",
          "type": "string",
          "description": "Optional actor IRI (prov:wasAttributedTo)"
        }
      ],
      "notes": [
        "Destructive and not reversible: the record bytes are removed from the bucket file. --confirm is mandatory, and a Tombstone audit marker is written in the record's place.",
        "For a reversible removal that keeps history, use `pod retract`, which appends a Retraction overlay instead of deleting."
      ],
      "status": "implemented"
    },
    {
      "name": "pod doctor",
      "description": "Diagnose a damaged Cascade Pod and, with --write, repair files whose only defect is a missing @prefix declaration. This is the recovery path when add-record, erase or import refuse a bucket that will not parse. Dry run by default; only ever PREPENDS declarations, never rewrites existing bytes; never invents a namespace.",
      "usage": "cascade pod doctor <pod-dir> [options]",
      "parameters": [
        {
          "name": "pod-dir",
          "type": "string",
          "required": true,
          "description": "Path to the Cascade Pod directory"
        },
        {
          "name": "--write",
          "type": "boolean",
          "description": "Apply the repairs. Without this nothing is modified."
        }
      ],
      "examples": [
        "cascade pod doctor ./my-pod",
        "cascade pod doctor ./my-pod --write",
        "cascade --json pod doctor ./my-pod"
      ],
      "outputSchema": {
        "description": "JSON report structure for --json",
        "shape": "{ pod, encrypted, mode: \"dry-run\"|\"write\", scanned, healthy, repaired, repairable, refused, unreadable, findings: Finding[] }",
        "findingShape": "{ file, status: \"repairable\"|\"repaired\"|\"refused\"|\"unreadable\", damage, reason, nextStep?, missingPrefixes?, triples?, preservedBytes?, backup? }",
        "exitCodes": "0 = nothing wrong or everything repaired; 1 = damage remains (or no pod at that path); 2 = the pod could not be opened"
      },
      "status": "implemented"
    },
    {
      "name": "pod reconcile",
      "description": "Find (and with --apply, merge) duplicates a pod ALREADY holds. Dry run by default.",
      "usage": "cascade pod reconcile <pod-dir> [options]",
      "parameters": [
        {
          "name": "pod-dir",
          "type": "string",
          "required": true,
          "description": "Path to the Cascade Pod directory"
        },
        {
          "name": "--apply",
          "type": "boolean",
          "description": "Actually merge and write. Without this the command only reports what it would do."
        },
        {
          "name": "--undo",
          "type": "boolean",
          "description": "Replay settings/tier0-merge-journal.json and put the silently merged records back. Reports by default; combine with --apply to write."
        },
        {
          "name": "--trust",
          "type": "string",
          "description": "Trust scores, e.g. hospital=0.95,clinic=0.85"
        },
        {
          "name": "--report",
          "type": "string",
          "description": "Write the full report as JSON to this file"
        }
      ],
      "status": "implemented"
    },
    {
      "name": "sources",
      "description": "Inspect the raw source documents a pod retained at import",
      "usage": "cascade sources <subcommand>",
      "parameters": [],
      "subcommands": [
        "coverage"
      ],
      "status": "implemented"
    },
    {
      "name": "sources coverage",
      "description": "Report which populated source fields the import did not carry into the pod",
      "usage": "cascade sources coverage <pod-dir>",
      "parameters": [
        {
          "name": "pod-dir",
          "type": "string",
          "required": true,
          "description": "Path to the Cascade Pod"
        }
      ],
      "status": "implemented"
    },
    {
      "name": "conformance",
      "description": "Run conformance test suite",
      "usage": "cascade conformance <subcommand>",
      "parameters": [],
      "subcommands": [
        "run"
      ],
      "status": "implemented"
    },
    {
      "name": "conformance run",
      "description": "Execute conformance tests",
      "usage": "cascade conformance run [options]",
      "parameters": [
        {
          "name": "--suite",
          "type": "string",
          "required": true,
          "description": "Path to test fixtures directory"
        },
        {
          "name": "--command",
          "type": "string",
          "description": "External command to test against"
        },
        {
          "name": "--self",
          "type": "boolean",
          "description": "Run self-conformance tests"
        }
      ],
      "examples": [
        "cascade conformance run --suite ./fixtures --self"
      ],
      "notes": [
        "Enforced at run time, not by the parser: exactly one mode must be chosen. Without --self or --command the command exits 1 with \"Either --command or --self must be specified\"."
      ],
      "status": "implemented"
    },
    {
      "name": "serve",
      "description": "Start the local MCP (Model Context Protocol) server, exposing the cascade_* tools listed under mcpTools to an MCP client such as Claude Desktop or Claude Code. Serves over stdio by default, or SSE on a port. Requires --mcp.",
      "usage": "cascade serve [options]",
      "parameters": [
        {
          "name": "--mcp",
          "type": "boolean",
          "description": "Enable MCP (Model Context Protocol) mode"
        },
        {
          "name": "--transport",
          "type": "string",
          "description": "Transport type (stdio|sse)",
          "default": "stdio"
        },
        {
          "name": "--port",
          "type": "string",
          "description": "Port for SSE transport",
          "default": "3000"
        },
        {
          "name": "--pod",
          "type": "string",
          "description": "Default Pod directory path"
        }
      ],
      "examples": [
        "cascade serve --mcp",
        "cascade serve --mcp --transport sse --port 3000"
      ],
      "notes": [
        "Enforced at run time, not by the parser: --mcp is mandatory in practice. `cascade serve` on its own exits 1 with \"The --mcp flag is required.\" There is no non-MCP server mode."
      ],
      "status": "implemented"
    },
    {
      "name": "capabilities",
      "description": "Show machine-readable tool descriptions",
      "usage": "cascade capabilities",
      "parameters": [],
      "examples": [
        "cascade capabilities"
      ],
      "status": "implemented"
    },
    {
      "name": "advisory",
      "description": "Cascade Advisory Patch (CAP) — validate, apply, list, revert, feed, dry-run",
      "usage": "cascade advisory <subcommand>",
      "parameters": [],
      "subcommands": [
        "validate",
        "apply",
        "list",
        "revert",
        "feed",
        "dry-run"
      ],
      "status": "implemented"
    },
    {
      "name": "advisory validate",
      "description": "Parse + profile-validate a CAP advisory (.ldpatch). No pod mutation.",
      "usage": "cascade advisory validate <patch>",
      "parameters": [
        {
          "name": "patch",
          "type": "string",
          "required": true,
          "description": "Path to the .ldpatch advisory file"
        }
      ],
      "status": "implemented"
    },
    {
      "name": "advisory apply",
      "description": "Verify JWS, evaluate selector, apply CAP advisory to a pod",
      "usage": "cascade advisory apply <patch> [options]",
      "parameters": [
        {
          "name": "patch",
          "type": "string",
          "required": true,
          "description": "Path to the .ldpatch advisory file"
        },
        {
          "name": "--pod",
          "type": "string",
          "required": true,
          "description": "Pod directory"
        },
        {
          "name": "--signature",
          "type": "string",
          "required": true,
          "description": "Path to the detached JWS file (header..signature)"
        },
        {
          "name": "--key",
          "type": "string",
          "description": "Trusted issuer Ed25519 public key as hex (32 bytes). Repeatable for key rotation."
        }
      ],
      "status": "implemented"
    },
    {
      "name": "advisory list",
      "description": "List CAP advisories cached in a pod, optionally filtered by status",
      "usage": "cascade advisory list [options]",
      "parameters": [
        {
          "name": "--pod",
          "type": "string",
          "required": true,
          "description": "Pod directory"
        },
        {
          "name": "--pending",
          "type": "boolean",
          "description": "Show only pending advisories"
        },
        {
          "name": "--applied",
          "type": "boolean",
          "description": "Show only applied advisories"
        },
        {
          "name": "--declined",
          "type": "boolean",
          "description": "Show only declined advisories"
        }
      ],
      "status": "implemented"
    },
    {
      "name": "advisory revert",
      "description": "Roll back a previously-applied CAP advisory using its activity log",
      "usage": "cascade advisory revert [options]",
      "parameters": [
        {
          "name": "--pod",
          "type": "string",
          "required": true,
          "description": "Pod directory"
        },
        {
          "name": "--advisory",
          "type": "string",
          "required": true,
          "description": "Advisory IRI to revert"
        }
      ],
      "status": "implemented"
    },
    {
      "name": "advisory feed",
      "description": "Manage advisory feeds",
      "usage": "cascade advisory feed <subcommand>",
      "parameters": [],
      "subcommands": [
        "pull"
      ],
      "status": "implemented"
    },
    {
      "name": "advisory feed pull",
      "description": "Pull an advisory feed and cache new entries",
      "usage": "cascade advisory feed pull <url> [options]",
      "parameters": [
        {
          "name": "url",
          "type": "string",
          "required": true,
          "description": "Feed URL (typically <issuer>/feed.jsonld)"
        },
        {
          "name": "--pod",
          "type": "string",
          "required": true,
          "description": "Pod directory"
        }
      ],
      "status": "implemented"
    },
    {
      "name": "advisory dry-run",
      "description": "Apply a CAP advisory to a clone of the pod and print the would-be triples",
      "usage": "cascade advisory dry-run <patch> [options]",
      "parameters": [
        {
          "name": "patch",
          "type": "string",
          "required": true,
          "description": "Path to the .ldpatch advisory file"
        },
        {
          "name": "--pod",
          "type": "string",
          "required": true,
          "description": "Pod directory"
        }
      ],
      "status": "implemented"
    },
    {
      "name": "agent",
      "description": "Natural language interface for Cascade Protocol operations",
      "usage": "cascade agent [prompt] [options]",
      "parameters": [
        {
          "name": "prompt",
          "type": "string",
          "description": "One-shot prompt (omit for interactive REPL)"
        },
        {
          "name": "--provider",
          "type": "string",
          "description": "Provider to use for this run",
          "short": "-p"
        },
        {
          "name": "--model",
          "type": "string",
          "description": "Model override for this run",
          "short": "-m"
        }
      ],
      "subcommands": [
        "serve",
        "review",
        "login",
        "provider",
        "model"
      ],
      "status": "implemented"
    },
    {
      "name": "agent serve",
      "description": "Start the document intelligence extraction server (POST /extract)",
      "usage": "cascade agent serve [options]",
      "parameters": [
        {
          "name": "--port",
          "type": "string",
          "description": "Port to listen on",
          "default": "8765"
        },
        {
          "name": "--web-review",
          "type": "boolean",
          "description": "Serve the web review UI"
        }
      ],
      "status": "implemented"
    },
    {
      "name": "agent review",
      "description": "Interactive terminal review of AI extraction queue",
      "usage": "cascade agent review [options]",
      "parameters": [
        {
          "name": "--pod",
          "type": "string",
          "description": "Path to the Cascade pod directory"
        },
        {
          "name": "--output",
          "type": "string",
          "description": "Path to write review results JSON"
        }
      ],
      "status": "implemented"
    },
    {
      "name": "agent login",
      "description": "Add or update credentials for a provider",
      "usage": "cascade agent login [options]",
      "parameters": [
        {
          "name": "--provider",
          "type": "string",
          "description": "Provider to configure",
          "short": "-p"
        }
      ],
      "status": "implemented"
    },
    {
      "name": "agent provider",
      "description": "Get or set the active provider",
      "usage": "cascade agent provider [name]",
      "parameters": [
        {
          "name": "name",
          "type": "string",
          "description": ""
        }
      ],
      "status": "implemented"
    },
    {
      "name": "agent model",
      "description": "Get or set the model for the active provider",
      "usage": "cascade agent model [name] [options]",
      "parameters": [
        {
          "name": "name",
          "type": "string",
          "description": ""
        },
        {
          "name": "--provider",
          "type": "string",
          "description": "Provider to configure",
          "short": "-p"
        }
      ],
      "status": "implemented"
    }
  ],
  "mcpTools": [
    {
      "name": "cascade_pod_read",
      "description": "Read a Cascade Pod and return a JSON summary of all contents including patient profile, record counts, provenance sources, and data inventory.",
      "parameters": {
        "path": {
          "type": "string",
          "description": "Path to the Pod directory. Uses CASCADE_POD_PATH if omitted.",
          "required": false
        }
      },
      "returns": "JSON with patient profile, record counts, provenance sources",
      "cliEquivalent": "cascade pod info <pod-dir> --json"
    },
    {
      "name": "cascade_pod_query",
      "description": "Query records from a Cascade Pod by data type. Returns JSON array of matching records with their properties and provenance. A pod with wellness data is large: for a clinical question use excludeDataTypes: [\"wellness\"]; for a wellness question read wellnessSeries (one small file) rather than the wellness records.",
      "parameters": {
        "path": {
          "type": "string",
          "description": "Path to the Pod directory. Uses CASCADE_POD_PATH if omitted.",
          "required": false
        },
        "dataType": {
          "type": "string",
          "description": "Data type to query, or \"all\" for everything. Optional when wellnessSeries is true.",
          "required": false,
          "enum": [
            "medications",
            "conditions",
            "allergies",
            "lab-results",
            "immunizations",
            "vital-signs",
            "supplements",
            "insurance",
            "patient-profile",
            "heart-rate",
            "blood-pressure",
            "activity",
            "sleep",
            "all"
          ]
        },
        "excludeDataTypes": {
          "type": "array",
          "description": "Data types whose files are never read (keys as for `pod query --exclude-data-type`). \"wellness\" = every wellness/ file plus the retained-sample descriptors; it also drops supplements, heart-rate vital signs (clinical ones included) and VO2 max, which share those files.",
          "required": false
        },
        "wellnessSeries": {
          "type": "boolean",
          "description": "Add wellnessDailySeries: one reading per type, statistic and day, each citing its record, with stale and staleReasons (stale: true means rebuild with `pod reconcile --apply`). Null when the pod has none.",
          "required": false
        }
      },
      "returns": "JSON array of matching records with properties",
      "cliEquivalent": "cascade pod query <pod-dir> --medications --json"
    },
    {
      "name": "cascade_validate",
      "description": "Validate Cascade Protocol Turtle data against SHACL shapes. Accepts either a file/directory path or inline Turtle content.",
      "parameters": {
        "path": {
          "type": "string",
          "description": "Path to a Turtle file or directory to validate.",
          "required": false
        },
        "content": {
          "type": "string",
          "description": "Inline Turtle content to validate (alternative to path).",
          "required": false
        }
      },
      "returns": "Validation results with pass/fail per constraint",
      "cliEquivalent": "cascade validate <file-or-dir> --json"
    },
    {
      "name": "cascade_convert",
      "description": "Convert between health data formats. Supports FHIR R4 JSON to Cascade Turtle/JSON-LD and vice versa.",
      "parameters": {
        "content": {
          "type": "string",
          "description": "The content to convert (FHIR JSON string or Cascade Turtle string).",
          "required": true
        },
        "from": {
          "type": "string",
          "description": "Source format.",
          "required": true,
          "enum": [
            "fhir",
            "cascade"
          ]
        },
        "to": {
          "type": "string",
          "description": "Target format.",
          "required": true,
          "enum": [
            "cascade",
            "fhir"
          ]
        },
        "format": {
          "type": "string",
          "description": "Output serialization format when converting to Cascade. Default: turtle.",
          "required": false,
          "enum": [
            "turtle",
            "jsonld"
          ]
        }
      },
      "returns": "Converted output",
      "cliEquivalent": "cascade convert --from fhir --to cascade <file>"
    },
    {
      "name": "cascade_write",
      "description": "Write a health record to a Cascade Pod with AIExtracted provenance. The record is serialized as Turtle and appended to the appropriate file in the Pod.",
      "parameters": {
        "path": {
          "type": "string",
          "description": "Path to the Pod directory. Uses CASCADE_POD_PATH if omitted.",
          "required": false
        },
        "dataType": {
          "type": "string",
          "description": "Type of health record to write.",
          "required": true,
          "enum": [
            "medications",
            "conditions",
            "allergies",
            "lab-results",
            "immunizations",
            "vital-signs",
            "supplements"
          ]
        },
        "record": {
          "type": "object",
          "description": "JSON object with record fields (e.g., { \"name\": \"Aspirin\", \"dose\": \"81 mg\" }).",
          "required": true
        },
        "provenance": {
          "type": "object",
          "description": "Provenance metadata for the written record.",
          "required": false,
          "properties": {
            "agentId": "string (optional) — Identifier of the AI agent writing the data.",
            "reason": "string (optional) — Reason for creating this record.",
            "confidence": "number (optional) — Agent confidence level (0.0-1.0).",
            "sourceRecords": "array (optional) — URIs of source records used to derive this data."
          }
        }
      },
      "returns": "Record URI, file path, provenance metadata"
    },
    {
      "name": "cascade_capabilities",
      "description": "Describe all available Cascade Protocol MCP tools, their parameters, and usage examples. Use this as the entry point for discovering what the server can do.",
      "parameters": {},
      "returns": "This capabilities document"
    }
  ]
}
