powerbi-mcp-local
Click on "Install Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@powerbi-mcp-localadd a measure for total revenue"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.

powerbi-mcp-local
Local-first MCP server for Power BI Desktop automation
Automate semantic model changes, DAX, Power Query, Excel, and report layout from MCP-capable AI clients.
Quick Links
Start | Setup | Tools | Security |
Related MCP server: Excel-Power-Pivot-MCP
What It Does
Connects AI tools to a running local Power BI Desktop engine.
Automates tables, columns, measures, relationships, DAX, refreshes, and Power Query.
Reads/writes Excel files for local BI workflows.
Extracts, patches, validates, and compiles report layouts through
pbi-tools.
No Power BI Pro license is required for the local Desktop workflow.
Architecture
MCP Client --(stdio or sse)--> src/server.py
|
+-- src/mcp_core.py FastMCP instance, _run, lifecycle
+-- src/wrappers/ 14 thin wrappers/<domain>.py
+-- src/tools/ business logic (the *_tool fns)
+-- TOM/.NET ─────────> Power BI Desktop local SSAS
+-- ADOMD ────────────> DAX query execution
+-- openpyxl ─────────> Excel read/write/format
+-- pbi-tools ────────> report extract/compile + visuals
+-- src/security.py path, DAX, payload safeguardsFull module layering and the visuals/ submodule fan-out: see ARCHITECTURE.md.
Requirements
Requirement | Install |
Windows | Power BI Desktop local engine is Windows-only |
Power BI Desktop |
|
Python 3.11+ |
|
.NET 6+ Runtime |
|
pbi-tools |
|
ADOMD.NET ships with Power BI Desktop. If pbi-tools is not on PATH, set PBI_TOOLS_PATH.
Quick Start
git clone https://github.com/imnotStealthy/powerbi-mcp-local.git
cd powerbi-mcp-local
pip install -r requirements.txtOpen Power BI Desktop with a .pbix file, then verify connectivity:
python tests/test_connection.pyStart the MCP server:
python src/server.pyUseful launch modes:
python src/server.py --transport sse --port 8765
python src/server.py --readonly
python src/server.py --profile readonly # ~56 read tools
python src/server.py --profile write # readonly + writes (no destructive)
python src/server.py --profile grading # 25-tool surface for evaluation workflows
python src/server.py --profile all # default — every toolFor SSE auth:
$env:PBI_MCP_AUTH_TOKEN = "your-secret-token"
python src/server.py --transport sse --port 8765Clients must send:
Authorization: Bearer your-secret-tokenMCP Client Setup
Standard stdio config:
{
"mcpServers": {
"powerbi": {
"command": "python",
"args": ["C:\\path\\to\\powerbi-mcp-local\\src\\server.py"]
}
}
}Google Antigravity (Gemini-based IDE) ships a stricter MCP client that
drops the connection at resources/list against the default FastMCP
1.27.x stdio server. Since v0.12.1 the project ships a dedicated entry
point — src/server_antigravity.py — that strips the capability
advertisement to tools only, forces UTF-8 / \n stdio, and routes
every log to stderr at ERROR level. The default src/server.py is left
untouched for Claude Desktop / Cursor / Anthropic CLI.
The PowerShell wrapper at tools/antigravity_mcp_launcher.ps1 pins the
working directory and exports PYTHONUTF8=1 / PYTHONIOENCODING=utf-8
before invoking the venv Python on server_antigravity.py, so the
encoding stays stable regardless of how Antigravity spawns the process.
%USERPROFILE%\.gemini\antigravity\mcp_config.json:
{
"mcpServers": {
"powerbi": {
"command": "powershell.exe",
"args": [
"-NoProfile",
"-ExecutionPolicy",
"Bypass",
"-File",
"D:\\Users\\stealthy\\Documents\\GitHub\\powerbi-mcp-local\\tools\\antigravity_mcp_launcher.ps1",
"--profile",
"readonly"
]
}
}
}Adjust the -File path to your clone. --profile readonly keeps
tools/list small while validating the client connection — switch to
--profile all (or omit the flag) once Antigravity lists tools
successfully. --readonly and --profile {readonly,write,all,grading}
are all forwarded through the wrapper to the entry point with the same
semantics as src/server.py.
Local compatibility probe:
$init = @(
'{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"antigravity-probe","version":"1.0"}}}',
'{"jsonrpc":"2.0","method":"notifications/initialized","params":{}}'
)
($init + '{"jsonrpc":"2.0","id":2,"method":"resources/list","params":{}}') -join "`n" |
powershell.exe -NoProfile -ExecutionPolicy Bypass -File .\tools\antigravity_mcp_launcher.ps1 --profile readonly
($init + '{"jsonrpc":"2.0","id":3,"method":"tools/list","params":{}}') -join "`n" |
powershell.exe -NoProfile -ExecutionPolicy Bypass -File .\tools\antigravity_mcp_launcher.ps1 --profile readonlySSE endpoint:
http://localhost:8765/sseSetup guides:
Example Prompts
Connect to Power BI and list all tables with columns.Create a measure called Total Sales in table Sales.Run this DAX query and show top 20 rows.Extract report, add a new page, place 3 visuals, then compile.
Tool Catalog
141 MCP tools are grouped into these areas. Visual creation is consolidated behind pbi_add_visual (28 chart types via visual_type) and pbi_add_visual_from_intent:
Area | Coverage |
Model discovery | instances, tables, measures, relationships, metadata, validation |
Model mutations | measures, columns, tables, relationships, formats, role-based DAX |
Query and import | DAX execution, traces, validation, refresh, model export |
Power Query (M) | read, write, create, import, bulk Excel/folder sources |
PBIP/TMDL | list, read, write, and patch TMDL project files |
Workflows | model audit, Excel import, measure workflow automation |
Quality gates | DAX linting, visual checks, persistence, scenarios, report validation |
RLS and calc groups | roles, filters, members, calculation groups |
Excel | workbook, sheet, cell/range, formatting, search, Power BI import checks |
Reports and visuals | extract, compile, pages, cards, charts, slicers, themes, dashboards |
Unified visual creation is available through:
pbi_add_visual(visual_type, config)In-place binding edits (no remove + recreate) via:
pbi_update_visual_bindings(extract_folder, page, visual_id,
projections={"Y": ["Total Sales"]})Force Power BI Desktop to flush in-memory TOM mutations to the PBIX file (opt-in UI automation):
$env:PBI_MCP_ALLOW_UI_AUTOMATION = "1" # set before launching the serverpbi_persist_now(pbix_path="C:\\reports\\sales.pbix", confirm=true)Automation Flow
Excel input -> Power Query -> model updates -> measures -> validation -> report layout -> compile PBIXCommon tool chain:
excel_write_range
pbi_create_import_query
pbi_create_relationship
pbi_create_measure
pbi_refresh
pbi_execute_dax
pbi_extract_report
pbi_build_dashboard
pbi_compile_reportTroubleshooting
Symptom | Fix |
| Install .NET 6+ runtime, then restart terminal |
| Open a |
| Add it to |
| Close Excel; workbook files are locked while open |
Path blocked by policy | Configure |
Security
Built-in safeguards include:
local path restrictions and traversal protection
DAX/DMV unsafe-query guards
Power Query SSRF protections (cloud DW + SaaS + reflection blocked by default)
export redaction controls
zip safety checks (Excel + PBIX)
response-size cap (16 MiB by default) + per-minute rate limit (600/min)
SSE Bearer auth + DNS-rebinding host/origin allowlist
tool-call auditing
Details: SECURITY.md
Environment variables (quick reference)
Variable | Default | Purpose |
| unset | SSE Bearer token (≥32 chars when set) |
| unset | Extra Host/Origin allowlist for SSE |
|
| Acknowledge non-loopback SSE without auth |
|
| Block all write/destructive tools |
|
| Allow |
|
| Bypass the M function blocklist |
|
| Required for |
|
| Fall back from PostMessage to SendInput |
|
|
|
| cwd |
|
| unset | Path or inline JSON for |
|
| Tool registry audit (CI) |
Development
pip install -e ".[dev]"
pytest -q
ruff check src tests
ruff format --check src testsCI runs pytest --cov=src --cov-fail-under=54 on Windows + an offline subset on Ubuntu, plus ruff lint + format check, on every PR. Strict registry audit (PBI_MCP_STRICT_REGISTRY=1) ensures every public pbi_*_tool has a matching @mcp.tool() wrapper.
Repository Layout
powerbi-mcp-local/
├── src/
│ ├── server.py CLI + transport launcher + @mcp.resource/@mcp.prompt (~320 L)
│ ├── mcp_core.py FastMCP instance + CONNECTION_MANAGER + lifecycle (~250 L)
│ ├── pbi_connection.py TOM + ADOMD bring-up, write helpers, op history
│ ├── security.py path / DAX / payload guards + tool category sets
│ ├── wrappers/ 14 domain modules — `register_tool(pbi_*_tool)` calls
│ └── tools/ business logic (*_tool functions)
│ └── visuals/ 17 focused submodules (layout, bindings, containers, charts, …)
├── tests/ 167 offline unit tests (live-only scripts gitignored)
├── .github/workflows/ci.yml pytest + coverage + ruff on Windows + Ubuntu, py3.11/3.12
├── docs/, specs/
├── ARCHITECTURE.md module layering, visuals/ tree, profiles, registry audit
├── CHANGELOG.md active changelog (last 3 releases)
├── CHANGELOG-archive.md historical changelog
├── SECURITY.md
├── pyproject.toml ruff, pytest, coverage config + dev deps
└── requirements.txtLicense
MIT
Maintenance
Latest Blog Posts
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/imnotStealthy/powerbi-mcp-local'
If you have feedback or need assistance with the MCP directory API, please join our Discord server