内容
# Power BI MCP 服务器
自主托管的 [MCP](https://modelcontextprotocol.io/) 服务器,用于查询 Power BI 语义模型。检索模型架构(表、列、度量值、关系)并执行 DAX 查询 — 所有操作均通过 Power BI REST API 和 Microsoft Fabric API 实现。
使用 [FastMCP](https://github.com/jlowin/fastmcp)(Python)构建。兼容任何 MCP 客户端,包括 Claude Desktop 和 Microsoft Copilot Studio。
## 容量要求
此服务器调用 Power BI REST API(`executeQueries`)和 Microsoft Fabric API(`getDefinition`)。这些 API 仅在以下一种容量中可用的数据集上可用:
| 容量 | SKU | 备注 |
|---|---|---|
| **Power BI Premium** | P1–P5 | 组织范围的专用容量 |
| **Power BI Premium Per User** | PPU | 每用户高级许可证 |
| **Power BI Embedded** | A/EM | Azure 提供的嵌入式容量 |
| **Microsoft Fabric** | F2+ | 任何 Fabric 容量 — 包括完整的 Power BI 语义模型 API 访问权限 |
共享(仅 Pro)容量上的数据集 **将不起作用** — `executeQueries` 和 `getDefinition` 端点返回 403。
## 工作原理
1. 通过 MSAL(基于证书)验证为 Azure AD **服务主体**
2. 通过 Power BI REST API 发现工作区、数据集和报告
3. 通过 Fabric `getDefinition` API(TMDL 格式)获取语义模型定义
4. 解析 TMDL 以提取表、列(带数据类型)、度量值(带 DAX 表达式)和关系
5. 通过 Power BI `executeQueries` REST 端点执行 DAX 查询
所有 API 调用都包含自动重试处理以应对速率限制(HTTP 429)。
## MCP 工具
### 发现
#### `list_workspaces`
列出服务主体可访问的所有 Power BI 工作区。以此作为发现可用数据集和报告的起点。
**输入:** 无
#### `list_datasets`
列出工作区中的数据集(语义模型),或在省略 `workspace_id` 时,列出所有可访问的工作区中的数据集。
**输入:**
- `workspace_id`(字符串,可选) — 限定为单个工作区(更快)。省略以列出所有工作区。
#### `list_reports`
列出工作区中的报告,或在省略 `workspace_id` 时,列出所有可访问的工作区中的报告。
**输入:**
- `workspace_id`(字符串,可选) — 限定为单个工作区(更快)。省略以列出所有工作区。
### 方案和查询
#### `get_semantic_model_schema`
检索 Power BI 语义模型的完整架构:表、列(带数据类型)、度量值(带 DAX 表达式)和关系。使用此功能在编写 DAX 查询前了解模型结构。
**输入:**
- `dataset_id`(字符串,必填) — Power BI 数据集/语义模型 ID
- `workspace_id`(字符串,可选) — 工作区 ID,用于跳过自动检测(更快)
#### `execute_dax_query`
对语义模型执行 DAX 查询,并返回带有列名和行的结构化结果。
**输入:**
- `dataset_id`(字符串,必填) — Power BI 数据集/语义模型 ID
- `dax_query`(字符串,必填) — 要执行的 DAX 查询
### 典型流程
```
list_workspaces → list_datasets(workspace_id) → get_semantic_model_schema(dataset_id) → execute_dax_query(dataset_id, dax_query)
```
## 身份验证
服务器每次请求 Power BI 令牌时,都需要向 Azure AD 证明其身份。对于 **两种身份验证模式**,如果没有有效的凭证,Azure AD 不会颁发任何令牌,服务器也无法访问 Power BI。
### 证书身份验证的工作原理
证书是加密密钥对:**私钥**(秘密,保留在服务器上)和**公钥**(与 Azure AD 共享)。
```mermaid
graph LR
A["<b>您的服务器</b><br>有 cert.pfx<br>(私钥)"] -->|"1. 使用私钥<br>签名请求"| B["<b>Azure AD</b><br>有 cert.pem<br>(公钥)"]
B -->|"2. 使用公钥<br>验证签名"| C{"签名<br>有效?"}
C -->|是| D["颁发 Power BI<br>访问令牌"]
C -->|否| E["拒绝请求"]
```
**文件存储位置:**
| 文件 | 位置 | 用途 |
|---|---|---|
| `cert.pfx` | 您的服务器(本地文件) | 包含私钥。MSAL 使用它来签署身份验证请求。 **请勿分享此文件。** |
| `cert.pem` | 上传到 Azure AD 应用注册 | 包含公钥。Azure AD 使用它来验证请求确实来自您的服务器。 |
**为什么不使用客户端密码?** 客户端密码只是一个密码字符串,存放在您的 `.env` 文件中 — 它可以被复制粘贴,泄露在日志中,或意外提交。证书私钥是一个二进制文件,更难意外暴露,并且 `.env` 只包含文件路径。
**为什么要将公钥上传到 Azure AD?** Azure AD 需要验证您的服务器身份。 当您的服务器使用私钥签署请求时,Azure AD 使用文件中的公钥验证签名。 如果它们匹配,则知道请求是合法的。
### 身份验证模式
```mermaid
graph LR
subgraph "AUTH_MODE=none"
A[MCP 客户端] -->|无身份验证| B[MCP 服务器]
B -->|证书| C[Azure AD]
C -->|服务主体令牌| D[Power BI API]
end
```
```mermaid
graph LR
subgraph "AUTH_MODE=obo"
A[MCP 客户端] -->|持有者令牌| B[MCP 服务器]
B -->|验证 JWT| C[Azure AD JWKS]
B -->|OBO + 证书| D[Azure AD 令牌]
D -->|用户范围的令牌| E[Power BI API]
end
```
#### `AUTH_MODE=none`(默认)
MCP 端点上没有身份验证。 服务器使用服务主体的证书通过客户端凭证访问 Power BI。 所有请求都使用相同的服务主体身份。
**使用场景:** 本地开发、受信任的网络、MCP Inspector 测试。
#### `AUTH_MODE=obo`(推荐用于生产)
MCP 端点需要有效的 Azure AD 持有者令牌。 服务器:
1. 使用 Azure AD 的 JWKS 端点验证令牌(支持 v1.0 和 v2.0 令牌)
2. 通过 On-Behalf-Of(OBO)流程将其兑换为 Power BI 令牌
3. Power BI 访问权限限定为调用用户 — 尊重行级安全(RLS)
**使用场景:** 生产部署、Copilot Studio 集成。
必需的 `.env` 设置:
```
AUTH_MODE=obo
MCP_BASE_URL=https://your-mcp-server.example.com
```
## 先决条件
- **Python 3.12+**,带有 [UV](https://docs.astral.sh/uv/)
- **Azure CLI**(`az`)和 **OpenSSL** 用于设置
- **Azure AD 应用注册**,带有证书凭证
- 语义模型托管在 **Premium、PPU、Embedded 或 Fabric** 容量上
- 服务主体添加为目标 Power BI 工作区的 **成员**
## 快速入门
```bash
# 安装依赖项
uv sync
# 运行设置脚本(创建应用注册、证书、权限)
./setup_azure_auth.sh
# 或手动配置:复制 .env.example 并编辑
cp .env.example .env
# 启动服务器
uv run python main.py
```
服务器在 `http://0.0.0.0:2009` 启动,带有 Streamable HTTP 传输。
## Azure AD 设置
### 自动化(推荐)
设置脚本创建应用注册、服务主体、证书,并配置所有权限,包括 OBO:
```bash
./setup_azure_auth.sh
```
需要 [Azure CLI](https://learn.microsoft.com/en-us/cli/azure/install-azure-cli) 和 `openssl`。 生成 `cert.pfx` 在项目根目录,并将配置写入 `.env`。
### 手动
1. 在 **Azure AD(Entra ID)** 中注册应用
2. 生成自签名证书:
```bash
openssl req -x509 -newkey rsa:2048 -keyout private.key -out cert.pem -days 365 -nodes -subj "/CN=PowerBI-MCP-Server"
openssl pkcs12 -export -out cert.pfx -inkey private.key -in cert.pem -passout pass:
rm private.key
```
3. 将证书上传到应用注册:
```bash
az ad app credential reset --id <your-client-id> --cert @cert.pem --append
```
4. 在 **API 权限**下,添加 **Power BI 服务**(仅限委托权限 — OBO 的最低特权):
| 权限 | 类型 | 所需功能 |
|---|---|---|
| `Dataset.Read.All` | 委托 | 列出/获取数据集、执行 DAX 查询 |
| `Workspace.Read.All` | 委托 | 列出工作区 |
| `Report.Read.All` | 委托 | 列出报告 |
| `SemanticModel.ReadWrite.All` | 委托 | 通过 Fabric `getDefinition` API 获取架构 |
> **为什么架构需要 ReadWrite?** `SemanticModel.Read.All` 不存在。Microsoft 将 `getDefinition` 分类为写入操作(它创建异步作业),尽管它只读取架构数据。 这是最低要求 — 没有只读替代项。
> **不需要应用程序权限。** OBO 模式专门使用委托权限。用户的 Power BI 访问权限决定了他们可以看到的内容。
5. **授予管理员同意和显式范围授予**:
```bash
az ad app permission admin-consent --id <your-client-id>
# 显式范围授予(必需 - 确保 Azure AD 在 OBO 令牌中包含所有范围)
az ad app permission grant --id <your-client-id> \
--api 00000009-0000-0000-c000-000000000000 \
--scope "Dataset.Read.All Report.Read.All Workspace.Read.All SemanticModel.ReadWrite.All"
```
6. 在 **Power BI 管理门户** → 租户设置 → 开发者设置中:
- 启用 **"允许服务主体使用 Power BI API"**
- 启用 **"服务主体可以调用 Fabric 公共 API"**
7. 在 **公开 API** 下:
- 将应用程序 ID URI 设置为 `api://<your-client-id>`
- 添加范围 `access_as_user`(类型:User)
8. 在应用 **清单**中,设置 `requestedAccessTokenVersion: 2`
9. 在您的 **Power BI 工作区** → 设置 → 访问中:
将服务主体添加为 **成员**
> 步骤 7-8 仅需 `AUTH_MODE=obo` 需要。 对于 `AUTH_MODE=none`,工作区成员资格和租户设置足够。
## 配置
所有设置均通过环境变量(`.env`)进行:
| 变量 | 默认值 | 描述 |
|---|---|---|
| `TENANT_ID` | - | Azure AD 租户 ID |
| `CLIENT_ID` | - | Azure AD 应用(客户端)ID |
| `CLIENT_CERT_PATH` | - | PFX 证书文件路径(使用绝对路径) |
| `CLIENT_CERT_BASE64` | - | Base64 编码的 PFX(容器替代路径) |
| `CLIENT_CERT_PASSPHRASE` | - | PFX 密码(可选) |
| `AUTH_MODE` | `none` | 身份验证模式:`none` 或 `obo` |
| `MCP_BASE_URL` | - | 服务器公共 URL(`obo` 需要) |
| `MCP_TRANSPORT` | `http` | 传输类型:`http` 或 `stdio` |
| `MCP_PORT` | `2009` | 服务器端口(HTTP 模式) |
| `LOG_LEVEL` | `INFO` | 日志级别 |
## 本地测试
### 使用 `AUTH_MODE=none` 测试
启动服务器并连接 MCP Inspector - 无需身份验证:
```bash
# 启动服务器
uv run python main.py
# 另一个终端
npx @modelcontextprotocol/inspector http://localhost:2009/mcp
```
MCP Inspector 在您的浏览器中打开。单击 **列出工具** 以查看所有可用工具,然后直接调用它们。
### 使用 curl 测试 OBO
OBO 需要有效的 Azure AD 令牌。以下是完整流程:
**1. 获取令牌:
```bash
# 登录到正确的租户
az login --tenant <your-tenant-id>
# 获取适用于 MCP 服务器的 API 范围的令牌
TOKEN=$(az account get-access-token \
--resource "api://<your-client-id>" \
--query accessToken -o tsv)
```
> **注意:** Azure CLI 通常返回 v1.0 令牌(发行者 `https://sts.windows.net/...`)即使 `requestedAccessTokenVersion` 设置为 2。 服务器接受 v1.0 和 v2.0 令牌。
**2. 初始化 MCP 会话:
```bash
INIT=$(curl -s -D - \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-X POST http://localhost:2009/mcp \
-d '{"jsonrpc":"2.0","method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"test","version":"1.0"}},"id":1}`)
SESSION=$(echo "$INIT" | grep -i mcp-session-id | tr -d '\r' | awk '{print $2}')
echo "Session: $SESSION"
```
**3. 调用工具:
```bash
curl -s \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-H "Mcp-Session-Id: $SESSION" \
-X POST http://localhost:2009/mcp \
-d '{"jsonrpc":"2.0","method":"tools/call","params":{"name":"list_workspaces","arguments":{},"id":2}'
```
### 使用 MCP Inspector 测试 OBO
MCP Inspector 的 CLI `--header` 标志 **不** 将自定义标头转发到 MCP 服务器 - 这是 [已知限制](https://github.com/modelcontextprotocol/inspector/issues/879)。
**解决方法 - 使用 Web UI:
1. 启动 MCP Inspector: `npx @modelcontextprotocol/inspector`
2. 在浏览器 UI 中,选择 **Streamable HTTP** 传输类型
3. 输入服务器 URL: `http://localhost:2009/mcp`
4. 在 **Bearer Token** 字段中,粘贴您的令牌(来自 `az account get-access-token`)
5. 点击 **连接**
**替代方案:** 使用 `AUTH_MODE=none` 在 MCP Inspector 中测试工具,并单独使用 curl 验证 OBO 身份验证。
## 生产部署
### 架构
```mermaid
graph LR
A[Copilot Studio] -->|HTTPS + Bearer token| B[反向代理<br>Traefik / Caddy]
B -->|HTTP :2009| C[MCP Server<br>FastMCP + Python]
C -->|OBO + Certificate| D[Azure AD]
D -->|用户范围的令牌|B Power BI API]
C -->|用户范围的令牌| E[Power BI API]
```
### 要求
- **HTTPS 是必需的** - Copilot Studio 需要受信任的 TLS 证书(自签名证书无效)
- **公共 DNS** - 您的服务器需要一个域名,可以从 Microsoft 的云中访问
- **端口 443** - 在您的路由器/防火墙中打开
### 部署在 Coolify 上(Nixpacks)
Coolify 自动检测 `pyproject.toml` 和 `uv.lock` 中的 Python/UV 项目:
1. 在 Coolify 中创建新服务,指向您的 Git 仓库
2. Coolify/Nixpacks 自动运行 `uv sync --no-dev --frozen`
3. 设置启动命令: `uv run python main.py`
4. 在本地 base64 编码您的证书(运行以下命令):
```bash
base64 -i cert.pfx | tr -d '\n'
```
5. 在 Coolify 的 UI 中配置环境变量:
```
TENANT_ID=<your-tenant-id>
CLIENT_ID=<your-client-id>
CLIENT_CERT_BASE64=<粘贴步骤 4 中的 base64 字符串>
CLIENT_CERT_PASSPHRASE=
AUTH_MODE=obo
MCP_BASE_URL=https://your-domain.com
MCP_TRANSPORT=http
MCP_PORT=2009
LOG_LEVEL=INFO
```
> 使用 `CLIENT_CERT_BASE64` 代替 `CLIENT_CERT_PATH` - 服务器在启动时将 base64 字符串解码为临时文件。无需文件挂载。
6. 在 Coolify 中分配您的域名 - Traefik 自动处理 TLS 终止和 Let's Encrypt 证书
7. 在 Coolify 的网络设置中,将暴露的端口设置为 `2009`
### 替代方案:使用 Caddy 作为反向代理
如果不使用 Coolify 内置的 Traefik:
```
your-domain.com {
reverse_proxy localhost:2009
}
```
Caddy 自动配置和续期 Let's Encrypt 证书。
## Copilot Studio 连接
### 先决条件(Azure AD 应用注册)
在连接 Copilot Studio 之前,您的应用注册需要进行以下配置:
1. **重定向 URI** - 在 **身份验证 → Web → 重定向 URI** 下添加:
```
https://token.botframework.com/.auth/web/redirect
```
> 需要第二个重定向 URI 以供您的特定连接器使用。在第一次登录尝试时,Azure AD 将显示类似 `AADSTS50011` 的错误,并显示确切的 URI - 添加该 URI。
2. **Copilot Studio 的客户端密码** - Copilot Studio 需要其自己的凭证来交换身份验证代码与 Azure AD。这是独立于 MCP 服务器的证书:
```bash
az ad app credential reset --id <your-client-id> \
--append --display-name "Copilot Studio" --years 1 \
--query password -o tsv
```
保存输出 - 您将在 Copilot Studio 中粘贴它。
3. **验证以下内容是否已设置**(来自 Azure AD 设置部分):
- 标识符 URI:`api://<your-client-id>`
- 暴露的范围:`access_as_user`
- `requestedAccessTokenVersion: 2` 在应用清单中
- 委托权限:Power BI `Dataset.Read.All`
- 管理员同意已授予
### 连接您的代理
1. 在 Copilot Studio 中,确保 **生成式编排** 已启用在您的代理上
2. 转到 **工具 → 添加工具 → 新工具 → MCP**
3. 输入您的服务器 HTTPS URL:`https://<your-domain>/mcp`
4. 选择 **OAuth 2.0** → **手动**
5. 填写 OAuth 配置:
| 字段 | 值 |
|---|---|
| **客户端 ID** | `<your-client-id>` |
| **客户端密码** | 上一步骤中输出的密码 |
| **授权 URL** | `https://login.microsoftonline.com/<your-tenant-id>/oauth2/v2.0/authorize` |
| **令牌 URL** | `https://login.microsoftonline.com/<your-tenant-id>/oauth2/v2.0/token` |
| **刷新 URL** | 与令牌 URL 相同 |
| **范围** | `api://<your-client-id>/access_as_user` |
6. 点击 **创建**
7. 当提示时,请登录 - Azure AD 将验证您并授予权限
8. Copilot Studio 自动发现服务器上的所有工具
> **重要提示:** Copilot Studio 需要 **Streamable HTTP** 传输类型。SSE 传输类型已弃用。
### 工作原理
```mermaid
sequenceDiagram
participant User
participant CopilotStudio as Copilot Studio
participant AzureAD as Azure AD
participant MCP as MCP Server
participant PBI as Power BI API
User->>CopilotStudio: 提问关于数据
CopilotStudio->>AzureAD: OAuth 流(客户端密码 + 用户登录)
AzureAD-->>CopilotStudio: Azure AD 令牌(范围:access_as_user)
CopilotStudio->>MCP: 工具调用 + 授权:Bearer <令牌>
MCP->>AzureAD: 验证令牌(JWKS)
MCP->>AzureAD: OBO 交换(证书 + 用户令牌)
AzureAD-->>MCP: Power BI 令牌(用户范围)
MCP->>PBI: API 调用,使用用户的 Power BI 令牌
PBI-->>MCP: 数据(尊重用户的 RLS 权限)
MCP-->>CopilotStudio: 结果
CopilotStudio-->>User: 基于他们的数据回答
```
**关键点:** Copilot Studio 中的客户端密码仅用于 Copilot Studio 与 Azure AD 通信。MCP 服务器从未看到它 - 它使用自己的 PFX 证书进行身份验证。两个独立的凭证,两种不同的用途。
## MCP 客户端配置
### Claude Desktop
将以下内容添加到您的 Claude Desktop MCP 配置文件 (`claude_desktop_config.json`):
```json
{
"mcpServers": {
"powerbi": {
"url": "http://localhost:2009/mcp"
}
}
}
```
## 测试
```bash
uv run pytest
```
## 许可
MIT
MCP 配置
以下是此 MCP Server 的配置信息,可直接复制到 Cursor 或其他 MCP 客户端中使用。
mcp.json
连接信息
猜你喜欢
暂无推荐服务器