feat(tools): add Power BI integration - initial structure with workspace and dataset refresh functions

This commit is contained in:
Your hh3538962
2026-02-10 13:23:32 +05:00
parent 0cd6f21980
commit e07703c01f
3 changed files with 159 additions and 0 deletions
@@ -0,0 +1,13 @@
# Power BI Tool
Power BI integration for dataset refresh and report management.
## Features
- Get list of workspaces
- Refresh datasets
- Export reports (coming soon)
## Authentication
Requires Power BI access token via credential store or environment variable.
@@ -0,0 +1,5 @@
"""Power BI Tool - Dataset refresh and report export."""
from .power_bi_tool import register_tools
__all__ = ["register_tools"]
@@ -0,0 +1,141 @@
"""
Power BI Tool - Dataset Refresh and Report Export
Supports Power BI REST API for:
- Refreshing datasets
- Exporting reports
- Managing workspaces
API Reference: https://learn.microsoft.com/en-us/rest/api/power-bi/
"""
from __future__ import annotations
import os
from typing import TYPE_CHECKING, Any
import httpx
from fastmcp import FastMCP
if TYPE_CHECKING:
from aden_tools.credentials import CredentialStoreAdapter
POWER_BI_API_BASE = "https://api.powerbi.com/v1.0/myorg"
class _PowerBIClient:
"""Internal client wrapping Power BI REST API calls."""
def __init__(self, access_token: str):
self._token = access_token
@property
def _headers(self) -> dict[str, str]:
return {
"Authorization": f"Bearer {self._token}",
"Content-Type": "application/json",
}
def _handle_response(self, response: httpx.Response) -> dict[str, Any]:
"""Handle common HTTP error codes."""
if response.status_code == 401:
return {"error": "Invalid or expired Power BI access token"}
if response.status_code == 403:
return {"error": "Insufficient permissions"}
if response.status_code == 404:
return {"error": "Resource not found"}
if response.status_code == 429:
return {"error": "Rate limit exceeded"}
if response.status_code >= 400:
try:
detail = response.json().get("message", response.text)
except Exception:
detail = response.text
return {"error": f"Power BI API error (HTTP {response.status_code}): {detail}"}
return response.json()
def get_workspaces(self) -> dict[str, Any]:
"""Get list of Power BI workspaces."""
response = httpx.get(
f"{POWER_BI_API_BASE}/groups",
headers=self._headers,
timeout=30.0,
)
return self._handle_response(response)
def refresh_dataset(self, workspace_id: str, dataset_id: str) -> dict[str, Any]:
"""Trigger a dataset refresh."""
response = httpx.post(
f"{POWER_BI_API_BASE}/groups/{workspace_id}/datasets/{dataset_id}/refreshes",
headers=self._headers,
timeout=30.0,
)
return self._handle_response(response)
def register_tools(
mcp: FastMCP,
credentials: CredentialStoreAdapter | None = None,
) -> None:
"""Register Power BI tools with the MCP server."""
def _get_token() -> str | None:
"""Get Power BI access token from credential manager or environment."""
if credentials is not None:
token = credentials.get("power_bi")
if token is not None and not isinstance(token, str):
raise TypeError(f"Expected string from credentials, got {type(token).__name__}")
return token
return os.getenv("POWER_BI_ACCESS_TOKEN")
def _get_client() -> _PowerBIClient | dict[str, str]:
"""Get a Power BI client, or return an error dict if no credentials."""
token = _get_token()
if not token:
return {
"error": "Power BI credentials not configured",
"help": "Set POWER_BI_ACCESS_TOKEN environment variable or configure via credential store",
}
return _PowerBIClient(token)
# --- Workspaces ---
@mcp.tool()
def power_bi_get_workspaces() -> dict:
"""
Get list of Power BI workspaces.
Returns:
Dict with list of workspaces or error
"""
client = _get_client()
if isinstance(client, dict):
return client
try:
return client.get_workspaces()
except httpx.TimeoutException:
return {"error": "Request timed out"}
except httpx.RequestError as e:
return {"error": f"Network error: {e}"}
@mcp.tool()
def power_bi_refresh_dataset(workspace_id: str, dataset_id: str) -> dict:
"""
Refresh a Power BI dataset.
Args:
workspace_id: The Power BI workspace ID
dataset_id: The dataset ID to refresh
Returns:
Dict with success status or error
"""
client = _get_client()
if isinstance(client, dict):
return client
try:
return client.refresh_dataset(workspace_id, dataset_id)
except httpx.TimeoutException:
return {"error": "Request timed out"}
except httpx.RequestError as e:
return {"error": "Network error: {e}"}