Skip to content

插件系统

插件系统是Molore的能力扩展机制:插件运行在本机后端进程内,可以提供 REST 接口、MCP 工具和定时同步任务。它解决的问题是——在不改动主干代码的前提下,把外部数据源(Notion、Pocket、Readwise)和外部 AI 客户端(经 MCP)接入本地知识库。本地优先架构下插件与后端同进程、同权限,因此系统选择了「声明制」而非「沙箱制」:权限如实声明、可读可查,但不做运行时拦截。

概念

概念说明
manifest插件的元数据描述文件(manifest.json),声明 id、入口、默认启停、配置 schema 与权限清单
BasePlugin插件基类(app/plugins/base.py),定义生命周期钩子与可选能力(路由 / MCP 工具 / 同步入口)
权限声明manifest 的 permissions 字段,枚举口径见 KNOWN_PERMISSIONSapp/plugins/base.py:12
用户级启停启用/禁用状态按用户存储在 user.settings.plugins,路由与 MCP 工具全局注册、按用户状态判定
自动同步同步类插件的定时任务,由 app/services/plugin_scheduler.py 按用户配置的间隔调度

功能清单

  • manifest 驱动加载:PluginManifest(pydantic)校验 manifest,entrypoint 指向 模块.类 路径,插件管理器 PluginManagerapp/plugins/manager.py)从内置目录与用户插件目录发现并加载。
  • BasePlugin 生命周期:子类构造签名固定为 (manifest, config),可选实现生命周期与能力钩子(见下表)。
  • 用户级启停与配置:set_enabled / set_config 读写 user.settings JSON,进程内 per-user 锁防并发写竞态;凭据类配置落库前加密存储,读取接口不回显明文。
  • 权限声明展示:安装、启用、列表接口都带出 permissions 清单,前端在启用前展示给用户决策。
  • 插件 REST 接口:get_routers() 返回的路由挂载在 /api/v1/plugins/{plugin_id} 下。
  • MCP 工具暴露:register_mcp_tools(mcp) 在共享 FastMCP 实例上注册工具,随启用自动加入 MCP Server、禁用即移除;外部客户端经 SSE 地址 /api/v1/mcp/sse 接入,需携带 Authorization: Bearer <access_token>
  • 定时同步:支持后台同步的插件实现 run_sync(user, db),调度器按 IntervalTrigger 触发,间隔可选 30 分钟 / 1 小时 / 6 小时 / 24 小时,面板显示上次/下次同步时间与上次错误。
  • 从 URL 安装(仅桌面端):app/services/plugin_installer.py 下载 zip → 校验 URL(禁内网地址、手动跟随重定向且每跳重验)→ 安全解压(防 zip slip、大小/数量上限)→ 立即生效无需重启;本地插件须先禁用再卸载,内置插件不可卸载。

BasePlugin 生命周期

钩子时机用途
initialize()应用启动加载插件时调用一次初始化资源
get_routers()注册阶段返回挂到 /api/v1/plugins/{plugin_id} 的 FastAPI 路由
register_mcp_tools(mcp)注册阶段在共享 MCP 实例上注册工具/资源
on_enable(user_id)某用户启用插件时用户级初始化
on_disable(user_id)某用户禁用插件时用户级清理
run_sync(user, db)定时任务或手动「立即同步」触发后台同步入口,返回含 created/skipped 的结果

权限声明口径

枚举口径共七项,定义在 app/plugins/base.pyKNOWN_PERMISSIONS,完整说明见 app/plugins/PERMISSIONS.md

权限含义
files.read读本地文件
files.write写本地文件
network.outbound发起出站网络请求
llm.call调用 LLM(产生 token 消耗)
storage.read读应用数据
storage.write写应用数据
mcp.expose通过 MCP 向外部 AI Agent 暴露工具

校验规则(PluginManifest._check_permissions):未声明按空集合处理并警告日志;声明了未知权限不拒绝安装(前向兼容,新版本权限在旧后端上仍可安装),仅在校验时警告。

内置插件

插件默认状态声明权限
MCP Server(app/plugins/builtin/mcp_server/启用storage.read, storage.write, mcp.expose
Notion 导入(app/plugins/builtin/notion_import/禁用network.outbound, storage.write
Pocket 同步(app/plugins/builtin/pocket_sync/禁用network.outbound, storage.write
Readwise 同步(app/plugins/builtin/readwise_sync/禁用network.outbound, storage.write

为什么是声明制而不是沙箱

MCP 生态与各类「技能沙箱」方案默认插件来自不可信来源,因此需要容器、seccomp、权限代理等重装备做运行时隔离。Molore的场景不同:本地优先意味着插件只运行在用户自己的机器上、处理用户自己的数据,没有多租户混部,也没有第三方插件市场分发链。在这个前提下:

维度沙箱类方案Molore插件系统
威胁模型多租户/不可信分发,插件即攻击面单机单用户,插件由用户自行选择安装
隔离成本容器/进程隔离,常驻额外资源同进程运行,零额外资源
权限机制运行时强制拦截manifest 声明 + 展示,不拦截
失效兜底沙箱拦截违规调用用户启用前审阅声明;只安装可信来源

项目口径是「防君子不防小人」——会员门(见 平台管理)与插件权限同哲学:声明的价值在于可读可查、启用前可决策,超声明能力属于违反约定的行为而非技术事故。代价与边界同样明确:插件能力等同于后端本身,只安装可信来源的包是唯一的硬前提。

下一步

基于 AGPL-3.0 协议发布。