跳到主要内容
MetaxisData
EN

接入 MCP 客户端

端点需要的两项工作区设置、客户端配置、授权流程与只读工具。

MetaxisData 可以把它的元数据注册中心与血缘,以一组只读工具的形式提供给 Model Context Protocol 客户端。端点是 /mcp,同一个服务端同时充当这些客户端注册与授权 所依赖的 OAuth 2.1 授权服务器。这里没有任何写操作。

在它开口之前

两项工作区设置,顺序不能反:

  1. 外部地址(External URL)(设置 → 通用设置)。整个流程以它作为 issuer 与 resource identifier 构建,因此它必须是客户端访问这套部署所用的地址。除了开发 期的回环地址,它必须是 https;如果服务端挂在前缀路径下,它也可以带路径前缀。
  2. mcp_enabled(同一页)。默认关闭。在外部地址设置之前,服务端拒绝打开它; 而在它关闭期间,端点、两份元数据文档以及每一条 /oauth/* 路由都返回 404 —— 被关闭的部署不对外声明任何东西。

配置客户端

一个讲 streamable HTTP 的 MCP 客户端只需要端点 URL。例如:

{
"mcpServers": {
"metaxisdata": {
"type": "http",
"url": "https://mx.example.com/mcp"
}
}
}

第一个请求没有 token,于是客户端被告知去哪里授权,然后它自己完成剩下的事:

  1. POST /mcp 以 401 回应,并带上 WWW-Authenticate: Bearer resource_metadata="https://mx.example.com/.well-known/oauth-protected-resource", scope="metaxisdata.mcp.read", error="invalid_token"。
  2. 客户端读取受保护资源元数据,沿 authorization_servers 找到授权服务器元数据,并用 POST /oauth/register 自行注册(仅公开客户端;PKCE 强制)。
  3. 客户端在浏览器中打开 GET /oauth/authorize,带上 PKCE challenge 与 resource=https://mx.example.com/mcp。已登录的用户在 /oauth/consent 页面上做 出决定,该页面显示客户端名称、授权码将被发往何处、资源与 scope。仅仅打开页面 不会被批准。
  4. 浏览器在 GET /oauth/authorize/complete 完成流程,服务端在此签发授权码并重定向 给客户端——授权码从不经过页面。
  5. 客户端在 POST /oauth/token 用授权码换取令牌,并在之后每一次 /mcp 请求中携带 得到的 access token。同一个响应里还有一个 refresh token,access token 过期时 客户端在同一个端点用它换取新的。

access token 的 audience 绑定为 <external URL>/mcp:Web 或 CLI 的会话令牌在 /mcp 会被拒绝,MCP 令牌在 ConnectRPC API 上同样会被拒绝。因此变更外部地址会让已签发的 MCP 令牌与授权立即失效,客户端需要重新授权。

刷新是轮换,而不是把同一个凭据续期:以 grant_type=refresh_token、client_id、 resource 以及(客户端若记录过)已同意的 scope 提交 refresh token,它会消耗掉这 枚令牌并返回一个新的 access token 加一个新的 refresh token。令牌是一次性的——被认领 时对应的行即被删除——所以两个客户端拿同一枚令牌并发刷新,只有一个会成功;而一份从 客户端存储里被偷走的副本,在真正的客户端刷新之后就一文不值。一次授权存活 30 天,并在 每次轮换时对照用户账号重新校验,因此被停用的用户、或在同意之后改过密码的用户,都必须 重新授权。

工具

工具 回答什么
list_instances 哪些实例已注册
list_databases 一个实例下有哪些数据库,以及标识它们的 GUID
search_metadata 只知道名字时,这个名字在哪
list_metadata 某个对象直接下面有什么(schema、表、视图、列)
get_metadata 一个对象的完整形态:列、索引、键、分区
get_ddl 该对象按源引擎所报告的定义
analyze_sql 一条 SQL 语句逐列读写了什么
get_lineage_graph 一个对象周围多层级的血缘
whoami 调用者是谁,能读什么

三条规则决定一次调用能不能成功:

  • 对象按名字寻址:{instance, database, schema?, name},每个值可以是名字,也可以 是某次列表返回的 id。此前结果里的 guid 同样可用。其余由服务端解析——引擎特有的 标识符布局从不是调用方要操心的事——而一个匹配不到任何东西、或匹配到多个东西的名字, 会连同候选一起返回,而不是猜一个。
  • analyze_sql 需要显式的分析范围:scopes: [{instance, database, schema?}]。 语句里未限定的名字无法说明自己属于哪个实例,因此这个工具拒绝替它选一个,而是列出 已存在的数据库。
  • 失败是一个「换个说法再问」的信号。 每一次失败都是一个 JSON 对象,带稳定的 code(invalid_argument、not_found、ambiguous、scope_required、 unsupported、permission_denied、unauthenticated、unavailable、timeout、 resource_exhausted、internal)、一个 message、通常还有一个 hint,并且—— 对于未知或有歧义的名字——还带 details.candidates。

工具结果与 API 其余部分使用同一套字段命名(lowerCamelCase、枚举用名字),但会省略 未设置的字段;列表返回的是一份投影而不是完整的元数据对象;需要细节时用 get_metadata 或 get_ddl 去取。

运维它

  • 每一次工具调用都会被审计,只读调用也不例外:一条 audit_log 记录,写明工具、 调用者、参数(凭据类字段已脱敏,过长的值已截断)与结果。因权限不足被拒绝的调用同样 会留下一行。账本永久保留,所以行数会随着模型的使用量增长。
  • 令牌可以被吊销。 把它提交给用户 API 的 Logout(也就是 mxd logout 发送的 请求)会立即吊销它,对 /mcp 与 API 一视同仁:服务端校验的是「这枚令牌是不是我签 的」,而不是它带着哪个 audience。它同时会删除该 access token 所指客户端的 refresh token,因此吊销一枚令牌就结束了这条连接,而不是留下一个「再刷新一次就能拿到新的一对」 的尾巴。
  • 匿名端点按地址限流——注册、令牌交换、以及两个浏览器步骤——而被关闭的部署在返回 404 时不会写入账本,所以探测它的成本为零。
  • 待处理的授权是进程内状态,和设备码登录一样。一次批准与它的完成必须落在同一个副本 上:要么只跑一个副本,要么在前面加粘性路由。refresh token 不是:它们存在数据库里, 因此一次刷新可以落到任意副本。
  • 端点是无状态的。 没有任何会话跨请求存活,这正是它可以安全地放在负载均衡后面的 原因;同时它被标准库的跨域保护包着。
  • 关闭 mcp_enabled 会立刻关闭这个面;它不会吊销令牌或授权,那些会自行过期。要退掉一 条活着的连接,用上面的 Logout。