annotating-variants
Annotates VCF variants and normalizes HGVS nomenclature with public, license-free annotators…
Links entities extracted by OpenMed to UMLS Metathesaurus CUIs using the USER'S OWN UTS API key, with nothing from the Metathesaurus bundled or cached. Use when the user wants to normalize concepts across vocabularies to a single CUI, resolve synonyms via the UMLS, filter by
$ npx -y skills add maziyarpanahi/openmed --skill linking-umls-concepts --agent claude-codeHow it fires
How this skill gets triggered: by you, by Claude, or both.
/linking-umls-conceptsContext preview
The summary Claude sees to decide when to auto-load this skill.
Links entities extracted by OpenMed to UMLS Metathesaurus CUIs using the USER'S OWN UTS API key, with nothing from the Metathesaurus bundled or cached. Use when the user wants to normalize concepts across vocabularies to a single CUI, resolve synonyms via the UMLS, filter by
name: linking-umls-concepts description: "Links entities extracted by OpenMed to UMLS Metathesaurus CUIs using the USER'S OWN UTS API key, with nothing from the Metathesaurus bundled or cached. Use when the user wants to normalize concepts across vocabularies to a single CUI, resolve synonyms via the UMLS, filter by semantic type, or cross-walk between SNOMED CT, ICD-10, RxNorm and MeSH through their shared CUI. Trigger keywords: UMLS, CUI, Metathesaurus, UTS API key, semantic type, TUI, MetaMap, QuickUMLS, concept normalization, cross-vocabulary. Pairs after OpenMed NER: consume Disease/Pharmaceutical/Chemical/Anatomy entities from openmed.analyze_text and resolve each span to a CUI out-of-process. UMLS is license-restricted — the Metathesaurus is NEVER bundled; every call uses the user's UTS account." license: Apache-2.0 metadata: project: OpenMed category: terminology-coding pairs: after version: "1.0"
Resolve concept spans that OpenMed extracts to **UMLS Metathesaurus** concepts. The atom is the **CUI** (Concept Unique Identifier, e.g. `C0011860`): one CUI unifies synonyms from many source vocabularies (SNOMED CT, ICD-10-CM, RxNorm, MeSH, LOINC), making the CUI the natural hub for cross-vocabulary normalization. Every concept also carries one or more **semantic types** (TUIs, e.g. *Disease or Syndrome* `T047`) for type-based filtering.
> **Hard licensing boundary — read first.** The UMLS Metathesaurus is > **license-restricted**. OpenMed and this skill **never bundle, ship, or cache** > Metathesaurus content. Concept linking runs **out-of-process against the NLM > UTS (UMLS Terminology Services) REST API using the user's own UTS API key**. > A free UTS account + API key is required (request at uts.nlm.nih.gov and accept > the UMLS license). The Metathesaurus stays user-supplied: your code holds only > the key (from the environment) and stores only returned CUIs/strings.
disorder, an ICD-10 code, and a free-text mention onto a single CUI.
attack" → `C0027051`).
Substance* or *Disease or Syndrome* entities.
RxNorm (`normalizing-rxnorm`) or SNOMED (`mapping-to-snomed`).
The UTS REST API base is `https://uts-ws.nlm.nih.gov/rest`. Authentication uses your API key as the `apiKey` query parameter (the modern, simplest method).
import os, requests
UTS = "https://uts-ws.nlm.nih.gov/rest"
API_KEY = os.environ["UTS_API_KEY"] # USER's own key — never hardcoded
VERSION = "current" # or a fixed release like 2024AB
def search(term: str, sabs: str | None = None, count: int = 10) -> list[dict]:
"""Search the Metathesaurus for a term; optionally restrict source vocabs."""
params = {"string": term, "apiKey": API_KEY, "pageSize": count}
if sabs: # e.g. "SNOMEDCT_US,RXNORM,ICD10CM"
params["sabs"] = sabs
r = requests.get(f"{UTS}/search/{VERSION}", params=params, timeout=15)
r.raise_for_status()
return r.json().get("result", {}).get("results", [])
def concept(cui: str) -> dict:
"""Pull a concept's preferred name and semantic types."""
r = requests.get(f"{UTS}/content/{VERSION}/CUI/{cui}",
params={"apiKey": API_KEY}, timeout=15)
r.raise_for_status()
return r.json().get("result", {})
def crosswalk(cui: str, target_sab: str) -> list[dict]:
"""Atoms of a CUI in a target vocabulary (the cross-walk)."""
r = requests.get(f"{UTS}/content/{VERSION}/CUI/{cui}/atoms",
params={"apiKey": API_KEY, "sabs": target_sab,
"pageSize": 50}, timeout=20)
r.raise_for_status()
return r.json().get("result", [])
hits = search("type 2 diabetes") # -> [{ui: 'C0011860', name: ...}, ...]
sct = crosswalk("C0011860", "SNOMEDCT_US") # CUI -> SNOMED CT codes1. **Extract** spans with OpenMed (Disease, Pharmaceutical, Chemical, Anatomy). 2. **Search** each span via `/search/{version}` for candidate CUIs. 3. **Filter by semantic type (TUI)** so a drug span resolves to a substance concept, not a same-named disease. Pull semantic types from `/content/.../CUI/{cui}` and keep only the expected group. 4. **Rank** candidates (exact preferred-name match > synonym match) and combine with OpenMed's `confidence` to choose one CUI. 5. **Cross-walk** the chosen CUI to whatever target you actually store — SNOMEDCT_US, ICD10CM, RXNORM, MSH — via `/CUI/{cui}/atoms?sabs=`. 6. **Emit** the CUI plus the target code(s) and OpenMed source offsets.
`openmed.analyze_text(..., output_format="dict")` returns `entities`, each a dict with `text`, `label`, `confidence`, `start`, `end`. Use the label to pick the semantic-type group you keep:
import openmed
note = "History of myocardial infarction; started on lisinopril."
result = openmed.analyze_text(
note,
model_name="disease_detection_superclinical", # Disease category
output_format="dict",
)
# OpenMed label -> acceptable UMLS semantic-type groups (TUI prefixes)
KEEP_STY = {
"DISEASE": {"Disease or Syndrome", "Sign or Symptom", "Neoplastic Process"},
"DRUG": {"Pharmacologic Substance", "Clinical Drug"},
"CHEM": {"Pharmacologic Substance", "Organic Chemical"},
}
for ent in result["entities"]:
for hit in search(ent["text"], count=5):
cui = hit["ui"]
stys = {s["name"] for s in concept(cui).get("semanticTypes", [])}
if not KEEP_STY.get(ent["label"]) or stys & KEEP_STY[ent["label"]]:
print(ent["text"], ent["start"],Local-first healthcare AI: clinical NER and HIPAA PII de-identification on hardware you control. 2,200+ medical models, 35 model-backed PII languages, and Python, MLX, Android and browser runtimes. Apache-2.0 SDK.
Repo: maziyarpanahi/openmed
Annotates VCF variants and normalizes HGVS nomenclature with public, license-free annotators…
Choose the first OpenMed workflow skill for an intake, privacy, extraction, exchange, or…
Package multiple FHIR R4 resources produced from OpenMed output into a single valid…
Adversarially scan already-de-identified clinical text for residual identifiers and emit a…
Produce a signed, reproducible, no-PHI audit trail for an OpenMed de-identification run via…
Generates and verifies 21 CFR Part 11-style audit trails — who/what/when, electronic…