xiaohongshu
xiaohongshu
health-record-assistant
Connect to and analyze a user's health records from their patient portal via SMART on FHIR. Use when the user wants to review medical records, understand lab results, see medications, analyze health trends, identify care gaps, or answer questions about personal health data.
Full skill instructions
Fetch and analyze electronic health records from patient portals using SMART on FHIR.
Unless the user specifically asks for a live app or artifact, you should:
This approach is important because:
If the user wants a live artifact/app, pre-processing is still valuable:
Helper scripts are provided in scripts/ to simplify the workflow.
Prerequisites: These scripts require Bun to be installed:
curl -fsSL https://bun.sh/install | bash
bun scripts/create-session.ts
Output:
{
"sessionId": "abc123...",
"userUrl": "https://health-skillz.exe.xyz/connect/abc123...",
"pollUrl": "https://health-skillz.exe.xyz/api/poll/abc123...",
"privateKeyJwk": { "kty": "EC", "crv": "P-256", "d": "...", ... }
}
Save the privateKeyJwk - you'll need it to decrypt the data.
Present userUrl to the user as a clickable link:
To access your health records, please click this link:
You'll sign into your patient portal (like Epic MyChart), and your records will be securely transferred for analysis.
🔒 Your data is end-to-end encrypted - only this conversation can decrypt it.
Once the user has connected their provider(s) and clicked "Done - Send to AI":
bun scripts/finalize-session.ts <sessionId> '<privateKeyJwk>' ./health-data
This script:
Example output:
{"status":"polling","sessionId":"abc123..."}
{"status":"waiting","sessionStatus":"collecting","providerCount":1,"attempt":1}
{"status":"ready","providerCount":1}
{"status":"decrypting"}
{"status":"wrote_file","file":"./health-data/unitypoint-health.json","provider":"UnityPoint Health","resources":277,"attachments":82}
{"status":"done","files":["./health-data/unitypoint-health.json"]}
Result:
health-data/
unitypoint-health.json
mayo-clinic.json
Each file contains a single provider's data:
interface ProviderData {
name: string;
fhirBaseUrl: string;
connectedAt: string;
fhir: {
Patient?: Patient[];
Condition?: Condition[];
Observation?: Observation[];
MedicationRequest?: MedicationRequest[];
Procedure?: Procedure[];
Immunization?: Immunization[];
AllergyIntolerance?: AllergyIntolerance[];
Encounter?: Encounter[];
DiagnosticReport?: DiagnosticReport[];
DocumentReference?: DocumentReference[];
CareTeam?: CareTeam[];
Goal?: Goal[];
};
attachments: Attachment[];
}
interface Attachment {
resourceType: string; // "DocumentReference" or "DiagnosticReport"
resourceId: string; // FHIR resource ID this attachment came from
contentType: string; // MIME type: "text/html", "text/rtf", "application/xml", etc.
contentPlaintext: string | null; // Extracted plain text (for text formats)
contentBase64: string | null; // Raw content, base64 encoded
}
Each provider is a separate slice - no merging, preserves data provenance.
data.fhir.Patient // Demographics (name, DOB, contact)
data.fhir.Condition // Diagnoses and health problems
data.fhir.MedicationRequest // Prescribed medications
data.fhir.Observation // Lab results, vital signs
data.fhir.Procedure // Surgeries and procedures
data.fhir.Immunization // Vaccination records
data.fhir.AllergyIntolerance// Allergies and reactions
data.fhir.Encounter // Healthcare visits
data.fhir.DocumentReference // Clinical documents
data.fhir.DiagnosticReport // Lab panels, imaging reports
function getLabsByLoinc(loincCode) {
return data.fhir.Observation?.filter(obs =>
obs.code?.coding?.some(c => c.code === loincCode)
).map(obs => ({
value: obs.valueQuantity?.value,
unit: obs.valueQuantity?.unit,
date: obs.effectiveDateTime,
flag: obs.interpretation?.[0]?.coding?.[0]?.code // H, L, N
})).sort((a, b) => new Date(b.date) - new Date(a.date));
}
// Common LOINC codes:
// 4548-4 = Hemoglobin A1c
// 2345-7 = Glucose
// 2093-3 = Total Cholesterol
// 2085-9 = HDL Cholesterol
// 13457-7 = LDL Cholesterol
// 2160-0 = Creatinine
// 8480-6 = Systolic Blood Pressure
// 8462-4 = Diastolic Blood Pressure
// 718-7 = Hemoglobin
// 39156-5 = BMI
const activeMeds = data.fhir.MedicationRequest
?.filter(m => m.status === 'active')
.map(m => ({
name: m.medicationCodeableConcept?.coding?.[0]?.display,
dosage: m.dosageInstruction?.[0]?.text,
prescribedDate: m.authoredOn
}));
const conditions = data.fhir.Condition
?.filter(c => c.clinicalStatus?.coding?.[0]?.code === 'active')
.map(c => ({
name: c.code?.coding?.[0]?.display,
onsetDate: c.onsetDateTime
}));
The attachments array contains clinical documents extracted from DocumentReference and DiagnosticReport resources. Each attachment has:
contentPlaintext: Extracted readable text (for HTML, RTF, XML, plain text formats)contentBase64: Raw file content, base64 encoded (always present)contentType: MIME type like text/html, text/rtf, application/xmlCommon patterns from Epic:
text/html and one text/rtf (same content, different formats)For analysis, use contentPlaintext - it's clean and searchable. The contentBase64 is available if you need the original format.
The attachments array contains extracted text from clinical documents:
function searchNotes(searchTerm) {
return data.attachments?.filter(att =>
att.contentPlaintext?.toLowerCase().includes(searchTerm.toLowerCase())
).map(att => {
const text = att.contentPlaintext || '';
const idx = text.toLowerCase().indexOf(searchTerm.toLowerCase());
const start = Math.max(0, idx - 150);
const end = Math.min(text.length, idx + searchTerm.length + 150);
return {
context: text.substring(start, end),
docType: att.resourceType
};
});
}
// Example: Find mentions of diabetes
const diabetesNotes = searchNotes('diabetes');
function checkCareGaps(patientAge) {
const gaps = [];
const now = new Date();
// Colonoscopy (age 45+, every 10 years)
if (patientAge >= 45) {
const colonoscopy = data.fhir.Procedure?.find(p =>
p.code?.coding?.[0]?.display?.toLowerCase().includes('colonoscopy')
);
const lastDate = colonoscopy ? new Date(colonoscopy.performedDateTime) : null;
const yearsSince = lastDate ? (now - lastDate) / (365 * 24 * 60 * 60 * 1000) : Infinity;
if (yearsSince > 10) {
gaps.push('Colonoscopy may be due (last: ' + (lastDate?.toLocaleDateString() || 'never') + ')');
}
}
// Annual flu shot
const fluShot = data.fhir.Immunization?.find(i =>
i.vaccineCode?.coding?.[0]?.display?.toLowerCase().includes('influenza') &&
new Date(i.occurrenceDateTime).getFullYear() === now.getFullYear()
);
if (!fluShot) {
gaps.push('Annual flu shot may be due');
}
return gaps;
}
function analyzeTrend(loincCode, testName) {
const values = getLabsByLoinc(loincCode);
if (values.length < 2) return `${testName}: Insufficient data for trend`;
const recent = values[0];
const previous = values[1];
const change = ((recent.value - previous.value) / previous.value * 100).toFixed(1);
let trend = 'stable';
if (change > 5) trend = `increased ${change}%`;
if (change < -5) trend = `decreased ${Math.abs(change)}%`;
return `${testName}: ${recent.value} ${recent.unit} (${trend} from ${previous.value})`;
}
// Example
analyzeTrend('4548-4', 'A1c');
The power is combining FHIR resources with clinical note text:
// 1. Check if patient has diabetes diagnosis
const hasDiabetes = data.fhir.Condition?.some(c =>
c.code?.coding?.[0]?.display?.toLowerCase().includes('diabetes')
);
// 2. Get A1c trend
const a1cValues = getLabsByLoinc('4548-4');
// 3. Find related medications
const diabetesMeds = data.fhir.MedicationRequest?.filter(m =>
['metformin', 'insulin', 'glipizide', 'januvia'].some(drug =>
m.medicationCodeableConcept?.coding?.[0]?.display?.toLowerCase().includes(drug)
)
);
// 4. Search notes for management discussions
const managementNotes = searchNotes('diabetes');
// Now provide comprehensive diabetes analysis
For testing with Epic's sandbox:
fhircamilaepicepic1xiaohongshu
technical spec
product ux expert
database patterns
Conduct multi-agent task orchestration and workflow coordination.
Initialize project with Conductor artifacts (product definition,
Expert in web animations, transitions, and motion design using Framer Motion and CSS
Creates Mermaid and ASCII diagrams for flowcharts, architecture, ERDs, state machines, mindmaps, and more. Use when user mentions diagram, flowchart, mermaid, ASCII diagram, text diagram, terminal diagram, visualize, C4, mindmap, architecture diagram, sequence diagram, ERD, or needs visual docume...
PostgreSQL bindings for H3 hexagonal grid system. Use when working with H3 cells in Postgres, including spatial indexing, geometry/geography integration, and raster analysis.
Context-Driven Development skill for projects using Conductor. Use this skill when you detect a `conductor/` directory in the project, when working on tasks defined in a `plan.md` file, or when the user asks about tracks, specs, or plans. Automatically applies TDD workflow, tracks task completion...
Display project status, active tracks, and next actions
Official Stakpak application containerization standard operating procedure, a step-by-step guidline to properly dockerize applications. This is a rule book curated by the Stakpak Team.
Generate, edit, and beat-sync AI video with leading models in one workspace.
The world's fastest calendar for remote work
Transform Your Design with AI Designer by ImgCreator.ai
Revolutionizing Video Production with AI-Powered Creativity
Extend an image past the frame and let AI fill the new aspect ratio.
Discover your celebrity doppelgänger with StarByFace!
ChainClarity explains 700+ crypto whitepapers in plain English, with layered summaries, comparisons, research tools, alerts, and a $4.99 Pro plan.
Opus.ai: Revolutionize Your Web Experience