# Fabric Ontology

> Use for the Microsoft Fabric Ontology item (preview, Fabric IQ workload) — `<Name>.Ontology/` in a Git-synced repo, `.platform` type `Ontology`. Covers the definition layout (an empty `definition.json` envelope, `EntityTypes/{bigint-id}/definition.json` plus `DataBindings/{guid}.json`, `Documents`, `Overviews`, `ResourceLinks`, and `RelationshipTypes/{id}/` plus `Contextualizations`), generating an ontology from a semantic model and the Import / Direct Lake / DirectQuery support matrix whose Direct Lake bindings fail silently when the backing lakehouse workspace has inbound public access disabled, the data-binding rules (one static binding per entity type but many time-series ones, static before time-series, entity keys string/integer only, managed tables only, no OneLake security, no delta column mapping), the `Decimal`-returns-null trap and its `Double` remedy, semantic enrichment, and consuming an ontology from the five agent paths including its own MCP endpoint.

- Skill: `wardawgmalvicious/fabric-ontology` (Agent Skill, multi-file: 2 files)
- Install (CLI): `npx skillmds@latest add wardawgmalvicious/fabric-ontology`
- Raw SKILL.md: https://api.skillmd.com/api/skills/wardawgmalvicious/fabric-ontology/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: AI & ML
- Author: wardawgmalvicious (https://skillmd.com/u/wardawgmalvicious)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/wardawgmalvicious/fabric-ontology

---


# Fabric Ontology (preview): the item, its definition, and its bindings

An **ontology** is Fabric IQ's shared business vocabulary: *entity types*
(`Customer`), *properties* (`email`), and *relationships*
(`Customer places Order`), bound to real OneLake data so agents and
people reason in the same terms.

**Item type name: `Ontology`** — the `metadata.type` written into
`.platform`, so a Git-synced workspace serializes it as
`<display name>.Ontology`. Git integration lists it under **"IQ (preview)
items"** alongside Plan — *not* under Data Science or Real-Time
Intelligence, which is where people look first.

Everything below is **preview**, verified against the docs on
**2026-09-02**. Re-check before relying on a limit; this surface moves.

**Not here, deliberately.** The ontology graph is *provided by* [Graph in
Microsoft Fabric](https://learn.microsoft.com/fabric/graph/overview) — a
separate `GraphModel` item. GQL, graph-type DDL and `executeQuery` belong
to `fabric-graph`; this skill only carries the graph constraints that bite
at **ontology** time. Agent configuration belongs to `fabric-data-agent`
(conversational, ≤5 sources) and `fabric-operations-agent` (autonomous,
single-source) — ontology is one *source* for each of those.

## The Git definition layout

Ontology definitions are JSON. Only the first two are required:

```
<Name>.Ontology/
  .platform                              # metadata.type = "Ontology"
  definition.json                        # REQUIRED, and literally {}
  EntityTypes/{entityTypeId}/
    definition.json                      # the entity type
    DataBindings/{guid}.json             # one file per binding
    Documents/document{n}.json           # {displayText, url}
    Overviews/definition.json            # preview-page widgets
    ResourceLinks/definition.json        # links to a Power BI report
  RelationshipTypes/{relationshipTypeId}/
    definition.json                      # source/target entityTypeId
    Contextualizations/{guid}.json       # binds the relationship to a table
```

Three things about the IDs, all of which surprise people reading a diff:

- **`{entityTypeId}` is a positive 64-bit integer, not a GUID** — and it
  is a *directory name*. So is `{relationshipTypeId}`. GUID filenames
  appear one level down, for `DataBindings` and `Contextualizations`.
- **Property IDs are also bigints**, and bindings reference them by
  `targetPropertyId`. A binding file names *no* property names — only
  `sourceColumnName` → `targetPropertyId` — so a diff of a binding is
  unreadable without the entity type's `definition.json` open beside it.
- **`definition.json` at the root is an empty object.** Do not "fix" it.
  The content all lives in the subdirectories.

Property `valueType` is one of *String, Boolean, DateTime, Object,
BigInt, Double* (plus *Any* for untyped properties). **There is no
`Decimal`** — see the trap below. Entity type and property `name` must
match `^[a-zA-Z][a-zA-Z0-9_-]{0,127}$`; note the portal is stricter than
the API here and caps custom property names at **26** characters.

Full part-by-part schemas, including every field of a data binding and
the Eventhouse variant, are in
[references/REFERENCE.md](references/REFERENCE.md).

## Generating an ontology from a semantic model

Generation creates the item, entity types from tables, static properties
from columns, and relationship types from model relationships. **What it
does not do** is bind time series data, review entity keys, or bind
relationship types — all three are manual follow-ups, every time.

Support depends entirely on the **semantic model's storage mode**:

| | Import | Direct Lake | DirectQuery |
| --- | --- | --- | --- |
| Entity / property / relationship **definitions** | ✅ | ✅ | ✅ |
| Entity type **bindings to data** | ❌ | ✅ *conditional* | ❌ |
| Relationship type **bindings** | ❌ | ✅ *conditional* | ❌ |
| **Querying** through bindings | ❌ | ✅ (no measures or calculated columns) | ❌ |

**This is the failure worth knowing.** Direct Lake entity bindings work
*only* when the backing lakehouse sits in a workspace with **inbound
public access enabled**. When it does not, "the ontology item is created
successfully but that entity type has no data bindings." A green
checkmark and an empty ontology. Relationship bindings have their own
condition: they generate only where a **primary key is identified**.

So an Import-mode model generates a correct-looking *schema* and nothing
queryable, by design. Check the mode before blaming the data.

You also **cannot generate from `My workspace`** — move the semantic
model to a real workspace first.

## The constraints that produce silent or confusing failures

- **Managed lakehouse tables only.** External tables that merely *appear*
  in a lakehouse are not supported, and the symptom is "entity type
  details shows no data" rather than an error at binding time.
- **No OneLake security on the source lakehouse.** A lakehouse with it
  enabled does not appear in the data-source picker at all — it looks
  like a permissions problem and is not.
- **No delta column mapping.** It is enabled *automatically* when column
  names contain `,` `;` `{}` `()` `\n` `\t` `=` **or a space**, and
  automatically on the delta tables backing **import-mode** semantic
  model tables. Symptom: the entity type details graph does not load.
- **Duplicate property names must share a type** across entity types. A
  string `ID` on one and an integer `ID` on another is what makes entity
  types go missing from a generated ontology.
- **Renaming a source table after binding breaks it.** Bindings carry
  `sourceTableName` as a string.
- **Refresh is manual.** New rows upstream are invisible until the graph
  model is refreshed; a refresh *schedule* on the child Graph item is
  what shows up as capacity usage.

**Verify these at the lakehouse, not in TMDL.** A semantic model's TMDL
`dataType` is not the delta column type and the TMDL table name is not
the delta table name, so grepping TMDL for `decimal` or for spaced column
names produces false confidence in both directions. The check belongs on
the delta tables.

## The `Decimal` trap, and its remedy

Fabric Graph does not support `Decimal`. Generate an ontology from a
model whose tables carry `Decimal` columns and those properties return
**null on every query** — no error, just nulls. `Decimal` is the natural
money type, so this hits currency columns first.

The remedy is documented and specific: **recreate the property as
`Double` in the ontology and bind it to the source data.** That works
because *manual* binding accepts a lakehouse `decimal` column as a source
for a `double` property — the type map is wider than the generator's
output. Full source-to-property type table in
[references/REFERENCE.md](references/REFERENCE.md); the one other trap in
it is that lakehouse `decimal(p, s)` maps to **string**, not double.

## Binding data: the ordering and cardinality rules

- **One static binding per entity type.** You cannot union static data
  from two sources into one entity type. Static sources must be
  OneLake-backed.
- **Many time-series bindings per entity type**, from lakehouse *and*
  eventhouse sources together.
- **Static first.** A time-series binding needs an existing statically
  bound property to contextualize against, and the static value must
  **exactly match** a column in the time-series data.
- **Entity type keys are `string` or `integer` only.** One or more
  columns, together unique.
- Time series data must be **columnar** — one row per timestamped
  observation.

## Semantic enrichment is what makes agents work

Descriptions, synonyms, and key-value metadata on entity types and
properties. It is not decoration: the documented example is a data agent
that cannot answer "which ice cream shops sold the most frozen desserts?"
until `Products` gains the synonym `frozen desserts`.

Scope it honestly, because the docs do: enrichment helps the agent during
**schema exploration and reasoning**, and **query generation does not use
the metadata directly**. Only entity types support synonyms — properties
and relationship types get descriptions and key-value pairs only, and
keys must be unique within each object.

## Consuming an ontology

Five paths, detailed in [references/REFERENCE.md](references/REFERENCE.md):
Fabric **operations agent** (monitoring + actions), Fabric **data agent**
(conversational Q&A), **Foundry IQ** agent, **Copilot Studio** agent, and
**custom agents over the ontology MCP server**.

The last is the one that matters outside Fabric: **an ontology is itself
an MCP server**, at

```
https://api.fabric.microsoft.com/v1/mcp/dataPlane/workspaces/<workspace-ID>/items/<ontology-item-ID>/ontologyEndpoint
```

Both IDs come out of the portal URL
(`.../groups/<workspace-ID>/ontologies/<ontology-item-ID>`). Note the
shape differs from the data agent's endpoint
(`/v1/mcp/workspaces/{ws}/dataagents/{id}/agent`) — `dataPlane`, `items`,
and a trailing `ontologyEndpoint`. It needs **F2+ capacity** and the
*Ontology item (preview)* tenant setting.

Two known agent behaviours worth carrying: a data agent's first few
queries after creation can fail while it initializes (wait, retry), and
aggregation is a known gap — add `Support group by in GQL` to the agent
instructions.

## Before you start: tenant settings

Creating the item at all requires the **Ontology item (preview)** tenant
setting. Failure to create a new ontology is *most commonly* this and not
anything about your data. Data agent and operations agent each need their
own settings on top.

