17 KiB
window.__TAURITAVERN__.api.mcp — MCP Manager API
本文档描述已经落地的 MCP Manager Host ABI,以及 Agent 与 Legacy generation 的消费契约。MCP 是独立平台能力;当前提供 registration、persistent tool catalog、model-facing tool description override、显式 refresh、第一方 Manager test call,并由 Agent 与 Legacy generation 消费 cached tools。
状态:Manager、Agent 与 Legacy generation 集成已实现,Project Contract(实验性)。
第一方管理 UI 位于 Extensions 抽屉的 MCP 内置扩展中。该扩展只是 api.mcp 的 React/strict TSX presentation;TauriTavern Settings 不再提供平行入口。
1. 入口
await (window.__TAURITAVERN__?.ready ?? window.__TAURITAVERN_MAIN_READY__);
const mcp = window.__TAURITAVERN__.api.mcp;
2. API
type TauriTavernMcpApi = {
servers: {
list(): Promise<{ servers: McpServer[]; storageIssues: McpStorageIssue[] }>;
create(input: {
displayName: string;
endpoint: string;
headers?: Record<string, string>;
protocolVersion?: McpProtocolVersion;
}): Promise<McpServer>;
update(input: {
registrationId: string;
displayName: string;
endpoint: string;
headers: Record<string, string>;
protocolVersion: McpProtocolVersion;
}): Promise<McpServer>;
setState(input: { registrationId: string; state: 'active' | 'paused' }): Promise<McpServer>;
remove(input: string | { registrationId: string }): Promise<void>;
discover(input: string | { registrationId: string }): Promise<McpDiscoveryResult>;
refresh(input: string | { registrationId: string }): Promise<McpDiscoveryResult>;
};
tools: {
setPermission(input: {
registrationId: string;
nativeName: string;
permission: 'off' | 'ask' | 'allow';
}): Promise<McpServer>;
setDescriptionOverride(input: {
registrationId: string;
nativeName: string;
override: ToolDescriptionOverride | null;
}): Promise<McpServer>;
testCall(input: {
registrationId: string;
nativeName: string;
argumentsJson: string;
}, options?: { signal?: AbortSignal }): Promise<McpTestCallOutcome>;
};
};
没有 connect、disconnect 或 connected。只有 cold discovery、显式 refresh 和 test call 才建立短生命周期 RMCP client;memory/disk catalog hit 不连接 server。active 表示允许读取该 registration 的 snapshot,并在需要时向其 endpoint 发起 discovery 或用户 test call。
3. DTO
type McpServer = {
id: string; // canonical lowercase UUID
displayName: string;
endpoint: string; // normalized HTTP(S) URL
headers: Record<string, string>;
protocolVersion: McpProtocolVersion;
state: 'active' | 'paused';
toolPermissions: Record<string, 'ask' | 'allow'>;
toolDescriptionOverrides: Record<string, ToolDescriptionOverride>;
};
type ToolDescriptionOverride = {
description?: string;
properties?: Record<string, string>;
};
type McpProtocolVersion =
| 'auto'
| '2026-07-28'
| '2025-11-25'
| '2025-06-18'
| '2025-03-26';
type McpDiscoveryResult = {
registrationId: string;
protocolVersion: string;
serverName?: string;
serverVersion?: string;
tools: Array<{
id: string; // mcp/<registration-uuid>:<native-name>
nativeName: string;
title?: string;
description?: string;
inputSchema: object;
outputSchema?: object;
annotations: object; // untrusted hints only
permission: 'off' | 'ask' | 'allow';
}>;
diagnostics: Array<{
code: string;
nativeName?: string;
message: string;
}>;
staleTools: Array<{
nativeName: string;
permission: 'ask' | 'allow';
}>;
};
type McpTestCallOutcome =
| {
outcome: 'known_response';
response:
| {
kind: 'tool_result';
isError: boolean;
textBlocks: Array<{ index: number; text: string }>;
structuredJson?: string;
diagnostics: Array<{ code: string; message: string; contentIndex?: number }>;
}
| { kind: 'server_error'; code: number; message: string; dataJson?: string }
| { kind: 'unsupported_response'; responseType: string; message: string };
}
| { outcome: 'not_sent'; code: string; message: string }
| { outcome: 'outcome_unknown'; code: string; message: string };
storageIssues 显式报告已经定位到具体文件的读取失败、损坏、未知 schema/kind、非 canonical 文件名或文件内 ID 不匹配;健康 registration 仍正常返回。registration 目录本身无法枚举时,list() 仍 reject。
4. Registration 契约
create()总是创建pausedregistration;Manager 在切换为 Active 前展示并确认 exact endpoint。- Manager 首次加载时会通过现有
create()契约添加一次 Paused 的 Exa Search 推荐项;处理标记保存在扩展 store 中。删除该普通 registration 不会清除标记,因此同一 data root 中不会自动恢复。 - display name、endpoint、custom request headers 与 protocol version 由
update()原子修改,不影响 UUID、ToolId、Active/Paused 或已保存权限。endpoint/headers/protocol 实际变化会删除该 registration 的 memory/disk catalog;仅改名称不清 catalog,也不会联网。 - headers 的名称和值原样保存;registration 不维护 reserved-header 列表或数量/总量上限,HTTP transport 无法接受的配置在发送前返回
not_sent。endpoint credentials 与 header values 明文保存在 registration 文件中、由servers.list()回传给同一 WebView 的编辑器,并随包含该 data root 的备份流转。 - protocol version 缺省为
auto。固定版本只允许该版本参与 lifecycle 协商;当前选项与 Streamable HTTP transport 实际支持集一致。 off是缺省值,不写入toolPermissions;setPermission(..., 'off')删除对应持久设置。setDescriptionOverride()按 native name 保存 model-facing description/property-description 覆盖;字符串原样保存,不做 trim 或改写。override: null是唯一删除语义,空对象显式拒绝。值不写入或改写 discovery catalog,也不改变 ToolId、schema 结构、permission 或执行目标。- 模型描述优先级固定为 server catalog → registration override → Agent Profile
tools.toolDescriptions。Legacy 消费前两层;Agent Profile 同一字段存在时覆盖 registration 值。 - discovery 消失的 Ask/Allow 设置作为
staleTools返回,但不会成为可用工具。 - registration 保存为
_tauritavern/mcp/registrations/<uuid>.json的严格 v1 单文件记录;persistent catalog 保存为_tauritavern/mcp/catalogs/<uuid>.json的独立严格 v1 记录。两者都没有旧 schema reader 或 revision graph。 - Manager 的 Add 弹窗提供手动与 JSON 两种输入;JSON 每次只接受一个 Streamable HTTP server,支持
{ "name": { "url": "...", "headers": {...}, "protocolVersion": "auto" } }和标准mcpServers包装。已保存 server 的 Edit 弹窗复用同一手动表单编辑名称、endpoint、headers 与 protocol version。无效 JSON、多个 server、非字符串 header、未知协议版本或不支持的 transport 会整体拒绝,不做 partial import。
5. Discovery 契约
- transport 仅支持 Streamable HTTP;不实现 OAuth 流程,认证可由 registration custom headers 提供。
- registration headers 会应用到 initialize、discovery、session 与 tool call 的全部 HTTP 请求;系统不在不同消费路径复制或重建认证状态。
- endpoint 接受任意带 host 的 HTTP(S) URL,包括公网 HTTP、userinfo 与 query;未加密 HTTP 的风险由 Manager 在激活前提示。fragment 不会进入 HTTP 请求,因此仍作为无效 endpoint 显式报错;redirect 仍由 transport 禁止。
protocolVersion: 'auto'使用 RMCP 3.1.2ClientLifecycleMode::Auto优先协商2026-07-28;固定值使用同一 lifecycle,但将 discover candidates 与 legacy initialize version 同时收窄到该版本。标准-32022协商与 SDK 可见的-32601legacy fallback 由 RMCP 处理。若启动返回 implementation-defined-32000,或有限 SSE error 响应在 SDK 中退化为ConnectionClosed,则用新 Peer 单次尝试相同配置的 legacy initialize;该额外路径不匹配其他错误。tools/list必须完整分页;cursor 循环、页数/工具数/catalog 总量超限或分页失败会使该 server 的本次 discovery 失败,不返回 partial catalog。- duplicate native name 隔离整个同名组;无效 input schema、单工具超限或名称无效只隔离该工具并返回 diagnostic。无效的可选 output schema 只移除该字段并返回 diagnostic,不阻止工具使用。
- input/output schema 按 JSON Schema 2020-12 编译验证;不会读取远端
$ref。 - annotations 只原样展示,不授予权限。
servers.discover()按 application memory、persistent file、真实 discovery 的顺序读取。磁盘 snapshot 绑定 registration UUID 与规范化 endpoint,载入后重新执行 application canonical validation。servers.refresh()始终使用新的 RMCP Peer,绕过 memory/disk snapshot。cold discovery/refresh 在完整分页和全部 validation 成功后发布;写盘失败返回可用的 memory-only catalog 与mcp.catalog_persistence_faileddiagnostic,并保留旧磁盘 snapshot。网络或 validation 失败仍 reject,旧 snapshot 保留但不作为该请求的返回值。- permission 与
staleTools每次按当前 registration 投影,不写入 catalog snapshot。Paused gate 先于 snapshot lookup;registration 删除不受派生 catalog 清理失败影响,清理失败只记录 warning。 - catalog 跨应用重启保留,由用户手动 refresh 决定何时更新;没有 TTL、后台 refresh、list-changed subscription、自动 retry、source/age 字段或 migration reader。损坏/未知 schema/UUID/endpoint mismatch 显式报错,refresh 可绕过并修复。
6. User test call 契约
tools.testCall() 是第一方 Manager 的显式用户动作,不是面向任意扩展的通用 raw RPC:
- frontend 只提交 registration ID、native tool name 与原始
argumentsJson;endpoint、header、RMCP session 均由 backend registration 与 transport 决定。 - server 必须为 Active。Off/Ask/Allow 不阻止用户 test call,调用也不会修改保存的 permission。
argumentsJson上限为 256 KiB,backend 权威解析为 JSON object;不按 input schema 补默认值、转换类型、删除字段或在 server 之前做业务校验。字符串 Host 边界与 Rustserde_json解析共同保留i64/u64范围内的 JavaScript 不安全整数。- 每次点击只发送一次
tools/call,没有自动 retry。RMCP 2026 协商会在同一 Peer 上分页tools/list直到找到目标工具,仅用于填充 SEP-2243 standard-header metadata;目标始终未出现时才遍历完整目录。它不成为 application catalog cache,也不改变调用参数。 - 使用一次响应的低层 request handle,不自动驱动
input_required、Tasks 或其他 MRTR 后续轮次;这些 server 响应以unsupported_response明确展示。 AbortSignal表示停止本地等待,不能撤销远端副作用。Host 在调用前使用私有 start acknowledgement 建立取消事实,避免 cancel-before-register 竞态;该命令不属于公共 API。
顶层 outcome 是远端事实,而非 UI loading 状态:
known_response:server 已明确响应。isError: true仍是 tool result;JSON-RPC error 是server_error,都不变成 command rejection。not_sent:backend 能证明目标tools/call尚未交给 transport,例如 registration 暂时无法读取、Paused、JSON 不合法、HTTP client/header 配置失败、metadata hydration 失败或发送前取消。outcome_unknown:request handle 已建立后发生 cancel、timeout、disconnect 或无法确认响应;调用可能已经执行,系统绝不自动重试。
已知 tool result 按原顺序投影 text、确定性 structured JSON、isError 与 diagnostic。当前不显示的 image/audio/resource block 与 metadata 会产生可见 diagnostic,不会抹掉“server 已响应”的事实。完整 response 已受 4 MiB wire 上限约束,text/structured 不再做第二次静默截断;raw response 超限或 malformed 时则是 outcome_unknown。取得已知响应后的 client close/join 失败只记录日志,不会改写 outcome。
该入口沿用当前 WebView trust model:同一 WebView 内的第一方/vendor extension script 被视为用户授权代码,backend 不声称能证明一次 command 源自物理点击或隔离 hostile extension。若 trust model 改变,应增加真实 command capability boundary,而不是在 DTO 中伪造 click flag。
7. Agent 消费契约
Agent 没有新增公共 raw-call API。Profile v3 以 mcp/<registration-uuid>:<native-name> 选择工具;每个 root、return-mode child 与 handoff invocation 都通过 McpService 读取 memory→disk persistent snapshot,绝不因启动 Agent 隐式 cold discovery。无 snapshot 或工具不在缓存时,工具从该 invocation 省略并留下 diagnostic;用户在 Manager 中 discovery/refresh 后,下一个 invocation 才观察新目录。
只有 Active 且 permission 为 Ask/Allow、input schema root 明确为 type: "object" 的工具可以进入 Agent binding。共享 resolver 先将 registration description override 应用到 catalog descriptor 的副本,Agent invocation 再应用 Profile tools.toolDescriptions,因此 Profile 优先。当前按用户要求不实现 Ask 审批 UI:Ask 与 Allow 都自动执行;Off 与 Paused 在广告前过滤,并在实际发送前重新读取 registration 复核。参数必须是 256 KiB 内 JSON object,Host 不按 schema 改写。alias 由 Agent snapshot 生成,不属于 Manager DTO 或 MCP identity。
已知结果投影为现有 AgentToolResult。内部 structured、error_code 与 resource_refs 不作为字段发送给模型;text 保持原文,structured content 去重后转为 ## Details Markdown,可操作的非文本 content diagnostic 转为 ## Notes,metadata diagnostic 不进入模型。模型可见 content 超过 Agent Profile 的 tools.mcpResultInlineCharLimit(默认 50,000)时,完整 JSON 保存在内部 audit,同时生成同一完整 Markdown 的行可读 .txt 视图;模型得到最多前 3,000 个 Unicode 字符的前缀预览、可读视图路径与分段读取指引,不接收 audit JSON 路径。.txt 视图换行超长物理行,JSON audit 保留精确原始结果。该值只属于 Agent invocation 投影,不进入共享 McpService 调用契约。outcome_unknown 不自动 retry,也不伪造 tool result;当前没有审批/未知结果交互状态机,因此终止当前 Agent run。
8. Legacy 消费契约
Legacy 集成没有扩展上面的公共 api.mcp。Legacy 使用第一方内部 commands 从 McpService 获取 Active + Ask/Allow 的 cached descriptors,并通过 permission-aware call 用例执行;第三方仍没有公共 raw MCP executor。
- catalog preparation 只读 memory/disk snapshot,cache miss 不联网;单 registration 的 snapshot 读取/校验失败形成 diagnostic,不阻塞健康 MCP/local tools 或普通生成。
- registration description override 在共享 resolver 中应用,Legacy root 冻结覆盖后的 descriptor;后续 Manager 修改只影响下一个 root。
- 每个实际 Legacy root generation 冻结 descriptors;工具递归复用 descriptor snapshot,但每轮根据当前 local view 重新分配 alias 并重建 provider schema。执行只解析当前 round binding;MCP 不进入全局 ToolManager、slash commands 或 extension enumeration。
- local alias 优先;settings hook 后只接受仍唯一存在的初始 MCP alias。删除、改名、重复或失去 binding 的 alias 不按名称/schema猜 canonical ToolId。
custom_include_body/custom_exclude_body继续作为用户最终 upstream body 意图;Legacy MCP 不保护、拒绝或重注入tools/tool_choice。- 调用只提交 canonical ToolId、ToolManager 序列化的
argumentsJson与独立 UUIDexecutionCallId;不做 input-schema coercion。providertool_call_id只用于聊天消息关联。发送前重新检查 registration Active、Off 与 cancellation;Ask/Allow 当前都自动执行。 - Known error 和普通 NotSent 进入现有 error Tool message,让模型下一轮修正。
outcome_unknown不重试、不伪造结果、不部分提交 Tool turn,并终止当前 root/group generation。 - 保存、事件与递归继续使用 SillyTavern Legacy first-class tool-turn contract;旧
extra.tool_invocations只读,不恢复新写。
9. 明确未支持
当前没有以下 API 或行为:
- Ask 审批、公共 raw-call API及 Legacy unknown-outcome 恢复交互;
- OAuth、credential、stdio、2024 HTTP+SSE;
- Resources、Prompts、Tasks、Apps、subscriptions/list-changed;
- background discovery、discovery/list 通用 retry、catalog TTL/revision history;
- registration/Profile 之外的 description scope hierarchy、Manager-defined/global model alias。
model alias 属于短命 Agent/Legacy invocation binding,不属于 registration/discovery/test call。Legacy 集成不把 MCP tool 注册进全局 SillyTavern ToolManager。