Install and first steps.
@polaris/sdk is the unified SDK entry point for MIRA consumers. It provides @polaris/sdk/fhir for the FHIR client and @polaris/sdk/llm for the LLM gateway types. Both live on the private Verdaccio registry at npm.cognovis.de.
Step 1
Configure the registry.
The @polaris scope is served from https://npm.cognovis.de. Add the scope mapping to your project before installing.
Option A — .npmrc
@polaris:registry=https://npm.cognovis.de Create or edit .npmrc at the project root. Applies to npm, pnpm, and Bun.
Option B — bunfig.toml
[install.scopes]
"@polaris" = "https://npm.cognovis.de" Bun-native configuration in bunfig.toml. Scoped to this project directory.
Registry access
The registry at npm.cognovis.de is read-only and available anonymously for authorized consumers. If you receive a 401 Unauthorized error, see the Troubleshooting section for registry auth configuration.
Step 2
Install the package.
Install @polaris/sdk as the recommended entry point. It provides both the FHIR client (@polaris/sdk/fhir) and the LLM gateway types (@polaris/sdk/llm) as subpath exports. Pin to an exact version — the package is in v0.x, minor versions may carry breaking changes.
Bun
bun add @polaris/sdk@0.1.0 npm
npm install @polaris/sdk@0.1.0 pnpm
pnpm add @polaris/sdk@0.1.0 Low-level access: @polaris/fhir-de
@polaris/fhir-de remains published and available for consumers that need direct access to resource builders, generated profile classes, or FHIR extension constants. For most MIRA integrations, @polaris/sdk is the preferred entry point.
bun add @polaris/fhir-de@0.2.0 # FHIR-specific / low-level access Builders vs. client API
For resource builders (buildPatient, buildEncounter, etc.), use @polaris/fhir-de directly. @polaris/sdk/fhir exposes the typed FHIR client API — createFhirDeClient, transport SPI, error types, and extractNextPageUrl. Both packages are available in the same project.
Step 3
Use the FHIR client.
Import createFhirDeClient from @polaris/sdk/fhir to build a typed FHIR client against your Aidbox instance.
import { createFhirDeClient } from '@polaris/sdk/fhir'
import { FPDEPatientProfile } from '@polaris/fhir-de'
const client = createFhirDeClient({
baseUrl: 'https://aidbox.example.com/fhir',
transport: myFetchTransport,
})
// Profile-scoped typed CRUD
const patientClient = client.forProfile(FPDEPatientProfile, 'Patient')
const patient = await patientClient.read('pat-12345') LLM gateway types
Import PII-safe prompt constructors, capability routing, and PII scanning from @polaris/sdk/llm. Server-side symbols (createApp, loadCatalogFromFile) are intentionally excluded — the /llm subpath is safe to bundle in browser and edge runtimes.
import { AnonymizedPrompt, routeRequest } from '@polaris/sdk/llm'
const prompt = AnonymizedPrompt.from({
anonymizedText: 'Diagnose: Hypertonie',
}) Optional piiMode
createFhirDeClient accepts an optional piiMode field. The default is 'real' (no post-processing). With piiMode: 'anonymized', PII fields (name, identifier, address, telecom, photo) are stripped from FHIR responses. birthDate is NOT stripped. Requires the POLARIS_ANON_SALT environment variable.
const client = createFhirDeClient({
baseUrl,
transport,
piiMode: 'anonymized',
}) Step 4
Combine FHIR + LLM in one flow.
The two SDK halves compose naturally: read a patient via @polaris/sdk/fhir with piiMode: 'anonymized', then feed the anonymized data into an AnonymizedPrompt from @polaris/sdk/llm for LLM routing. This example uses createMockFhirTransport so no live Aidbox is required.
import {
createFhirDeClient,
createMockFhirTransport,
} from '@polaris/sdk/fhir'
import { AnonymizedPrompt } from '@polaris/sdk/llm'
import { FPDEPatientProfile } from '@polaris/fhir-de'
// 1. Set up an in-memory mock transport (no Aidbox needed for local dev)
const mock = createMockFhirTransport({
fixtures: [{
resourceType: 'Patient',
id: 'pat-001',
meta: { profile: ['https://fhir.cognovis.de/praxis/StructureDefinition/fpde-patient'] },
name: [{ family: 'Mustermann', given: ['Max'] }],
identifier: [{ value: '123456789' }],
address: [{ city: 'Berlin' }],
telecom: [{ value: '+49 30 12345' }],
birthDate: '1975-03-22',
gender: 'male',
}],
})
// 2. Build the FHIR client with PII anonymization enabled
// Requires POLARIS_ANON_SALT to be set in the environment
// In production, generate with: crypto.randomBytes(32).toString('hex')
process.env.POLARIS_ANON_SALT = 'demo-salt-replace-in-production-use-32-plus-random-bytes'
const client = createFhirDeClient({
baseUrl: 'http://mock',
transport: mock,
piiMode: 'anonymized', // name, identifier, address, telecom, photo stripped from responses
})
// 3. Read patient — PII is stripped before reaching this code
const patients = client.forProfile(FPDEPatientProfile, 'Patient')
const patient = await patients.read('pat-001')
// patient.name === undefined (stripped)
// patient.identifier === undefined (stripped)
// patient.address === undefined (stripped)
// patient.telecom === undefined (stripped)
// patient.birthDate === '1975-03-22' (NOT stripped — birthDate is preserved)
// patient.id === 'anon-3f8a2b1c94d7' (SHA-256 hash of salt:pat-001, first 12 hex chars)
// patient.gender === 'male' (non-PII, preserved)
// 4. Build an AnonymizedPrompt from the de-identified data
// This is the PII-safe input for LLM routing
const prompt = AnonymizedPrompt.from({
anonymizedText: [
`Patient (ID: ${patient?.id ?? 'unknown'}, gender: ${patient?.gender ?? 'unknown'})`,
'Chief complaint: persistent back pain for 3 weeks, no radiation.',
'Please suggest relevant ICD-10 codes.',
].join(' '),
})
// 5. Route to the LLM gateway (gateway not configured here — shows integration point)
// In a real MIRA integration, pass prompt.text to the gateway:
//
// const response = await app.fetch(new Request('http://localhost/v1/chat/completions', {
// method: 'POST',
// headers: { 'Content-Type': 'application/json' },
// body: JSON.stringify({
// messages: [{ role: 'user', content: prompt.text }],
// gateway: {
// requires: ['text', 'medicalCoding', 'germanLanguage'],
// intent: 'billing-coding-suggest',
// pii: 'anonymized',
// },
// }),
// }))
//
// The AnonymizedPrompt type enforces at compile time that no RealPrompt reaches
// an intent declared pii: 'anonymized' — the TypeScript compiler rejects it.
console.log('Prompt ready for LLM routing:', prompt.text.slice(0, 80) + '...') FHIR side
piiMode: 'anonymized' on createFhirDeClient wraps the transport in AnonymizingTransport. Resource IDs are hashed, name / identifier / address / telecom / photo removed. birthDate is NOT stripped.
Boundary
AnonymizedPrompt.from() is the compile-time boundary between PII-containing and PII-free data. It can only be constructed from AnonymizedInput — no raw patient strings slip through.
LLM side
prompt.text is safe to send to any intent with pii: 'anonymized'. The gateway validates PII mode before dispatch and writes an audit entry.
Next steps
fhir client api
Full FHIR API reference
createFhirDeClient, ResourceClient, transport SPI, AnonymizingTransport, errors, mock transport.
api examples
MIRA workflows
Patient lookup, writes, encounters, ChargeItems, and billing lookup with copyable TypeScript.
resource builders
All core builders
Working examples for Patient, Practitioner, PractitionerRole, Organization, Encounter, Condition, Claim.
troubleshooting
Common errors and fixes
Organization.address required, birthDate key semantics, Transport SPI, registry auth.