# Open Ontologies

## Ontology Engineering Workflow

When building or modifying ontologies, follow this workflow. Claude decides which tools to call and in what order based on results — this is not a fixed pipeline.

### Generate

1. Understand the domain requirements (natural language, competency questions, methodology constraints)
2. Generate Turtle/OWL directly — Claude knows OWL, RDF, BORO, 4D modeling natively

### Validate and Load

3. Call `onto_validate` on the generated Turtle — if it fails, fix the syntax errors and re-validate
4. Call `onto_load` to load into the Oxigraph triple store
5. Call `onto_stats` to verify class count, property count, triple count match expectations

### Reason

6. Call `onto_reason` with profile `rdfs` or `owl-rl` to materialize inferred triples (transitive subclass chains, domain/range propagation, equivalentClass expansion)
7. Call `onto_stats` again to verify inferred triple counts are reasonable

### Verify

8. Call `onto_lint` to check for missing labels, comments, domains, ranges — fix any issues found
9. Call `onto_enforce` with rule pack `generic` to check design pattern compliance — fix any violations
10. Call `onto_query` with SPARQL to verify structure:
    - Are all expected classes present?
    - Do subclass hierarchies match the spec?
    - Can competency questions be answered?
11. If a reference ontology exists, call `onto_diff` to compare

### Iterate

12. If any step above reveals problems, fix the Turtle and restart from step 3
13. This loop continues until validation passes, stats match, lint is clean, enforce has no violations, and SPARQL queries return expected results

### Persist

14. Call `onto_save` to write the final ontology to a .ttl file
15. Call `onto_version` to save a named snapshot for rollback — always version after save

### Key Principle

Claude dynamically decides the next tool call based on what the previous tool returned. If `onto_validate` fails, Claude fixes and retries. If `onto_stats` shows wrong counts, Claude regenerates. If `onto_lint` finds missing labels, Claude adds them. The MCP tools are individual operations — Claude is the orchestrator.

## Tool Reference

| Tool | When to use |
| ---- | ----------- |
| `onto_status` | To check if the server is running and healthy |
| `onto_validate` | After generating or modifying Turtle — always validate first |
| `onto_load` | After validation passes — loads into triple store for querying |
| `onto_stats` | After loading — sanity check on class/property/triple counts |
| `onto_lint` | After loading — catches missing labels, domains, ranges |
| `onto_query` | To verify structure, answer competency questions, explore the ontology |
| `onto_diff` | To compare against a reference or previous version |
| `onto_save` | To persist the ontology to a file |
| `onto_convert` | To convert between formats (Turtle, N-Triples, RDF/XML, N-Quads, TriG) |
| `onto_clear` | To reset the store before loading a different ontology |
| `onto_marketplace` | To browse and install standard ontologies from a curated catalogue of 29 W3C/ISO/industry standards |
| `onto_pull` | To fetch an ontology from a remote URL or SPARQL endpoint |
| `onto_push` | To push an ontology to a SPARQL endpoint |
| `onto_import` | To resolve and load owl:imports chains |
| `onto_version` | To save a named snapshot before making changes |
| `onto_history` | To list saved version snapshots |
| `onto_rollback` | To restore a previous version if something goes wrong |
| `onto_ingest` | To parse structured data (CSV, JSON, NDJSON, XML, YAML, XLSX, Parquet) into RDF and load into the store |
| `onto_sql_ingest` | To run a SQL `SELECT` against PostgreSQL or DuckDB and ingest the result rows into RDF (uses the same mapping format as `onto_ingest`). DuckDB acts as a federation backbone via its `httpfs`/`parquet`/`csv`/`postgres_scanner`/`iceberg` extensions. Connection strings: `postgres://…`, `duckdb:///path.duckdb`, `:memory:`, or `*.duckdb` file path. |
| `onto_map` | To generate a mapping config from data schema + loaded ontology for review |
| `onto_shacl` | To validate loaded data against SHACL shapes (cardinality, datatypes, classes) |
| `onto_reason` | To run RDFS or OWL-RL inference, materializing inferred triples |
| `onto_extend` | To run the full pipeline: ingest → SHACL validate → reason in one call |
| `onto_import_schema` | To import a PostgreSQL or DuckDB database schema as an OWL ontology (requires `postgres` and/or `duckdb` features). Auto-dispatches on connection-string scheme. |
| `onto_plan` | Before applying changes — shows added/removed classes, blast radius, risk score |
| `onto_apply` | After plan + enforce — applies changes in `safe` or `migrate` mode |
| `onto_lock` | To protect production IRIs from removal |
| `onto_drift` | To compare two versions — rename detection, drift velocity, self-calibrating confidence |
| `onto_enforce` | After loading — design pattern checks: `generic`, `boro`, `value_partition`, `hierarchy`, or custom rules |
| `onto_monitor` | After apply — run SPARQL watchers with threshold alerts. Watchers with `webhook_url` POST alerts to external systems (Slack, PagerDuty, etc.) |
| `onto_monitor_clear` | To clear blocked state after resolving monitor alerts |
| `onto_crosswalk` | To look up clinical terminology mappings (ICD-10 ↔ SNOMED ↔ MeSH) |
| `onto_enrich` | To add skos:exactMatch triples linking classes to clinical codes |
| `onto_validate_clinical` | To check class labels against clinical crosswalk terminology |
| `onto_align` | To detect alignment candidates (equivalentClass, exactMatch, subClassOf) between two ontologies using 7 weighted signals (6 structural + embedding similarity when embeddings are loaded) |
| `onto_align_feedback` | To accept/reject alignment candidates for self-calibrating confidence weights |
| `onto_lineage` | To view the session's lineage trail (plan → enforce → apply → monitor → drift) |
| `onto_lint_feedback` | To accept/dismiss a lint issue — teaches lint to suppress repeatedly dismissed warnings |
| `onto_enforce_feedback` | To accept/dismiss an enforce violation — teaches enforce to suppress repeatedly dismissed violations |
| `onto_dl_explain` | To explain why a class is unsatisfiable using DL tableaux reasoning — returns clash trace |
| `onto_dl_check` | To check if one class is subsumed by another using DL tableaux reasoning |
| `onto_embed` | After loading an ontology — generates text + Poincaré structural embeddings for all classes. Honours `[embeddings] provider = "local" \| "openai"` in `config.toml`; OpenAI-compatible gateways (Azure, Ollama, vLLM, LocalAI, …) are supported via `OPEN_ONTOLOGIES_EMBEDDINGS_*` env vars |
| `onto_search` | To find classes by natural language description — requires onto_embed first |
| `onto_similarity` | To compute embedding similarity between two specific IRIs |
| `onto_unload` | To unload the active ontology from memory. Optional `name` targets a specific cached entry; `delete_cache=true` also removes the on-disk N-Triples cache file |
| `onto_recompile` | To re-parse the source file and rebuild the cache. Optional `name` rebuilds a non-active cached entry without disturbing the in-memory store (safe background refresh) |
| `onto_cache_status` | To inspect the compile cache: active slot, all cached entries, and effective `[cache]` config (TTL, auto_refresh, dir) |
| `onto_cache_list` | To list cached ontologies with metadata (`is_active`, `in_memory`, mtime, size) — lighter than `onto_cache_status` |
| `onto_cache_remove` | To remove a cached ontology by `name`. Pass `delete_file=false` to keep the on-disk N-Triples |
| `onto_repo_list` | To enumerate RDF/OWL files in directories configured under `[general] ontology_dirs`. Use in containerized deployments to discover ontologies without hardcoding paths |
| `onto_repo_load` | To load an ontology from a configured `ontology_dirs` repo by bare name, relative path, or absolute path. Reuses the same compile-cache / TTL-eviction path as `onto_load` |

## Ontology Lifecycle

When evolving an ontology in production, follow this Terraform-style cycle. Claude decides which steps to include based on the change.

### Plan

1. Call `onto_plan` with the proposed Turtle — returns added/removed classes/properties, blast radius, risk score
2. If any IRIs are locked (`onto_lock`), locked violations will appear in the plan — resolve before proceeding
3. Review the risk score: `low` (additions only), `medium` (modifications), `high` (removals with dependents)

### Enforce

4. Call `onto_enforce` with a rule pack (`generic`, `boro`, `value_partition`, `hierarchy`, `ies4`) — checks design pattern compliance. The `ies4` pack catches 4D-modelling violations specific to the UK Information Exchange Standard (particular/ClassOfEntity overlap as error; State without `isStateOf` and Event without participant pattern as warnings).
5. Fix any violations before applying

### Apply

6. Call `onto_apply` with mode `safe` (clear + reload) or `migrate` (add owl:equivalentClass/Property bridges)
7. Lineage is recorded automatically

### Monitor

8. Call `onto_monitor` to run SPARQL watchers — alerts trigger notify, block, or auto-rollback actions
9. If blocked, resolve the issue and call `onto_monitor_clear`

### Drift

1. Call `onto_drift` to compare versions — drift velocity, rename detection with self-calibrating confidence
2. Feed back rename accuracy to improve future confidence scores

## Data Extension Workflow

When applying an ontology to external data:

### Inspect and Map

1. Call `onto_map` with the data file — it returns field names, ontology classes/properties, and a suggested mapping
2. Review the mapping — adjust predicates, set the class, mark lookup fields
3. Optionally save the mapping to a file for reuse

### Ingest

4. Call `onto_ingest` with the data file and mapping — it generates RDF triples and loads them into the store
5. Call `onto_stats` to verify triple counts match expectations

### Validate

6. Call `onto_shacl` with SHACL shapes to validate the data against constraints
7. Fix any violations (adjust mapping or data), re-ingest if needed

### Reason

8. Call `onto_reason` with profile `rdfs` or `owl-rl` to infer new triples
9. Call `onto_query` to verify inferred knowledge is correct

### Or use the convenience pipeline

10. Call `onto_extend` to run ingest → SHACL → reason in one call

## Semantic Search & Embedding Workflow

When exploring or aligning ontologies using semantic embeddings:

### Setup

1. Ensure the embedding model is downloaded (`open-ontologies init`)
2. Call `onto_load` to load the ontology
3. Call `onto_embed` to generate text + structural embeddings for all classes

### Search

4. Call `onto_search` with a natural language query — returns most similar classes
5. Use `mode: "text"` for label/definition similarity, `mode: "structure"` for hierarchy position, `mode: "product"` for combined

### Compare

6. Call `onto_similarity` with two IRIs to see cosine + Poincaré distance between them

### Alignment Enhancement

7. When running `onto_align`, embedding similarity is automatically used as signal #7 if embeddings are loaded
8. This catches semantically equivalent classes that have different labels (e.g., Vehicle ↔ Automobile)

### Borderline-Candidate Review (LLM-as-Oracle Pattern)

`onto_align` partitions candidates into three buckets by confidence:

- `auto_applied`: confidence ≥ `high_threshold` (default 0.85) — applied as triples
- `borderline`: confidence in [`low_threshold`, `high_threshold`) (default low 0.4) — surfaced with rich `context` (source/target labels, source/target parents) for review
- below `low_threshold`: dropped

When borderline pairs are present, the tool returns a `summary_for_review` string instructing you (the connected LLM) to:

1. **Inspect each borderline pair** — read its `context` block (labels, parent classes) and the per-signal breakdown in `signals`. The structural context tells you whether the pair represents a true match obscured by label difference, a partial overlap that warrants `skos:closeMatch`, or a false positive that needs rejection.
2. **Decide accept / reject** based on the structural and lexical evidence in the conversation, plus any external knowledge you have about the domain.
3. **Call `onto_align_feedback`** for each verdict — this writes to the SQLite feedback table and the self-calibrating-weights model learns from it. Future `onto_align` runs will weight the seven signals better.

This is the MCP-native form of the LogMap-LLM "LLM-as-oracle" pattern (Jiménez-Ruiz et al., EACL 2026, top-2 OAEI 2025 Bio-ML). The server provides the scorer + borderline surface; you do the judging in-conversation; the feedback loop closes via existing tools.

## Architecture Convention: MCP-Native Tool Design

When a tool needs LLM-style judgment (NL generation, semantic matching, accept/reject decisions), the server must NOT embed its own LLM client. The connected orchestrator (you, over MCP) is already a capable LLM. The server's role is to provide:

- **Validation primitives** that the orchestrator can't compute structurally itself (e.g., `onto_shacl_check` verifies proposed SHACL references real classes/properties in the loaded ontology)
- **Scaffolding outputs** that give the orchestrator the schema context it needs to author against (e.g., `onto_stats`, `onto_query` for SPARQL, `borderline` candidates with parent IRIs)
- **Feedback channels** so the orchestrator's verdicts can train the server's self-calibrating models (e.g., `onto_align_feedback`, `onto_lint_feedback`, `onto_enforce_feedback`)

Concrete shape: if a tool description starts with "the server will call an LLM to ..." — flip the design. Have the server return what needs judging; do the judging in the conversation; pipe verdicts back through a feedback tool.

This pattern is the project convention going forward. See `onto_align` borderline buckets (commit a7d3990) and `onto_shacl_check` (commit 9867133) for canonical examples.

## Enforcer Rules (Optional)

If [OpenCheir](https://github.com/fabio-rovai/opencheir) is also connected as an MCP server, its enforcer rules provide workflow safety:

- **onto_validate_after_save** — warns if you save 3+ times without validating
- **onto_version_before_push** — warns if you push without saving a version snapshot first

To enable automatic governance (no Claude orchestration needed), start with the governance webhook:

```bash
# Start OpenCheir first (it listens on port 9900 by default)
opencheir serve &

# Then start Open Ontologies pointing at OpenCheir's enforcer endpoint
GOVERNANCE_WEBHOOK=http://localhost:9900/api/enforcer/event open-ontologies serve
```

Every lineage event (plan, apply, save, push, etc.) is automatically POSTed to OpenCheir's enforcer, which evaluates rules and logs verdicts.

These rules are optional — Open Ontologies works perfectly without OpenCheir.

## Benchmarks

This repo contains reference ontologies and comparison scripts in `benchmark/`. Use them as starting points or to verify the AI-native approach against traditional methods.
