powerbi-plus
by rajivdatta
README.md
# powerbi-plus
An MCP server for **building and inspecting Power BI report visuals** by editing
the report's PBIR files on disk. It complements the model-layer MCPs (XMLA/TOM)
by owning the layer they can't reach: pages, charts, slicers, cards, layout.
## Why two layers
Power BI automates in two very different ways:
| Layer | What | How reached |
|---|---|---|
| **Semantic model** | tables, columns, measures, relationships, DAX | **live** via the Analysis Services / XMLA endpoint while Desktop is open |
| **Report / visuals** | pages, charts, slicers, layout, formatting | **file-based** — no live canvas API |
This server is the **report layer**. It reads and edits the per-visual JSON
files of a report saved in **PBIR** format. Changes land on disk; **Power BI
Desktop picks them up when the project is reopened/reloaded** — there is no
supported way to draw on a live session's canvas.
## Prerequisite: save your report as PBIP with PBIR format
A classic `.pbix` stores the report as an opaque blob this server can't safely
edit. One-time conversion in Power BI Desktop:
1. **File → Options and settings → Options → Preview features** — enable:
- **Power BI Project (.pbip) save option**
- **Store reports using enhanced metadata format (PBIR)**
2. Restart Desktop if prompted.
3. **File → Save as → Power BI project (.pbip)**.
You get a folder tree:
```
<Project>.pbip
<Project>.Report/
definition.pbir
definition/
report.json
pages/
pages.json
<pageName>/
page.json
visuals/
<visualName>/visual.json ← one file per visual
<Project>.SemanticModel/
```
`open_project` reports the format; if it says anything other than `PBIR`,
finish the conversion above before inspecting/editing.
## Install
```powershell
cd C:\path\to\mcp-powerbi-plus
python -m venv .venv
.\.venv\Scripts\python.exe -m pip install -r requirements.txt
```
### Register the server
Add the entry below to your MCP config, fixing the two paths to point at this
checkout's `.venv` python and `server.py`. Use **double backslashes** in JSON on
Windows, and write the file as **UTF-8 without a BOM** (a BOM breaks the JSON
parser). Restart the client afterward — MCP servers load at launch.
```json
{
"mcpServers": {
"powerbi-plus": {
"command": "C:\\path\\to\\mcp-powerbi-plus\\.venv\\Scripts\\python.exe",
"args": [
"C:\\path\\to\\mcp-powerbi-plus\\server.py"
]
}
}
}
```
Where the config lives, by client:
| Client | Config file | Notes |
|---|---|---|
| **Claude Desktop** | `%APPDATA%\Claude\claude_desktop_config.json` (Windows) · `~/Library/Application Support/Claude/claude_desktop_config.json` (macOS) | Merge `powerbi-plus` into the top-level `mcpServers`. Fully **quit and relaunch** the app (closing the window isn't enough — use *File → Exit* / the tray icon). |
| **Claude Code** | `~/.claude.json` | Add to the top-level `mcpServers`, or run `claude mcp add`. Restart the CLI session. |
A ready-to-edit snippet is in [`examples/mcp.json`](examples/mcp.json). Once
registered, the tools appear as `powerbi-plus`'s tools (e.g.
`mcp__powerbi-plus__open_project`).
## Config (`config.json`)
Pure filesystem — no Azure auth.
| Key | Meaning |
|---|---|
| `writable` | gates future create/update/delete/theme tools. Read/inspect always work. |
| `default_project` | optional path used when a tool's `project` arg is omitted; a `.pbip` file or `*.Report` folder. |
## Tools — read / inspect
| Tool | Does |
|---|---|
| `open_project` | format (PBIR vs legacy blob), name, dataset pointer, theme, page count, active page |
| `list_pages` | pages in order: internal name, displayName, size, visual count, active flag |
| `inspect_page` | visuals on a page: name, type, title, position, bound roles |
| `inspect_visual` | one visual in full: type, title, position, field bindings per role, formatting objects, filters |
| `list_model_fields` | tables / columns (+dataType) / measures from the sibling `*.SemanticModel` — use as binding `entity`/`property` |
## Tools — write (gated by `"writable": true`)
| Tool | Does |
|---|---|
| `add_visual` | create a visual on a page (type + field bindings + position + title) |
| `update_visual` | edit an existing visual in place (type / bindings / title / position) |
| `move_visual` | reposition / resize (x / y / width / height / z) |
| `delete_visual` | remove a visual |
| `add_page` | create a page and register it in `pages.json` |
| `delete_page` | remove a page + its visuals, reassign active page |
| `generate_page` | create a page and **auto-lay-out** a list of visuals on a column grid in one call |
| `format_visual` | set formatting on a visual (background, title, legend, dataColors…), merging into existing objects |
| `apply_theme` | apply a custom report theme — update an existing registered theme in place, or stage a new one |
`project` is accepted on every tool and may be a `.pbip` file, the `*.Report`
folder, the `definition` folder, or a parent dir holding one `*.Report`. Omit it
to use `default_project`.
### Binding shape
`bindings` maps a visual **role** to one field spec or a list of them:
```jsonc
{
"Category": { "entity": "DimRegion", "property": "Region" }, // column (default)
"Y": { "entity": "FactSales", "property": "Total Sales", "kind": "measure" },
"Values": { "entity": "FactSales", "property": "Amount", "aggregation": "sum" } // wraps a column
}
```
Roles by visual: chart → `Category` + `Y`; `card` → `Values`; matrix
(`pivotTable`) → `Rows` + `Columns` + `Values`; `slicer` → `Values`.
`aggregation` ∈ sum / avg / min / max / count / countNonNull / median / stdev / var.
> Edits land on disk. **Close the report in Power BI Desktop before writing, then
> reopen the project** to see changes — there is no live-canvas push.
### `generate_page` layout
Items flow left-to-right across a column grid (default 12 cols) and wrap to a
new row when full; a row's height is its tallest item. Per item: `colSpan`
(grid width, default half), `height` (points). An item with an explicit
`position` is pinned and skips the flow. Optional `layout` overrides
`{columns, margin, gutter, rowHeight}`. The whole spec is validated and built
in memory **before** the page is created — an invalid spec writes nothing.
### `format_visual` value encoding
`formatting` is `{object: {prop: value}}`. Values auto-encode to PBIR property
expressions: bool → `true/false`, int → whole-number literal (`14L`), float →
decimal (`50.5D`), `"#RRGGBB"` or a color-named prop → solid color, other text →
quoted string. Force a type with `{"type":"color|text|int|number|bool","value":…}`
or pass a ready node with `{"raw": {…}}`. `target` auto-routes container objects
(title/background/border/shadow/…) to `visualContainerObjects` and the rest to
`objects`; override with `"objects"` / `"container"`.
### `apply_theme` — what's actually supported
PBIR is in preview, and **registering a brand-new resource by external edit is
not supported** (it needs a `report.json` change Desktop owns). So:
- **Theme already registered** → `apply_theme` overwrites it (or deep-merges
with `merge: true`). Reopen Desktop to load. ✅ supported.
- **No theme registered yet** → the file is *staged* under
`StaticResources/RegisteredResources`; register it once via Desktop **View →
Themes → Browse for themes**, after which updates work in place. The returned
`supported` flag tells you which path ran.
### `list_model_fields`
Reads the report's semantic model (resolved via `definition.pbir` `byPath`, or a
single sibling `*.SemanticModel`) and returns its tables, columns (with
`dataType`), and measures — so you can bind visuals to fields that actually
exist. TMDL and legacy `model.bim`/TMSL are both parsed; hidden fields are
excluded unless `include_hidden: true`. Reports on a live (`byConnection`)
dataset have no local model files — query those via the `powerbi-modeling` or
`powerbi` MCP instead.
## Status
All 14 tools are verified against a synthetic PBIR fixture (every write
round-trips through the read layer). **Not yet validated against a report
opened in Power BI Desktop** — schema details like literal suffixes (`L`/`D`),
the title bucket, and theme registration should be confirmed on a real `.pbip`
before relying on the write tools in production.
## License
MIT.
This server cannot be deployed
Maintenance
ActivityInactive
ResponsivenessNo issues