Skip to main content

Universal lineage graph for Power BI (.pbix) files, from physical source to report display.

Project description

pbix-lineage

Universal lineage graph for Power BI (.pbix) files: from physical source to the field displayed in a report visual.

source (HTTP, OData, SQL, file...) --> Power Query (M)
    --> column / calculated column --> measure (DAX)
        --> field displayed in a report visual

The graph is a standard networkx.DiGraph, natively bidirectional: trace a visual back to its source (upstream), or list everything a source feeds (downstream).

Why

pbixray (used here) extracts the data model (tables, DAX, Power Query, schema) but says nothing about where each column or measure ends up displayed — that lives in a separate, undocumented part of the file (Report/Layout). pbix-lineage connects both into one traversable graph.

Install

pip install pbix-lineage
# or
uv add pbix-lineage

Usage

from pbix_lineage import LineageGraphBuilder, upstream, downstream, print_tree, find_nodes

graph = LineageGraphBuilder().build("my_report.pbix")

find_nodes(graph, "customer_name")
# -> ['column::DIM_CUSTOMER::customer_name']

print_tree(graph, "visual_field::my_report.pbix::Page1::16::customer_name", direction="upstream")
print_tree(graph, "source::odata::example.com/odata/", direction="downstream")

Export

from pbix_lineage import export_graphml, export_nodes_csv, export_edges_csv, graph_summary

graph_summary(graph)                       # {'query': 70, 'column': 183, ...}
export_graphml(graph, "lineage.graphml")   # opens in Gephi / yEd
export_nodes_csv(graph, "nodes.csv")
export_edges_csv(graph, "edges.csv")

Source-agnostic

Source detection (pbix_lineage.sources) relies only on native M function names (Web.Contents, OData.Feed, Sql.Database, Folder.Files, SharePoint.Files, Excel.Workbook, AnalysisServices.Database, ...) — never a specific system or domain. Adding a new source type is one config entry in MFunctionSourceDetector.DEFAULT_PATTERNS, no other code touched.

HTTP API / MCP server

The package also exposes a FastAPI app, mounted as an MCP server via FastMCP (FastMCP.from_fastapi): every route becomes an MCP tool automatically.

uv sync --extra api
uv run pbix-lineage    # starts on http://127.0.0.1:8080
  • REST API: POST /graphs, /search, /upstream, /downstream, /tree, /export, GET /graphs.
  • MCP server (streamable HTTP) at http://127.0.0.1:8080/mcp/: same operations as tools (build_graph, search_nodes, get_upstream, get_downstream, get_lineage_tree, export_graph, list_loaded_graphs), plus a lineage_guidance prompt.
  • Env vars: PBIX_LINEAGE_HOST (default 0.0.0.0), PBIX_LINEAGE_PORT (default 8080).

Each .pbix is parsed once and cached in memory (LineageGraphCache, framework-agnostic).

Publish to the MCP registry

server.json describes this server for registry.modelcontextprotocol.io. After replacing votre-org with your GitHub account everywhere:

uv build && uv publish          # publish the package to PyPI first
mcp-publisher login github
mcp-publisher publish --dry-run
mcp-publisher publish

The registry verifies PyPI ownership via the <!-- mcp-name: ... --> marker at the top of this README. The name in server.json, this marker, and your authenticated GitHub namespace must all match.

Architecture

Module Responsibility
models.py Node/edge types and shared data structures
sources.py Physical source detection (agnostic, configurable)
pbix_model.py Adapter isolating the rest of the code frompbixray
dax.py DAX reference parsing (Table[Field] / [Field])
mquery.py Dependencies between Power Query queries (table-level)
layout.py Parsing of the internalReport/Layout format
graph_builder.py Orchestrator: builds thenetworkx.DiGraph
navigation.py Upstream/downstream traversal, search, export
api/schemas.py Pydantic request/response models
api/service.py Graph cache, framework-agnostic
api/app.py FastAPI app + MCP mount (FastMCP)

Known limitations

  • M query dependencies are resolved at table level, not step-by-step inside a single let ... in query.
  • A source reached only through a literal M parameter may be tagged with a generic system (http) instead of the exact consumer connector (odata, etc.).
  • Unqualified DAX references ([MeasureName]) are resolved same-table first, then globally; homonyms across tables resolve to the first match.

Development

uv sync --extra dev
uv run pytest     # BDD tests (pytest-bdd) under tests/features/*.feature
uv build          # produces dist/*.whl and dist/*.tar.gz

License

MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

pbix_lineage-0.1.0.tar.gz (16.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

pbix_lineage-0.1.0-py3-none-any.whl (18.7 kB view details)

Uploaded Python 3

File details

Details for the file pbix_lineage-0.1.0.tar.gz.

File metadata

  • Download URL: pbix_lineage-0.1.0.tar.gz
  • Upload date:
  • Size: 16.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.9.26 {"installer":{"name":"uv","version":"0.9.26","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"26.04","id":"resolute","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for pbix_lineage-0.1.0.tar.gz
Algorithm Hash digest
SHA256 f004a24d3d80cf90e5a2cca7411ddbc4574b4155981a3e2813b6c306f1a257ea
MD5 30eee44d99297a520061aa8fb8143dbf
BLAKE2b-256 8a7f60718cc5deda4f8d8a51b040bf958cc31c8a6b6163ed0aa192f1bbcab5b2

See more details on using hashes here.

File details

Details for the file pbix_lineage-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: pbix_lineage-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 18.7 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.9.26 {"installer":{"name":"uv","version":"0.9.26","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"26.04","id":"resolute","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for pbix_lineage-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 30549ccb77bb3bd0eac5b79a56292d4e103767670811665a94a1f56d866f5900
MD5 8ba35f72657c2d6bf92496d0bc7a9f0d
BLAKE2b-256 b43be2df6549b7aa7f22370e750b78dcab4b0cc3f5d61228d227fb397e3f7a4b

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page