Semanticus
Documentation

Model & Properties

The model tree and the properties grid are the everyday surface of Semanticus: browse every table, measure, column and relationship, then edit names, descriptions, formats and any other property. It is the same live model your AI Assistant reads and writes over MCP, on one shared undo timeline.

The model tree and Properties grid showing editable model settings.

The grid includes model settings as well as tables, columns, measures, relationships and other objects. Selecting a measure also exposes its TMDL editor, so schema and expression work stay in one place.

In the Studio

The model tree is a native VS Code tree (not a webview), so it behaves like the rest of your editor. It expands to every object kind: each table, and under it that table's measures, columns, hierarchies and partitions, plus top-level folders for relationships, roles, perspectives and calculation groups (with their items).

A curated right-click menu over every object type does the common authoring jobs: create, script to DAX or TMDL, hide or show, and delete, with multi-select for bulk actions. Find in Model and Reveal in tree get you to an object fast, and you can mark a date table or generate a time-intelligence suite straight from the menu.

Selecting an object opens the Properties grid, a dedicated webview with typed editors (text, checkbox, number, dropdown), search and filter, collapsible categories, and multiline editors that grow as you type. Invalid input is flagged inline. Select several objects at once and the grid shows a tri-state, multi-select bulk edit, so one change lands on all of them. Editing an object's Name renames it and rewrites every DAX reference automatically, so a rename never leaves a broken measure behind.

Real choices, not blank boxes

The fields you fill most often come prefilled with real options. Display Folder suggests the folders already in use, the object's own table first and then the rest of the model, so an existing folder structure stays consistent instead of fragmenting on typos. The Data Type dropdown lists exactly the types the engine can actually set (it is fed by the engine's own allow-list, so the pick list can never offer something the write would refuse), and on a calculated column it is locked with a plain reason, because a calculated column's type comes from its DAX. Format string on a measure or column opens a picker over the curated format library, grouped by category with an example of what each format renders. Free text stays available everywhere; the pickers are a head start, not a cage.

Over MCP

Your AI Assistant walks the same tree and edits the same objects by name:

# the agent, browsing then editing one measure
list_objects                                    # -> the tables
list_objects   table:Sales                      # -> Sales measures + columns
get_object     measure:Sales/Margin             # -> properties, incl. description
set_description measure:Sales/Margin  "Gross margin as a percent of net sales."
rename_object  measure:Sales/Margin  "Margin %" # every DAX reference rewritten

Format strings, from a library

Formats are where hand-typed strings go wrong: the glyphs, the scaling commas, the sign handling. The measure's Format string picker is backed by a curated library of about 90 number formats across 17 categories (whole numbers, decimals, percentages, currency, K/M/B scaling, signed variance, change indicators, accounting, dates, times, durations, KPI glyphs, ratios, basis points and more), each with a live example of what it renders. The twenty or so most common are pinned first, and a custom escape hatch remains for anything else.

The library carries both kinds of format. A static format is a literal string (set_measure_format). A dynamic format is DAX that returns a format string per filter context (set_measure_format_expression): that is how one measure renders 500 as "500" and 5,000,000 as "5M", or switches format per selected currency. Browse the catalog with list_format_templates, which works offline and with no model open.

Editing a dynamic format

Every measure's Properties grid carries a Format expression row that shows at a glance whether a dynamic format exists, with a preview of the DAX. Edit expands it into a lightweight DAX editor: monospace, an insert-from-template picker over the library's dynamic entries so the common patterns start from working DAX, and a balanced-quotes-and-parentheses check before Apply. Applying writes through the engine like any other edit (undoable, visible in Edit History, broadcast to the other door), and clearing the expression honestly restores the static format string. Dynamic format strings need compatibility level 1601: below that, the row says so plainly instead of showing a dead control.

Update Schema: when the source moved under you

Sources drift: a column is added upstream, one is dropped, a type changes. Right-click a table and choose Update Schema. Semanticus re-reads the table's source columns schema-only (no data is imported), diffs them against the model, and shows the drift grouped as added, removed and type-changed, each with a checkbox. Apply the subset you accept as one undoable change; anything that cannot apply (a calculated column, a name clash) is skipped with its reason instead of failing the whole batch.

get_source_schema  table:Sales                 # re-read the source's columns, schema-only
diff_schema        table:Sales                 # added / removed / type-changed vs the model
apply_schema_update table:Sales items=[...]    # apply the accepted subset, one undoable step

All three are free. The probe needs a reachable SQL or Fabric source; offline, or on a source it cannot read, it says so plainly instead of guessing, and you can hand diff_schema a column list to diff against instead. Calculated columns are never flagged: they are derived from DAX, not from the source.

One tree, both doors

Whether you rename in the grid or your AI Assistant calls rename_object, the change lands on the same session and broadcasts a model/didChange event, so the other door redraws immediately and the edit joins one shared undo timeline. You can veto anything with a single undo, no matter which door made it, because it is one model, not a copy.

Renames stay safe inside the model

A rename rewrites every DAX reference within the model. Bindings that live outside the model (a report's field references) cannot be rewritten from here, so review renamed fields that reports use before you ship.