mcpbeat Sign in

Ontology Term Resolution Agent Skill

Resolve free-text scientific labels to ontology term IDs and validate existing CURIEs against the EBI Ontology Lookup Service (OLS4). Use whenever an ontology identifier must be produced or checked - annotating tissue, cell type, disease, phenotype, assay, chemical, organism, sex, or developmental stage fields; preparing metadata for GEO, ENA, BioSamples, CELLxGENE, HCA, or ISA-Tab submission; auditing a metadata table of term IDs; checking whether a term is obsolete and what replaced it; or mapping between ontologies. Triggers include "ontology term", "ontology ID", "CURIE", "controlled vocabulary", "UBERON", "CL:", "MONDO", "HPO", "EFO", "ChEBI", "NCBITaxon", "GO term", "PATO", "annotate this tissue/cell type/disease", and any request to emit or verify an identifier shaped like PREFIX:0001234.

14k tokens
context cost
the whole folder, loaded on every use
7
files
ships runnable scripts
1
copies elsewhere
how many repositories repackaged it
32514
stars on the repo
on the repository, not the skill itself

Install

one command, takes just this skill from the repository
npx skills add https://github.com/K-Dense-AI/scientific-agent-skills --skill ontology-term-resolution

The instruction itself

10 sections, as written by the author

Ontology Term Resolution

When to use

Any time an ontology identifier is about to be written down or trusted: annotating a metadata

column, filling a submission template, auditing a table someone else produced, or checking whether

an ID in an old file is still current.

The rule

Never write an ontology ID from memory, and never accept one without checking it.

Ontology IDs are memorable in form and arbitrary in detail. A plausible-looking UBERON:0002108

is a real term (small intestine) that is not the liver, and nothing downstream will catch the

substitution — the ID is well-formed, the ontology is right, and the metadata is silently wrong.

Reviewers cannot spot it either, which is why these errors persist into published datasets.

Every ID this skill emits comes from a live OLS lookup. Every ID it is handed gets verified.

Two directions

| Direction | Script | Question answered |

| --- | --- | --- |

| text → ID | scripts/resolve_terms.py | What is the term for "left ventricle"? |

| ID → verdict | scripts/validate_terms.py | Is EFO:0001067 real, current, and labelled what this file claims? |

Both take single values or files, emit TSV or JSON, and need no packages beyond the standard

library.

Resolve text to terms

cd skills/ontology-term-resolution/scripts

# one string, constrained to the ontology that should define it
python3 resolve_terms.py "liver" --ontology uberon
query   rank  curie           label  ontology  match_type   strategy  defining_ontology
liver   1     UBERON:0002107  liver  uberon    exact_label  exact     true
# a column of tissue names; anything not an exact hit is reported, not guessed
python3 resolve_terms.py --input tissues.txt --ontology uberon \
    --exact-only --format tsv -o resolved.tsv

# accept fuzzy fallbacks, then review the partial hits by hand
python3 resolve_terms.py "left ventrical of heart" --ontology uberon --top 3

The search escalates exact (label and synonym) → tokenfulltext and stops at the first

strategy that returns anything, reporting which one fired. --exact-only disables the ladder.

--branch UBERON:0000465 restricts candidates to descendants of a term.

Read match_type before using a result. exact_label and exact_synonym are safe;

partial means OLS returned its best guess for a string that does not exist as written, and

needs a human decision. unresolved is a legitimate output — see references/curation-rules.md

for the normalisations worth retrying first.

Validate existing IDs

python3 validate_terms.py UBERON:0002107 EFO:0001067 UBERON:9999999
id              status     actual_label                  ontology  replacement     detail
UBERON:0002107  ok         liver                         uberon
EFO:0001067     obsolete   obsolete_parasitic infection  efo       MONDO:0005135   obsolete; replaced by MONDO:0005135
UBERON:9999999  not_found                                                          no such term in the ontology this prefix names

Exit code is 1 if anything failed, 0 otherwise, 2 on usage or network trouble — so it works as a

CI gate on a metadata file:

# id + label columns; catches IDs that exist but are labelled as something else
python3 validate_terms.py --input metadata.tsv --strict

# a tissue column must hold UBERON anatomical entities and nothing else
python3 validate_terms.py --input tissue_ids.tsv \
    --branch UBERON:0000465 --expect-ontology uberon

| Status | Meaning | Verdict |

| --- | --- | --- |

| ok | Exists, current, consistent with everything asserted | pass |

| matched_synonym | Claimed label is a synonym; primary label differs | warn |

| imported_only | Home ontology no longer asserts this ID | warn |

| not_a_class | Term is a property or individual | warn |

| not_found | No such term | fail |

| obsolete | Obsoleted; replacement gives the successor when one exists | fail |

| label_mismatch | ID and claimed label describe different things | fail |

| wrong_ontology | Right kind of ID, wrong ontology for this column | fail |

| wrong_branch | Not a descendant of the required root | fail |

| malformed_curie | Not of the form PREFIX:local | fail |

--strict promotes warnings to failures.

API behaviour that will mislead you

These are verified against the live service and are the reason this skill ships scripts rather

than a recipe. Full detail in references/ols4-api.md.

| Trap | Consequence |

| --- | --- |

| exact=true is exact token matching | liver returns 161 hits in UBERON; adding queryFields=label returns 1 |

| /search never returns is_obsolete or term_replaced_by | Named in fieldList they are dropped silently; only term detail can answer "is this ID still current" |

| ontology=efo returns MONDO and CL hits | Ontologies import each other; filter on the CURIE prefix yourself |

| The same term appears once per importing ontology | Deduplicate on obo_id, keep is_defining_ontology: true |

| The obo_id index has holes | MONDO:0000001 is live but unindexed by obo_id; an IRI fallback is required to avoid a false not_found |

| IRIs are not all OBO PURLs | EFO and Orphanet use their own namespaces — resolve IRIs, do not template them |

| OxO is retired | Returns HTML with HTTP 200; use term cross-references or SSSOM instead |

| A branch check does not exclude cell types from anatomy | CARO puts cell under anatomical structure; constrain the prefix too |

Choosing the ontology

MONDO for disease, HP for phenotype, UBERON for tissue, CL for cell type, EFO for assay, ChEBI for

compounds, NCBITaxon for organism, PATO for sex and for normal. Prefix-to-OLS-id mappings (HP

is served as hp, Orphanet as ordo), branch roots for --branch, and the overlapping-ontology

judgement calls are in references/ontology-registry.md.

Reporting results

Give the ID and the label, and say how each was matched. A table of bare IDs cannot be

reviewed. State unresolved terms explicitly rather than filling them with the nearest hit.

References

  • references/ols4-api.md — endpoints, parameters, response fields, and every verified trap.
  • references/ontology-registry.md — prefix/ontology-id table, branch roots, which ontology owns

which concept.

  • references/curation-rules.md — candidate-selection procedure, normalisations to retry,

auditing an existing table, obsolete terms, cross-ontology mapping.

Other skills for the same job

different authors, same section of the catalogue
Protocolsio Integration
by christophacham
×4

Integration with protocols.io API for managing scientific protocols. This skill should be used when working with protocols.io to search, create, update, or publish protocols; manage protocol steps and materials; handle discussions and comments; organize workspaces; upload and manage files; or integrate protocols.io functionality into workflows. Applicable for protocol discovery, collaborative protocol development, experiment tracking, lab protocol management, and scientific documentation.

16k tokens
Tailored Resume Generator
by frostant
×4

Analyzes job descriptions and generates tailored resumes that highlight relevant experience, skills, and achievements to maximize interview chances

3k tokens
Excalidraw Diagram Generator
by github
vendor ×3

Generate Excalidraw diagrams from natural language descriptions. Use when asked to "create a diagram", "make a flowchart", "visualize a process", "draw a system architecture", "create a mind map", or "generate an Excalidraw file". Supports flowcharts, relationship diagrams, mind maps, and system architecture diagrams. Outputs .excalidraw JSON files that can be opened directly in Excalidraw.

36k tokens scripts
Expo Dev Client
by openai
vendor ×3

Build and distribute Expo development clients locally or via TestFlight

961 tokens
Executing Plans
by ZhanlinCui
×3

Use when you have a written implementation plan to execute in a separate session with review checkpoints

542 tokens
Anndata
by christophacham
×3

Data structure for annotated matrices in single-cell analysis. Use when working with .h5ad files or integrating with the scverse ecosystem. This is the data format skill—for analysis workflows use scanpy; for probabilistic models use scvi-tools; for population-scale queries use cellxgene-census.

16k tokens
Benchling Integration
by christophacham
×3

Benchling R&D platform integration. Access registry (DNA, proteins), inventory, ELN entries, workflows via API, build Benchling Apps, query Data Warehouse, for lab data management automation.

14k tokens
Biopython
by christophacham
×3

Comprehensive molecular biology toolkit. Use for sequence manipulation, file parsing (FASTA/GenBank/PDB), phylogenetics, and programmatic NCBI/PubMed access (Bio.Entrez). Best for batch processing, custom bioinformatics pipelines, BLAST automation. For quick lookups use gget; for multi-service integration use bioservices.

24k tokens

How to use it

Copy the folder

Take k-dense-ai/ontology-term-resolution from the repository into ~/.claude/skills for personal use, or into .claude/skills inside a project.

Check the name does not clash

The agent identifies a skill by the name field in its header. Two skills with the same name cannot sit side by side — one of them will be ignored.