首页/参考/MCP 集成

MCP 集成

Trove 可以把问数能力作为 MCP 服务暴露给 AI 客户端 (Claude Desktop / Cursor / Claude Code 等)。这一页讲三种传输方式、 鉴权边界,以及对外暴露的 3 个 tool、3 个 resource、2 个 prompt。

启动

trove mcp                                  # stdio(默认)
trove mcp --transport sse --port 8001
trove mcp --transport streamable-http --port 8001 --token <trove-token>

main.py:963 main_mcp main.py:848 --transport server.py:82 FastMCP

transport典型用途
stdio 本地挂载——Claude Desktop / Cursor / Claude Code 直接拉起进程
sseHTTP 传输,Server-Sent Events
streamable-httpHTTP 传输(http 是它的别名)

鉴权:网络绑定必须带 token

这里有一条 fail-closed 的硬规则:绑定到非回环地址时,必须提供 --token,否则直接拒绝启动。

main.py:877 _LOOPBACK_HOSTS

这个 token 不是随便一个字符串,而是必须能被解析成真实的 Trove 用户—— 解析失败也拒绝启动。也就是说 MCP 通道的鉴权复用同一套用户体系, 没有第二套凭证。

main.py:911 _build_mcp_auth_middleware

HTTP 传输下带 Bearer 中间件,连 health 端点也不豁免—— 未授权的客户端应当快速失败,而不是从一个开放的探活端点推断出服务存在。

身份语义

传输身份数据源可见性
stdio 视作本机可信(等价 admin) 全部
HTTP + token token 对应的用户 按 grants 校验,与 HTTP API 同语义

server.py:141 _authorize_datasource policy.py:146 Principal

授权判定不在这里实现:本模块只解析目标数据源、把主体挂上,结论问 services/authz/policy.py。MCP 与 HTTP 两侧共用同一份策略—— 此前各有一份手抄并且已经漂移过。

「stdio 视作本机可信」是合理的:能拉起这个进程的人本来就有本机权限。 但这也意味着不要把 stdio 形态包装成一个别人能远程触发的服务。

flowchart TB
    CLI["trove mcp"]:::entry --> T{"--transport"}:::entry
    T -- "stdio(默认)" --> L["身份 = 本机可信(等价 admin)
数据源:全部"]:::entry T -- "sse / streamable-http" --> B{"绑定非回环地址?"}:::gate B -- "否 · 回环" --> OK["不强制 token"]:::entry B -- "是" --> TOK{"--token 能解析成真实用户?"}:::gate TOK -- "否" --> NO["拒绝启动"]:::err TOK -- "是" --> U["身份 = token 对应的用户
数据源按 grants 校验,与 HTTP API 同语义"]:::entry classDef entry fill:#71717a14,stroke:#71717a59,stroke-width:1px; classDef gate fill:#f59e0b14,stroke:#f59e0b99,stroke-width:1.5px; classDef err fill:#dc262614,stroke:#dc262699,stroke-width:1.5px; linkStyle 5 stroke:#f59e0b,stroke-width:1.5px
图 1 · fail-closed 在启动这一刻就生效:绑非回环就必须带 token,且 token 必须解析成真实用户,否则直接拒绝启动。 上面这段以源码显示:图渲染库没加载起来(离线、CDN 被拦或浏览器禁用了 JavaScript 时会这样)。这段 Mermaid 文本本身就说明了上面那些框与连线。

三个 tool

tool作用
ask_data 问一个问题,走完整链路
list_datasources列出当前身份可见的数据源
kb_status看某个数据源的 KB 状态

server.py:349 ask_data server.py:398 list_datasources server.py:405 kb_status

ask_data 的返回里除了答案还带 sql、row_count、 verdict,以及 refusal / no_model 两个状态位。 后两者很重要——拒绝回答与「数据源没有语义模型」不是同一种失败, 客户端应当能区分并给出不同的提示。

多轮对话在进程内按 session 缓存(上限 200),数据源名做了路径穿越防护。

server.py:61 _SESSION_CACHE_MAX server.py:197 _ds_name_safe

三个 resource

URI内容
trove://datasources可用数据源清单
trove://{datasource}/schema该数据源的表结构
trove://{datasource}/semantics该数据源的语义模型

server.py:219 datasources server.py:239 schema server.py:245 semantics

resource 与 tool 的区别在这里体现得很清楚:ask_data 是动作, 而语义模型是可以读的上下文。把语义模型作为 resource 暴露, 意味着客户端可以把「这个库有哪些指标」直接放进模型上下文, 从而在提问之前就知道边界在哪。

两个 prompt

prompt作用
datasource_guide介绍某个数据源该怎么问
ask_data把一个自然语言意图转成 ask_data 调用

server.py:323 datasource_guide server.py:338 ask_data

语义优先也在这里生效

可见数据源清单和 HTTP 侧遵循同一条规则:没有语义模型的数据源不出现在清单里。

server.py:152 _datasources_visible

客户端因此不会看到一个「问什么都会失败」的数据源。 这是语义优先在接口层的体现: 可答边界不只影响生成,也影响暴露什么。

客户端配置示例

{
  "mcpServers": {
    "trove": {
      "command": "uv",
      "args": ["run", "trove", "mcp"],
      "cwd": "/path/to/trove"
    }
  }
}

stdio 形态下客户端会自动拉起进程,不需要预先启动服务。 记得 cwd 要指向仓库根——配置与 .trove/ 都是相对它解析的。

继续读

想了解去哪
HTTP 接口API 参考
token 怎么签发运维管理
语义优先的完整含义产品概念
授予数据源访问权安全边界
本页源码锚点的基准提交:anchor-base: 211ae86 GitHub 仓库 文档首页