mcpbeat

Tom Operations

microsoft/tom-operations

Guide for working with the TOM (Tabular Object Model) wrapper. Use this when modifying semantic models programmatically.

3k tokens
context cost
the whole folder, loaded on every use
1
files
instructions only
0
copies elsewhere
how many repositories repackaged it
559
stars on the repo
on the repository, not the skill itself

Install

one command, takes just this skill from the repository
npx skills add https://github.com/microsoft/semantic-link-labs --skill tom-operations

The instruction itself

35 sections, as written by the author

TOM (Tabular Object Model) Operations

This skill covers working with the TOM wrapper in Semantic Link Labs for programmatic semantic model management.

When to Use This Skill

Use this skill when you need to:

  • Read or modify semantic model metadata
  • Add/remove tables, columns, measures, relationships
  • Work with calculation groups and items
  • Manage partitions and data sources
  • Implement model management features

Overview

The TOM wrapper provides a Pythonic interface to the Tabular Object Model (TOM), which is the object model for Analysis Services semantic models.

Key Files

| File | Purpose |

|------|---------|

| src/sempy_labs/tom/__init__.py | Module exports |

| src/sempy_labs/tom/_model.py | TOMWrapper class implementation |


Basic Usage

Connecting to a Semantic Model

from sempy_labs.tom import connect_semantic_model

# Read-only connection
with connect_semantic_model(
    dataset="My Semantic Model",
    readonly=True,
    workspace="My Workspace"
) as tom:
    # Read model metadata
    for measure in tom.all_measures():
        print(f"Measure: {measure.Name}")

# Read-write connection (requires XMLA read/write enabled)
with connect_semantic_model(
    dataset="My Semantic Model",
    readonly=False,
    workspace="My Workspace"
) as tom:
    # Modify model
    measure = tom.model.Tables["Sales"].Measures["Revenue"]
    measure.Description = "Total revenue in USD"
    # Changes are saved when context exits

Important Notes

  • XMLA Endpoint: Read-write operations require XMLA read/write enabled on the capacity
  • Context Manager: Always use with statement - changes are saved on exit
  • Service Principal: Supported via service_principal_authentication context

TOMWrapper Properties and Methods

Core Properties

with connect_semantic_model(dataset, workspace=workspace) as tom:
    # Access the model
    model = tom.model

    # Get dataset info
    dataset_id = tom._dataset_id
    dataset_name = tom._dataset_name
    workspace_id = tom._workspace_id
    workspace_name = tom._workspace_name

    # Check compatibility level
    compat_level = tom._compat_level

Iterator Methods

| Method | Returns |

|--------|---------|

| all_columns() | All columns in all tables |

| all_calculated_columns() | All calculated columns |

| all_calculated_tables() | All calculated tables |

| all_calculation_groups() | All calculation groups |

| all_measures() | All measures |

| all_partitions() | All partitions |

| all_hierarchies() | All hierarchies |

| all_levels() | All hierarchy levels |

| all_calculation_items() | All calculation items |

| all_functions() | All user-defined functions |

Example: Iterating Objects

with connect_semantic_model(dataset, workspace=workspace) as tom:
    # List all measures
    for measure in tom.all_measures():
        print(f"Table: {measure.Parent.Name}, Measure: {measure.Name}")

    # List all columns
    for column in tom.all_columns():
        print(f"Table: {column.Table.Name}, Column: {column.Name}, Type: {column.DataType}")

    # List all partitions
    for partition in tom.all_partitions():
        print(f"Table: {partition.Table.Name}, Partition: {partition.Name}")

Reading Model Metadata

Tables

with connect_semantic_model(dataset, workspace=workspace) as tom:
    for table in tom.model.Tables:
        print(f"Table: {table.Name}")
        print(f"  Description: {table.Description}")
        print(f"  Is Hidden: {table.IsHidden}")

Columns

with connect_semantic_model(dataset, workspace=workspace) as tom:
    for column in tom.all_columns():
        print(f"Column: {column.Name}")
        print(f"  Table: {column.Table.Name}")
        print(f"  Data Type: {column.DataType}")
        print(f"  Is Hidden: {column.IsHidden}")

Measures

with connect_semantic_model(dataset, workspace=workspace) as tom:
    for measure in tom.all_measures():
        print(f"Measure: {measure.Name}")
        print(f"  Table: {measure.Parent.Name}")
        print(f"  Expression: {measure.Expression}")
        print(f"  Format String: {measure.FormatString}")

Relationships

with connect_semantic_model(dataset, workspace=workspace) as tom:
    for rel in tom.model.Relationships:
        print(f"From: {rel.FromTable.Name}[{rel.FromColumn.Name}]")
        print(f"To: {rel.ToTable.Name}[{rel.ToColumn.Name}]")
        print(f"Cross Filter: {rel.CrossFilteringBehavior}")

Modifying Model Metadata

Update Measure Properties

with connect_semantic_model(dataset, readonly=False, workspace=workspace) as tom:
    measure = tom.model.Tables["Sales"].Measures["Revenue"]
    measure.Description = "Total revenue"
    measure.FormatString = "$#,##0.00"
    measure.DisplayFolder = "Financial Metrics"

Update Column Properties

with connect_semantic_model(dataset, readonly=False, workspace=workspace) as tom:
    column = tom.model.Tables["Date"].Columns["Month"]
    column.IsHidden = False
    column.Description = "Calendar month"
    column.SortByColumn = tom.model.Tables["Date"].Columns["MonthNumber"]

Update Table Properties

with connect_semantic_model(dataset, readonly=False, workspace=workspace) as tom:
    table = tom.model.Tables["Sales"]
    table.Description = "Sales transactions"
    table.IsHidden = False

Adding Model Objects

Add a Measure

import Microsoft.AnalysisServices.Tabular as TOM

with connect_semantic_model(dataset, readonly=False, workspace=workspace) as tom:
    table = tom.model.Tables["Sales"]

    new_measure = TOM.Measure()
    new_measure.Name = "Total Sales"
    new_measure.Expression = "SUM(Sales[Amount])"
    new_measure.FormatString = "$#,##0.00"
    new_measure.Description = "Sum of all sales amounts"

    table.Measures.Add(new_measure)

Add a Calculated Column

import Microsoft.AnalysisServices.Tabular as TOM

with connect_semantic_model(dataset, readonly=False, workspace=workspace) as tom:
    table = tom.model.Tables["Products"]

    calc_column = TOM.Column()
    calc_column.Name = "Profit Margin"
    calc_column.Type = TOM.ColumnType.Calculated
    calc_column.Expression = "[Revenue] - [Cost]"
    calc_column.DataType = TOM.DataType.Decimal

    table.Columns.Add(calc_column)

Add a Relationship

import Microsoft.AnalysisServices.Tabular as TOM

with connect_semantic_model(dataset, readonly=False, workspace=workspace) as tom:
    from_column = tom.model.Tables["Sales"].Columns["ProductKey"]
    to_column = tom.model.Tables["Products"].Columns["ProductKey"]

    relationship = TOM.SingleColumnRelationship()
    relationship.Name = f"{from_column.Table.Name}_{to_column.Table.Name}"
    relationship.FromColumn = from_column
    relationship.ToColumn = to_column
    relationship.CrossFilteringBehavior = TOM.CrossFilteringBehavior.OneDirection

    tom.model.Relationships.Add(relationship)

TOMWrapper Helper Methods

The TOMWrapper class includes many helper methods for common operations:

Display Methods (return DataFrames)

with connect_semantic_model(dataset, workspace=workspace) as tom:
    # List all measures
    df = tom.list_measures()

    # List all columns
    df = tom.list_columns()

    # List all relationships
    df = tom.list_relationships()

    # List all partitions
    df = tom.list_partitions()

Add Methods

with connect_semantic_model(dataset, readonly=False, workspace=workspace) as tom:
    # Add measure
    tom.add_measure(
        table_name="Sales",
        measure_name="Total Sales",
        expression="SUM(Sales[Amount])",
        format_string="$#,##0.00"
    )

    # Add calculated column
    tom.add_calculated_column(
        table_name="Products",
        column_name="Margin",
        expression="[Revenue] - [Cost]"
    )

Update Methods

with connect_semantic_model(dataset, readonly=False, workspace=workspace) as tom:
    # Update measure expression
    tom.update_measure(
        table_name="Sales",
        measure_name="Total Sales",
        expression="SUMX(Sales, Sales[Quantity] * Sales[Price])"
    )

Service Principal Authentication

from sempy_labs import service_principal_authentication
from sempy_labs.tom import connect_semantic_model

# Using Service Principal
with service_principal_authentication(
    tenant_id="...",
    client_id="...",
    client_secret="..."
):
    with connect_semantic_model(
        dataset="My Model",
        readonly=False,
        workspace="My Workspace"
    ) as tom:
        # Operations use Service Principal credentials
        for measure in tom.all_measures():
            print(measure.Name)

Azure Analysis Services Support

The TOMWrapper also supports Azure Analysis Services:

with connect_semantic_model(
    dataset="MyDatabase",
    workspace="asazure://westus2.asazure.windows.net/myserver",
    readonly=True
) as tom:
    for table in tom.model.Tables:
        print(table.Name)

Best Practices

Do's

  • ✅ Always use context manager (with statement)
  • ✅ Use readonly=True when only reading metadata
  • ✅ Use iterator methods (all_measures(), etc.) for efficient traversal
  • ✅ Save changes by exiting the context normally

Don'ts

  • ❌ Don't open read-write connections when only reading
  • ❌ Don't forget to handle exceptions (changes won't save on exception)
  • ❌ Don't hold connections open longer than necessary

Error Handling

from sempy.fabric.exceptions import FabricHTTPException

try:
    with connect_semantic_model(
        dataset="My Model",
        readonly=False,
        workspace="My Workspace"
    ) as tom:
        # Modifications...
        pass
except FabricHTTPException as e:
    print(f"API error: {e}")
except Exception as e:
    print(f"Error: {e}")
    # Changes NOT saved due to exception

Additional Resources

| Resource | URL |

|----------|-----|

| TOM Reference | Microsoft Docs |

| Sample Notebook | Tabular Object Model.ipynb |

| API Documentation | ReadTheDocs |

How to use it

Copy the folder

Take microsoft/tom-operations from the repository into ~/.claude/skills for personal use, or into .claude/skills inside a project.

Check the name does not clash

The agent identifies a skill by the name field in its header. Two skills with the same name cannot sit side by side — one of them will be ignored.