Skip to main content
Glama
App Data MCP

App Data MCP

by jedshen123
README.md
# App Data MCP

这是一个面向内部数据平台的 MCP 服务。Metabase、PostHog 等元信息同步到 PostgreSQL,由管理员在后台决定是否对 MCP 用户开放,让 Codex、Claude Code 等 AI 客户端完成资产发现、详情查看、数据出处追踪和受控取数。

## 当前能力

- `search_assets`: 搜索看板、卡片、Model、insight、指标、表、事件。
- `get_asset`: 查看单个资产完整元信息。
- `trace_asset`: 查看资产的 SQL / 事件 / 上游表 / 原始链接。
- `run_asset`: 只读返回支持资产的真实数据;不支持时读取本地 `sampleData` 兜底。
- `query_audience`: 使用统一 `uid` 对 2-10 个 Metabase Model 做交集、并集或差集计算。
- `export_audience`: 将完整 UID 人群导出为有期限的服务端 CSV 下载文件。
- `query_starrocks`: 治理资产无法回答后的受控 StarRocks SQL 回退;执行前会再次搜索 Metric/Model/Card。
- `list_domains`: 查看配置里已有的业务域。
- `auth_status`: 查看当前请求用户和 Metabase 授权状态。
- `catalog_status`: 查看当前个人 MCP 用户可见的元信息数量和分类统计。
- `connector_status`: 查看连接器配置状态和只读访问策略。

## 只读访问策略

这个 MCP 只做只读数据网关,不提供任何创建、更新、删除、写回能力。

允许的操作:

- 搜索和读取 PostgreSQL 中已开放的元信息。
- 读取看板、卡片、insight、指标口径和来源链接。
- 追踪上游表、事件、SQL、原平台 URL。
- 执行 Metabase/PostHog 查询时,只允许调用读取类 API,并限制为只读查询结果。
- 执行 StarRocks 查询时,只允许单条 `SELECT`、`WITH ... SELECT`、`SHOW`、`DESCRIBE/DESC` 或 `EXPLAIN`。

禁止的操作:

- 创建、编辑、删除 Metabase dashboard/card/question。
- 创建、编辑、删除 PostHog dashboard/insight/cohort/action。
- 写数据库、执行 DDL/DML、保存 AI 生成的 SQL 到平台。
- 通过 MCP 修改任何业务数据、平台配置或权限配置。

后续新增 connector 或 tool 时,命名和实现都必须维持这个边界。`connector_status` 会返回 `accessMode: "read-only"`,用于让 AI 客户端确认当前服务策略。

## 数据量限制

默认会限制返回数据量,避免 AI 平台一次拉取过大的结果。

- `search_assets` 默认且最多向 AI 返回 5 个资产;服务端内部仍可召回更多候选,并完整写入审计日志。
- `run_asset` 默认返回 100 行,最多 500 行。
- 单次 MCP 响应默认最大约 1 MB,超过会返回 `response_too_large`,提示缩小查询范围。
- `query_starrocks` 与 `run_asset` 共用行数上限;同时在 StarRocks 会话设置 `sql_select_limit` 和 `query_timeout`。

可以通过环境变量调整,但建议生产环境保持保守:

```bash
DATA_DEFAULT_SEARCH_LIMIT=5
DATA_MAX_SEARCH_LIMIT=50
DATA_DEFAULT_RESULT_ROW_LIMIT=100
DATA_MAX_RESULT_ROW_LIMIT=500
DATA_MAX_RESPONSE_BYTES=1000000
```

Metabase/PostHog connector 会继续在返回侧做行数和响应字节限制。`connector_status` 会返回当前 `dataLimits`。

## 真实数据读取

`run_asset` 现在会优先调用平台只读 API 获取真实数据:

- `metabase:card:*` / `metabase:model:*` / `metabase:metric:*`: 调用 Metabase card query endpoint;Model 和 Metric 在 Metabase API 中同样通过 card query 执行。
- `metabase:dashboard:*`: 读取 dashboard 下的卡片,并按 dashboard 筛选器映射逐个执行卡片查询。
- `posthog:insight:*`: 调用 PostHog insight read endpoint,并请求刷新/返回当前结果。

仍然不支持写入操作,也不会保存查询、修改看板或更新 insight。

注意:

- Dashboard 取数会限制最多执行 20 张卡片,可用 `DATA_MAX_DASHBOARD_CARDS` 调整。
- 每张卡片/insight 结果继续受 `DATA_MAX_RESULT_ROW_LIMIT` 和 `DATA_MAX_RESPONSE_BYTES` 限制。
- 资产声明了 `parameters` 时,可以给 `run_asset` 传维度/日期参数;未声明的友好参数会被拒绝,避免 AI 临时拼接不受控条件。
- Metabase dashboard 会读取 `parameter_mappings`,只把已经映射到对应 card 的筛选参数下发,并在返回里给出每张 card 的 `parameterMappingStatus`。
- 如果 live connector 失败且资产里配置了 `sampleData`,会回退返回 sampleData。
- 如果既无法 live 查询又没有 sampleData,会返回 `live_connector_failed` 或 `asset_not_runnable`。

Metabase 友好参数示例:

```json
{
  "asset_id": "metabase:card:81",
  "params": {
    "date": {
      "from": "2026-07-01",
      "to": "2026-07-09"
    },
    "country": "US"
  },
  "limit": 100
}
```

### Model / Metric / Card 指标集语义查询

`run_asset.semantic` 可以在不修改 Metabase 对象、不拼接 SQL 的情况下,对已经同步的 Model、Metric 和声明为 `semantic.role=metric_set` 的 Card 进行受控分析。所有字段都必须来自 `get_asset` 返回的治理元数据,服务端会校验字段引用、操作符、数量上限和数据类型。

Metric 保持已经治理的公式不变,只允许追加筛选和替换拆分维度:

```json
{
  "asset_id": "metabase:metric:480",
  "semantic": {
    "filters": [
      { "field": "country_ad_ch", "operator": "eq", "value": "CN" }
    ],
    "breakouts": [
      { "field": "data_source" },
      { "field": "date(t1.event_date)", "unit": "month" }
    ]
  },
  "limit": 100
}
```

传入 `"breakouts": []` 可以移除 Metric 的默认时间分组并返回总值;省略 `breakouts` 则保留 Metric 原有默认分组。

Model 可以选择明细字段:

```json
{
  "asset_id": "metabase:model:479",
  "semantic": {
    "fields": ["uid", "data_source", "date(t1.event_date)"],
    "filters": [
      { "field": "is_active_on_date", "operator": "eq", "value": 1 }
    ]
  },
  "limit": 100
}
```

也可以动态聚合和拆分:

```json
{
  "asset_id": "metabase:model:479",
  "semantic": {
    "filters": [
      { "field": "is_active_on_date", "operator": "eq", "value": 1 }
    ],
    "aggregations": [
      { "operator": "distinct", "field": "uid", "alias": "有效用户数" }
    ],
    "breakouts": [
      { "field": "data_source" }
    ]
  },
  "limit": 100
}
```

Card 指标集由管理员在资产编辑页通过可视化表单维护:可以直接为同步到的 Card 输出列勾选“维度”或“指标”,配置基础粒度、默认时间维度、时间粒度、单位、同义词、Rollup、重算分子/分母和运行累计。后台仍保存下面的结构化语义覆盖;“高级:JSON 预览与导入”只用于排障或批量迁移,不要求日常手写。

```json
{
  "role": "metric_set",
  "baseGrain": ["stat_date", "region"],
  "defaultTimeDimension": {
    "field": "stat_date",
    "defaultUnit": "day",
    "supportedUnits": ["day", "week", "month"],
    "timezone": "Asia/Shanghai",
    "dateMeaning": "支付成功日期"
  },
  "dimensions": [
    { "field": "stat_date", "label": "统计日期" },
    { "field": "region", "label": "地区" }
  ],
  "measures": [
    {
      "name": "revenue",
      "label": "支付金额",
      "synonyms": ["成交金额"],
      "unit": "CNY",
      "rollup": {
        "strategy": "sum",
        "allowedGroupBy": ["stat_date", "region"]
      },
      "timeAggregation": {
        "metricType": "flow",
        "strategy": "sum",
        "defaultMode": "require_explicit",
        "reason": "每笔支付只归属一个支付成功日期"
      },
      "cumulative": {
        "supported": true,
        "strategy": "running_sum"
      }
    },
    {
      "name": "paid_users",
      "label": "支付用户数",
      "rollup": {
        "strategy": "forbidden",
        "reason": "基础结果已经去重,跨日期或地区相加会重复计算用户"
      },
      "timeAggregation": {
        "metricType": "distinct_count",
        "strategy": "forbidden",
        "defaultMode": "latest_date",
        "reason": "同一用户可能出现在多个日期"
      }
    }
  ]
}
```

查询 Card 指标集:

```json
{
  "asset_id": "metabase:card:123",
  "semantic": {
    "measures": ["revenue"],
    "timeScope": {
      "mode": "range",
      "start": "2026-07-01",
      "end": "2026-07-31",
      "unit": "day"
    },
    "resultMode": "cumulative_trend",
    "breakouts": [{ "field": "region" }]
  },
  "limit": 100
}
```

每个 Card 指标都必须配置 `timeAggregation`,不再通过 `breakouts` 或旧配置猜测时间语义。查询必须使用 `timeScope`,或者由指标的 `defaultMode` 提供默认范围;`resultMode=value/total/trend/cumulative_trend` 分别表示单值、跨期总量、时间趋势和累计趋势。服务端只允许 `sum` 或 `recompute` 指标生成跨期总量,只允许 `sum` 且声明 `cumulative.running_sum` 的指标生成累计趋势;快照和日去重人数不会被静默跨日期相加。

`timeAggregation.metricType` 提供四类管理预设:`flow` 使用 `sum`,`snapshot` 使用 `latest`,`distinct_count` 使用 `recompute_distinct` 或 `forbidden`,`ratio` 使用 `recompute`。其中“最新日期”是时间选择,不是对指标取 `max`;`latest_date` 会先执行 `max(date) where date <= 业务今天 - completeLagDays`,再用所得日期筛选正式指标查询。`completeLagDays` 默认是 `1`(T+1),所以不会选择今天;若昨天尚无数据,则自动回退到更早的最大可用日期。管理页中的累计开关仅表示“允许生成累计趋势(running sum)”,不控制普通跨日期汇总。

筛选操作符包括 `eq`、`neq`、`gt`、`gte`、`lt`、`lte`、`in`、`not_in`、`contains`、`is_null`、`not_null`、`between`;Model 聚合包括 `count`、`distinct`、`sum`、`avg`、`min`、`max`。语义查询与 `params` 不能同时使用,普通 Card、Dashboard 和 PostHog Insight 不接受 `semantic`。

### 用户人群组合查询

Metabase 同步会为包含 `uid` 字段的 Model 生成 `audience` 元数据。`query_audience` 要求所有输入 Model 位于同一个 Metabase database,并使用当前用户的 Metabase Session 在 `/api/dataset` 内完成集合计算。每个 Model 会先在数据源侧应用筛选并按 `uid` 去重,再执行集合连接,不会先把各 Model 的 UID 拉回 MCP 客户端。

```json
{
  "operator": "intersection",
  "models": [
    {
      "asset_id": "metabase:model:101",
      "filters": [
        { "field": "event_time", "operator": "gte", "value": "2026-07-01" }
      ]
    },
    {
      "asset_id": "metabase:model:102",
      "filters": [
        { "field": "topic", "operator": "eq", "value": "母婴" }
      ]
    }
  ],
  "output": "count",
  "limit": 100
}
```

`operator` 支持 `intersection`、`union` 和 `difference`;`difference` 表示第一个 Model 减去后续所有 Model。`output=count` 默认只返回去重用户数;`output=uids` 返回去重 UID,并受全局最大行数和响应大小限制。每个 Model 最多包含 20 个受控语义筛选条件。

需要完整 UID 文件时使用 `export_audience`,不要循环调用有限的 `query_audience` 结果:

```json
{
  "operator": "intersection",
  "models": [
    { "asset_id": "metabase:model:493", "filters": [{ "field": "deleted", "operator": "eq", "value": 0 }] },
    { "asset_id": "metabase:model:496", "filters": [{ "field": "status", "operator": "eq", "value": 1 }] }
  ],
  "filename": "community-active-users.csv"
}
```

服务端通过 Metabase CSV endpoint 获取完整的单列 UID 结果,生成随机 capability 下载地址,默认 24 小时后失效,并由后台定时清理。超过行数或文件字节上限时整个导出失败,不会生成截断文件。下载地址相当于临时访问凭证,不应公开分享。

### 治理资产优先与 SQL 回退

新数据主题或上下文中尚未确定可用资产时,先调用 `search_assets`。同一会话的后续问题如果只是修改已选资产支持的时间范围、筛选条件、分组维度、排序或返回范围,应复用已有 `asset_id` 和 `get_asset` 元数据,直接调用 `run_asset`;只有主题改变、已有资产缺少所需能力、执行失败或上下文中没有可靠资产信息时才重新搜索。`question` 始终原样保留当前轮用户问题用于审计。调用方 AI 应优先通过 `query_spec` 提交一个原子化、可独立理解的结构化数据需求:保留指标、业务对象、时间、筛选、拆分、比较和分析类型,只删除工具名称、礼貌语及回答格式要求;显式语义、会话补全和不确定推断分别标记为 `explicit`、`conversation` 和 `inferred`。`retrieval_queries` 只提供别名、事件名、缩写或替代表达用于扩召回,不改变硬能力要求。旧的 `query`、`resolved_query`、`expansion_queries` 和 `queries[]` 保持兼容;长报告仍通过 `queries[]` 按章节拆成独立数据问题。排序会直接学习当前同步到目录中的 Metabase 字段、参数、Metric 公式/维度、Card 指标集语义,以及 PostHog 的分析类型、事件、属性和 breakdown:先计算语义覆盖、关键业务词覆盖、时间/拆分/明细能力、元信息完整度和风险惩罚,再结合问题意图与治理优先级形成综合可用性评分。普通指标、趋势和拆分查询优先选择能力确实覆盖问题的指标集 Card 或 Metric;明细查询优先 Model,总览优先 Dashboard,固定报表优先 Card,漏斗/留存/路径分析优先匹配分析类型的 PostHog Insight。AI 首次使用所选资产时仍需用 `get_asset` 核对字段、维度、参数和公式。返回值中的 `selection.intent`、`recommendedAssetId`、`candidateOrder`,以及每项资产的 `selection.suitability/reasons/missingCapabilities/evaluation` 会解释推荐结果。只有管理后台已开放且有效的资产会参与搜索和治理检查。

管理后台的“检索匹配规则”工作台可配置业务关键词到平台、资产类型和 PostHog 分析类型的路由策略。`Boost` 模式仅增加 0–40 分,多个规则累计仍封顶 40;`Prefer` 模式会把通过语义与能力门槛的目标资产放入优先组,没有合格目标资产时可回退其他平台。规则支持 All of、Any of、None of、启停、优先级和实时问题预览。默认内置三条可编辑策略:行为分析优先 PostHog、页面/功能/访问/曝光/点击等行为日活优先 PostHog Trends,以及整体/业务/口径/报表语境的经营日活优先 Metabase。命中规则还会生成受控的核心词召回扩展,防止规则在候选召回前失效;最终推荐仍使用完整原问题进行关键业务词和能力校验。

`search_assets` 使用“权限过滤 → 结构化能力匹配 → 全量评分 → 治理与路由重排 → 执行前核验”链路。当前目录规模下,所有已发布、当前用户可见且符合平台、类型和业务域范围的资产都会参与主问题评分;`retrieval_queries` 只保留为召回与审计证据,不参与硬能力判定。提供 `query_spec` 时,服务端直接使用结构化指标、业务对象、拆分维度、筛选字段和时间要求;未提供时继续使用旧的规则分类器兼容现有客户端。能力结果分为 `supported`、`verification_required` 和 `unsupported`:只有治理元数据能够明确证明不支持的资产才会被过滤;元数据不足但可能正确的候选会返回给 AI,并要求先通过 `get_asset` 核验。指标查询中的 Model 只有声明 `modelSemantic.aggregationPolicy=guarded` 才具备聚合候选资格。每个返回候选都带有 `requirements.termClassifications`、`capabilityMatch.status` 和 `executionVerification`,AI 必须完成对应核验清单后再调用 `run_asset`。

Model 的聚合授权按字段和函数的精确组合配置。`count` 不传字段表示 `COUNT(*)`,由 `allowRowCount` 单独控制;传字段表示 `COUNT(field)`。`distinct` 表示 `COUNT(DISTINCT field)`。例如:

```json
{
  "role": "detail_dataset",
  "aggregationPolicy": "guarded",
  "baseGrain": ["event_id"],
  "primaryTimeField": "event_time",
  "allowRowCount": true,
  "aggregationRules": [
    {
      "field": "uid",
      "operators": ["count", "distinct"],
      "capabilities": {
        "distinct": {
          "label": "APP用户数",
          "synonyms": ["用户数", "APP人数"]
        }
      }
    },
    { "field": "amount", "operators": ["sum", "min", "max"] },
    { "field": "event_time", "operators": ["min", "max"] }
  ]
}
```

旧的 `entityFields` 和 `additiveFields` 配置仍分别兼容为 `distinct`、`sum` 授权;管理后台再次保存时会统一写入 `aggregationRules`。

`allowRowCount` 不依赖 `baseGrain`:即使没有声明每行粒度,也可以执行 `COUNT(*)`。此时结果只能解释为物理记录数;只有明确声明了每行的业务粒度后,才可以进一步说明这些记录代表订单、事件等业务对象。用户、设备等实体数量通常应使用已授权字段的 `distinct`。

管理后台的“启用受控聚合”开关直接对应 `aggregationPolicy=guarded`,不再单独展示聚合策略下拉框。每行粒度从 Model 输出字段中选择;主时间字段和可选的强制过滤条件收在“高级配置”中。关闭开关会删除该 Model 的聚合语义配置,使其回到默认仅明细状态。

每个已授权的字段/聚合函数组合会自动产生保守的检索能力,例如 `distinct(uid)` 根据“用户ID”推导“用户数、去重用户数”,`count(uid)` 只推导“用户ID非空数”,`count(*)` 只表示“记录数”。管理员可以在 `capabilities` 中补充业务名称和搜索别名;配置值优先用于检索,但不会改变实际聚合函数。管理后台会展示自动识别结果,业务名称和别名均为可选。

当 AI 在 Model 上使用 `semantic.aggregations` 重新计算 `count`、`distinct`、`sum` 等指标时,`run_asset` 会进行第二次服务端检查:

- 必须传入用户原始 `question`,否则返回 `asset_question_required`。
- 如果仍有匹配的 Metric 或 Card,返回 `higher_priority_asset_available` 并拒绝执行 Model。
- AI 必须逐个 `get_asset` 检查候选 Metric 与 Card;确认不适用后,把所有相关 ID 放入 `rejected_asset_ids` 并提供具体 `fallback_reason`。
- 拒绝 Metric 或 Card 却不说明原因时返回 `fallback_reason_required`。
- Model 的 `semantic.fields` 明细查询不属于重新计算指标,不受这项拦截影响。

例如只有在治理 Metric 和现有 Card 都无法覆盖用户要求的维度时,才允许这样显式降级:

```json
{
  "asset_id": "metabase:model:492",
  "question": "查询按实验分组拆分的中国地区绑定设备用户数",
  "rejected_asset_ids": ["metabase:metric:483", "metabase:card:530"],
  "fallback_reason": "Metric 483 没有实验分组维度,Card 530 也未提供该筛选参数",
  "semantic": {
    "filters": [{ "field": "country_ad_ch", "operator": "eq", "value": "中国" }],
    "aggregations": [{ "operator": "distinct", "field": "uid", "alias": "user_count" }],
    "breakouts": [{ "field": "experiment_group" }]
  }
}
```

`query_starrocks` 要求同时传入用户原始问题,并在执行 SQL 前由服务端再次搜索治理资产:

```json
{
  "question": "最近15天有效绑定M9设备的社区活跃用户数趋势",
  "sql": "select ...",
  "purpose": "data_question",
  "limit": 100
}
```

如果存在匹配的 Metric/Model/Card,服务端返回 `governed_assets_available`、候选资产和下一步说明,且 `sqlExecuted=false`。AI 应先使用 `get_asset` 和 `run_asset`。检查后确认候选资产不适用,才可显式拒绝并回退:

```json
{
  "question": "最近15天有效绑定M9设备的社区活跃用户数趋势",
  "sql": "select ...",
  "purpose": "data_question",
  "rejected_asset_ids": ["metabase:metric:480", "metabase:model:479"],
  "fallback_reason": "候选资产没有所需的实验组字段",
  "limit": 100
}
```

`purpose=user_requested_sql` 只能用于用户明确要求直接执行 SQL 的场景;`purpose=metadata_inspection` 只允许 `SHOW`、`DESCRIBE/DESC` 和 `EXPLAIN`。

Metabase dashboard 参数执行结果会包含筛选器覆盖情况:

```json
{
  "requestedParameters": ["date", "country"],
  "parameterCoverage": [
    {
      "parameter": "date",
      "mappedCardCount": 3,
      "mappedCards": [
        {
          "cardId": "81",
          "title": "APP 日活"
        }
      ]
    }
  ],
  "cards": [
    {
      "cardId": "81",
      "parameterMappingStatus": {
        "status": "partially_mapped",
        "requestedParameters": ["date", "country"],
        "appliedParameters": ["date"],
        "unmappedParameters": ["country"],
        "mappedParametersAvailable": ["date"]
      }
    }
  ]
}
```

Metabase 原生参数仍可透传:

```json
{
  "asset_id": "metabase:card:81",
  "params": {
    "parameters": [
      {
        "type": "date/range",
        "target": ["variable", ["template-tag", "date"]],
        "value": "2026-07-01~2026-07-09"
      }
    ]
  }
}
```

PostHog insight 支持常见只读覆盖参数:

```json
{
  "asset_id": "posthog:insight:abc123",
  "params": {
    "date_from": "-30d",
    "date_to": "now",
    "breakdown": "country",
    "properties": [
      {
        "key": "country",
        "value": "US",
        "operator": "exact",
        "type": "event"
      }
    ]
  }
}
```

PostHog 参数不会再作为裸 `date_from` / `properties` 查询参数发送。服务端会读取已经同步并由管理员开放的
PostHog 公共属性,将显示名称、翻译或同义词解析成原生字段名,并校验是否允许过滤/分组。普通筛选使用 Insight
Retrieve API 的 `filters_override` 参数;动态分组基于已保存 Insight 构造临时只读 Query API 请求。未开放、
已下线、歧义、禁止过滤或禁止分组的属性会被拒绝。

例如管理员可以把 `$geoip_country_code` 配置为“国家代码”,并将“国家码、country”维护为同义词;调用方随后
可以在 `properties[].property` 或 `breakdown` 中使用任意一个已治理名称。原生 `properties[].key` 继续兼容,
但也必须对应一条有效且已开放的公共属性。

## StarRocks 自助 SQL

`query_starrocks` 仅用于治理资产无法回答问题后的 SQL 回退。服务端强制执行下面的顺序:

1. 新主题或没有可复用资产时先调用 `search_assets`;同一主题已检查过的资产可直接复用。
2. 能由治理资产回答时使用 `get_asset` 和 `run_asset`。
3. 没有合适资产、已明确拒绝候选资产,或用户明确要求 SQL 时,才使用 `query_starrocks`。
4. 不熟悉表结构时,先执行 `SHOW TABLES`、`DESCRIBE table_name` 或 `SHOW CREATE TABLE table_name`。
5. 根据真实字段生成带明确日期范围和过滤条件的 `SELECT`,避免扫描无关数据。

示例:

```json
{
  "question": "最近15天活跃用户数趋势",
  "sql": "SELECT dt, count(*) AS active_users FROM ads_app_daily WHERE dt >= '2026-07-01' GROUP BY dt ORDER BY dt",
  "purpose": "data_question",
  "limit": 100
}
```

服务会拒绝 DDL、DML、多语句、`INTO OUTFILE`、`LOAD_FILE`、`FILES`、`SLEEP`、可执行注释和自定义查询 Hint 等危险或消耗型 SQL。每次执行还会设置 StarRocks 当前会话的查询超时与最大返回行数,并继续受 MCP 响应字节数限制。

StarRocks 配置放在 `.env`,不要写入元信息表:

```bash
STARROCKS_HOST=127.0.0.1
STARROCKS_PORT=9030
STARROCKS_USER=app_data_mcp_reader
STARROCKS_PASSWORD=your-password
STARROCKS_DATABASE=your_database
STARROCKS_CONNECTION_LIMIT=10
STARROCKS_CONNECT_TIMEOUT_MS=10000
STARROCKS_QUERY_TIMEOUT_MS=30000
STARROCKS_MAX_SQL_LENGTH=50000
STARROCKS_SSL=false
```

必须使用专用只读账号。MCP token 负责识别和审计查询人,真正的数据表权限由这个 StarRocks 账号控制。管理员可以按实际数据库创建最小权限账号,例如:

```sql
CREATE USER 'app_data_mcp_reader' IDENTIFIED BY 'replace-with-a-strong-password';
GRANT SELECT ON ALL TABLES IN DATABASE your_database TO USER 'app_data_mcp_reader';
GRANT SELECT ON ALL VIEWS IN DATABASE your_database TO USER 'app_data_mcp_reader';
```

不要给该账号授予 `INSERT`、`UPDATE`、`DELETE`、`CREATE`、`DROP`、`ALTER`、`EXPORT` 或管理角色。配置后可先调用 `connector_status` 确认 `starrocks.configured=true`,再让 AI 执行 `SHOW TABLES` 验证连接。

## 本地运行

第一次部署先配置 PostgreSQL。服务或同步脚本首次访问时会自动创建 `DB_SCHEMA.METADATA_TABLE`,默认是 `public.app_data_mcp_assets`。

stdio 模式,适合本机 Codex / Claude Code 通过命令启动:

```bash
npm install
npm run dev
```

HTTP 模式,适合部署成团队共享服务:

```bash
npm run dev:http
```

默认地址:

```text
http://127.0.0.1:3000/mcp
```

健康检查:

```text
http://127.0.0.1:3000/health
```

如果当前用户没有可见资产,`catalog_status` 会显示 `scope: "current_user"` 和 `assetCount: 0`,`search_assets` 会返回空列表。先确认该用户的 Metabase 权限,再检查管理员是否已在 `http://127.0.0.1:3000/admin` 勾选开放相应资产。

## 同步平台元信息

可以运行只读同步脚本,把 Metabase/PostHog 元信息 upsert 到 PostgreSQL。

同步 Metabase:

```bash
npm run sync:metabase
```

同步 PostHog:

```bash
npm run sync:posthog
```

全部同步:

```bash
npm run sync:all
```

同步脚本只读取平台 API,然后更新元信息表:

- 已存在的资产更新 `metadata` 和同步时间,不覆盖管理员设置的 `is_published` 和人工配置。
- 新资产默认 `is_published=false`;可通过 `METADATA_DEFAULT_PUBLISHED=true` 调整,但生产环境不建议。
- 平台中已经消失的资产会标记为 inactive,并自动设置 `is_published=false`,不再对 MCP 暴露;未来重新同步出现时也不会自动恢复开放。
- 不会创建、更新、删除 Metabase/PostHog 平台内的任何对象。

PostHog 同步会继续读取 Dashboard 详情,建立 Dashboard 到 Insight 的 `children` 关系;Insight 会自动抽取 `analysisType`、事件、拆分字段和过滤属性到 `insightSemantic`。同步任务还会读取 event/person/session/group 四类 Property Definitions,保存 PostHog 原始元数据并保留后台人工覆盖字段。负责人优先使用 PostHog 用户姓名,其次使用邮箱,只有两者都不存在时才回退到 `distinct_id`。

管理后台的“PostHog 公共属性”页面用于维护属性的开放状态、显示名称、翻译、类型、过滤能力、分组能力、说明和
同义词。PostHog 新发现的属性默认关闭;后续同步只更新源元数据,不覆盖人工配置。PostHog 已消失的属性会标记
为下线并立即停止供 MCP 查询使用。

Metabase 同步会为 dashboard/card/model/metric 写入权限快照 `asset.access`,包括 collection、creator、archived、personal collection、同步时间等。MCP 使用两级过滤:

Card、Model 与 Metric 同步会在读取 `/api/card` 列表后,以受限并发继续读取 `/api/card/:id` 详情,并优先使用详情中的 `dataset_query`、`result_metadata`、参数和更新时间。Model 保存为 `metabase:model:<id>`,Metric 保存为 `metabase:metric:<id>`。包含 `uid` 的 Model 会额外保存 `audience` 元数据和 database id,用于服务端人群组合查询。Metric 还会保存公式、筛选条件、数据来源、默认时间维度、可拆分维度和上下游资产依赖;字段元信息区分 `name`、`displayName` 与真实 `description`。对象在 Card、Model、Metric 之间转换时,会迁移原记录并保留开放状态和后台人工配置。详情请求失败时会保留列表数据,并在同步日志和资产 `warnings` 中标记。

- `search_assets`: 用本地权限快照缩小候选集,再对候选资产做实时权限校验。
- `list_domains` / `catalog_status`: 先做快照过滤,再批量读取当前用户可见的 Metabase Card 与 Dashboard ID。
- `get_asset` / `trace_asset`: 先做本地快照过滤,再用当前用户 Metabase session 实时请求 `GET /api/card/:id` 或 `GET /api/dashboard/:id` 校验可见性。
- `run_asset`: 继续使用用户 Metabase session 执行只读查询,保留平台实时权限判断。
- `query_audience`: 对每个输入 Model 做快照和实时权限校验,再使用同一用户 Session 执行组合查询。

需要统计整个可见目录时,服务会优先并行读取 Metabase `/api/card` 与 `/api/dashboard` 列表,一次取得当前用户可见 ID,而不是逐资产请求详情;读取失败时才回退逐资产校验。结果默认按用户 Session 缓存 30 秒,可通过 `METABASE_LIVE_ACCESS_CACHE_SECONDS` 调整,以减少连续调用 `list_domains`、`catalog_status` 的重复开销。

Dashboard 的开放状态会被其直接子 Card、Model、Metric 继承。未单独开放的子资产不会出现在
`search_assets` 中,但只要它仍属于一个已开放 Dashboard,且当前用户能实时访问该 Dashboard,
就可以通过子资产 id 使用 `get_asset`、`trace_asset` 和 `run_asset`。返回结果中的
`access.mode=dashboard_inherited` 会列出授权来源 Dashboard。Card 数据查询仍由 Metabase 使用
当前用户 Session 做最终权限判断;从 Dashboard 移除子 Card 后,继承访问会在下一次元信息同步后失效。

部署或升级后执行一次完整同步:

```bash
npm run sync:metabase
```

同步频率建议:

```bash
METABASE_METADATA_SYNC_INTERVAL_HOURS=6
METABASE_PERMISSION_SYNC_INTERVAL_HOURS=6
POSTHOG_METADATA_SYNC_INTERVAL_HOURS=12
```

如果 Metabase 权限、collection、核心看板变化频繁,可以把 Metabase 调整到 1 小时;如果变化少,6 小时通常够用。权限大调整、核心看板发布后建议手动运行:

```bash
npm run sync:metabase
```

生产环境可以用 cron 或调度器定时执行:

```cron
0 */6 * * * cd /path/to/app-data-mcp && npm run sync:metabase
15 */12 * * * cd /path/to/app-data-mcp && npm run sync:posthog
```

`catalog_status` 需要个人 MCP token,并按该账号的 Metabase 实时权限统计可见资产;同时返回 metadata/access snapshot 的 `latestSyncedAt`、`ageHours` 和 `stale`,超过上述间隔时会提示重新同步。

如果同步失败,常见原因是 `.env` 里平台地址或凭据未填完整。可以先在 MCP 里调用 `connector_status` 检查配置。

HTTP 模式可配置:

```bash
MCP_HTTP_HOST=0.0.0.0 MCP_HTTP_PORT=3000 npm run dev:http
```

`MCP_HTTP_BEARER_TOKEN` 是旧的共享 HTTP 保护 token。多人使用时更推荐后面的“Metabase 用户授权”流程,由每位用户登录后生成自己的个人 MCP token。

如果仍设置 `MCP_HTTP_BEARER_TOKEN`,客户端需要带共享 token 或用户个人 token 才能进入 `/mcp`。共享 token 只做传输入口保护;当 `APP_DATA_REQUIRE_AUTH_TOKEN=true` 时,数据类 tools 仍然需要用户个人 token 才能识别具体权限。

```text
Authorization: Bearer <token>
```

## Codex / Claude Code 配置示例

stdio:

```json
{
  "mcpServers": {
    "app-data": {
      "command": "npm",
      "args": ["run", "dev"],
      "cwd": "/Users/lute/code/app-data-mcp"
    }
  }
}
```

HTTP / Streamable HTTP:

```json
{
  "mcpServers": {
    "app-data": {
      "url": "http://127.0.0.1:3000/mcp",
      "headers": {
        "Authorization": "Bearer appdata_your-personal-token"
      }
    }
  }
}
```

不同 AI 平台的配置字段名可能略有差异,但核心就是把 MCP endpoint 指到 `/mcp`。

## Metabase / PostHog API 配置

不要把 API key、用户名、密码写入元信息表。密钥只通过部署环境变量提供。

复制 `.env.example` 为 `.env`,在部署环境里填写:

```bash
METABASE_BASE_URL=https://metabase.example.com
METABASE_PUBLIC_URL=https://metabase.example.com
METABASE_API_KEY=...

POSTHOG_BASE_URL=https://posthog.example.com
POSTHOG_PROJECT_ID=...
POSTHOG_PERSONAL_API_KEY=...
```

`run_asset` 会使用这些环境变量调用 Metabase/PostHog 只读 API。资产不可 live 查询或连接失败时,才会按配置回退到本地 `sampleData`。

`METABASE_BASE_URL` 是 MCP 服务端访问 Metabase API 的地址;`METABASE_PUBLIC_URL` 是返回给 AI 用户点击的数据来源地址。远程部署时如果服务端通过本机端口访问 Metabase,例如:

```bash
METABASE_BASE_URL=http://127.0.0.1:3000
METABASE_PUBLIC_URL=http://54.226.190.74:3000
```

这样 MCP 查询仍走服务器本地 API,但返回的 `asset.url` / `source.url` 会是远程可访问地址。修改后建议重新运行 `npm run sync:metabase`;即使暂时不重同步,服务读取时也会按 `METABASE_PUBLIC_URL` 重写 Metabase 来源链接。

如果没有拿到 `METABASE_API_KEY`,可以用 Metabase 用户名密码:

```bash
METABASE_BASE_URL=https://metabase.example.com
METABASE_USER=your-user@example.com
METABASE_PASS=your-password
```

Metabase connector 会用这组配置调用 `POST /api/session` 换取 session id,再用 `X-Metabase-Session` 请求 dashboard/card API。也兼容旧变量名 `METABASE_USERNAME` 和 `METABASE_PASSWORD`。

可以通过 MCP tool `connector_status` 检查配置是否齐全;它只返回是否配置和认证模式,不返回密钥或密码。

## Metabase 用户授权与后台管理员登录

每个用户配置个人 MCP token,服务端用 token 反查对应的 Metabase 账号和 Session,并严格按该账号权限查询:

```bash
METABASE_BASE_URL=https://app-data.luteos.site
METABASE_LOGIN_URL=https://app-data.luteos.site
METABASE_AUTH_MODE=user-session
METABASE_ALLOW_SERVICE_FALLBACK=false
APP_DATA_MCP_PUBLIC_BASE_URL=http://127.0.0.1:3000
APP_DATA_REQUIRE_AUTH_TOKEN=true
APP_DATA_SESSION_FILE=.data/metabase-sessions.json
```

个人 MCP token 本身不按时间过期,MCP 也不再使用 `METABASE_SESSION_TTL_HOURS` 主动判定底层 Session 过期。只要 Metabase 接受该 Session,服务就持续使用它。只有 Metabase 实际返回 HTTP 401 时,才返回 `reauth_required`,要求对应账号重新授权以替换平台 Session;同一账号重新授权时会生成新的个人 MCP token,并立即使该账号之前的 token 失效,也不会自动切换到统一服务账号绕过用户权限。

用户首次授权打开:

```text
http://127.0.0.1:3000/auth/metabase/login
```

授权后将页面生成的 `Authorization: Bearer appdata_xxx` 配置到 AI 助手。服务端只保存 token 哈希和 Metabase Session,不保存用户密码。

管理后台仍然要求使用 Metabase 管理员账号登录:

```text
http://127.0.0.1:3000/admin
```

只保存随机后台 Session 的哈希、CSRF token 和管理员邮箱到 PostgreSQL,不保存管理员密码。设置 `ADMIN_SESSION_PERSISTENT=true` 后,后台 Session 默认不设置服务端过期时间,并在管理员访问后台时滚动刷新浏览器 Cookie;服务重启不会要求重新登录。主动退出、清理数据库 Session 或浏览器清除 Cookie 后仍需重新登录。

## 审计日志

MCP tool 调用会写入 Postgres 审计表,默认表名:

```text
public.app_data_mcp_audit_logs
```

本地配置示例:

```bash
AUDIT_LOG_ENABLED=true
AUDIT_LOG_TABLE=app_data_mcp_audit_logs
AUDIT_REQUEST_PREVIEW_BYTES=12000
AUDIT_RESULT_PREVIEW_BYTES=16000
AUDIT_RESULT_PREVIEW_ITEMS=5
DB_TYPE=postgres
DB_HOST=127.0.0.1
DB_PORT=5432
DB_USER=superset
DB_PASSWORD=123456
DB_NAME=cubecore
DB_SCHEMA=public
DB_SSL=false
DB_SSL_REJECT_UNAUTHORIZED=true
DB_SSL_CA_FILE=
```

Amazon RDS 等要求加密连接的 PostgreSQL 需要设置:

```bash
DB_SSL=true
DB_SSL_REJECT_UNAUTHORIZED=true
DB_SSL_CA_FILE=/path/to/global-bundle.pem
```

`DB_SSL_CA_FILE` 应指向数据库服务商提供的 CA bundle。仅在临时排障且无法立即安装 CA 时,可以设置 `DB_SSL_REJECT_UNAUTHORIZED=false`;连接仍会加密,但不会验证数据库服务器证书身份,不建议长期使用。

服务首次写入时会自动执行 `create table if not exists`,表名前缀为 `app_data_mcp_`,避免和业务表冲突。已存在的审计表会自动补新增列。审计日志记录:

- 用户邮箱、鉴权方式、AI 助手平台、request id、IP、user agent。
- tool 名称、asset id、平台、资产类型、用户原始数据问题 `question_text`、查询文本 `query_text`、limit。
- 客户端传入的 `conversation_id`、`turn_id`、`tool_call_id`;未提供 `tool_call_id` 时使用服务端 request id。
- 对 `query_starrocks` 记录 SQL 文本、默认数据库和 SQL limit,便于追责与排障。
- 问题、参数和实际返回内容的 SHA-256 哈希,便于判断内容是否变化。
- 脱敏后的完整工具请求参数预览,包括 `question`、调用关联 ID、资产、筛选条件和语义查询参数;默认最多 12 KB。
- 返回行数、返回字节数、耗时、状态、错误摘要和结果预览。

每个 MCP tool 都要求 AI 在 `question` 参数中原样传递用户当前轮次的完整问题,并要求同一轮问题触发的多次调用复用相同 `turn_id`。`conversation_id` 和 `tool_call_id` 在客户端可以提供时一并传入。通用 MCP 客户端仍由模型填写这些参数;如果可以控制 AI 客户端或 MCP 网关,建议在发送 `tools/call` 前自动注入,以保证问题文本和关联 ID 完全准确。

请求参数预览默认最多保存 12 KB,普通工具的结果预览默认最多保存 16 KB。对象结构、计数、列信息等会保留;数组只保存前 5 项,并附带总项数和省略项数,因此大型 Dashboard、Card 或 SQL 查询不会把全部数据写入审计表。`search_assets` 是资产治理链路的核心审计信息:AI 侧最多看到 5 个可用候选,审计侧则完整保存权限过滤后的全部召回候选、是否符合精简响应门槛、评分依据和元数据,不受结果预览条数和字节数限制。审计中的 `candidateCount`、`eligibleCandidateCount` 和 `returnedCount` 可用于区分召回量、合格候选量和实际返回量。所有预览中的 Authorization、token、Session、下载 URL、本地路径等敏感字段仍会被遮盖。普通工具的限制可通过 `AUDIT_REQUEST_PREVIEW_BYTES`、`AUDIT_RESULT_PREVIEW_BYTES` 和 `AUDIT_RESULT_PREVIEW_ITEMS` 调整。

AI 助手平台优先读取请求头:

```text
X-App-Data-Client: claude-code
X-App-Data-Client-Version: 1.2.3
```

客户端名称和版本优先读取上述请求头;未提供时,服务会读取 MCP 标准初始化请求中的 `clientInfo`,并优先按 `Mcp-Session-Id`、否则按个人 MCP Token 与 `User-Agent` 的组合指纹,将该身份关联到后续工具请求,避免同一 token 在多个客户端之间串标。Codex 的无状态工具调用还会通过请求 `_meta` 中的 Codex turn metadata 直接识别;WorkBuddy 会通过 `codebuddy.ai` 请求元数据直接识别,二者都不依赖身份缓存。其他客户端最后再尝试从 `user_agent` 推断。当前可识别 WorkBuddy、Claude Code、Claude Desktop、Codex、ChatGPT、Cursor、Windsurf、Roo Code、Cline、Continue、GitHub Copilot、Gemini CLI、Kiro、Amazon Q、Goose、OpenCode、Zed、VS Code、JetBrains、MCP Inspector、Dify、Coze、n8n、LobeChat、Cherry Studio、AnythingLLM、Open WebUI、LangChain,以及常见 HTTP 调试客户端。未知但具有标准 `产品名/版本` 格式的客户端会保留产品名;确实没有可用标识时才记录为 `unknown`。历史审计记录如果曾写成 `unknown` 或未保存版本,后台列表也会根据已保存的 `user_agent` 重新识别。

不会记录完整的大型数据结果,也不会记录用户密码、Metabase session、个人 MCP token 明文。审计后台的“查看”操作可以看到用户原始问题、调用关联 ID、查询文本、返回大小、结果预览和各类哈希。

查看最近调用:

```sql
select
  created_at,
  user_email,
  ai_client,
  tool_name,
  question_text,
  turn_id,
  asset_id,
  status,
  row_count,
  result_bytes,
  result_truncated,
  duration_ms,
  error_code
from public.app_data_mcp_audit_logs
order by created_at desc
limit 50;
```

如果要临时关闭审计:

```bash
AUDIT_LOG_ENABLED=false
```

PostHog 同步脚本访问的是 private API,例如 dashboards 和 insights,需要 Personal API key。不要使用 Project API key / project token。`POSTHOG_API_KEY` 仍作为兼容别名支持,但建议新配置统一使用:

```bash
POSTHOG_PERSONAL_API_KEY=phx_...
```

Personal API key 还需要 `property_definition:read`,用于只读同步公共属性定义。运行 Insight 需要
`insight:read`;执行动态 breakdown 还需要 `query:read`。

## 资产 ID 规范

建议使用统一 ID,避免 AI 客户端理解不同平台的内部 ID:

- `metabase:dashboard:101`
- `metabase:card:456` 或 `metabase:model:388`
- `posthog:dashboard:abc`
- `posthog:insight:activation-funnel`
- `metric:activation_rate`

## 后台管理与元信息配置

HTTP 服务启动后访问:

```text
http://127.0.0.1:3000/admin
```

后台使用 Metabase 账号登录,并通过 `/api/user/current` 校验 `is_superuser=true`。管理员可以:

- 在 Metabase、PostHog 导航中查看同步状态和元信息。
- 勾选或取消 `is_published`,也可以选择多条后批量开放或关闭;变更会立即影响 MCP 搜索和读取。
- 点击列表字段标题可按开放状态、标题、类型、业务域、有效状态和同步时间排序。
- Metabase、PostHog 和审计列表使用固定默认列宽;拖拽表头右侧分隔线可调整列宽,结果保存在当前浏览器。
- 业务域使用可搜索组合框,支持输入过滤候选项;也可按资产类型和开放状态筛选,并与关键词搜索、排序和分页组合使用。
- 编辑标题、描述、搜索别名/业务叫法、适用问题正例、不适用问题反例和 Card 指标集语义。Metabase 业务域只读并始终跟随同步的 Collection;PostHog 业务域可选择已有值,也可输入新名称加入候选项。其他人工配置保存在 `admin_overrides`,后续同步不会覆盖;标签和负责人在编辑弹窗中只读展示。
- 在详情弹窗查看 URL、SQL/查询定义、字段、参数、Dashboard 映射、血缘、权限快照、警告、样例数据和完整 JSON;同步字段只读。
- 在审计导航查看 `AUDIT_LOG_TABLE` 中的用户、AI 客户端、tool、用户数据问题、资产、状态、行数和耗时。新日志直接展示 `question_text`;旧日志兼容读取 `metadata.question` 或 `query_text`。
- 在总览查看按 `turn_id` 聚合的用户对话统计、每日去重用户趋势,以及 `get_asset` / `run_asset` 的资产查询与运行次数;统计日期按中国时区自然日计算。
- 在 MCP 工具管理导航查看工具名称、分类、风险、详情描述和开放状态;关闭后工具不会出现在未授权账号的新 MCP 连接 `tools/list` 中。
- `query_starrocks` 和 `export_audience` 支持在操作列按用户邮箱授权。全局开放表示所有账号可见;全局关闭时只有显式授权账号能看见和调用,撤销授权后立即失效。
- MCP 工具管理页可查看和编辑连接时发送给 AI 的中文全局 `instructions`,并在工具详情中查看调用时机和参数说明;工具标识和参数名保留英文,确保客户端能准确调用。
- 未授权工具不会出现在 `tools/list` 中,实际发送给 AI 的说明也会移除所有提及该工具的语句,不暴露工具名称或禁用状态;服务端调用层会再次校验权限。
- 在“问题回归测试”中自动汇集已发布资产的适用问题正例,也可新增管理员维护的问题;每个问题可配置多个预期资产,任一预期资产进入 AI 前五即视为通过。一键执行会复用资产检索模拟的生产排序逻辑,保存逐题候选、最佳排名、失败原因和 Top 1/3/5、MRR 等批次指标。
- 资产正例由资产元信息单向同步,在回归测试页面只读;需要增删正例时应编辑对应资产的元信息。管理员维护的问题可直接在回归测试页面编辑。问题只要出现在列表中就会参与每次回归测试,避免因停用产生评测盲区。

问题回归测试表会随元信息表按相同前缀自动创建,包括 `*_search_regression_cases`、`*_search_regression_expectations`、`*_search_regression_runs` 和 `*_search_regression_results`。自动正例首次编辑后会转为管理员维护,后续资产正例同步不会覆盖其预期关系。

管理员登录会话持久化到 PostgreSQL 的 `app_data_mcp_admin_sessions` 表。浏览器 Cookie 只保存随机令牌,数据库只保存令牌哈希;服务重启后在有效期内无需重新登录。默认有效期为 168 小时,可配置:

```bash
ADMIN_SESSION_TTL_HOURS=168
ADMIN_SESSION_TABLE=app_data_mcp_admin_sessions
```

元信息表默认名为 `public.app_data_mcp_assets`,主要字段包括 `asset_id`、`platform`、`metadata jsonb`、`admin_overrides jsonb`、`is_published`、`is_active` 和同步时间。`metadata` 内的核心内容包括:

- `title` / `description`: 给 AI 搜索和理解使用。
- `businessDomain` / `tags`: 用于业务域过滤和召回。
- `owner`: 平台同步的负责人姓名或邮箱,也可由后台人工覆盖。
- `retrieval`: 人工维护的检索同义词、适用问题正例和不适用问题反例;正例增强召回,反例按问题相似度降权而不硬过滤。
- `analysisType` / `insightSemantic`: PostHog Insight 的分析类型、事件、拆分字段和过滤属性。
- `url`: 必填,保证每个结果都有原始出处。
- `queryText`: SQL、PostHog insight 描述或指标口径。
- `columns`: 字段说明。
- `parameters`: 可传给 `run_asset` 的只读参数说明,例如日期、国家、渠道、PostHog properties 等。
- `semantic`: Metabase Card 的指标集定义,包括基础粒度、默认时间维度、维度、指标、Rollup 与累计能力;由后台写入 `admin_overrides`。
- `dashboardParameterMappings`: Metabase dashboard 筛选器到下属 card 的映射关系,用于判断某个筛选参数是否会真正影响某张 card。
- `sourceRefs`: 上游表、事件、引用资产等血缘信息。
- `sampleData`: 本地样例数据;live connector 失败或不支持时可作为兜底。
- `warnings`: 数据延迟、口径注意事项、MVP 限制。

MCP 工具配置默认保存在 `public.app_data_mcp_tools`,全局说明保存在 `public.app_data_mcp_settings`,按用户授权保存在 `public.app_data_mcp_tool_permissions`;可以通过 `MCP_TOOLS_TABLE`、`MCP_SETTINGS_TABLE` 和 `MCP_TOOL_PERMISSIONS_TABLE` 修改表名。HTTP 模式每次请求都会按个人 MCP token 对应账号读取最新开关、授权和说明;stdio 或已经建立的长连接需要重新连接后才会更新工具清单。

## 下一步建议

1. 接入 momcozy-data-agent 的受控语义层查询 API,作为 Metabase/PostHog curated assets 的 fallback。
2. 增加 token 自助吊销/轮换页面。
3. 增加敏感字段脱敏策略。
4. 根据你们实际口径补充指标 registry 和数据负责人信息。

TDQS

A3.8/5.0

Scored across 8 tools

Disambiguation5/5

Each tool has a clearly distinct purpose: status checks on different aspects (auth, catalog, connectors), asset retrieval (get, run, search, trace), and domain listing (list_domains). No overlapping functionality; descriptions clarify boundaries between similar actions like get_asset (metadata) and run_asset (live data).

Naming Consistency5/5

All tool names follow a consistent verb_noun pattern using snake_case: auth_status, catalog_status, connector_status, get_asset, list_domains, run_asset, search_assets, trace_asset. Verbs are imperative and nouns are singular/plural appropriately, creating a predictable and readable API.

Tool Count5/5

8 tools is well-scoped for a data asset exploration server. The count covers necessary discovery (search, list), inspection (get, trace), execution (run), and health checks (statuses) without being overwhelming or sparse.

Completeness5/5

The tool set provides complete coverage for its read-only domain: status checks, domain listing, asset search, full metadata retrieval, live data execution, and lineage tracing. No missing operations for the stated purpose of exploring Metabase and PostHog assets.