# How to Write Custom MDL Manifest Files in WrenAI: Models, Relationships, and Views

> Learn to write custom MDL manifest files in WrenAI. Define models, relationships, and views to power your agent's data warehouse understanding. Compile YAML to JSON schema.

- Repository: [Canner/WrenAI](https://github.com/Canner/WrenAI)
- Tags: how-to-guide
- Published: 2026-07-20

---

**WrenAI compiles YAML-based custom MDL manifest files into a camelCase JSON schema that powers the agent's understanding of your data warehouse.**

The Canner/WrenAI repository uses a YAML-based Model Definition Language (MDL) to describe the logical schema that the AI agent queries against. When you write custom MDL manifest files, you create a version-controlled project structure that the CLI compiles into an engine-ready artifact. This guide covers the exact file paths, field names, and compilation steps used in the WrenAI source code.

## Start with Project Metadata in [`wren_project.yml`](https://github.com/Canner/WrenAI/blob/main/wren_project.yml)

Every MDL project begins at the root with a [`wren_project.yml`](https://github.com/Canner/WrenAI/blob/main/wren_project.yml) file. This file sets the global layout version, namespace, and connection profile that the compiler expects.

```yaml
schema_version: 5
name: my_project
version: "1.0"
catalog: wren
schema: public
data_source: postgres
profile: my-pg

```

The `schema_version` must match the current layout version—version `5`—so the CLI knows how to parse the manifest according to the WrenAI reference documentation. The `catalog` and `schema` fields define WrenAI namespaces, not the underlying database catalog, while `profile` references a connection you previously set with `wren context set-profile`.

## Define Models Using `models/<model_name>/metadata.yml`

Models live under `models/<name>/metadata.yml` and represent either a physical table or a SQL-defined dataset. Each model must declare its columns and, if it participates in `TO_MANY` traversals, a `primary_key`.

### Map Physical Tables with `table_reference`

Use `table_reference` when a model maps directly to an existing database table. The `primary_key` field is required for any `TO_MANY` relationship traversals as implemented in the MDL schema reference.

```yaml
name: customers
table_reference:
  catalog: jaffle_shop
  schema: main
  table: customers
primary_key: customer_id
columns:
  - name: customer_id
    type: INTEGER
    is_primary_key: true
    not_null: true
  - name: first_name
    type: VARCHAR
  - name: last_name
    type: VARCHAR
  - name: number_of_orders
    type: BIGINT

```

### Build Virtual Datasets with `ref_sql`

Alternatively, define a model with `ref_sql` to create a virtual dataset from a SELECT statement. You can write the SQL inline inside [`metadata.yml`](https://github.com/Canner/WrenAI/blob/main/metadata.yml) or place it in a sibling [`ref_sql.sql`](https://github.com/Canner/WrenAI/blob/main/ref_sql.sql) file, which takes precedence over the inline definition.

```yaml
name: revenue_summary
ref_sql: |
  SELECT DATE_TRUNC('month', order_date) AS month,
         SUM(total) AS total_revenue
  FROM orders
  GROUP BY 1
columns:
  - name: month
    type: DATE
  - name: total_revenue
    type: DECIMAL

```

## Join Models with [`relationships.yml`](https://github.com/Canner/WrenAI/blob/main/relationships.yml)

The [`relationships.yml`](https://github.com/Canner/WrenAI/blob/main/relationships.yml) file at the project root declares how two models join together. It contains a top-level `relationships:` list where each entry specifies the models, join type, and condition.

```yaml
relationships:
  - name: orders_customers
    models:
      - orders
      - customers
    join_type: MANY_TO_ONE
    condition: orders.customer_id = customers.customer_id

```

Only equality conditions are supported, and the first model in the `models` list must appear on the left side of the `condition` string. Supported join types include `MANY_TO_ONE`, `ONE_TO_MANY`, and `ONE_TO_ONE` according to the MDL relationship specification.

## Expose Curated Datasets with `views/<view_name>/metadata.yml`

Views provide reusable SQL SELECT statements that can reference models or other views, inheriting their schema automatically from the query result. Store the SQL inline under `statement` or in a sibling [`sql.yml`](https://github.com/Canner/WrenAI/blob/main/sql.yml) file that overrides the inline version.

```yaml
name: top_customers
statement: |
  SELECT customer_id, SUM(total) AS lifetime_value
  FROM wren.public.orders
  GROUP BY 1
  ORDER BY 2 DESC
  LIMIT 100
properties:
  description: "Top customers by lifetime value"

```

## Build the Manifest with `wren context build`

WrenAI converts your YAML project into an engine-ready artifact during compilation. All YAML files use **snake_case** field names, and the build step translates them into **camelCase** inside [`target/mdl.json`](https://github.com/Canner/WrenAI/blob/main/target/mdl.json). Run the following commands from the project root:

```bash
wren context build   # compiles YAML into target/mdl.json

wren memory index    # builds the LanceDB index for knowledge (optional)

```

The generated [`target/mdl.json`](https://github.com/Canner/WrenAI/blob/main/target/mdl.json) is what the Wren engine consumes, while your original YAML files remain under version control.

## Summary

- Place project-wide settings in [`wren_project.yml`](https://github.com/Canner/WrenAI/blob/main/wren_project.yml) and set `schema_version` to `5` for compatibility with the current WrenAI compiler.
- Create models under `models/<name>/metadata.yml` using either `table_reference` for physical tables or `ref_sql` for SQL-defined virtual datasets.
- Define joins between models in [`relationships.yml`](https://github.com/Canner/WrenAI/blob/main/relationships.yml), ensuring equality conditions place the first listed model on the left side.
- Add views under `views/<name>/metadata.yml` to expose curated SELECT statements with automatic schema inference.
- Compile your custom MDL manifest files with `wren context build` to produce the [`target/mdl.json`](https://github.com/Canner/WrenAI/blob/main/target/mdl.json) consumed by the engine.

## Frequently Asked Questions

### What schema version does WrenAI require for custom MDL manifest files?

WrenAI requires `schema_version: 5` in [`wren_project.yml`](https://github.com/Canner/WrenAI/blob/main/wren_project.yml) so that the CLI knows how to compile the manifest layout into the correct engine format. Using an outdated version will cause the `wren context build` step to fail.

### Can I extract SQL into separate files instead of using inline YAML?

Yes. For models, place SQL in a sibling [`ref_sql.sql`](https://github.com/Canner/WrenAI/blob/main/ref_sql.sql) file next to [`metadata.yml`](https://github.com/Canner/WrenAI/blob/main/metadata.yml); for views, use a sibling [`sql.yml`](https://github.com/Canner/WrenAI/blob/main/sql.yml) file. When present, these external files take precedence over any inline `ref_sql` or `statement` definitions.

### Which join types are supported in MDL relationships?

The MDL schema supports `MANY_TO_ONE`, `ONE_TO_MANY`, and `ONE_TO_ONE` join types. Only equality conditions are permitted, and the first model listed in the `models` array must appear on the left side of the condition expression.

### How does WrenAI handle case conversion during compilation?

All YAML source files use **snake_case** keys for readability. During the `wren context build` step, the compiler transforms these keys into **camelCase** inside the generated [`target/mdl.json`](https://github.com/Canner/WrenAI/blob/main/target/mdl.json) that the engine queries against.