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 上只有协议内容。
常规的那些参数都适用(--connections、--db、--disable-execute、
--osi-basic 等);两种传输方式提供完全相同的工具集。
工具¶
| 工具 | 输入 | 返回 |
|---|---|---|
list_datasets |
— | 数据集:名称、来源、主键、字段、时间维度 |
list_metrics |
— | 指标:名称、种类、数据集、度量、时间维度、描述 |
list_dimensions |
— | dataset.field 名称,带时间标记和粒度 |
describe_metric |
name |
单个指标的那一行;未知名称会返回候选项 |
list_connections |
— | 具名的数仓配置:名称、方言、可用性 |
get_capabilities |
— | 引擎模式、支持的方言、是否启用执行、模型统计 |
compile_sql |
指标查询 + dialect、pretty |
{dialect, sql},不执行 |
explain_query |
指标查询 | 以文本形式给出的逻辑计划 |
run_query |
指标查询 + dialect、connection、row_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_by、where_sql、
time_range、order_by、limit)。工具的 schema 由同一批类型生成,
不可能产生漂移。
执行方面的保护¶
run_query 复用服务端的执行机制:并发信号量
(--max-concurrent-executions)、执行超时,以及 --disable-execute
(它会让 run_query 变成一个工具级的 forbidden 错误)。
结果会进入 LLM 上下文,所以行数有上限:没写 limit 的查询自动加 LIMIT 500,
row_limit 最高钳制在 5000,实际生效的上限由 row_limit_applied 报出。
不带 connection 时查询跑在服务端的本地 DuckDB 上(--db 指定的文件或内存库);
要在数仓上跑,点名一个 list_connections 里的配置即可。
错误¶
引擎的失败以工具级错误返回,携带引擎的结构化 JSON,
字段与 REST API 和 CLI 相同:稳定的 code、candidates、suggested_retry。
{"error": {"code": "unknown_metric", "message": "unknown metric \"revenu\"",
"candidates": ["revenue"], "suggested_retry": {"metrics": ["revenue"]}}}
Agent 应当读错误再改查询,而不是原样重试。服务端的 MCP 指引里也是这么写的。