@xiongtao.xt/tau-plugins 0.9.0 → 0.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/MCP.md ADDED
@@ -0,0 +1,89 @@
1
+ # 插件 MCP
2
+
3
+ 通过 `context.mcp.register()` 向 Tau 的 AI 提供工具,一个插件可以注册多个 MCP。它们直接出现在已有 MCP 页面和聊天快捷面板,用户可以整项启停、逐工具启停,不需要插件自行开发管理界面。
4
+
5
+ 需要 SDK 0.11.0 和包含此能力的 Tau 0.59.0 桌面客户端构建。旧客户端没有 `context.mcp`,应检查并明确提示升级;SDK 类型版本不能替代客户端能力检查。
6
+
7
+ ## 本地工具和外部服务器
8
+
9
+ 每次注册填写局部 `id`、`name` 和可选 `description`,再提供 `tools` 或 `transport`,两者互斥。相同插件内 MCP 的 ID 不能重复;不同插件可以使用相同局部 ID。每个 MCP 中的工具名称唯一。
10
+
11
+ 下面是一个完整插件示例。第一项提供本地查询函数;第二项连接外部 MCP,示例地址须替换成真实服务。
12
+
13
+ ```ts
14
+ import { definePlugin } from "@tau/plugins";
15
+
16
+ declare const __PLUGIN_META__: { id: string; name: string; version: string };
17
+
18
+ export default definePlugin({
19
+ ...__PLUGIN_META__,
20
+ create: () => ({
21
+ activate(context) {
22
+ if (!context.mcp) throw new Error("请升级到支持插件 MCP 的 Tau 客户端");
23
+
24
+ context.mcp.register({
25
+ id: "notes",
26
+ name: "我的笔记",
27
+ description: "读取本插件保存的笔记",
28
+ tools: [
29
+ {
30
+ name: "read_note",
31
+ description: "按笔记键读取本插件中的一条笔记",
32
+ inputSchema: {
33
+ type: "object",
34
+ properties: { key: { type: "string", description: "笔记键" } },
35
+ required: ["key"],
36
+ additionalProperties: false,
37
+ },
38
+ async execute(args, { signal }) {
39
+ signal.throwIfAborted();
40
+ if (typeof args.key !== "string") throw new Error("请提供笔记键");
41
+ const note = await context.storage.get(args.key);
42
+ signal.throwIfAborted();
43
+ return { content: note === undefined ? "未找到笔记" : JSON.stringify(note) };
44
+ },
45
+ },
46
+ ],
47
+ });
48
+
49
+ context.mcp.register({
50
+ id: "remote",
51
+ name: "外部知识服务",
52
+ transport: { type: "streamable-http", url: "https://your-service.example/mcp" },
53
+ });
54
+ },
55
+ }),
56
+ });
57
+ ```
58
+
59
+ 本地工具填写 `name`、`description`、对象类型的 JSON Schema `inputSchema`,以及 `execute(args, { signal })`。执行结果为 `{ content: string }`,结构化数据可用 JSON.stringify 返回。工具名称使用 1–128 位字母、数字、点、横线或下划线;宿主会生成全局工具名称,作者不要自行拼接命名空间。所有插件工具沿用现有 execute 级审批。
60
+
61
+ 需要本地进程时,把 transport 改为下面的结构,命令、参数、工作目录与应用内手动添加 MCP 的含义相同:
62
+
63
+ ```ts
64
+ context.mcp.register({
65
+ id: "local-server",
66
+ name: "本地 MCP 服务",
67
+ transport: {
68
+ type: "stdio",
69
+ command: "node",
70
+ args: ["/path/to/your-server.mjs"],
71
+ },
72
+ });
73
+ ```
74
+
75
+ 该片段放在 activate(context) 内,路径须替换。不会自动安装 Node、Python、npm 包或其他依赖,启动前须具备对应运行环境。stdio 支持可选 args、env、cwd;streamable-http 支持可选 headers。不要把密钥、令牌或用户机器路径打进分发包,用户可在 MCP 页面配置本机连接参数。
76
+
77
+ ## 管理和生命周期
78
+
79
+ - 激活成功后才展示和提供工具;重复启用不会重复登记。激活失败、插件停用、切库或卸载自动回收。
80
+ - MCP 页面关闭一项只关闭该项 AI 能力,不停用插件的页面、命令或其他 MCP。用户选择会跨启用和重启保留,重新注册不会强行开启。
81
+ - 用户可管理每个工具的开关。插件提供的名称和说明由作者维护;外部服务器支持连接配置、日志和重连,插件函数没有连接配置。
82
+ - 返回的 Disposable 可提前注销单项 MCP,无需再交给 context.register。
83
+ - 工具调用 signal 在取消、插件停用、整项或对应工具关闭时中止,调用上限为 60 秒。异步代码应响应 signal;取消不能撤销已经完成的写入或外部请求。
84
+ - 旧实例、旧数据库和重连前的调用不能转交给新实例。不要缓存上下文到下一次启用。
85
+ - Skill 可描述这些工具的用途;ctx.ai.chat/stream 仍是文本生成接口,不会因为注册 MCP 而自动获得工具调用能力。
86
+
87
+ ## 验证
88
+
89
+ 检查类型并打包后,在包含此实现的客户端启用插件:确认两项 MCP 出现在现有页面和快捷面板;测试整项开关、逐工具开关、外部连接配置和重连,再测试停用、重新启用及重启后偏好保留。真实服务器连接与模型调用需使用自己的服务和已配置的模型。
package/SYNC.md CHANGED
@@ -1,21 +1,37 @@
1
1
  # 同步存储插件
2
2
 
3
- 需要 SDK 0.9.0 及以上和包含同步插件支持的 Tau 0.59.0 客户端。插件提供存储操作与配置表单,客户端负责数据库、资源、用量同步和冲突处理;CLI也可调用同一后台入口。
3
+ 需要 SDK 0.10.0 及以上,以及包含渲染端同步注册支持的 Tau 0.59.0 客户端构建。插件只在渲染进程执行,沿用main.js入口,不声明manifest.sync,也不提供Node后台产物。
4
4
 
5
- 后台从@tau/plugins/sync导入defineSyncProvider、SyncProvider与RemoteFileInfo。defineSyncProvider接收id、name、create(config);id与manifest.id一致,create根据本次任务配置返回提供方。用户只填写服务地址、桶、路径与凭据,没有能力声明或服务商认证流程。
5
+ ## 注册与职责
6
6
 
7
- 同步存储插件的manifest可声明sync为sync.js,模板从src/sync.ts构建Node后台入口;main.js继续承载UI。@tau/plugins/sync由客户端后台提供,保留模板external配置。普通npm依赖打包进sync.js,Node内置模块保留运行时引用。
7
+ 在activate(context)中调用context.sync.register({ id: context.pluginId, name, create })。一个插件注册一个提供方,create(config)根据本次任务的JSON配置返回下列五个方法;停用或激活失败时注册自动回收。create在真正发起任务时调用,打开配置页不会连接存储服务。
8
8
 
9
- 提供方只有uploadFile、downloadFile、fileExists、listFiles、createFileIfAbsent五个操作,使用Uint8Array、boolean和RemoteFileInfo[]等现有返回形状。分页在listFiles内部聚合;不新增stat/remove、流式参数、公开游标、目标身份字段或统一错误码。
9
+ | 方法 | 返回值 | 用途 |
10
+ | ------------------------------- | ------------------------- | ---------------------------------------------------- |
11
+ | uploadFile(name, bytes) | Promise<boolean> | 普通上传 |
12
+ | downloadFile(name) | Promise<Uint8Array> | 读取对象 |
13
+ | fileExists(name) | Promise<boolean> | 检查对象是否存在 |
14
+ | listFiles(prefix?) | Promise<RemoteFileInfo[]> | 内部完成分页,返回相对路径、名称、大小及可选修改时间 |
15
+ | createFileIfAbsent(name, bytes) | Promise<boolean> | 按远端原子条件创建对象 |
10
16
 
11
- 上传返回false或抛异常的处理由现有客户端控制流承担。createFileIfAbsent保留现有版本提交调用,不要求所有服务使用同一个HTTP头;响应异常后仍由客户端核验提交记录。不能用检查后覆盖偷换其含义,也不要求安装前进行额外探测。同步内容、冲突判断与恢复归客户端。
17
+ SyncProvider、SyncProviderDefinition和RemoteFileInfo类型从@tau/plugins/sync导入。该模块仅提供纯契约与声明辅助函数,不是Node执行入口。
12
18
 
13
- 后台入口同样参与manifest摘要、ZIP检查和安装事务。不要手动删掉完整性字段或把源码路径直接交给宿主执行。构建后通过客户端插件页安装ZIP,启用后在“设置→同步”选择插件并保存配置。
19
+ 客户端负责同步内容、对象命名、版本提交、冲突、资源计划和恢复。插件负责存储服务请求,不增加能力声明、服务商探测或独立同步引擎。条件创建不能用先查询再覆盖替代;请求失败后由客户端按原提交规则核验结果。
20
+
21
+ ## 网络与依赖
22
+
23
+ 通过context.fetch发送请求。它由宿主提供网络传输,并与插件生命周期关联。签名、URL与协议逻辑在插件内执行;浏览器可用的SDK可以把自己的HTTP传输接到context.fetch。
24
+
25
+ 普通npm依赖随main.js按浏览器目标打包;不导入fs、http、Node Buffer或其他Node内置模块,不借用宿主node_modules,不另打包Node依赖目录。保留模板external配置。
14
26
 
15
27
  ## 配置界面
16
28
 
17
- 在前端activate中调用context.settings.register({ id: "sync", title: "同步存储", target: "sync", component: SyncSettings }),每个插件注册一个同步配置分区。客户端将它放在“设置→同步”,插件详情页的设置入口也跳转到这里。
29
+ 通过context.settings.register({ id: "sync", title, target: "sync", component })将配置表单挂在“设置→同步”。每个插件注册一个同步配置分区。
30
+
31
+ 组件从PluginSettingsProps.syncConfig.value读取配置,通过await syncConfig.save(config)保存。字段和校验由插件定义;失败时保留输入并展示错误。凭据不要写入日志或工作区context.storage;宿主按插件ID在本机应用级保存配置。
32
+
33
+ 客户端每次任务固定配置。关闭设置页不影响任务,停用插件保留配置;运行中的任务阻止停用和卸载。窗口关闭或崩溃时任务不会转移到Node进程继续执行。CLI不再提供同步功能。
18
34
 
19
- 组件接收PluginSettingsProps,使用syncConfig.value初始化表单,提交时await syncConfig.save(config)。配置必须为JSON值;字段与校验由插件定义。保存失败保留输入并展示错误,保存成功后用户使用客户端已有的检查、上传和下载按钮。凭据不要写入日志或context.storage,后者按工作区隔离,不是同步配置的存储位置。
35
+ ## 从旧同步示例升级
20
36
 
21
- 客户端在应用级按插件ID保存配置,并为每次任务提供固定快照。停用插件保留配置;后台任务运行时不能停用或卸载。作者不需要实现会话、dispose、能力探测或同步冲突引擎。CLI也从同一约定目录加载该后台入口,尊重启用意图;任务回收和进程间占用由客户端管理。
37
+ SDK0.9.0的manifest.sync/sync.js示例已撤回。将存储实现作为普通模块由main.js导入,在activate内注册,并改用浏览器兼容依赖及context.fetch;删除manifest.sync,重新打包。不要通过其他字段恢复Node入口。
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xiongtao.xt/tau-plugins",
3
- "version": "0.9.0",
3
+ "version": "0.11.0",
4
4
  "license": "MIT",
5
5
  "repository": {
6
6
  "type": "git",
@@ -7,8 +7,10 @@ import type { PluginEditor } from "./editor";
7
7
  import type { PluginEditorExtension } from "./editor";
8
8
  import type { MarkdownCodec } from "../../markdown/src/index";
9
9
  import type { PluginSettings } from "./settings";
10
+ import type { PluginSync } from "./sync";
10
11
  import type { PluginSidebar } from "./sidebar";
11
12
  import type { PluginExcerpts, PluginMaterials } from "./excerpts";
13
+ import type { PluginMCP } from "./mcp";
12
14
  import type { JsonValue } from "./json";
13
15
  export type { JsonValue } from "./json";
14
16
  export interface Disposable {
@@ -36,12 +38,14 @@ export interface PluginContext {
36
38
  readonly pages: PluginPages;
37
39
  readonly editor: PluginEditor;
38
40
  readonly settings: PluginSettings;
41
+ readonly sync: PluginSync;
39
42
  readonly sidebar: PluginSidebar;
40
43
  readonly excerpts: PluginExcerpts;
41
44
  readonly materials: PluginMaterials;
42
45
  readonly fetch: typeof globalThis.fetch;
43
46
  readonly services: PluginServices;
44
47
  readonly ai: PluginAI;
48
+ readonly mcp: PluginMCP;
45
49
  /** 将订阅等资源交给宿主,在停用或激活失败时逆序回收。 */
46
50
  register(resource: Disposable): Disposable;
47
51
  }
@@ -8,6 +8,7 @@ export * from "./sidebar";
8
8
  export type * from "./excerpts";
9
9
  export type * from "./services";
10
10
  export type { PluginSkill } from "./skills";
11
+ export type { PluginMCP, PluginMCPDefinition, PluginMCPTool, PluginMCPTransport } from "./mcp";
11
12
  export type { PluginPageDefinition, PluginPageHandle, PluginPageParams, PluginPageProps, PluginPages, } from "./pages";
12
13
  export declare function definePlugin(definition: Omit<PluginDefinition, "apiVersion" | "create"> & Partial<Pick<PluginDefinition, "create">>): PluginDefinition;
13
14
  export type { PluginAI, PluginAIFeature, PluginAIRequest, PluginAIResult, PluginAIEvent, } from "./ai";
@@ -0,0 +1,45 @@
1
+ import type { Disposable } from "./contract";
2
+ import type { JsonValue } from "./json";
3
+ export type PluginMCPTransport = {
4
+ type: "stdio";
5
+ command: string;
6
+ args?: string[];
7
+ env?: Record<string, string>;
8
+ cwd?: string;
9
+ } | {
10
+ type: "streamable-http";
11
+ url: string;
12
+ headers?: Record<string, string>;
13
+ };
14
+ export interface PluginMCPTool {
15
+ name: string;
16
+ description: string;
17
+ inputSchema: {
18
+ type: "object";
19
+ properties?: Record<string, JsonValue>;
20
+ [key: string]: JsonValue | undefined;
21
+ };
22
+ execute(args: Record<string, JsonValue>, context: {
23
+ signal: AbortSignal;
24
+ }): Promise<{
25
+ content: string;
26
+ }> | {
27
+ content: string;
28
+ };
29
+ }
30
+ export type PluginMCPDefinition = {
31
+ id: string;
32
+ name: string;
33
+ description?: string;
34
+ } & ({
35
+ tools: readonly PluginMCPTool[];
36
+ transport?: never;
37
+ } | {
38
+ transport: PluginMCPTransport;
39
+ tools?: never;
40
+ });
41
+ export interface PluginMCP {
42
+ /** 同一插件可注册多个 MCP;停用时由宿主自动回收。 */
43
+ register(definition: PluginMCPDefinition): Disposable;
44
+ }
45
+ export declare function snapshotMCPDefinition(definition: PluginMCPDefinition): PluginMCPDefinition;
@@ -1,5 +1,9 @@
1
1
  import type { JsonValue } from "./json";
2
2
  export type { JsonValue } from "./json";
3
+ import type { Disposable } from "./contract";
4
+ export interface PluginSync {
5
+ register(definition: SyncProviderDefinition): Disposable;
6
+ }
3
7
  export interface RemoteFileInfo {
4
8
  name: string;
5
9
  path: string;