# Source changes

The Data source review queue over MCP. A source change is one detected difference between the data source and what the ontology tracks: a table or column added, removed, renamed, or retyped. It is the same queue the web app's Data source feed shows.

Changes appear after a warehouse resync or an updated DDL upload. Read them before editing the affected tables: a `breaking` change means something curated already depends on the object that moved. For the review workflow itself, see [Review data-source changes](/curate/data-source-changes/).

## list\_source\_changes

The review queue, newest first, one page at a time.

| Name | Type | Notes |
| --- | --- | --- |
| `project_id` | UUID, required | The project whose changes to list |
| `status` | enum | `pending` (the default, and the review queue), `approved`, `rejected`, or `superseded` |
| `limit` | integer, 1–500 | Page size. Defaults to 100 |
| `offset` | integer | Page start, newest first. Defaults to 0 |

Returns `{items, total}`, or `{error}` when the request fails. `total` counts every change matching `status`, so it tells you how much queue is left beyond the page you asked for.

Each entry carries `{id, change_type, severity, status, target, times_raised, last_detected_at, has_suggested_edit, suggested_edit_summary, breaking_refs}`.

| Field | Values | Meaning |
| --- | --- | --- |
| `change_type` | `table_added`, `column_added`, `table_renamed`, `column_renamed`, `column_retyped`, `table_removed`, `column_removed` | What the detector saw |
| `severity` | `benign`, `breaking` | `breaking` means at least one curated object references the changed object. Every `*_added` is benign by construction |
| `status` | `pending`, `approved`, `rejected`, `superseded` | Review state. `superseded` is the system’s own tombstone: a later detection run found that the change no longer holds |
| `target` | `schema.table`, or `schema.table.column` | The changed object, dotted |
| `times_raised` | integer | How many detection runs re-detected this change while it sat unreviewed |
| `has_suggested_edit` | boolean | Whether a curation-preserving edit was computed. Breaking removals have none |
| `suggested_edit_summary` | string or null | One line describing that edit. The operations themselves are fetched per change with `get_source_change` |
| `breaking_refs` | integer | How many curated objects reference the changed object. Zero for a benign change |

## get\_source\_change

One change in full: what the detector recorded, everything that depends on the changed object, and the edit that would fix it. Parameters: `project_id`, `change_id`.

Returns the list fields `{id, change_type, severity, status, target, times_raised, last_detected_at}` plus:

| Field | Notes |
| --- | --- |
| `payload` | The detector’s record of the change, keyed by `change_type`: old and new names and data types for a rename or retype, the preserved column curation for a removal, the column count and per-column delta for a renamed table |
| `impact` | `[{kind, confidence, object_label, detail}]` — every curated object that references the changed source object. Empty when the change is benign |
| `suggested_edit` | The edit that reviewing in the app would apply, or null when there is no safe one |
| `first_detected_at` | When the change was first seen. Only on this tool; the list rows carry `last_detected_at` only |
| `error` | Set instead of the keys above when the request fails. An unknown change id returns `"Source change not found"` |

The three derived fields the list rows carry (`has_suggested_edit`, `suggested_edit_summary`, `breaking_refs`) are not repeated here — read them off `suggested_edit` and `impact` instead.

### Impact references

| Field | Values | Meaning |
| --- | --- | --- |
| `kind` | `join`, `metric`, `virtual_table`, `grain` | Which kind of curated object holds the reference |
| `confidence` | `exact`, `heuristic`, `unverified` | `exact` is a structured match or a resolved qualifier; `heuristic` resolved an unqualified column through its home table; `unverified` means the object’s SQL did not parse and the name merely appears as an identifier in it |
| `object_label` | string | The referencing object, as it is named in the ontology |
| `detail` | string | Where inside that object the reference sits |

Read the impact list before touching anything. An `unverified` reference is a place to look, not a proven dependency.

### The suggested edit

`suggested_edit` is a description, not an edit that has happened. It carries `kind` (`rename_column`, `rename_table`, `retype_column`, `add_table`, `add_column`, `remove_table`), an `operations` list of declarative ontology operations, a one-line `human_summary`, and `manual_review` when part of the fix could not be derived safely.

Read `manual_review` whenever it is present. Column references inside free SQL are rewritten only where the reference can be proven; anything ambiguous is left alone and listed there, so the failure mode is an under-rewritten expression you finish by hand, never a silent rewrite of a different table’s same-named column.

**Both tools are read-only, and this is deliberate.** Reviewing a change — approving it so its edit is applied, or dismissing it — happens in the web app. There is no MCP tool that does either. The headless way to act on a change is the same as for any ontology work: edit the ontology files in the repository and open a pull request. `suggested_edit` tells you what that change should be.

`get_project_status` reports the queue size without listing it, as `pending_source_changes: {total, breaking}` — see [Projects and status](/reference/mcp/projects/). From a shell, [`cassis source-changes`](/reference/cli/source-changes/) reads the same queue.
