Skip to content

Commit 316566f

Browse files
committed
update README and plan to reflect v0.23.0–v0.42.0 phases shipped
Spec Coverage table now matches reality: every Phase 1–5 box is green, plus 7.x CLI commands, 2.1/2.2/2.3 IG fidelity, and the MCP server skeleton. Adds documentation for the new fhir-gen subcommands (capability/validate/scaffold-ig/diff), the FHIRPath invariant evaluator, and the @fhir-dsl/mcp server with quickstart code. The plan's tracking section gets a shipped/remaining ledger so the next contributor can pick up cleanly.
1 parent d924e44 commit 316566f

2 files changed

Lines changed: 142 additions & 19 deletions

File tree

FHIR_COMPLIANCE_PLAN.md

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -545,6 +545,40 @@ Tool catalog (all schemas are discriminated unions over `resourceType`):
545545
- After a phase ships: bump `pnpm version:bump` (minor for new pillars,
546546
patch for fidelity-only fixes), regenerate the spec coverage table.
547547

548+
### Shipped phases
549+
550+
| Version | Phase | Notes |
551+
|---|---|---|
552+
| v0.23.0 | 1.1 | Branded primitives via `unique symbol` markers |
553+
| v0.24.0 | 1.2 | `ChoiceOf<T, Prefix>` + `choiceOf()` runtime helper |
554+
| v0.25.0 | 1.3 + 1.4 | `_field` siblings, full `Extension.value[x]` union (49 variants) |
555+
| v0.26.0 | 3.1 | Typed terminology operations on `client.terminology.*` |
556+
| v0.27.0 | 4.1 | `createCapabilityGuard` for capability-narrowed clients |
557+
| v0.28.0 | 3.2 | Concept hierarchy + `is-a` / `descendent-of` / `regex` ValueSet filters |
558+
| v0.29.0 | 4.2 | `Bundle.resolveReference` / `resolveReferences` runtime helpers |
559+
| v0.30.0 | 5 | Layered framework: `LAYER_OF`, `referencesUpward` |
560+
| v0.32.0 | 7.2 | `fhir-gen capability <baseUrl>` |
561+
| v0.33.0 | (audit) | Export-surface snapshot in CI |
562+
| v0.34.0 | 2.3 | IG manifest (`global`, `dependsOn`) parsing |
563+
| v0.35.0 | 7.1 | `fhir-gen validate` |
564+
| v0.36.0 | 7.3 | `fhir-gen scaffold-ig` |
565+
| v0.37.0 | 7.4 | `fhir-gen diff` (exits 2 on breaking changes) |
566+
| v0.38.0 | 2.1 | StructureDefinition slicing parse + emit + runtime helpers |
567+
| v0.39.0 | 2.2 | Branded `Extension<URL>` + IG extension SD parsing |
568+
| v0.40.0 | 6 | FHIRPath invariant evaluator (subset) |
569+
| v0.41.0 | 8.1 | `@fhir-dsl/mcp` package skeleton |
570+
| v0.42.0 | 8.2 | MCP generic verb runners (read/search/etc. against upstream) |
571+
572+
### Remaining
573+
574+
- Phase 8.3 — MCP `resources/read` (URI-addressable get-by-id)
575+
- Phase 8.4 — Backend-services + patient-launch auth wiring
576+
- Phase 8.5 — Write gating + safety
577+
- Phase 8.7 — Token economy defaults (response truncation, `_summary`)
578+
- Phase 8.8 — Generator integration: `fhir-gen generate --mcp <out>`
579+
- Phase 8.9 — CLI ergonomics (`fhir-gen mcp` to launch a configured server)
580+
- Phase 6 follow-up — wire compiled invariants into emitted Standard Schema validators
581+
548582
## Out of scope (intentionally)
549583

550584
- A full FHIR server. fhir-dsl is a typed client + generator; server

README.md

Lines changed: 108 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -34,12 +34,15 @@ Working with FHIR APIs in TypeScript typically means dealing with untyped JSON,
3434

3535
| Package | Description | When to Install |
3636
|---|---|---|
37-
| [`@fhir-dsl/core`](./packages/core) | Query builder DSL (search, read, batch, transactions) | Always — this is the query builder |
38-
| [`@fhir-dsl/runtime`](./packages/runtime) | HTTP executor with pagination and error handling | Always — provides the HTTP executor |
39-
| [`@fhir-dsl/cli`](./packages/cli) | CLI for generating types from FHIR specs | Dev dependency — generates types for your project |
40-
| [`@fhir-dsl/types`](./packages/types) | Base FHIR R4/R5 type definitions | Automatically installed as a dependency of `@fhir-dsl/core` |
41-
| [`@fhir-dsl/generator`](./packages/generator) | Code generation engine | Only if building custom tooling on top of the generator |
42-
| [`@fhir-dsl/fhirpath`](./packages/fhirpath) | Type-safe FHIRPath expression builder | When working with FHIRPath expressions |
37+
| [`@fhir-dsl/core`](./packages/core) | Query builder DSL (search, read, batch, transactions, terminology ops, capability guard) | Always — this is the query builder |
38+
| [`@fhir-dsl/runtime`](./packages/runtime) | HTTP executor with pagination, error handling, bundle resolution, slice helpers | Always — provides the HTTP executor |
39+
| [`@fhir-dsl/cli`](./packages/cli) | `fhir-gen` CLI: `generate`, `capability`, `validate`, `scaffold-ig`, `diff` | Dev dependency — generates types for your project |
40+
| [`@fhir-dsl/types`](./packages/types) | Branded FHIR R4/R5 primitives + base datatypes + parsers | Automatically installed as a dependency of `@fhir-dsl/core` |
41+
| [`@fhir-dsl/generator`](./packages/generator) | Code generation engine (resources, profiles, slices, typed extensions, layers, IG manifests) | Only if building custom tooling on top of the generator |
42+
| [`@fhir-dsl/fhirpath`](./packages/fhirpath) | Type-safe FHIRPath expression builder + invariant evaluator | When working with FHIRPath expressions or compiling invariants |
43+
| [`@fhir-dsl/terminology`](./packages/terminology) | CodeSystem hierarchy + ValueSet filter engine (`is-a`, `descendent-of`, `regex`) | Used internally by the generator; also usable standalone |
44+
| [`@fhir-dsl/smart`](./packages/smart) | SMART on FHIR v2 — PKCE-S256, backend services, scope DSL | Only when integrating with SMART-secured FHIR servers |
45+
| [`@fhir-dsl/mcp`](./packages/mcp) | MCP server: ~10 generic FHIR verbs as tools, pluggable auth/audit | When exposing a FHIR endpoint to an LLM agent |
4346
| [`@fhir-dsl/utils`](./packages/utils) | Shared utilities | Only if building custom tooling |
4447

4548
For detailed installation instructions, see the [Installation Guide](https://awbx.github.io/fhir-dsl/docs/getting-started/installation).
@@ -196,24 +199,28 @@ fhir-dsl is audited against the FHIR architectural overview (https://build.fhir.
196199
| Pillar | Status | Notes |
197200
|---|---|---|
198201
| Information Model — base classes & datatypes || `Element`, `Resource`, `DomainResource`, `BackboneElement`, all complex datatypes typed. |
199-
| Information Model — primitives | 🟡 | All 19 FHIR primitives present; branded types ship in Phase 1.1. |
200-
| Information Model — choice types `value[x]` | 🟡 | Flattened to optional siblings today; discriminated unions in Phase 1.2. |
201-
| Information Model — primitive `_field` siblings || Round-trip-safe in Phase 1.3. |
202+
| Information Model — primitives || All 19 FHIR primitives, branded with `unique symbol` markers (Phase 1.1, v0.23.0). |
203+
| Information Model — choice types `value[x]` || Discriminated `ChoiceOf<T, Prefix>` + `choiceOf()` runtime helper (Phase 1.2, v0.24.0). |
204+
| Information Model — primitive `_field` siblings || `_id`, `_extension`, etc. round-trip via `Element` siblings (Phase 1.3, v0.25.0). |
205+
| Information Model — full `Extension.value[x]` union || All 49 value variants (Phase 1.4, v0.25.0). |
202206
| Conformance — profiles || Type-narrowing on `.search("RT", profileUrl)`. |
203-
| Conformance — slicing || Generator skips slices today; Phase 2.1. |
204-
| Conformance — CapabilityStatement-driven client | 🟡 | `metadata` GET only; `fromCapabilities()` in Phase 4.1. |
205-
| Conformance — extensions | 🟡 | Structural only; typed extensions from IGs in Phase 2.2. |
207+
| Conformance — StructureDefinition slicing || Slice-named optional fields (`extension_usCoreRace?`, `component_systolic?`) + runtime `extensionByUrl` / `findSliceByPath` helpers (Phase 2.1, v0.38.0). |
208+
| Conformance — typed extensions from IGs || Branded `Extension<URL>` interfaces emitted per IG-defined extension SD (Phase 2.2, v0.39.0). |
209+
| Conformance — IG manifest as first-class || `ImplementationGuide.global` + `dependsOn` parsed by the downloader (Phase 2.3, v0.34.0). |
210+
| Conformance — CapabilityStatement-driven client || `createCapabilityGuard` narrows the client surface to advertised capabilities (Phase 4.1, v0.27.0). |
206211
| Terminology — typed bindings (generate-time) || Required/extensible/preferred resolved offline. |
207-
| Terminology — `$expand` / `$validate-code` / `$lookup` / etc. || First-class typed builders in Phase 3.1. |
212+
| Terminology — `$expand` / `$validate-code` / `$lookup` / `$translate` / `$subsumes` || Typed `client.terminology.*` operations (Phase 3.1, v0.26.0). |
213+
| Terminology — concept hierarchy + ValueSet filters || `is-a`, `descendent-of`, `regex` filters with transitive subsumption (Phase 3.2, v0.28.0). |
208214
| REST — read/vread/search/history/transaction/batch/operation || Full surface in `packages/core`. |
209215
| REST — PATCH + conditional headers + retry + AbortSignal || json-patch / xml-patch / fhirpath-patch all wired. |
210-
| REST — `_include` / `_revinclude` runtime resolution | 🟡 | Typed; `bundle.resolve()` lands in Phase 4.2. |
216+
| REST — `_include` / `_revinclude` runtime resolution | | `Bundle.resolveReference` walks fragment → fullUrl → `Type/id` (Phase 4.2, v0.29.0). |
211217
| References — `Reference<T>` target narrowing || Generated from `targetProfile`. |
212218
| FHIRPath — N1 core || Arithmetic, env vars, `$index`/`$total`, aggregates, `resolve`/`hasValue`, `extension(url)`. |
213-
| FHIRPath — invariants compiled to validators | | Phase 6. |
219+
| FHIRPath — invariants compiled to runtime predicates | | `compileInvariant` + `validateInvariants` returning OperationOutcome (Phase 6, v0.40.0). Subset: identifiers/member access, `exists`/`empty`/`matches`/`count`/`where`/`hasValue`, `and`/`or`/`xor`/`implies`/`not`, comparisons, parentheses, indexers, three-valued logic. |
214220
| SMART on FHIR v2 || PKCE-S256, backend services, scope DSL. |
215-
| Layered framework (Foundation/Base/Clinical/…) || Phase 5. |
216-
| MCP server generation || Phase 8. |
221+
| Layered framework (Foundation/Base/Clinical/Financial/Specialized) || `LAYER_OF`, `referencesUpward` emitted under `<version>/layers.ts` (Phase 5, v0.30.0). |
222+
| MCP server generation — package + dispatcher || `@fhir-dsl/mcp` ships generic verb tools, three pluggable auth strategies, audit sinks, stdio transport (Phase 8.1+8.2, v0.41.0+v0.42.0). |
223+
| MCP server generation — generator `--mcp` integration || Phase 8.8. |
217224

218225
Drift between this table and the code is caught by `pnpm audit:export-surface` — every PR that changes the public surface must refresh `.surface-snapshot.json`.
219226

@@ -255,22 +262,104 @@ Mapping of [FHIR R5 search features](https://fhir.hl7.org/fhir/search.html) to t
255262
| POST `_search` (long URLs / sensitive params) | `.usePost()` (auto-switch over ~1900 chars) |
256263
| `_count`, `_sort` | `.count(n)`, `.sort(param, dir)` |
257264

265+
## Invariants (FHIRPath → Predicates)
266+
267+
`@fhir-dsl/fhirpath` exposes a runtime invariant evaluator that compiles `ElementDefinition.constraint[*].expression` strings into predicates and surfaces results as `OperationOutcome` issues. The supported subset covers the patterns FHIR core invariants actually use:
268+
269+
- Identifiers and member access (`name.given`, `extension.url`)
270+
- Function calls: `exists()`, `empty()`, `count()`, `where()`, `select()`, `matches('regex')`, `hasValue()`, `first()`/`last()`/`tail()`/`single()`, `iif()`, `distinct()`, `startsWith`/`endsWith`/`contains`/`length`/`toString`
271+
- Boolean operators: `and`, `or`, `xor`, `implies`, `not` (with FHIRPath three-valued logic)
272+
- Comparison/equivalence: `=`, `!=`, `<`, `>`, `<=`, `>=`, `~`, `!~`, `in`
273+
- Arithmetic, parentheses, indexers, `$this`, scalar literals
274+
275+
```ts
276+
import { compileInvariant, validateInvariants } from "@fhir-dsl/fhirpath";
277+
278+
const inv = compileInvariant({
279+
key: "pat-1",
280+
expression: "name.exists() or telecom.exists() or address.exists()",
281+
severity: "error",
282+
human: "Patient must have a contact mechanism",
283+
});
284+
285+
const result = inv.check(patient); // { passed: true | false | "indeterminate", ... }
286+
const oo = validateInvariants(patient, [inv]); // { resourceType: "OperationOutcome", issue: [...] }
287+
```
288+
289+
Generator wiring (so the emitted Standard Schema validators run invariants automatically) is a follow-up phase.
290+
291+
## MCP Server (Model Context Protocol)
292+
293+
`@fhir-dsl/mcp` exposes any FHIR endpoint as an MCP tool surface for LLM agents — one server === one upstream + one IG.
294+
295+
```ts
296+
import { createServer, stdioTransport } from "@fhir-dsl/mcp";
297+
298+
const server = createServer({
299+
name: "us-core-mcp",
300+
version: "1.0.0",
301+
baseUrl: "https://hapi.fhir.org/baseR4",
302+
resourceTypes: ["Patient", "Observation", "Encounter"],
303+
auth: { kind: "bearer", token: process.env.FHIR_TOKEN! },
304+
// writes default to none — opt in explicitly:
305+
// writes: ["create", "update"],
306+
});
307+
308+
await server.listen(stdioTransport());
309+
```
310+
311+
Locked design (see `FHIR_COMPLIANCE_PLAN.md`):
312+
313+
- ~10 generic verbs typed by `resourceType` discriminated union: `read`, `vread`, `search`, `history`, `create`, `update`, `patch`, `delete`, `operation`, `capabilities`
314+
- Read-only by default; writes opt in via `writes`
315+
- Three pluggable auth strategies — `bearer` works today, `backend-services` and `patient-launch` are wired in a later phase
316+
- Pluggable `AuditSink` (`JsonLogAuditSink`, `MemoryAuditSink`, `NullAuditSink` ship by default)
317+
- Both `stdio` and Streamable HTTP transports planned (stdio only today)
318+
- `fhir://<ResourceType>/{id}` URI templates exposed via MCP `resources/list`
319+
258320
## CLI Reference
259321

322+
The `fhir-gen` binary ships five commands:
323+
260324
```bash
261-
fhir-gen generate [options]
325+
fhir-gen generate # generate TypeScript from a FHIR version (+ optional IG)
326+
fhir-gen capability # snapshot a server's CapabilityStatement
327+
fhir-gen validate # structurally check a FHIR JSON resource
328+
fhir-gen scaffold-ig # initialise a project with an IG pre-wired
329+
fhir-gen diff # report breaking changes between two generated outputs
262330
```
263331

332+
### `fhir-gen generate`
333+
264334
| Option | Description | Required |
265335
|---|---|---|
266336
| `--version <version>` | FHIR version: `r4`, `r4b`, `r5`, `r6` | Yes |
267337
| `--out <dir>` | Output directory for generated files | Yes |
268-
| `--ig <packages...>` | Implementation Guide packages to include | No |
338+
| `--ig <packages...>` | Implementation Guide packages (`hl7.fhir.us.core@6.1.0`) | No |
269339
| `--resources <list>` | Comma-separated list of resource names to generate | No |
270340
| `--src <path>` | Local FHIR definitions directory (skips download) | No |
271341
| `--cache <dir>` | Cache directory for downloaded specs | No |
272342
| `--validator <target>` | Emit Standard Schema validators: `native` or `zod` | No |
273343
| `--strict-extensible` | Treat extensible bindings as closed enums (validator only) | No |
344+
| `--expand-valuesets` | Generate typed unions from FHIR ValueSet bindings | No |
345+
| `--resolve-codesystems` | Generate CodeSystem namespace objects for IntelliSense | No |
346+
| `--include-spec` | Emit markdown spec files alongside types for AI/LLM context | No |
347+
348+
### `fhir-gen capability <baseUrl>`
349+
350+
Fetches `<baseUrl>/metadata` and prints a table of supported interactions, formats, search params, and conditional-* flags. `--out <file>` dumps the raw JSON, `--json` prints it to stdout. (Phase 7.2, v0.32.0.)
351+
352+
### `fhir-gen validate <file>`
353+
354+
Structural sanity-check on a FHIR JSON resource: parses, validates the `resourceType` is known, checks basic invariants (string `id`, `Bundle.entry` is an array, no NaN/Infinity in numbers). Designed for CI gates around LLM-generated payloads. `--quiet` suppresses warnings on success. (Phase 7.1, v0.35.0.)
355+
356+
### `fhir-gen scaffold-ig <pkg>`
357+
358+
Initialises a starter project with the IG pre-wired. Writes `package.json`, `tsconfig.json`, `fhir-dsl.config.json`, and `src/client.ts` calling the generator's emitted `createClient`. `--out <dir>` (default cwd), `--version <ver>` (default `r4`), `--name <project>`, `--force` to overwrite. (Phase 7.3, v0.36.0.)
359+
360+
### `fhir-gen diff <oldDir> <newDir>`
361+
362+
Compares two generated outputs and reports added/removed resources, removed fields, optional→required changes, and type narrowing. Exits 2 when breaking changes are detected — wire it into CI to gate FHIR version bumps. `--json` for a machine-readable report. (Phase 7.4, v0.37.0.)
274363

275364
For full CLI details and examples, see the [CLI Usage Guide](https://awbx.github.io/fhir-dsl/docs/cli/usage).
276365

0 commit comments

Comments
 (0)