From firely-skills
FHIR validation specialist. Use when validating FHIR resources, profiles, or Implementation Guides against conformance rules. Covers Firely Terminal (.NET, profile-aware, free + paid QC rules), HL7 Java validator (thorough, profile-aware), and Python schema validation. Guides project assessment, runs both profile-aware validators for consistency, and supports CI/CD via FirelyTeam/firely-terminal-pipeline.
How this skill is triggered — by the user, by Claude, or both
Slash command
/firely-skills:fhir-validationThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Expert guidance for validating FHIR resources, profiles, and Implementation Guides. This skill assesses your project setup, selects the right validators, and runs them in the right order.
Expert guidance for validating FHIR resources, profiles, and Implementation Guides. This skill assesses your project setup, selects the right validators, and runs them in the right order.
Firely Terminal commands: Only use commands that exist in Firely Terminal. This https://docs.fire.ly/projects/Firely-Terminal/Command-Reference.html is the authoritative source — do not invent command names, flags, or parameters. If you are unsure whether a command or flag exists, look it up in that file before suggesting it.
There are three approaches. Use profile-aware validators (Firely Terminal and/or Java) for real conformance checking.
| Approach | Tool | Profile-aware | Speed | Requires |
|---|---|---|---|---|
| Firely Terminal (.NET SDK) | fhir check / fhir validate | Yes | Fast | .NET SDK 8 |
| HL7 Java Validator | validator_cli.jar | Yes | Slower | Java |
| Python fhir.resources | fhir.resources lib | No — schema only | Fastest | Python |
Firely Terminal is built around an internal resource stack. All resource operations (push, validate, snapshot, etc.) act on that stack. It uses the same validation engine as Simplifier.net and Forge.
fhir check or fhir validate.rules.yaml on top of fhir checkfhir validate loop onlyHL7 Java Validator is the official HL7 reference validator — thorough but slower. Best run alongside Firely Terminal to cross-check results.
Python fhir.resources performs JSON schema validation only. It does not check profile constraints, slices, or FHIRPath invariants. Use only for in-app embedded validation where no external tool can be invoked.
Three quick checks before running any validator:
FSH project? — if sushi-config.yaml and .fsh files in input/fsh/ are present, compile first:
sushi build # compiles FSH → fsh-generated/resources/
Fix any SUSHI errors before validating. Never edit fsh-generated/ — it is regenerated on every build.
Check tooling availability (if unsure):
dotnet --version # .NET SDK 8+ required by Firely Terminal
fhir --version # Firely Terminal installation
java -version # Java for HL7 Java validator
If fhir --version fails, Firely Terminal is not installed. Install it with dotnet tool install -g firely.terminal before proceeding.
For any Firely Terminal command not shown in this skill, look it up in references/firely_terminal_commands.md before suggesting it — never guess flags or subcommands.
dotnet tool install -g firely.terminal # first time
dotnet tool update -g firely.terminal # to update
Check for package.json first. If it is missing, run fhir init to create a minimal one, then restore:
if (-not (Test-Path "package.json")) {
fhir init # creates a default package.json; edit name/version/dependencies as needed
}
fhir restore # downloads declared dependencies; without this, validators may produce spurious "unknown profile" errors
Preferred — fhir check (full profile-aware validation; also runs QC rules if .rules.yaml files are present):
Always try fhir check first. It runs full validation regardless of whether .rules.yaml files exist — the paid license only adds the QC rules layer on top.
fhir check
If not authenticated:
fhir login [email protected] password=yourpassword
fhir check
Fallback — fhir validate loop (use only when no Simplifier.net account is available):
Use fhir validate [file] --push to validate a file and keep the resulting OperationOutcome on the stack for later review. On Windows, avoid absolute backslash paths — pass relative paths or use Push-Location to cd into the folder first and pass a bare filename.
# PowerShell
$folders = @("./input/resources", "./input/examples", "./fsh-generated/resources")
foreach ($folder in $folders) {
if (Test-Path $folder) {
Push-Location $folder
Get-ChildItem -Filter "*.json" | ForEach-Object {
fhir validate $_.Name --push
}
Pop-Location
}
}
After validation, the stack contains the original resources interleaved with OperationOutcomes. Bundle and inspect:
fhir bundle # collect all stack items into one Bundle
fhir save validation-results.json --json --pretty # Save to file for large result sets or programmatic processing
Process the saved results with Python:
import json
from pathlib import Path
def summarize_validation_results(path="validation-results.json"):
data = json.loads(Path(path).read_text())
entries = data.get("entry", []) if data.get("resourceType") == "Bundle" else [{"resource": data}]
issues = []
for entry in entries:
resource = entry.get("resource", {})
if resource.get("resourceType") == "OperationOutcome":
for issue in resource.get("issue", []):
issues.append({
"severity": issue.get("severity"),
"message": (issue.get("details") or {}).get("text") or issue.get("diagnostics", ""),
"location": issue.get("expression") or issue.get("location") or [],
})
by_severity = {}
for i in issues:
by_severity.setdefault(i["severity"], []).append(i)
for severity in ("error", "warning", "information"):
items = by_severity.get(severity, [])
if items:
print(f"\n{severity.upper()} ({len(items)}):")
for i in items:
loc = ", ".join(i["location"]) if isinstance(i["location"], list) else str(i["location"])
print(f" {i['message']}" + (f" [{loc}]" if loc else ""))
summarize_validation_results()
Run this alongside Phase 1 to cross-check results.
Locate an existing validator_cli.jar anywhere in the project first:
# PowerShell
$jarItem = Get-ChildItem -Recurse -Filter "validator_cli.jar" -ErrorAction SilentlyContinue | Select-Object -First 1
if ($jarItem) {
$jar = $jarItem.FullName -replace '\\', '/'
} else {
New-Item -ItemType Directory -Force -Path "./input-cache" | Out-Null
Invoke-WebRequest -Uri "https://github.com/hapifhir/org.hl7.fhir.core/releases/latest/download/validator_cli.jar" `
-OutFile "./input-cache/validator_cli.jar"
$jar = "./input-cache/validator_cli.jar"
}
# Validate against base FHIR spec
java -jar $jar patient.json -version 4.0.1
# Validate against a specific IG and profile
java -jar $jar patient.json `
-version 4.0.1 `
-ig hl7.fhir.us.core#6.1.0 `
-profile http://hl7.org/fhir/us/core/StructureDefinition/us-core-patient
# Validate multiple files
Get-ChildItem ./input/resources -Filter *.json | ForEach-Object {
java -jar $jar $_.FullName -version 4.0.1
}
-ig) guidanceWhen validating against local project conformance artifacts, do not use -ig .. Point -ig to concrete folders that actually contain IG resources (for example StructureDefinition-*.json, ValueSet-*.json, CodeSystem-*.json).
Typical local folders to include when they exist:
./fsh-generated/resources (compiled FSH output)./input/resources (hand-authored conformance resources)./package (IG Publisher package output, if present)Use one -ig per folder or package:
java -jar $jar $OUTPUT_FILE `
-version 4.0.1 `
-ig ./fsh-generated/resources `
-ig ./input/resources `
-ig "nictiz.fhir.nl.r4.zib2020#0.12.0-beta.4" `
-ig "nictiz.fhir.nl.r4.nl-core#0.12.0-beta.4"
PowerShell helper to add only existing local IG folders:
$candidateIgDirs = @("./fsh-generated/resources", "./input/resources", "./package")
$igArgs = @()
foreach ($dir in $candidateIgDirs) {
if (Test-Path $dir) {
$igArgs += @("-ig", $dir)
}
}
java -jar $jar $OUTPUT_FILE -version 4.0.1 `
@igArgs `
-ig "nictiz.fhir.nl.r4.zib2020#0.12.0-beta.4" `
-ig "nictiz.fhir.nl.r4.nl-core#0.12.0-beta.4"
After running both validators, compare their output:
If the project uses FSH (SUSHI), never edit files in fsh-generated/ — that folder is deleted and regenerated on every sushi build. All fixes must be made in the .fsh source files under input/fsh/.
Validation errors reference the generated JSON file (e.g. fsh-generated/resources/StructureDefinition-MyProfile.json). The naming pattern is {ResourceType}-{id}.json. To find the FSH source:
id from the error (e.g. MyProfile)input/fsh/: Get-ChildItem input/fsh -Recurse -Filter *.fsh | Select-String 'Id: MyProfile'.fsh file is where the fix goesUse fhir.resources only when validation logic must be embedded inside an application without invoking external tools. This is schema-only — profiles, slices, and FHIRPath invariants are not checked.
from fhir.resources import get_fhir_model_class
def validate_resource_schema(resource_data: dict) -> bool:
"""Schema-level validation only. Use Firely Terminal or Java validator for full conformance."""
try:
resource_type = resource_data.get('resourceType')
resource_class = get_fhir_model_class(resource_type)
resource_class(**resource_data)
return True
except Exception as e:
print(f"Validation error: {e}")
return False
Validation can be automated in CI/CD pipelines using the official FirelyTeam/firely-terminal-pipeline GitHub Action, which runs Firely Terminal (and optionally the Java validator) on every push or pull request.
See references/firely_terminal.md for a full workflow example, or the source documentation at https://github.com/FirelyTeam/firely-terminal-pipeline.
references/firely_terminal.md — workflow guide including, quality control, CI/CD GitHub Actions setupGuides creation, editing, and verification of skills for AI coding agents using test-driven development with subagent scenarios. Use when authoring or debugging skills.
npx claudepluginhub firelyteam/skills --plugin firely-skills