# dsh-tool-grafana

[English](./README.md) | 简体中文

面向 [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness)（dsh）的 Grafana 能力插件。

本插件**不拥有任何 Grafana 工具逻辑**。它把社区版
[`mcp-grafana`](https://github.com/grafana/mcp-grafana) 服务器作为子插件，挂载在
[`@deepseek-ai/dsh-mcp-client`](https://www.npmjs.com/package/@deepseek-ai/dsh-mcp-client)
之上——模型看到的 `mcp__grafana__*` 工具与任何 MCP 客户端（Cursor、Claude Code 等）完全一致——并在此之上叠加纯 MCP 桥接无法提供的三层增量：

1. **统一的连接配置Owner**：URL、服务账号令牌、工具类别选择、只读模式，替代裸环境变量透传。
2. **常驻系统提示段落**（`grafana:workflow`，order 115）：把上游的上下文窗口纪律固化为指令——先看摘要再拉全量 JSON、用 JSONPath 读局部、查询前先发现数据源。
3. **内嵌运行时 skill**（`grafana-troubleshooting`）：系统化的"告警 → 仪表盘 → 指标 → 日志 → incident"排障流程，模型和用户两个入口都可调用。

```
┌─ dsh-tool-grafana（本插件）──────────────────────┐
│  配置校验 · 提示段落 · skill                      │
└──────────────┬─────────────────────────────────────┘
               │ ctx.plugin(dsh-mcp-client, plan)
               ▼
┌─ mcp-grafana（社区 Go 二进制，stdio）─────────────┐
│  mcp__grafana__search_dashboards                  │
│  mcp__grafana__query_prometheus / loki / ...      │
└───────────────────────────────────────────────────┘
```

同一个 `mcp-grafana` 二进制仍可直接被 Cursor、Claude Code、OpenClaw 等使用——一份核心，两个生态。

## 安装到 profile

```sh
dsh plugin --profile web add dsh-tool-grafana
```

这就是安装的全部：包内置 bundle patch（`cordis.patch.yml` + `dsh.bundle` 声明），插件以 schema 默认值自动挂载——无需手写 YAML。随后打开
**Settings → Plugins → Grafana observability** 完成设置：填写 Grafana URL 和服务账号令牌，重启 profile 即可。

### 自定义配置（可选）

如需钉住工具类别、launcher 或不用卡片的默认值，在 `~/.dsh/profiles/web/cordis.patch.yml` 中加一条同 `id` 行——profile 层会覆盖包内 bundle 层（后写生效）：

```yaml
- insert:
    - id: grafana
      name: 'dsh-tool-grafana'
      config:
        url: https://myinstance.grafana.net   # 默认值；设置卡片可覆盖
        # 令牌通过 DSH 凭证库按此引用解析——永不内联到本文件：
        tokenCredentialRef: GRAFANA_SERVICE_ACCOUNT_TOKEN
        readOnly: true
        categories: [search, datasource, dashboard, prometheus, loki, alerting]
        launcher:
          kind: uvx
          version: '0.7.9'   # 钉住上游版本；留空则跟踪最新
        # kind: binary
        # path: /usr/local/bin/mcp-grafana   # go install github.com/grafana/mcp-grafana/cmd/mcp-grafana@vX.Y.Z
```

卡片值存储在 DSH 设置文档 / 凭证库中，并**按字段覆盖 patch 配置**；patch 里的 `url` 作为兜底默认值保留。

## 配置项

| 字段 | 默认值 | 含义 |
|---|---|---|
| `url` | `''` | 默认 Grafana 地址；设置卡片的值（web）会覆盖它。两者皆空则激活失败并给出指引 |
| `tokenCredentialRef` | `GRAFANA_SERVICE_ACCOUNT_TOKEN` | 通过 `ctx.credentials` 解析的凭证引用（见下文）。设计上不存在内联令牌字段 |
| `categories` | 上游默认集* | mcp-grafana 的 `--enabled-tools` 类别 |
| `readOnly` | `true` | `--disable-write` 的默认值；设置卡片开关可覆盖（web） |
| `launcher` | `{kind: 'uvx', version: ''}` | 服务器进程启动方式；钉住 `version`（uvx）或使用带版本的 binary/docker 镜像，避免上游漂移 |
| `env` | `{}` | 合并进服务器进程的额外环境变量 |
| `toolCallTimeoutMs` | `60000` | 单次工具调用超时（dsh-mcp-client） |

\* `search, datasource, dashboard, folder, prometheus, loki, alerting,
incident, oncall, sift, navigation, annotations`。重量级后端
（`clickhouse`、`cloudwatch`、`snowflake`、`athena`、`elasticsearch`、
`quickwit`、`graphite`、`influxdb`）以及 `runpanelquery`/`examples`/
`agento11y`/`assistant` 组保持按需开启。

## 凭证

令牌通过 DSH 凭证接缝（`ctx.credentials`）解析，而非读取插件自己的环境变量。使用默认本地提供者（`dsh-credentials-local`）时，第一个已配置的来源生效：

1. **启动环境** — `GRAFANA_SERVICE_ACCOUNT_TOKEN=… dsh web`
2. **托管存储** — `$DSH_HOME/.credentials.yaml`（Web Models 页写入的位置；可在 DSH 内写）
3. **项目 `.env`**（调用目录下）
4. **用户 `.env`**（`$DSH_HOME/.env`）

不存在内联令牌配置项，因此令牌不可能被提交进 `cordis.patch.yml`，也不会通过 `--dump-config` 输出泄露。

**连接变更在 profile 重启后生效。** 令牌和 URL 在激活时解析一次并注入 mcp-grafana 进程环境；stdio MCP 服务器启动后无法接收环境更新。（像 `dsh-grafana` 仪表盘编辑器那样的原生工具插件在每次工具调用时解析、可以实时生效——这是"组合优于重写"的取舍。）

## Web 设置卡片

包内置 `dsh.client` 入口（`client.js`），在 **Settings → Plugins → Grafana observability** 下挂载卡片：

- **Service Account Token** — 写入凭证库（仅写不读；卡片显示掩码，永不显示明文）
- **Grafana URL** — 存于 `grafana` settings namespace，保存后明文回显核对
- **只读开关** — 存于同一 namespace

优先级按字段独立生效：**卡片值 > patch 配置 > 激活报错**。

## 开发

```sh
npm install
npm run build   # tsc → lib/
npm test        # node --test（单元 + 真实 Cordis 集成）
```

集成测试在真实 Cordis 上下文、真实 `dsh-system-prompt` 与 `dsh-skill` 服务下启动本插件，仅对子插件 mcp-client 的挂载打桩，因此不需要真实 Grafana。

## 设计说明

- **为什么不原生重写这些工具？** 社区服务器提供 60+ 工具、按类别的开关、认证处理和活跃的上游维护。重写买不到任何东西，只会分叉维护负担。
- **为什么不直接用 `dsh-mcp-client`？** 可以——一行 YAML 就能跑。本插件存在的意义是 MCP 表达不了的增量：受校验的配置、提示词层面的使用纪律、打包的工作流知识。
- **令牌卫生**：令牌只会进入子进程环境，永不进入提示词、工具输出或日志（在本包的 `./invariant` 中声明）。

## License

MIT
