A remote Model Context Protocol (MCP) server that exposes Power BI operations as tools for AI assistants. Built with the official Microsoft MCP C# SDK and served over Streamable HTTP.
- What it demonstrates: A typed, remote MCP integration over enterprise analytics rather than a local toy server.
- Tools: Workspace and dataset discovery, schema inspection, and DAX query execution.
- Identity:
DefaultAzureCredentialsupports local development, service principals, workload identity, and managed identity. - Engineering: .NET 8, Streamable HTTP, structured error handling, tests, and client configuration for VS Code.
| Tool | Description | Parameters |
|---|---|---|
list_workspaces |
List all Power BI workspaces accessible to the authenticated user | — |
list_datasets |
List datasets in a workspace | workspaceId |
list_tables |
List tables and columns in a dataset (via DAX COLUMNSTATISTICS()) |
workspaceId, datasetId |
execute_dax |
Execute a DAX query via REST API | workspaceId, datasetId, daxQuery |
- .NET 8 SDK
- ASP.NET Core 8 Runtime (
aspnetcore-runtime-8.0) - Azure CLI (
az login) or a service principal for authentication - A Power BI Pro/PPU/Premium workspace
# 1. Clone and enter the repo
git clone <repo-url> && cd pbi-mcp
# 2. Log in to Azure (use your Power BI tenant)
az login --tenant <your-tenant-id>
# 3. Update appsettings.json with your tenant ID
# (already configured if you cloned this repo)
# 4. Build and run
dotnet runThe server starts at http://localhost:3001/mcp.
{
"PowerBi": {
"TenantId": "your-tenant-id-here"
}
}The TenantId is passed to DefaultAzureCredential to ensure tokens are acquired for the correct tenant.
The server uses DefaultAzureCredential, which tries these sources in order:
| Priority | Source | Use case |
|---|---|---|
| 1 | AZURE_TENANT_ID / AZURE_CLIENT_ID / AZURE_CLIENT_SECRET env vars |
Service principal / CI |
| 2 | Workload Identity | Kubernetes |
| 3 | Managed Identity | Azure VMs, App Service, ACA |
| 4 | Azure CLI (az login) |
Local development |
az login --tenant <your-tenant-id>
dotnet runexport AZURE_TENANT_ID="your-tenant-id"
export AZURE_CLIENT_ID="your-sp-app-id"
export AZURE_CLIENT_SECRET="your-sp-secret"
dotnet runNo code changes needed — DefaultAzureCredential picks up the env vars automatically.
The repo includes .vscode/mcp.json:
{
"servers": {
"pbi-mcp": {
"type": "http",
"url": "http://localhost:3001/mcp"
}
}
}Start the server with dotnet run, then Copilot will discover the tools automatically.
npx @modelcontextprotocol/inspectorConnect to http://localhost:3001/mcp using the Streamable HTTP transport.
PbiMcpServer.csproj # ASP.NET Core Web project
Program.cs # Entry point — DI, MCP server, HTTP transport
PowerBiService.cs # Power BI REST API + XMLA service layer
PowerBiTools.cs # MCP tool definitions (4 tools)
appsettings.json # Tenant ID + logging config
.vscode/mcp.json # VS Code MCP client config
tests/
PbiMcpServer.Tests/
PowerBiServiceUnitTests.cs # 11 offline tests (mocked HTTP)
PowerBiServiceIntegrationTests.cs # 5 live API tests
XunitLoggerProvider.cs # xUnit ↔ ILogger bridge
# Unit tests (offline, fast — no credentials needed)
dotnet test tests/PbiMcpServer.Tests/PbiMcpServer.Tests.csproj \
--filter "Category!=Integration"
# Integration tests (hits live Power BI API — requires az login)
dotnet test tests/PbiMcpServer.Tests/PbiMcpServer.Tests.csproj \
--filter "Category=Integration"
# All tests
dotnet test tests/PbiMcpServer.Tests/PbiMcpServer.Tests.csproj
# With detailed HTTP request/response logging
dotnet test tests/PbiMcpServer.Tests/PbiMcpServer.Tests.csproj \
--filter "Category=Integration" \
--logger "console;verbosity=detailed"All DAX queries use the Power BI REST API (POST /datasets/{id}/executeQueries). This works with any Pro/PPU/Premium workspace — no XMLA endpoint required.
The XMLA path (via ADOMD.NET) is included in the codebase but commented out. It requires Premium/PPU capacity with XMLA read enabled by a tenant admin.
The list_tables tool uses EVALUATE COLUMNSTATISTICS() via the REST DAX endpoint rather than the REST /tables endpoint, because /tables only works for push datasets. The DAX approach works for all dataset types (import, DirectQuery, composite).
This project requires both:
Microsoft.NETCore.App— base .NET runtime (console, I/O, networking)Microsoft.AspNetCore.App— web runtime (Kestrel, routing, middleware)
Install with: sudo apt install aspnetcore-runtime-8.0 (includes both).
- .NET 8 / ASP.NET Core
- ModelContextProtocol.AspNetCore — Official MCP C# SDK with Streamable HTTP transport
- Azure.Identity —
DefaultAzureCredentialfor token acquisition - Microsoft.AnalysisServices.AdomdClient — XMLA/DAX connectivity
- xUnit + Moq — Testing
This project is licensed under the MIT License.