内容
# 数据简报
> 仪表盘是拉取(你访问它们)。定时分页报告是推送
> (弥补了访问仪表盘的摩擦力)。代理返回我们到
> 拉取 — 但没有仪表盘。调度器、SMTP 服务器、接收者列表总是
> 从数据必须 *到达* 用户的假设中产生的不必要复杂性。
一个由 Databricks 支持的 MCP 服务器,将配置驱动的 SQL 转换为任意 MCP 代理可以调用的工具。
与捆绑的 `data-briefing` 技能配对,代理生成一份按需的高质量 HTML + PDF 简报。
MCP 是自带代理的;技能是特定的客户端。
## 工作原理
```
[代理:Claude Desktop / Claude Code / Cursor / ...]
│
│ 用户输入 /data-briefing
▼
[data-briefing 技能]
│ 读取 SKILL.md,调用每个 MCP 工具,获取 JSON 数据
│ { rows, columns, hints, revenue_chart, ... } ← 服务器端预渲染的命名 SVG
│
│ 组装 ~/Briefings/YYYY-MM-DD-briefing.json
│ { executive_summary, kpi_snapshot, reports[], watch_outs, actions }
│
│ 调用 MCP render_briefing 工具(服务器端 Mustache → HTML)
│ 写入 ~/Briefings/YYYY-MM-DD-briefing.html
│
│ 调用系统 Chrome / Edge 以无头模式运行
│ chrome --headless --print-to-pdf=... file://...
▼
~/Briefings/YYYY-MM-DD-briefing.{json, html, pdf}
```
MCP 服务器端:
```
[FastAPI / FastMCP 应用,作为 Databricks 应用部署]
/health (REST)
/api/v1/reports (REST 列表,用于人类发现)
/api/v1/render (REST:简报 JSON → HTML 文档)
/mcp (可流式 HTTP)
├── <report_name> 每个配置文件 *.yaml 的一个工具
├── ... 在启动时动态注册
└── render_briefing 静态:run_id → HTML
OBO 通过 X-Forwarded-Access-Token (每个请求,缓存 60 秒)
ConnectorCache 平衡仓库会话预热
Altair + vl-convert 将每个报告的命名图表渲染为内联 SVG
每个报告的 html_template 片段通过 chevron 在服务器端渲染
```
## 设计原则
- **拉取,而不是推送。** 没有调度器,没有电子邮件,没有接收者列表。
用户在需要时触发;可以免费覆盖即席参数。
- **MCP 返回结构化数据 + SVG.** 文本是代理的工作;
结构是技能的工作;数据是服务器的工作。
- **仪表盘的消亡。** 没有 Web UI。产物是一个可移植的
HTML/PDF,存放在 `~/Briefings/` 中。
- **配置驱动。** 一个新报告是一个 YAML 文件。新图表是 Vega-Lite
规格在该 YAML 中。无需后端代码更改。
- **OBO 端到端。** 每个简报都在调用用户的身份上运行;
他们只看到他们在 SQL 中可以读取的数据。
- **自带代理用于 MCP,特定技能用于产物。** 任何
MCP 代理都可以驱动服务器;技能强制执行固定的
JSON 形状和 Mustache 模板以实现视觉一致性。
## 仓库布局
```
app/ FastAPI + FastMCP + 图表渲染 + HTML 渲染
├─ main.py 生命周期,安装 MCP 在 /mcp
├─ mcp_server.py 动态工具注册(每个 YAML + render_briefing)
├─ registry.py YAML 加载 + QuerySpec + ChartSpec + HintsSpec + Pydantic 参数模型
├─ runner.py 多查询执行,命名图表 SVG,行限制,html_template 渲染
├─ viz.py Vega-Lite → SVG 通过 Altair + vl-convert
├─ hints.py 阈值突破,顶部/底部
├─ cache.py TTL+LRU 连接器缓存(每个请求 OBO 持有者)
├─ sql.py databricks-sql-connector 包装器,:name 参数
├─ auth.py X-Forwarded-Email / X-Forwarded-Access-Token
├─ render.py chevron(template.html, briefing) → HTML
├─ templates/
│ └─ template.html Mustache 高质量页面(渲染合约)
└─ rest_routes.py /health, /api/v1/reports, /api/v1/render
configs/ YAML 报告定义(每个文件一个工具)
├─ _schema.md 配置模式参考(加载器忽略)
└─ *.yaml 每个文件在启动时成为一个键入的 MCP 工具
skill/ 捆绑特定客户端(一个 Claude 技能)
├─ SKILL.md 工作流程 + JSON 模式;调用 Chrome 以生成 PDF
├─ SKILL-data-only.md 数据优先变体,专注于结构化 JSON 输出
└─ mcp-setup.md 每个代理安装片段
scripts/install_skill.sh 将 skill/ → ~/.claude/skills/data-briefing/ 建立符号链接
databricks.yml DAB:应用 + 仓库 + OBO 范围
```
## 快速入门(本地)
```bash
uv sync --extra dev
cp .env.example .env
# 首选:设置 DATABRICKS_CONFIG_PROFILE(在 `databricks auth login` 后)
# 和 DATABRICKS_WAREHOUSE_ID。配置文件解析主机 + 凭证。
# 或旧版:设置 DATABRICKS_HOST + DATABRICKS_TOKEN + DATABRICKS_WAREHOUSE_ID。
uv run uvicorn app.main:app --reload --port 8000
# 在另一个 shell 中:
curl localhost:8000/api/v1/reports
```
本地身份验证优先级(由 `app/sql.py` 通过 `databricks-sdk` 解析):
1. **OBO 持有者** — `X-Forwarded-Access-Token`(仅限 Databricks 应用)。
2. **`DATABRICKS_CONFIG_PROFILE`** — 来自 `~/.databrickscfg` 的配置文件,
由 `databricks auth login -p <profile>` 填充。适用于 PAT、
OAuth U2M 或服务主体配置文件。
3. **`DATABRICKS_TOKEN` + `DATABRICKS_HOST`** — PAT 回退。
将 MCP 客户端指向 `http://localhost:8000/mcp` — 请参见 `skill/mcp-setup.md`。
## 添加报告
在 `configs/` 中丢弃一个 YAML 文件(参见 `configs/_schema.md`)。声明一个或多个
`queries:`(查询名称为 `rows` 是主要的),可选 `charts:`(命名
Vega-Lite 规格 — 无 `data` 块),和可选的 `html_template:` Mustache
片段。命名图表 SVG 通过 `{{{chart_name}}}` 注入。在下一次重启时,
报告作为一个键入的 MCP 工具出现。
旧的 `sql:` 和 `chart:` 快捷方式仍然被接受并自动规范化 —
现有的配置文件不需要更改。
## 部署为 Databricks 应用
```bash
databricks bundle validate -t dev
databricks bundle deploy -t dev
```
DAB 声明 `user_api_scopes: [sql, catalog.*]`,因此 OBO 无需额外设置即可工作。
应用提取每个请求的 `X-Forwarded-Access-Token`
并在调用用户的身份上运行 SQL。
## 安装技能
```bash
bash scripts/install_skill.sh
```
然后在一个全新的 Claude Code(或其他代理)会话中,输入 `/data-briefing` —
或“给我执行数据简报”。
## 示例报告
`configs/` 中有两个报告,在启动时作为 MCP 工具公开:
- **`bakehouse_briefing`** — `samples.bakehouse` 上的 4 页执行布局
(客户 + 特许经营 + 供应商)。 KPI 瓦片,每日收入趋势,
顶级特许经营,国家 / 产品组合,供应商排行榜。
- **`spend_briefing`** — IT 领导者成本报告,将 `system.billing.usage`
连接到 `system.billing.list_prices` 以获取 USD。用于 SQL、AI/Agentic、
作业/ETL、交互式、应用、Lakebase 的瓦片;堆积面积每日趋势;
仓库 / 作业 / 工作区排行榜。
技能通过调用报告工具,将 `run_id` 传递给 `render_briefing` 以获取 HTML,
将其写入 `~/Briefings/`,然后驱动无头 Chrome 以获取兄弟 PDF:
```bash
chrome --headless --no-pdf-header-footer \
--print-to-pdf=~/Briefings/YYYY-MM-DD-<report>.pdf \
file://~/Briefings/YYYY-MM-DD-<report>.html
```
客户端上没有 Python 或 Playwright — 只有 Chrome(或 Edge)。
## 限制(v1)
- **应用权限默认情况下是开放的** 在 `databricks.yml`
(`user_name: "*"`)。OBO 按用户划分数据范围,但任何工作区用户都可以
调用工具并使用仓库计算资源。根据 `group_name`
进行生产限制。
- **连接器缓存是每个进程的,内存中的.** 短 TTL LRU 池,平衡报告 + 渲染
工具调用在一个流程中的 SQL 仓库会话预热。多个副本,每个副本都有自己的池。
- **报告默认返回最多 `BRIEFING_MAX_ROWS=1000` 行。**
更大的结果集被截断,信封设置 `truncated: true`
和 `total_rows: <n>`。
- **MissingAuthError 作为结构化信封出现,** 而不是 HTTP 500。
如果 Databricks 应用代理不转发 `X-Forwarded-Access-Token`
(并且没有设置本地 `DATABRICKS_TOKEN`),工具返回
`{report, error: "auth_missing", message: ...}`。
- **PDF 渲染通过客户端系统 Chrome / Edge 进行.** 服务器
渲染 HTML;技能调用 Chromium 家族浏览器以无头模式运行
(`--print-to-pdf`)。如果找不到浏览器,HTML 仍然被写入,用户可以打开并另存为 PDF。
## 验证
```bash
uv run pytest tests/ # 47 个测试
uv run ruff check .
```