docs(extension): 补充阶段C MCP开发说明
This commit is contained in:
@@ -2,7 +2,7 @@
|
||||
|
||||
> 本文档用于团队开发和模块联调,记录当前已经落地的核心边界与使用方式。
|
||||
|
||||
> 更新日期:2026-09-01。第一阶段 AI Core、Agent Core、Extension Core 和 Model Core 主链路已经完成;第二阶段 Agent Trace 持久化和可恢复 SSE 已落地,后端当前回归基线为 81 项测试通过。
|
||||
> 更新日期:2026-09-01。第一阶段 AI Core、Agent Core、Extension Core 和 Model Core 主链路已经完成;第二阶段 Agent Trace 持久化、可恢复 SSE、stdio MCP Bridge 与隔离 Plugin Host 已落地,后端当前回归基线为 87 项测试通过。
|
||||
|
||||
## 当前实现
|
||||
|
||||
@@ -33,12 +33,14 @@ backend/app/
|
||||
│ ├── permissions.py 权限策略、确认请求和会话授权
|
||||
│ └── builtin_tools.py 无副作用的内置开发 Tool
|
||||
├── extensions/
|
||||
│ └── runtime.py Skill/Plugin Manifest、生命周期、依赖与 Tool Contribution
|
||||
│ ├── runtime.py Skill/Plugin Manifest、生命周期、依赖与 Tool Contribution
|
||||
│ └── mcp.py stdio JSON-RPC、MCP 生命周期、发现、调用与 Host 隔离
|
||||
└── container.py AI Core 依赖组装
|
||||
|
||||
backend/extensions/
|
||||
├── skills/knowledge-assistant/ 内置知识库 Skill
|
||||
└── plugins/text-tools/ 内置示例 Plugin
|
||||
├── plugins/text-tools/ 内置声明式 Plugin
|
||||
└── fixtures/mcp-echo/ 离线 MCP Server 联调 Fixture
|
||||
```
|
||||
|
||||
Router 只负责 HTTP/SSE 与错误转换,不实现 Agent、Tool 或 Provider 业务逻辑。
|
||||
@@ -57,6 +59,7 @@ Router 只负责 HTTP/SSE 与错误转换,不实现 Agent、Tool 或 Provider
|
||||
- SQLite Trace、分页快照与可恢复 SSE;
|
||||
- Skill Manifest、Prompt、Tool/Permission/模型能力解析;
|
||||
- Plugin Manifest、生命周期和 Tool Contribution;
|
||||
- stdio MCP Bridge、隔离进程生命周期、Tool 映射与 Host 健康状态;
|
||||
- Skill 调用内置 Tool 与 Plugin Tool;
|
||||
- 公共 Contract 和 API 接入。
|
||||
|
||||
@@ -65,7 +68,7 @@ Router 只负责 HTTP/SSE 与错误转换,不实现 Agent、Tool 或 Provider
|
||||
- Note、NoteBlock、Markdown Parser:由 Knowledge Core 提供;
|
||||
- FTS5、Vector、RRF、Reranker、Citation:由 Retrieval Core 提供;
|
||||
- 文件系统和 API Key 明文读取:由 Rust Host 提供;
|
||||
- MCP Plugin Host、Frontend Extension Slot:按技术基线放在第二阶段实现。
|
||||
- Frontend Extension Slot 与 Plugin Command/Settings:按第二阶段后续阶段实现。
|
||||
|
||||
## Provider
|
||||
|
||||
@@ -302,7 +305,7 @@ DELETE /api/skills/{skill_id}
|
||||
|
||||
### Plugin Runtime
|
||||
|
||||
第一阶段 Plugin Runtime 完成 Manifest 校验、安装、启用、停用、卸载和 Tool Contribution。第三方代码不会直接 import 到 AI Core;当前 Declarative Plugin Host 只执行宿主实现的白名单 handler,MCP Host 留到第二阶段。
|
||||
第一阶段 Plugin Runtime 完成 Manifest 校验、安装、启用、停用、卸载和声明式 Tool Contribution。阶段 C 增加 stdio MCP Bridge:第三方代码不会直接 import 到 AI Core,而由独立子进程运行,通过换行分隔 JSON-RPC 完成 initialize、Tool 发现和调用。
|
||||
|
||||
启用 Plugin 时将 Tool 注册到统一 Tool Registry,并标记 `source=plugin`;停用或异常时注销 Tool。启用中的 Skill 依赖某 Plugin Tool 时,Plugin 不能直接卸载。
|
||||
|
||||
@@ -315,11 +318,15 @@ GET /api/plugins/{plugin_id}
|
||||
POST /api/plugins/{plugin_id}/enable
|
||||
POST /api/plugins/{plugin_id}/disable
|
||||
PUT /api/plugins/{plugin_id}/permissions
|
||||
GET /api/plugins/{plugin_id}/host
|
||||
POST /api/plugins/{plugin_id}/host/restart
|
||||
DELETE /api/plugins/{plugin_id}
|
||||
```
|
||||
|
||||
Plugin Manifest 中的权限只是声明,不代表已经授权。带权限的 Plugin 安装后进入 `permission_required`,Host 必须通过权限接口记录用户授权,之后才能启用。JSON Schema 在安装阶段校验,Tool 调用时再次校验实际参数。
|
||||
|
||||
MCP Tool 进入 Registry 前统一增加 `<plugin_id>.<remote_name>` 命名空间。Server 声明的 `notesagent/permission` 必须属于已知权限并出现在 Plugin Manifest;发现集合还必须与 Manifest Contribution 完全一致。启用失败会回滚全部 Tool 并关闭子进程,异常退出会把 Plugin 标记为 `error` 并立即注销对应 Tool。详细实现和 Fixture 操作见 [MCP Bridge 与 Plugin Host 开发说明](MCP-Bridge与Plugin-Host开发说明.md)。
|
||||
|
||||
内置示例 `text-tools` 注册 `text.uppercase`。内置 `knowledge-assistant` Skill 同时声明 `notes.search` 和 `text.uppercase`,用于验证完整链路:
|
||||
|
||||
```text
|
||||
@@ -342,4 +349,4 @@ Skill Manifest
|
||||
- Task 已持久化到 SQLite;Attachment Tool 读取 Host 管理目录中的 UTF-8 文件。
|
||||
- `audio.transcribe` 当前消费 Host 预生成的 transcript;faster-whisper 与说话人分离仍按技术基线在第二阶段接入。
|
||||
- Extension 安装记录暂存内存;后续接入持久化 Registry 与版本升级流程。
|
||||
- 当前 Plugin Host 只支持内置声明式白名单 handler;MCP Bridge、独立进程健康检查与 UI Contribution 在第二阶段实现。
|
||||
- 当前 Plugin Host 支持内置声明式 handler 和本地 stdio MCP Server;Streamable HTTP、OS 级沙箱、Plugin Command/Settings 与 UI Contribution 留在后续阶段。
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
> 本文档用于团队开发和模块联调,记录 Knowledge Core / Retrieval Core 已经落地的
|
||||
> 模块边界、数据模型、接口与使用方式,对应分工表中的杨星萱。
|
||||
|
||||
> 更新日期:2026-09-01。第一阶段 Knowledge/Retrieval 主链路已经完成,并已接入 Agent Tool Registry;完整后端回归基线为 81 项测试通过。
|
||||
> 更新日期:2026-09-01。第一阶段 Knowledge/Retrieval 主链路已经完成,并已接入 Agent Tool Registry;完整后端回归基线为 87 项测试通过。
|
||||
|
||||
## 当前实现
|
||||
|
||||
|
||||
@@ -0,0 +1,262 @@
|
||||
# MCP Bridge 与 Plugin Host 开发说明
|
||||
|
||||
> 更新日期:2026-09-01。本文记录第二阶段阶段 C 已实现的本地 stdio MCP Bridge、隔离 Plugin Host、Tool Contract 转换和离线测试方式。Plugin Command 与 Settings 属于阶段 D,不在本文实现范围内。
|
||||
|
||||
## 1. 目标与实现状态
|
||||
|
||||
阶段 C 的目标是让外部 MCP Server 进入既有 Plugin、Tool、Permission、Agent 和 Trace 链路,同时避免 Agent Runtime、前端或 Benchmark 直接依赖 MCP 原始消息。
|
||||
|
||||
当前链路:
|
||||
|
||||
```text
|
||||
Plugin Manifest
|
||||
→ Plugin Runtime
|
||||
→ 独立 stdio MCP Server 进程
|
||||
→ initialize / capability negotiation
|
||||
→ tools/list 分页发现与校验
|
||||
→ NotesAgent ToolDefinition
|
||||
→ Tool Registry / Permission Manager
|
||||
→ Agent Runtime / Agent Trace
|
||||
```
|
||||
|
||||
已经实现:
|
||||
|
||||
- 本地 stdio 子进程启动、关闭和异常退出检测;
|
||||
- UTF-8、换行分隔的 JSON-RPC 2.0 消息;
|
||||
- initialize、协议版本与 tools capability 协商;
|
||||
- `notifications/initialized`;
|
||||
- 分页 `tools/list`;
|
||||
- `tools/call`、业务错误与 JSON-RPC 错误转换;
|
||||
- 超时和 `notifications/cancelled`;
|
||||
- Tool 命名空间、JSON Schema、权限和 Manifest 集合校验;
|
||||
- Host 状态查询、重启和异常后的 Tool 自动注销;
|
||||
- stderr 隔离、环境变量裁剪、消息及结果大小限制;
|
||||
- 无网络、无密钥的确定性 MCP Fixture。
|
||||
|
||||
实现依据为 MCP 官方 [Lifecycle 2025-11-25](https://modelcontextprotocol.io/specification/2025-11-25/basic/lifecycle)、[Transports 2025-11-25](https://modelcontextprotocol.io/specification/2025-11-25/basic/transports) 和 [Tools 2025-11-25](https://modelcontextprotocol.io/specification/2025-11-25/server/tools)。
|
||||
|
||||
## 2. 代码位置
|
||||
|
||||
```text
|
||||
backend/app/extensions/mcp.py
|
||||
stdio 进程、JSON-RPC、MCP 生命周期、发现、调用和 Host 状态
|
||||
|
||||
backend/app/extensions/runtime.py
|
||||
Plugin Manifest、权限、MCP Tool 批量注册/回滚和生命周期集成
|
||||
|
||||
backend/app/agent/tools.py
|
||||
内部 Tool 参数校验、结构化执行错误和线程安全 Registry
|
||||
|
||||
backend/extensions/fixtures/mcp-echo/
|
||||
确定性 stdio MCP Server 与 Plugin Manifest
|
||||
```
|
||||
|
||||
## 3. Plugin Manifest
|
||||
|
||||
MCP Plugin 的后端配置示例:
|
||||
|
||||
```yaml
|
||||
id: example-mcp
|
||||
name: Example MCP
|
||||
version: 1.0.0
|
||||
permissions:
|
||||
- notes.read
|
||||
contributes:
|
||||
tools:
|
||||
- example-mcp.search
|
||||
backend:
|
||||
type: mcp
|
||||
transport: stdio
|
||||
command: python
|
||||
args: [server.py]
|
||||
startup_timeout_seconds: 5
|
||||
tool_timeout_seconds: 30
|
||||
```
|
||||
|
||||
约束:
|
||||
|
||||
- 阶段 C 只接受 `type: mcp` 与 `transport: stdio`;
|
||||
- 命令和参数通过数组直接传给 `subprocess.Popen`,不经过 Shell;
|
||||
- PATH 中的 executable 使用名称,例如 `python`、`node`;
|
||||
- manifest 中带目录的 executable 必须解析到 Plugin 包内部;
|
||||
- `contributes.tools` 使用 `<plugin_id>.<remote_name>`;
|
||||
- 安装阶段只读 Manifest,不启动第三方进程;
|
||||
- 完成用户授权后,`enable` 才启动 Host。
|
||||
|
||||
## 4. 生命周期
|
||||
|
||||
### 4.1 启动
|
||||
|
||||
启用 MCP Plugin 时依次执行:
|
||||
|
||||
1. 检查 Plugin 声明权限是否全部获得授权;
|
||||
2. 检查 Manifest 声明的 Tool ID 是否与现有 Registry 冲突;
|
||||
3. 启动独立 stdio Server;
|
||||
4. 发送 `initialize`;
|
||||
5. 校验协商版本和 `tools` capability;
|
||||
6. 发送 `notifications/initialized`;
|
||||
7. 分页读取 `tools/list`;
|
||||
8. 校验全部 Tool;
|
||||
9. 确认发现集合与 Manifest 完全一致;
|
||||
10. 将完整集合注册到 Tool Registry;
|
||||
11. Plugin 和 Host 进入 `ready`。
|
||||
|
||||
任何步骤失败都会注销本轮已注册 Tool、关闭子进程并把 Plugin 标记为 `error`,不会留下半启用状态。
|
||||
|
||||
### 4.2 停止与异常退出
|
||||
|
||||
停用、卸载或应用关闭时,先注销 Tool,再关闭 stdin,等待 Server 正常退出。超时后依次 terminate 和 kill。
|
||||
|
||||
Server 异常退出、stdout 出现非 JSON-RPC 内容或发送超大协议消息时:
|
||||
|
||||
- 未完成请求返回 `PLUGIN_HOST_UNAVAILABLE`;
|
||||
- Host 进入 `unhealthy`;
|
||||
- Plugin 进入 `error`;
|
||||
- 对应 Tool 从 Registry 中立即注销;
|
||||
- 用户可以调用 Host Restart 接口重新协商和发现。
|
||||
|
||||
Server 发送 `notifications/tools/list_changed` 时不会直接信任新集合。当前实现先把 Host 标记为不健康并注销旧 Tool,要求通过 Restart 重新执行完整发现与校验。
|
||||
|
||||
## 5. Tool Contract 转换
|
||||
|
||||
MCP Tool:
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "search",
|
||||
"description": "Search notes",
|
||||
"inputSchema": { "type": "object", "properties": {} },
|
||||
"_meta": { "notesagent/permission": "notes.read" }
|
||||
}
|
||||
```
|
||||
|
||||
进入系统后转换为:
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "example-mcp.search",
|
||||
"description": "Search notes",
|
||||
"parameters": { "type": "object", "properties": {} },
|
||||
"permission": "notes.read",
|
||||
"source": "plugin"
|
||||
}
|
||||
```
|
||||
|
||||
转换规则:
|
||||
|
||||
- 远端名称必须能转换为合法且稳定的项目 Tool ID;
|
||||
- `inputSchema` 必须是有效的 object JSON Schema;
|
||||
- `_meta.notesagent/permission` 必须属于项目已知权限;
|
||||
- Tool 权限必须同时出现在 Plugin Manifest 中;
|
||||
- Agent 仍通过 Tool Registry 执行参数校验、Permission、超时和 Trace;
|
||||
- MCP `structuredContent` 存在时映射为内部 output;否则保留为受控 `content` 数组;
|
||||
- MCP `isError: true` 映射为 `MCP_TOOL_CALL_FAILED`;
|
||||
- 结果超过 256 KiB 映射为 `MCP_TOOL_RESULT_TOO_LARGE`。
|
||||
|
||||
## 6. 隔离与安全边界
|
||||
|
||||
当前隔离是“独立进程 + 协议边界”,不是完整的操作系统沙箱。
|
||||
|
||||
已经执行的保护:
|
||||
|
||||
- 第三方模块不 import 到 AI Core;
|
||||
- 子进程 `cwd` 固定为 Plugin 包目录;
|
||||
- 不使用 Shell 拼接命令;
|
||||
- 不把 Provider API Key、`APP_DB_PATH`、Vault 路径和其他宿主环境变量传入子进程;
|
||||
- stderr 与 JSON-RPC stdout 分离,stderr 不进入 API 和 Agent Trace;
|
||||
- stdout 只能发送合法 MCP JSON-RPC;
|
||||
- 单条协议消息上限 2 MiB;
|
||||
- 单次 Tool Result 上限 256 KiB;
|
||||
- MCP Tool 不绕过 Permission Manager 和 Agent Tool Timeout。
|
||||
|
||||
当前尚未提供容器、受限系统账户、seccomp、Windows AppContainer 或 macOS Sandbox,因此 Plugin 进程仍具有当前操作系统用户授予的一般文件访问能力。正式社区插件分发前必须继续增加包签名、来源验证和平台级沙箱;不得把当前进程隔离描述为完全安全执行任意不可信代码。
|
||||
|
||||
## 7. Host API
|
||||
|
||||
```http
|
||||
GET /api/plugins/{plugin_id}/host
|
||||
POST /api/plugins/{plugin_id}/host/restart
|
||||
```
|
||||
|
||||
状态响应包含:
|
||||
|
||||
```text
|
||||
plugin_id
|
||||
backend_type / transport
|
||||
status
|
||||
tools_count
|
||||
started_at / last_seen_at
|
||||
protocol_version
|
||||
server_name / server_version
|
||||
error
|
||||
```
|
||||
|
||||
状态值:
|
||||
|
||||
```text
|
||||
stopped
|
||||
starting
|
||||
ready
|
||||
unhealthy
|
||||
error
|
||||
```
|
||||
|
||||
Restart 返回 `202 OperationResponse`。接口返回前已完成本地 Host 重启和 Tool 重新发现;`message` 中给出最终 Host 状态。
|
||||
|
||||
## 8. 离线 Fixture
|
||||
|
||||
Fixture 位于:
|
||||
|
||||
```text
|
||||
backend/extensions/fixtures/mcp-echo
|
||||
```
|
||||
|
||||
它提供:
|
||||
|
||||
- `mcp-fixture.echo`:返回 structuredContent;
|
||||
- `mcp-fixture.fail`:返回 `isError: true`;
|
||||
- `mcp-fixture.sleep`:验证超时和取消;
|
||||
- `mcp-fixture.large`:验证结果大小上限;
|
||||
- `mcp-fixture.environment`:验证宿主 Secret/路径没有进入子进程;
|
||||
- `mcp-fixture.exit`:验证异常退出、Tool 注销和 Restart。
|
||||
|
||||
Fixture 的 `tools/list` 使用两页响应,用于覆盖分页发现。测试还会启动缺少 tools capability 和返回无效 Schema 的变体。
|
||||
|
||||
## 9. 验证
|
||||
|
||||
```powershell
|
||||
cd backend
|
||||
uv run python -m compileall -q app
|
||||
uv run pytest
|
||||
|
||||
cd ../frontend
|
||||
pnpm test
|
||||
pnpm type-check
|
||||
pnpm build
|
||||
```
|
||||
|
||||
阶段 C 新增测试覆盖:
|
||||
|
||||
- initialize、版本和 capability negotiation;
|
||||
- 分页 `tools/list` 与命名空间映射;
|
||||
- Permission、JSON Schema 与 Contribution 集合;
|
||||
- Tool 成功、业务错误、结果过大和超时;
|
||||
- Agent Runtime 调用 MCP Tool 并写入正式 Trace;
|
||||
- Secret/Vault 环境隔离;
|
||||
- Server 异常退出、Tool 注销和 Host Restart;
|
||||
- 缺少 capability 与无效 MCP Schema;
|
||||
- OpenAPI 发布 Host 状态和重启路径。
|
||||
|
||||
## 10. 当前边界与后续阶段
|
||||
|
||||
阶段 C 不包含:
|
||||
|
||||
- Streamable HTTP MCP transport;
|
||||
- Resources、Prompts、Sampling、Elicitation 和 MCP Tasks;
|
||||
- Plugin Command 与 Settings Contribution;
|
||||
- Secret Reference 注入;
|
||||
- Plugin Registry 持久化、签名与社区来源校验;
|
||||
- 操作系统级沙箱;
|
||||
- Tool 列表热更新的无中断替换。
|
||||
|
||||
阶段 D 将在当前 Plugin Runtime 上继续增加 Command、Settings、Secret Contract 和命名空间 Storage,不修改 Agent 使用内部 Tool Contract 的原则。
|
||||
@@ -187,12 +187,12 @@ pnpm build
|
||||
```text
|
||||
pnpm build passed
|
||||
pnpm test 27 passed
|
||||
uv run pytest 81 passed
|
||||
uv run pytest 87 passed
|
||||
preview smoke HTTP 200
|
||||
git diff --check passed
|
||||
```
|
||||
|
||||
当前前端使用 Vitest 执行 Store、Workspace API Adapter、SSE 恢复游标、文件树、编辑器组件、智能体标签、轻量动效约束、Markdown 对比度 Token、scoped CSS 选择器约束和 Shiki GitHub 双主题测试;`pnpm build` 同时执行 `vue-tsc -b` 与 Vite 生产构建。后端测试出现过 `.pytest_cache` 无法写入的 Windows 权限警告,不影响 81 项测试结果,也不涉及产品代码。
|
||||
当前前端使用 Vitest 执行 Store、Workspace API Adapter、SSE 恢复游标、文件树、编辑器组件、智能体标签、轻量动效约束、Markdown 对比度 Token、scoped CSS 选择器约束和 Shiki GitHub 双主题测试;`pnpm build` 同时执行 `vue-tsc -b` 与 Vite 生产构建。后端测试出现过 `.pytest_cache` 无法写入的 Windows 权限警告,不影响 87 项测试结果,也不涉及产品代码。
|
||||
|
||||
Vite 当前会提示 Chat 与 Workspace 的部分异步 Chunk 超过 500 kB,这是 Milkdown、CodeMirror、KaTeX 和 Shiki 等编辑/渲染依赖带来的性能优化项,不影响构建成功或功能正确性;进入桌面打包前应通过手动分包或更细粒度动态加载继续优化。
|
||||
|
||||
|
||||
@@ -104,4 +104,4 @@ pnpm build
|
||||
|
||||
自动化验证覆盖 Provider 预设、OpenAI-Compatible `/models` 请求与鉴权头、模型映射、前端自动刷新、排序去重及按 Provider 隔离错误。生产构建同时执行 Vue 和 TypeScript 类型检查。
|
||||
|
||||
当前完整回归基线:后端 81 项测试、前端 27 项测试通过,前端类型检查和生产构建通过。Provider 配置目前仍保存在内存 Registry,AI Core 重启后需要重新创建;凭据密文会保留。OpenAI Responses 与 Anthropic Messages Adapter 尚未实现,设置页正式预设不会使用这两种协议。
|
||||
当前完整回归基线:后端 87 项测试、前端 27 项测试通过,前端类型检查和生产构建通过。Provider 配置目前仍保存在内存 Registry,AI Core 重启后需要重新创建;凭据密文会保留。OpenAI Responses 与 Anthropic Messages Adapter 尚未实现,设置页正式预设不会使用这两种协议。
|
||||
|
||||
Reference in New Issue
Block a user