Skip to main content
Glama
jedshen123

App Data MCP

by jedshen123

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: 查看连接器配置状态和只读访问策略。

Related MCP server: socrata-mcp

只读访问策略

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

允许的操作:

  • 搜索和读取 PostgreSQL 中已开放的元信息。

  • 读取看板、卡片、insight、指标口径和来源链接。

  • 追踪上游表、事件、SQL、原平台 URL。

  • 执行 Metabase/PostHog 查询时,只允许调用读取类 API,并限制为只读查询结果。

  • 执行 StarRocks 查询时,只允许单条 SELECTWITH ... SELECTSHOWDESCRIBE/DESCEXPLAIN

禁止的操作:

  • 创建、编辑、删除 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 默认返回 10 条,最多 50 条。

  • run_asset 默认返回 100 行,最多 500 行。

  • 单次 MCP 响应默认最大约 1 MB,超过会返回 response_too_large,提示缩小查询范围。

  • query_starrocksrun_asset 共用行数上限;同时在 StarRocks 会话设置 sql_select_limitquery_timeout

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

DATA_DEFAULT_SEARCH_LIMIT=10
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_LIMITDATA_MAX_RESPONSE_BYTES 限制。

  • 资产声明了 parameters 时,可以给 run_asset 传维度/日期参数;未声明的友好参数会被拒绝,避免 AI 临时拼接不受控条件。

  • Metabase dashboard 会读取 parameter_mappings,只把已经映射到对应 card 的筛选参数下发,并在返回里给出每张 card 的 parameterMappingStatus

  • 如果 live connector 失败且资产里配置了 sampleData,会回退返回 sampleData。

  • 如果既无法 live 查询又没有 sampleData,会返回 live_connector_failedasset_not_runnable

Metabase 友好参数示例:

{
  "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 保持已经治理的公式不变,只允许追加筛选和替换拆分维度:

{
  "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 可以选择明细字段:

{
  "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
}

也可以动态聚合和拆分:

{
  "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 预览与导入”只用于排障或批量迁移,不要求日常手写。

{
  "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"]
      },
      "cumulative": {
        "supported": true,
        "strategy": "running_sum"
      }
    },
    {
      "name": "paid_users",
      "label": "支付用户数",
      "rollup": {
        "strategy": "forbidden",
        "reason": "基础结果已经去重,跨日期或地区相加会重复计算用户"
      }
    }
  ]
}

查询 Card 指标集:

{
  "asset_id": "metabase:card:123",
  "semantic": {
    "measures": ["revenue"],
    "breakouts": [
      { "field": "stat_date", "unit": "day" },
      { "field": "region" }
    ],
    "cumulative": [
      {
        "measure": "revenue",
        "orderBy": "stat_date",
        "partitionBy": ["region"],
        "alias": "revenue_running"
      }
    ]
  },
  "limit": 100
}

Card 查询省略 breakouts 时采用指标级时间维度或 Card 的 defaultTimeDimension;传入空数组表示请求跨时间总值,仅 sum/min/max/recompute 等安全 Rollup 会执行。forbidden 指标只能保持完整 baseGrainrecompute 可用治理过的分子、分母重新计算比率。运行累计只支持显式声明为 running_sum 的可加指标,且结果必须在行数限制内完整返回;日去重用户数等不可加指标会被拒绝。

筛选操作符包括 eqneqgtgteltlteinnot_incontainsis_nullnot_nullbetween;Model 聚合包括 countdistinctsumavgminmax。语义查询与 params 不能同时使用,普通 Card、Dashboard 和 PostHog Insight 不接受 semantic

用户人群组合查询

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

{
  "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 支持 intersectionuniondifferencedifference 表示第一个 Model 减去后续所有 Model。output=count 默认只返回去重用户数;output=uids 返回去重 UID,并受全局最大行数和响应大小限制。每个 Model 最多包含 20 个受控语义筛选条件。

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

{
  "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。搜索支持直接传入中文长句,会拆分中文二元/三元语义片段,并在召回后按问题意图动态重排。普通指标、趋势和拆分查询采用 Metric > Card > Model > Dashboard 的安全顺序:先检查标准 Metric,再检查可能包含多个现成指标的 Card,最后才用 Model 重算;明细查询仍优先 Model,总览优先 Dashboard,固定报表优先 Card,漏斗/留存/路径分析优先 PostHog Insight。类型只是意图适配后的兜底排序,AI 仍需用 get_asset 核对字段、维度、参数和公式。返回值中的 selection.intentrecommendedAssetIdcandidateOrder,以及每项资产的 selection.suitability/reasons/missingCapabilities 会解释推荐结果。只有管理后台已开放且有效的资产会参与搜索和治理检查。

当 AI 在 Model 上使用 semantic.aggregations 重新计算 countdistinctsum 等指标时,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 都无法覆盖用户要求的维度时,才允许这样显式降级:

{
  "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 前由服务端再次搜索治理资产:

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

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

{
  "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 只允许 SHOWDESCRIBE/DESCEXPLAIN

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

{
  "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 原生参数仍可透传:

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

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

{
  "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[].propertybreakdown 中使用任意一个已治理名称。原生 properties[].key 继续兼容, 但也必须对应一条有效且已开放的公共属性。

StarRocks 自助 SQL

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

  1. 必须先调用 search_assets,优先检查 Metric、Model,再检查 Card、Dashboard 或 Insight。

  2. 能由治理资产回答时使用 get_assetrun_asset

  3. 没有合适资产、已明确拒绝候选资产,或用户明确要求 SQL 时,才使用 query_starrocks

  4. 不熟悉表结构时,先执行 SHOW TABLESDESCRIBE table_nameSHOW CREATE TABLE table_name

  5. 根据真实字段生成带明确日期范围和过滤条件的 SELECT,避免扫描无关数据。

示例:

{
  "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 OUTFILELOAD_FILEFILESSLEEP、可执行注释和自定义查询 Hint 等危险或消耗型 SQL。每次执行还会设置 StarRocks 当前会话的查询超时与最大返回行数,并继续受 MCP 响应字节数限制。

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

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 账号控制。管理员可以按实际数据库创建最小权限账号,例如:

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';

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

本地运行

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

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

npm install
npm run dev

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

npm run dev:http

默认地址:

http://127.0.0.1:3000/mcp

健康检查:

http://127.0.0.1:3000/health

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

同步平台元信息

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

同步 Metabase:

npm run sync:metabase

同步 PostHog:

npm run sync:posthog

全部同步:

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

  • search_assets: 用本地权限快照缩小候选集,再对候选资产做实时权限校验。

  • list_domains / catalog_status: 先做快照过滤,再批量读取当前用户可见的 Metabase Card 与 Dashboard ID。

  • get_asset / trace_asset: 先做本地快照过滤,再用当前用户 Metabase session 实时请求 GET /api/card/:idGET /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_domainscatalog_status 的重复开销。

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

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

npm run sync:metabase

同步频率建议:

METABASE_METADATA_SYNC_INTERVAL_HOURS=6
METABASE_PERMISSION_SYNC_INTERVAL_HOURS=6
POSTHOG_METADATA_SYNC_INTERVAL_HOURS=12

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

npm run sync:metabase

生产环境可以用 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 的 latestSyncedAtageHoursstale,超过上述间隔时会提示重新同步。

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

HTTP 模式可配置:

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 才能识别具体权限。

Authorization: Bearer <token>

Codex / Claude Code 配置示例

stdio:

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

HTTP / Streamable HTTP:

{
  "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,在部署环境里填写:

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,例如:

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 用户名密码:

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_USERNAMEMETABASE_PASSWORD

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

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

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

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 失效,也不会自动切换到统一服务账号绕过用户权限。

用户首次授权打开:

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

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

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

http://127.0.0.1:3000/admin

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

审计日志

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

public.app_data_mcp_audit_logs

本地配置示例:

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 需要设置:

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_idturn_idtool_call_id;未提供 tool_call_id 时使用服务端 request id。

  • query_starrocks 记录 SQL 文本、默认数据库和 SQL limit,便于追责与排障。

  • 问题、参数和实际返回内容的 SHA-256 哈希,便于判断内容是否变化。

  • 脱敏后的完整工具请求参数预览,包括 question、调用关联 ID、资产、筛选条件和语义查询参数;默认最多 12 KB。

  • 返回行数、返回字节数、耗时、状态、错误摘要和结果预览。

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

请求参数预览默认最多保存 12 KB,普通工具的结果预览默认最多保存 16 KB。对象结构、计数、列信息等会保留;数组只保存前 5 项,并附带总项数和省略项数,因此大型 Dashboard、Card 或 SQL 查询不会把全部数据写入审计表。search_assets 是资产治理链路的核心审计信息,会完整保存本次返回的全部资产及其元数据,不受结果预览条数和字节数限制。所有预览中的 Authorization、token、Session、下载 URL、本地路径等敏感字段仍会被遮盖。普通工具的限制可通过 AUDIT_REQUEST_PREVIEW_BYTESAUDIT_RESULT_PREVIEW_BYTESAUDIT_RESULT_PREVIEW_ITEMS 调整。

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

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

客户端名称和版本优先读取上述请求头;未提供时,服务会读取 MCP 标准初始化请求中的 clientInfo,并按个人 MCP Token 将该身份关联到后续工具请求。Codex 的无状态工具调用还会通过请求 _meta 中的 Codex turn metadata 直接识别,不依赖 User-Agent 或服务进程内缓存;其他客户端最后再尝试从 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、查询文本、返回大小、结果预览和各类哈希。

查看最近调用:

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;

如果要临时关闭审计:

AUDIT_LOG_ENABLED=false

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

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:456metabase:model:388

  • posthog:dashboard:abc

  • posthog:insight:activation-funnel

  • metric:activation_rate

后台管理与元信息配置

HTTP 服务启动后访问:

http://127.0.0.1:3000/admin

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

  • 在 Metabase、PostHog 导航中查看同步状态和元信息。

  • 勾选或取消 is_published,也可以选择多条后批量开放或关闭;变更会立即影响 MCP 搜索和读取。

  • 点击列表字段标题可按开放状态、标题、类型、业务域、有效状态和同步时间排序。

  • Metabase、PostHog 和审计列表使用固定默认列宽;拖拽表头右侧分隔线可调整列宽,结果保存在当前浏览器。

  • 业务域使用可搜索组合框,支持输入过滤候选项;也可按资产类型和开放状态筛选,并与关键词搜索、排序和分页组合使用。

  • 编辑标题、描述、业务域、搜索别名/业务叫法、适用问题正例、不适用问题反例和 Card 指标集语义;业务域可选择已有值,也可输入新名称加入候选项,标签和负责人在编辑弹窗中只读展示。人工配置保存在 admin_overrides,后续同步不会覆盖。

  • 在详情弹窗查看 URL、SQL/查询定义、字段、参数、Dashboard 映射、血缘、权限快照、警告、样例数据和完整 JSON;同步字段只读。

  • 在审计导航查看 AUDIT_LOG_TABLE 中的用户、AI 客户端、tool、用户数据问题、资产、状态、行数和耗时。新日志直接展示 question_text;旧日志兼容读取 metadata.questionquery_text

  • 在 MCP 工具管理导航查看工具名称、分类、风险、详情描述和开放状态;关闭后工具不会出现在未授权账号的新 MCP 连接 tools/list 中。

  • query_starrocksexport_audience 支持在操作列按用户邮箱授权。全局开放表示所有账号可见;全局关闭时只有显式授权账号能看见和调用,撤销授权后立即失效。

  • MCP 工具管理页可查看和编辑连接时发送给 AI 的中文全局 instructions,并在工具详情中查看调用时机和参数说明;工具标识和参数名保留英文,确保客户端能准确调用。

  • 未授权工具不会出现在 tools/list 中,实际发送给 AI 的说明也会移除所有提及该工具的语句,不暴露工具名称或禁用状态;服务端调用层会再次校验权限。

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

ADMIN_SESSION_TTL_HOURS=168
ADMIN_SESSION_TABLE=app_data_mcp_admin_sessions

元信息表默认名为 public.app_data_mcp_assets,主要字段包括 asset_idplatformmetadata jsonbadmin_overrides jsonbis_publishedis_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_TABLEMCP_SETTINGS_TABLEMCP_TOOL_PERMISSIONS_TABLE 修改表名。HTTP 模式每次请求都会按个人 MCP token 对应账号读取最新开关、授权和说明;stdio 或已经建立的长连接需要重新连接后才会更新工具清单。

下一步建议

  1. 接入 momcozy-data-agent 的受控语义层查询 API,作为 Metabase/PostHog curated assets 的 fallback。

  2. 增加 token 自助吊销/轮换页面。

  3. 增加敏感字段脱敏策略。

  4. 根据你们实际口径补充指标 registry 和数据负责人信息。

Install Server
F
license - not found
A
quality
B
maintenance

Maintenance

Maintainers
Response time
Release cycle
Releases (12mo)
Commit activity

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/jedshen123/app-data-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server