跳到主要内容
MetaxisData
EN

mxd 命令行客户端

每条命令一个 JSON 文档、稳定的退出码、分析范围与设备码登录。

mxd 是 metaxisdata 服务端的命令行客户端。它是为 Agent 而建的:每条命令只往 stdout 写 一个 JSON 文档,进度、警告与错误都走 stderr。失败会带上稳定的 code 与稳定的退出码, 调用方无需解析自然语言就能决定下一步做什么。

安装与登录

Terminal window
$ make build-cli # 产物为 ./build/mxd
$ mxd auth login --server https://mx.example.com

命令会打印一个 URL 和一个验证码。URL 里已经带着验证码,因此打开它会直接进入确认页面; mxd 会轮询直到你做出决定,然后把令牌存进 ~/.config/metaxisdata/config.json(权限 0600)。传 --no-prefill-url 会改为打印不带验证码的页面地址,适用于需要手工输入验证码的 流程。

确认页面始终把验证码显示在客户端名称与版本、来源地址和时间的旁边,并且始终要求显式做出 决定——因此一个不是你发起的请求,依然是可以被认出来的。

地址来自工作区的外部地址设置。全新的或本地运行的工作区通常没有这个设置,与其让你无 处可去,mxd 会回落到 <server>/device。只要服务端同时提供 Web 应用,这就是对的;如果 你把单页应用单独跑(本地常见的做法是 Vite 跑在 :3000),就把工作区外部地址设成那个地址, 服务端自己会给出正确的页面。

服务端地址是和令牌一起保存的,因为一枚令牌由某一个服务端签发;之后的命令不再需要 --server。mxd auth logout 会吊销令牌并保留地址。

--login-timeout(默认 10m)限定等待这次批准的上限。它与 --timeout(默认 30s)是两 回事,后者限定单次请求:批准等的是一个人,不是一台服务器。

要在一台机器上保留多个身份,就让每个身份指向自己的文件:

Terminal window
$ METAXISDATA_CONFIG=/tmp/agent-a/mxd.json mxd auth login --server https://mx.example.com

在 CI 里,mxd auth login --service-account <email> 会用服务账号的密钥(从 METAXISDATA_SERVICE_KEY 读取)换取一枚 API token。这枚 token 只活一小时,因此长跑的作业 应当改为注入 METAXISDATA_TOKEN。

分析范围

一条 SQL 语句离开数据库上下文就无法解析:语句最多命名到 database.schema.table,从不 命名实例。因此每一次 lineage sql 调用都至少需要一个范围。

范围从 METAXISDATA_SCOPES 读取,且只在该命令执行期间生效:

Terminal window
$ export METAXISDATA_SCOPES='dev=1;shop,prod=9;shop;public'

每一项要么是裸 GUID,要么是 name=guid。--scope 为单次调用收窄选择;它接受已配置的名 字、一个 GUID,或 all。

范围从不写入文件。 多个 Agent 可能共用一台机器却服务于不同项目,而一个被记住的范围集 会被最后运行的那个 Agent 悄悄覆盖。一个项目用哪些范围是每个项目自己的决定:把那份列表 放在该项目自己的文档里,并在调用 mxd 之前导出它。

一个范围是独立地对照列表里的每一个范围解析的,结果也按范围分别报告。选择多个范围不会 合并上下文,也不会发现元数据里本就不存在的跨环境关系。

GUID 从哪来

不要手工构造 GUID:分段布局因引擎而异(MySQL 系的 schema 段为空,PostgreSQL 需要它)。 请复制服务端给出的值。

Terminal window
$ mxd database list --instance 1 # 每一行带 "guid",例如 "1;shop"
$ mxd meta list '1;shop;' --type SCHEMA # 每一行带 "guid",例如 "1;shop;public"

命令

Terminal window
mxd auth login [--server <url>] [--no-browser] [--no-prefill-url] [--login-timeout 10m]
mxd auth status
mxd auth logout
mxd config show # 生效的服务端、凭据来源与范围
mxd config path
mxd instance list
mxd database list [--instance <id>]
mxd meta list <parent-guid> [--type TABLE]
mxd meta get <guid> [--type]
mxd meta search <keyword> [--type] [--parent-guid-prefix]
mxd meta ddl <guid> [--type]
mxd lineage sql [--scope <name|guid|all>]... --file <path|-> [--depth N] [--include-temp]
mxd lineage graph <guid> [--depth N] [--direction up|down|both] [--column <name>]
mxd skill install [--dir <path>]
mxd skill show
mxd version

教会你的 Agent

mxd 自带一份 agent skill,并会替你安装它:

Terminal window
$ mxd skill install
{"changed": true, "name": "mxd-cli", "path": "/home/u/.agents/skills/mxd-cli/SKILL.md"}

这份 skill 内嵌在二进制里,因此一台装了 mxd 的机器无需任何 checkout 就能给它的 Agent 同 样的指令。它落在 ~/.agents/skills/mxd-cli/SKILL.md,也就是 Agent 运行时能发现的位置; --dir 可以指向别处,比如 ~/.claude/skills 或项目自己的 .agents/skills。已存在的文件 会被替换,所以升级之后重跑一次,正是这台机器拿到新措辞的方式。

mxd skill show 以 JSON 打印同一份文档,供更愿意自行抓取而不是读文件的 harness 使用。

这份 skill 也覆盖了「同一套部署通过 Model Context Protocol 提供相同能力」的情形:支持 MCP 的 harness 应当使用那些工具,而不是去 shell 调用。规则是一样的,只是分析范围作为工具参数 传入,而不是从 METAXISDATA_SCOPES 读取;对象按名字而非 GUID 寻址。

mxd config show 是回答「这个进程实际在用哪份配置」的方式:它报告每一个生效值,以及它来自 哪一层(flag、env:...、file:... 或 none)。

所有列表都会翻页取完。当某个列表触到 --max-items(默认 1000)时,信封里会出现 "truncated": true 并带上 "nextPageToken"。meta list 不带 --type 是唯一的例外:它的 响应里每种 meta 类型各带一个 page token,而不是整个响应共用一个,所以想继续往下走就需要 --type。

读它的输出

Terminal window
$ mxd lineage sql --file etl.sql --depth 2
{
"results": [
{
"scopeName": "dev",
"scopeGuid": "1;shop",
"relations": [
{"sourceGuid": "1;shop;;orders", "sourceColumn": "amount",
"targetGuid": "1;shop;;order_daily", "targetColumn": "amount",
"isTemp": false}
],
"graphs": [{"rootGuid": "1;shop;;order_daily", "nodes": [], "edges": []}],
"warnings": []
}
],
"warnings": []
}

结果即使只有一个范围也按范围分组,调用方因此永远不必根据「自己请求了几个范围」去分支。

临时关系默认隐藏。 这种关系的目标只存在于语句内部。它恰好只在一种情形下有意义:当这 条语句的结果没有写到任何地方时——也就是每一条裸 SELECT。一旦语句有了真实目标(视图、它 插入进去的表),那些合成关系就是该目标的重复,服务端会把它们丢掉,于是什么也没有隐藏。

这意味着默认行为可能让某个范围没有任何关系可看。真发生的时候,该条目会带上 "tempRelationsHidden": N 以及一条点名那个 flag 的警告,因此一个空列表永远不会被读成 「这条语句没有血缘」:

{"results": [{"scopeName": "dev", "scopeGuid": "1;shop", "relations": [],
"tempRelationsHidden": 2,
"warnings": ["all 2 relations of this scope are temporary: ... pass --include-temp to see them"]}],
"warnings": []}

--include-temp 会把它们报出来;此时 targetGuid 为空,而 targetColumn 是查询的输出 别名。这个 flag 只影响显示什么:--depth 展开从不追踪临时目标,因为只存在于语句内部的目 标没有图可走。

JSON 的形状

Protobuf 消息用 protojson 渲染,与 REST 网关和审计日志用的是同一种编码,因此字段名是 lowerCamelCase(是 sourceGuid,不是 source_guid),枚举是名字(是 "TABLE",不是 4)。未设置的字段会被输出而不是省略——缺席的列表是 [],未设置的字符串是 "",为假的 flag 是 false——因此文档的形状不会随数据而变,调用方也不必区分「缺失」与「为空」。

退出码

退出码 含义 该做什么
0 成功 —
1 输入有误:flags、空语句、server_required、scope_required、config_invalid 修正调用;错误里带 hint
2 未认证:令牌缺失、过期或被吊销,或者设备码登录会话已不存在或被拒绝 执行 mxd auth login
3 未找到 检查 GUID
4 无权限 请联系管理员
5 未能完成:unavailable(服务端不可达)、resource_exhausted,或服务端故障 重试,然后确认服务端在运行并读它的日志
6 超时或被取消,无论本地还是远端,也包括 --login-timeout 到期 重试,或调大 --timeout / --login-timeout

错误信封里也带一个 code 字符串,它比退出码更细:server_required、scope_required 与 config_invalid 都退出 1,但各自指出不同的修法;服务端不可达报的是 unavailable,而不是 它自身故障才会给出的 internal。

备注

  • mxd 用 bearer token 讲 ConnectRPC,因此不受 Web 会话那套基于 cookie 的 CSRF 保护影响。
  • --ca-cert <pem> 为使用自签名的部署额外信任一张证书;--insecure 跳过校验并打印警告。
  • 令牌以明文保存。它等价于一个七天的会话;CLI 从不打印它。