mxd 命令行客户端
每条命令一个 JSON 文档、稳定的退出码、分析范围与设备码登录。
mxd 是 metaxisdata 服务端的命令行客户端。它是为 Agent 而建的:每条命令只往 stdout 写
一个 JSON 文档,进度、警告与错误都走 stderr。失败会带上稳定的 code 与稳定的退出码,
调用方无需解析自然语言就能决定下一步做什么。
安装与登录
$ 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)是两
回事,后者限定单次请求:批准等的是一个人,不是一台服务器。
要在一台机器上保留多个身份,就让每个身份指向自己的文件:
$ 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 读取,且只在该命令执行期间生效:
$ export METAXISDATA_SCOPES='dev=1;shop,prod=9;shop;public'每一项要么是裸 GUID,要么是 name=guid。--scope 为单次调用收窄选择;它接受已配置的名
字、一个 GUID,或 all。
范围从不写入文件。 多个 Agent 可能共用一台机器却服务于不同项目,而一个被记住的范围集
会被最后运行的那个 Agent 悄悄覆盖。一个项目用哪些范围是每个项目自己的决定:把那份列表
放在该项目自己的文档里,并在调用 mxd 之前导出它。
一个范围是独立地对照列表里的每一个范围解析的,结果也按范围分别报告。选择多个范围不会 合并上下文,也不会发现元数据里本就不存在的跨环境关系。
GUID 从哪来
不要手工构造 GUID:分段布局因引擎而异(MySQL 系的 schema 段为空,PostgreSQL 需要它)。 请复制服务端给出的值。
$ mxd database list --instance 1 # 每一行带 "guid",例如 "1;shop"$ mxd meta list '1;shop;' --type SCHEMA # 每一行带 "guid",例如 "1;shop;public"命令
mxd auth login [--server <url>] [--no-browser] [--no-prefill-url] [--login-timeout 10m]mxd auth statusmxd auth logout
mxd config show # 生效的服务端、凭据来源与范围mxd config path
mxd instance listmxd 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,并会替你安装它:
$ 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。
读它的输出
$ 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 从不打印它。