跳转至

MCP 服务

dosi-server 原生支持 Model Context Protocol, 所以 AI Agent(Claude Code、datus-agent、任何 MCP 客户端)可以发现指标和维度、 把指标查询编译成方言 SQL 并执行,中间不需要一层 HTTP 垫片。 这些 MCP 工具调用的是与 REST API 相同的引擎接缝, 返回的 JSON 形态和结构化错误也与 dosi --format json 一致。

协议:面向 MCP 规范 2026-07-28,端到端无状态: 没有 initialize 握手,没有 Mcp-Session-Id 头,客户端元数据随每个请求的 _meta 一起携带。旧版(2026-07-28 之前)客户端同样以无会话方式服务, 简单的请求/响应式工具调用会得到普通的 application/json 回复。 负载均衡后的任何副本都能服务任何请求,会话状态一律不驻留在服务端。

传输方式

Streamable HTTP(主要方式)

每个运行中的 dosi-server 都会把 MCP 挂在 POST /mcp 上,与 REST API 并列:

cargo run -p dosi-server -- --model fixtures/orders/model.yaml
curl -s -X POST localhost:8081/mcp \
  -H 'content-type: application/json' \
  -H 'accept: application/json, text/event-stream' \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'

设置了 --auth-token(环境变量 DOSI_SERVER_TOKEN)时,/mcp 会像 API 其余部分 一样要求 Authorization: Bearer <token>,每个请求都校验, 这正是无状态部署需要的。Claude Code 的远程配置:

claude mcp add --transport http dosi http://localhost:8081/mcp \
  --header "Authorization: Bearer $DOSI_SERVER_TOKEN"

stdio(本地单客户端)

--mcp-stdio 通过 stdin/stdout 提供 MCP,不绑定 HTTP, 这是本地 Agent 把服务作为子进程拉起的标准做法。日志走 stderr, stdout 上只有协议内容。

claude mcp add dosi -- \
  cargo run -p dosi-server -- --model /path/to/model.yaml --mcp-stdio

常规的那些参数都适用(--connections--db--disable-execute--osi-basic 等);两种传输方式提供完全相同的工具集。

工具

工具 输入 返回
list_datasets 数据集:名称、来源、主键、字段、时间维度
list_metrics 指标:名称、种类、数据集、度量、时间维度、描述
list_dimensions dataset.field 名称,带时间标记和粒度
describe_metric name 单个指标的那一行;未知名称会返回候选项
list_connections 具名的数仓配置:名称、方言、可用性
get_capabilities 引擎模式、支持的方言、是否启用执行、模型统计
compile_sql 指标查询 + dialectpretty {dialect, sql},不执行
explain_query 指标查询 以文本形式给出的逻辑计划
run_query 指标查询 + dialectconnectionrow_limit {dialect, sql, columns, rows, row_count, row_limit_applied}
validate_model model_yaml {valid, issues, compile_errors, warnings}

指标查询类的输入就是 REST API 和 CLI 所用的那个 MetricQuery 传输形态 (metrics、写作 [{field, grain}]group_bywhere_sqltime_rangeorder_bylimit)。工具的 schema 由同一批类型生成, 不可能产生漂移。

执行方面的保护

run_query 复用服务端的执行机制:并发信号量 (--max-concurrent-executions)、执行超时,以及 --disable-execute (它会让 run_query 变成一个工具级的 forbidden 错误)。 结果会进入 LLM 上下文,所以行数有上限:没写 limit 的查询自动加 LIMIT 500row_limit 最高钳制在 5000,实际生效的上限由 row_limit_applied 报出。 不带 connection 时查询跑在服务端的本地 DuckDB 上(--db 指定的文件或内存库); 要在数仓上跑,点名一个 list_connections 里的配置即可。

错误

引擎的失败以工具级错误返回,携带引擎的结构化 JSON, 字段与 REST API 和 CLI 相同:稳定的 codecandidatessuggested_retry

{"error": {"code": "unknown_metric", "message": "unknown metric \"revenu\"",
           "candidates": ["revenue"], "suggested_retry": {"metrics": ["revenue"]}}}

Agent 应当读错误再改查询,而不是原样重试。服务端的 MCP 指引里也是这么写的。

交互式试用

npx @modelcontextprotocol/inspector \
  cargo run -p dosi-server -- --model fixtures/orders/model.yaml --mcp-stdio