Skip to content
文档首页

插件内部集成规范 ​

本文面向 Mogick 产品内部集成人员,不是插件市场发布规范。

如需了解插件页面的使用方式,请参阅插件。

本文以 mogick-v26.7.17 为唯一实现基线,描述宿主当前真正读取、确认和投影的字段。当前交付方式是产品内部集成:普通插件以目录接受校验,产品连接器随应用放入内置插件目录。当前没有插件市场发布流程、插件压缩包安装能力,也没有面向第三方的自助权限授权流程。

适用范围 ​

本规范覆盖两类包:

  • 普通插件:通过普通 manifest contribution 提供 agent capability、文件渲染器、文件打开器、MCP 或 Skill 等能力。解析成功只表示 manifest 合法;非内置插件还必须由宿主写入 confirmed permission grant,经过兼容性、权限、运行状态和互斥选择后,才可能进入有效投影。虽然 parser 能读取 externalConnectors 字段,但把同一目录作为普通本地插件安装,不会使它进入 26.7.17 的连接器自动探测,也不会生成受支持的 detected connector sidecar;当前连接器交付仅限下述产品内置可信路径。
  • 产品内置连接器插件:声明 externalConnectors,随产品放入宿主内置插件目录,由内置 connector provider 自动枚举、加载和探测本机产品。只有 source: builtin 且 trustLevel: host-bundled 的记录属于这一信任域。

本文不定义公网分发、市场审核、签名发布、远程下载、压缩包安装或第三方自助授权。需要这些能力时,必须先补齐产品实现和单独的安全设计,不能把本文当作已有承诺。

插件包结构 ​

宿主把插件包视为一个目录,根目录的 plugin.json 是唯一必需文件:

text
com.example.analysis/
├─ plugin.json              # 必需;必须位于包根目录
├─ assets/                  # 可选;图标等静态资源
├─ runtime/                 # 可选;插件自己的运行时入口
└─ skills/                  # 可选;随包提供的 Skills

package.json、安装脚本、构建脚本和特定压缩格式都不是插件契约的一部分。26.7.17 的本地安装入口只接受目录;packageKind 不是 directory 时直接返回 plugin.archive-not-supported。产品内置连接器的物理位置是宿主打包的 plugins/builtin/<plugin-id>/,打包态应确保需要由外部进程读取的文件落在可访问的 unpacked 实体目录。

插件从声明到生效的机制 ​

一个 contribution 会依次经过三层状态,不能把它们混为一谈:

  1. manifest 声明态:宿主读取包根目录的 plugin.json,执行 mogick.plugin.v1 解析。这里确认字段形状、枚举、ID 冲突和禁用脚本键,但 provides 中的 MCP 等载荷会在后续消费时再严格解析。
  2. 宿主 confirmed state:安装或内置目录合并后,宿主记录来源、信任级别、启停状态、健康状态、安装 sidecar 和 grantedPermissions。普通本地插件初始的 grantedPermissions 为空;产品集成必须由宿主侧的受控流程写入与审计 confirmed grant。清单自报权限不等于授权。
  3. 最终有效投影:宿主检查兼容性、confirmed 权限、运行时可用性、互斥组选择和 MCP 载荷,再把选中的 contribution 写入 effectiveContributions,投影到 mcp-config、skill-resolver、文件处理注册表或工具注册表。未被选择或校验失败的声明不会成为当前运行能力。

内置可信插件是明确例外:只有同时满足 source === "builtin" 和 trustLevel === "host-bundled" 才跳过普通插件的兼容性与权限准入。连接器 provider 从内置目录枚举带有非空 externalConnectors 的包,当前只用数组第一项进行产品探测;因此一个产品连接器包应只声明一个主 connector contribution。

plugin.json 顶层字段 ​

顶层字段表 ​

字段必选类型或允许值当前行为
schemaVersion是固定为 mogick.plugin.v1其他值以 manifest.unsupportedSchema 拒绝。
id是非空字符串;内部规范要求稳定的反向域名 ID,如 com.example.analysisparser 只检查非空,不替团队检查命名格式;本地安装还会拒绝宿主内置目录保留的 ID。
version是非空字符串parser 当前不做 SemVer 校验;集成方仍应使用可比较的稳定版本。
displayName是非空字符串作为默认展示名。
publisher是对象按下表解析发布者字段。
hostCompatibility是对象可为空对象;非内置贡献在后续准入时使用其内容。
contributions是对象至少包含一个非空贡献数组,否则以 manifest.invalidField 拒绝。
description否字符串原样保留;当前不要求非空。
locales否locale 到 { displayName?, description? } 的对象映射内部交付规范要求 locale 键以及至少一个文案字段在 trim 后非空。26.7.17 parser 会 trim locale 键,但文案主要按字符串真值判断:"" 会失败,纯空白字符串仍可能通过;空映射当前也可通过。不要依赖这些宽松行为。
icon否包内 SVG、PNG、JPG、JPEG 或 WebP 相对路径统一反斜杠后校验;绝对路径、盘符路径、空段、.、.. 或其他扩展名会被拒绝。
license否字符串原样保留,不验证许可证标识。
homepage否字符串原样保留,不做 URL 校验。
repository否字符串原样保留,不做 URL 校验。
packageIntegrity否对象解析算法和摘要声明;26.7.17 的目录安装器不重新计算或验证摘要。
runtime条件必选对象不包含 MCP 时可省略;普通 contribution 只要包含任意 MCP,就必须提供合法顶层 runtime,否则准入按 permission-denied 拒绝。stdio 使用 kind: subprocess;HTTP 的内部规范使用 { "kind": "none" }。
permissions否权限声明数组只接受下文列出的 kind;这是申请范围,不是 confirmed grant。

未识别的顶层键不会进入 parser 返回的 manifest;但任意层级一旦出现 scripts、install、postinstall、preinstall 或 commands,整份清单都会被视为安装脚本或宿主命令声明并拒绝。

publisher ​

字段必选类型或允许值当前行为
publisher.id是非空字符串作为发布者稳定 ID 保留。
publisher.displayName是非空字符串作为发布者展示名保留。
publisher.verified否booleanboolean 会保留;其他类型当前被当作未提供,而不是报错。不要依赖这个宽松行为。
publisher.contact否字符串非字符串会拒绝;当前不校验联系方式格式。

hostCompatibility ​

字段必选类型或允许值当前行为
hostCompatibility.mogickAppDesktop否字符串parser 只保留字符串;当前非内置 contribution 只要该值非空,就按 host-incompatible 处理,尚未执行版本范围匹配。
hostCompatibility.pluginApi否字符串与 mogickAppDesktop 相同:非空会使当前非内置 contribution 不兼容。
hostCompatibility.platforms否darwin-arm64、darwin-x64、linux-x64、win32-x64 的数组未提供或空数组表示不限制;非内置 contribution 必须包含当前宿主平台。未知值会在解析时拒绝。

因此,普通插件在 26.7.17 中不要填写非空的 mogickAppDesktop 或 pluginApi;产品内置可信连接器可以携带产品版本说明,因为它走 host-bundled 信任路径,但仍应在打包测试中验证真实兼容性。

runtime 与 resourceLimits ​

字段必选类型或允许值当前行为
runtime.kindruntime 存在时是none、subprocess、renderer-isolate、wasm-worker未知值拒绝。包含任意 MCP 的普通 contribution 必须存在顶层 runtime;stdio 必须声明 subprocess,HTTP 的内部交付使用 none。
runtime.entry否字符串仅保留;manifest parser 当前不验证文件存在或是否位于包内。
runtime.protocol否json-rpc、stdio-json-rpc、http-local、custom-hosted未知值拒绝。
runtime.minHostApi否字符串仅保留;26.7.17 准入逻辑尚不比较该版本。
runtime.resourceLimits否对象非对象当前被当作未提供;对象会原样浅拷贝,parser 不逐项验证数值。
resourceLimits.maxMemoryMb否number类型声明为 number,但当前 parser 不做运行时校验或强制执行。
resourceLimits.startupTimeoutMs否number类型声明为 number,但当前 parser 不做运行时校验或强制执行。
resourceLimits.requestTimeoutMs否number类型声明为 number,但当前 parser 不做运行时校验或强制执行。
resourceLimits.maxConcurrentRequests否number类型声明为 number,但当前 parser 不做运行时校验或强制执行。

不要把未执行的 resourceLimits 当作安全沙箱。产品审核仍需对进程生命周期、资源消耗和故障恢复设置宿主侧限制。

packageIntegrity ​

字段必选类型或允许值当前行为
packageIntegrity.algorithmpackageIntegrity 存在时是sha256 或 sha512未知值拒绝。
packageIntegrity.digestpackageIntegrity 存在时是非空字符串作为包摘要声明保留。
packageIntegrity.files否资源摘要对象数组每项必须有非空 path、sha256/sha512 的 algorithm 和非空 digest;可选 sizeBytes 必须是大于等于 0 的 number。空数组当前可通过。

权限与 contribution 契约 ​

权限声明 ​

字段必选类型或允许值当前行为
workspace.read否{ kind, patterns?: string[] }patterns 如存在必须全是非空字符串;空数组可通过。
workspace.write否{ kind, patterns?: string[] }与 workspace.read 相同。
file.render否{ kind, maxFileSizeMb?: number }非 number 会拒绝;当前不检查正负或强制限制。
network否{ kind, hosts: string[] }hosts 必须是至少一项的非空字符串数组。HTTP MCP 会自动把 network 加入所需权限集合。
externalProcess.launch否{ kind, targets?: string[] }targets 如存在必须全是非空字符串。stdio MCP 会自动要求此权限。
externalProduct.discover否{ kind, productIds?: string[] }productIds 如存在必须全是非空字符串。
mcp.server否{ kind, serverIds: string[] }serverIds 必须是至少一项的非空字符串数组;任何 MCP 声明都会自动要求此权限。
agent.tool否{ kind, toolIds?: string[] }toolIds 如存在必须全是非空字符串。

requiredPermissions、顶层 permissions 与宿主 grantedPermissions 是三件事:前者说明某个 contribution 的需求,第二项声明插件申请的范围,第三项才是宿主确认的授权。普通插件只有所需 kind 同时出现在声明和 confirmed grant 中才会通过;目标、host 或 server ID 等细粒度 scope 在 26.7.17 的 contribution admission 中尚未交叉比对,内部授权流程不能因此省略人工审查。

contribution 类型 ​

字段必选类型或允许值当前行为
contributions.agentCapabilities否contribution 对象数组注册为 agent-capability;可通过 provides 声明 MCP、Skills 或 tools。
contributions.fileRenderers否contribution 对象数组注册为 file-renderer;被选择时投影到文件渲染器注册表。
contributions.fileOpeners否contribution 对象数组注册为 file-opener;被选择时投影到文件打开器注册表。
contributions.externalConnectors否contribution 对象数组parser 会注册为 external-connector;但 26.7.17 只有内置 connector provider 从宿主 plugins/builtin 枚举这类包、读取第一项并创建 builtin / host-bundled detected sidecar。普通本地安装即使可解析,也不会因此成为受支持的连接器交付。
contributions.exclusiveGroups否互斥组声明数组每项要求非空 id、owner 为 host 或 third-party,以及 { userSelectable: boolean } 的 selectionPolicy;组 ID 在该数组内必须唯一。宿主策略只采信内置 host-bundled 插件的声明。

前四类 contribution 的 id 在整个插件内必须唯一,不能只在各自数组内唯一。每项 id 必须非空;hostDefault 如存在必须是 boolean;exclusiveGroup 如存在必须是非空字符串。requiredPermissions 如存在,内部集成规则要求至少一项且每项必须是上表支持的非空 kind;26.7.17 parser 会校验元素,却不会单独拒绝空数组,因此审查时应拒绝空数组或直接省略字段。贡献专用的其他字段会保留给后续消费者。

contributions 至少要有一个非空数组。只写空对象、只写空数组或让所有数组都为空都会被拒绝。

普通 manifest contribution 的 provides.mcpServers ​

本节只描述普通的、非连接器 manifest contribution 消费路径;内置连接器走下一节的 provider 专用路径,字段和默认值不能混用。顶层 manifest parser 会保留 contribution 的 provides,真正的 MCP 字段校验发生在 contribution 已进入投影候选之后。provides.mcpServers 必须是数组,同一个 contribution 内的 MCP id 必须唯一。

字段必选类型或允许值当前行为
id是非空字符串组成运行时全局 ID <plugin-id>/<server-id>。
name是非空字符串作为 MCP 展示名和配置名。
transport是stdio 或 http其他值拒绝。
isolation否shared 或 per-session默认 shared。
useProxy否boolean默认 true。
commandstdio 时是非空字符串解析模板后规范化为本地命令路径。
argsstdio 时否字符串数组每项展开模板;当前允许空字符串。
envstdio 时否string 到 string 的映射每个 value 展开模板。
urlhttp 时是非空字符串展开模板后作为 HTTP MCP 地址。
headershttp 时否string 到 string 的映射每个 value 展开模板。
activation否;仅内置可信插件{ provider: "product-runtime", productId, capability, mode: "headless-or-attach" }仅 source: builtin 且 trustLevel: host-bundled 可使用;第三方或普通本地插件会被拒绝并 fail closed。

普通 manifest MCP 只允许 ${pluginRoot} 与 ${userWorkspace} 两个模板变量。未知变量会使该 contribution 的 MCP 投影失败。只要 contribution 包含任意 MCP,准入逻辑就要求 manifest 存在顶层 runtime:stdio MCP 必须使用 runtime.kind: subprocess,并需要 externalProcess.launch 和 mcp.server;HTTP MCP 的产品内部规范使用 runtime: { "kind": "none" },并需要 network 和 mcp.server。缺失 runtime,或 stdio MCP 的 runtime 不是 subprocess,都会按 permission-denied 拒绝。权限既要在顶层声明,也要由宿主 confirmed grant。

内置连接器的 provides.mcpServers ​

连接器 MCP 不经过上一节的 plugin-manifest-mcp-entries 解析器。26.7.17 有两条连接器来源:静态 provider 读取 externalConnectors[0].provides.mcpServers;配置了 helper 时,connector-runtime-helper 读取 helper JSON 的 mcpServers。两者先写入 connector sidecar,只有 connector 为 detected、launchFileStatus 为 confirmed、插件已启用且 contribution 已投影时,才会进入 MCP runtime。

字段产品内部要求静态 provider 当前行为helper JSON 当前行为
idtrim 后非空,且同一连接器内唯一只要求是 string;非 string 的整项被过滤,不检查空值或重复与静态路径相同;只要求是 string,不检查空值或重复
nametrim 后非空只要求是 string;否则整项被过滤与静态路径相同
transportstdio 或 http只有精确 http 保留为 HTTP,其他值(含缺失或未知值)都归一化为 stdio与静态路径相同
isolation显式填写 shared 或 per-session只有精确 shared 取 shared,其他值默认为 per-session只有精确 per-session 取 per-session,其他值默认为 shared
commandstdio 必填且为已审核的非空路径string 会展开连接器模板并规范化;其他类型被丢弃string 会规范化;helper 必须直接输出已解析路径,不再展开模板
argsstdio 可选字符串数组过滤非字符串项,并逐项展开连接器模板过滤非字符串项,不展开模板
envstdio 可选字符串映射过滤非字符串 value,并展开连接器模板过滤非字符串 value,不展开模板
endpointHTTP 必填且为非空地址string 会展开连接器模板;投影时映射为 MCP config 的 urlstring 原样保留;投影时映射为 MCP config 的 url
headers当前不得依赖26.7.17 连接器路径不读取或投影该字段26.7.17 helper 路径同样不读取或投影该字段
useProxy可选 boolean非 boolean 或省略时默认为 true与静态路径相同
activation仅在 helper 交付并通过宿主审核时使用受支持的 product-runtime 形状静态 provider 不读取该字段合法对象会保留;不合法对象被静默丢弃

连接器 provider 会构造新的 MCP 对象,表外字段不会原样穿透;许多类型错误会被过滤、默认化或静默丢弃,而不是像普通 manifest MCP 那样严格报错。因此,产品内部交付必须额外验证 MCP id 非空且唯一、HTTP 必须存在非空 endpoint、stdio 必须存在非空 command,并显式填写合法 transport 与 isolation。普通 MCP 使用的 url / headers 契约不能移植到连接器:连接器 HTTP 只消费 endpoint,当前不消费 headers。

provides.skills ​

Skill 声明至少包含:

字段必选类型或允许值当前行为
id是非空字符串作为 Skill bundle 的稳定 ID。
path是非空字符串普通本地插件必须解析到插件根目录之内;路径不存在时不会进入详情能力。

普通插件详情投影当前只展开 ${pluginRoot}。绝对路径或相对路径最终逃逸出插件根目录、包含其他模板变量、或指向不存在目录的 Skill 声明都会被忽略。连接器的 Skill 路径属于另一条内置 provider 路径,可在发现产品后使用下文限定的 ${installRoot}。

普通插件完整示例 ​

下面的包会通过 mogick.plugin.v1 manifest 解析;它声明一个 stdio MCP 和一个包内 Skills 目录:

json
{
  "schemaVersion": "mogick.plugin.v1",
  "id": "com.example.analysis",
  "version": "1.0.0",
  "displayName": "Example Analysis",
  "publisher": {
    "id": "example",
    "displayName": "Example Team"
  },
  "hostCompatibility": {
    "platforms": ["win32-x64"]
  },
  "runtime": {
    "kind": "subprocess",
    "entry": "runtime/example-mcp.exe",
    "protocol": "stdio-json-rpc"
  },
  "permissions": [
    {
      "kind": "externalProcess.launch",
      "targets": ["example-mcp"]
    },
    {
      "kind": "mcp.server",
      "serverIds": ["analysis"]
    }
  ],
  "contributions": {
    "agentCapabilities": [
      {
        "id": "analysis",
        "displayName": "Example Analysis",
        "requiredPermissions": ["externalProcess.launch", "mcp.server"],
        "exclusiveGroup": "com.example.analysis.engine",
        "provides": {
          "mcpServers": [
            {
              "id": "analysis",
              "name": "example-analysis",
              "transport": "stdio",
              "isolation": "per-session",
              "command": "${pluginRoot}/runtime/example-mcp.exe",
              "args": ["--workspace", "${userWorkspace}"],
              "useProxy": true
            }
          ],
          "skills": [
            {
              "id": "analysis-skills",
              "path": "${pluginRoot}/skills"
            }
          ]
        }
      }
    ]
  }
}

包能被解析不等于 MCP 已获授权。普通本地插件安装记录的 grantedPermissions 初始为空;产品集成时还需要宿主受控地写入 externalProcess.launch 和 mcp.server 的 confirmed grant,并保留授权时间与审计证据,之后 contribution 才可能进入 effective projection。26.7.17 没有提供给第三方的自助授权界面或流程。

产品内置连接器完整示例 ​

下面的示例同时提供 connector helper 和静态发现回退。helper 的 command、args 与 connectorRuntime.env 只用可靠的 ${pluginRoot};静态发现成功后的 MCP 与 Skills 使用内置 ${installRoot}、${installRootBasename},也可使用 ${productVersion} 或由 context-root 成功解析出的动态变量:

json
{
  "schemaVersion": "mogick.plugin.v1",
  "id": "com.example.product-connector",
  "version": "1.0.0",
  "displayName": "Example Product Connector",
  "publisher": {
    "id": "example",
    "displayName": "Example Team",
    "verified": true
  },
  "hostCompatibility": {
    "mogickAppDesktop": ">=26.7.17",
    "platforms": ["win32-x64"]
  },
  "runtime": {
    "kind": "none"
  },
  "permissions": [
    {
      "kind": "externalProduct.discover",
      "productIds": ["example-product"]
    },
    {
      "kind": "externalProcess.launch",
      "targets": ["Example Product"]
    },
    {
      "kind": "mcp.server",
      "serverIds": ["example-product"]
    }
  ],
  "contributions": {
    "externalConnectors": [
      {
        "id": "default",
        "displayName": "Example Product",
        "exclusiveGroup": "com.example.product.connector",
        "requiredPermissions": [
          "externalProduct.discover",
          "externalProcess.launch",
          "mcp.server"
        ],
        "product": {
          "productId": "example-product",
          "displayName": "Example Product",
          "envVar": "EXAMPLE_PRODUCT_HOME",
          "exampleFolder": "Example Product 2026"
        },
        "connectorRuntime": {
          "command": "${pluginRoot}/bin/example-connector-helper.exe",
          "args": ["--plugin-root", "${pluginRoot}"],
          "env": {
            "EXAMPLE_CONNECTOR_PLUGIN_ROOT": "${pluginRoot}"
          },
          "timeoutMs": 10000
        },
        "discovery": [
          {
            "kind": "environment-variable",
            "names": ["EXAMPLE_PRODUCT_HOME"]
          },
          {
            "kind": "known-location",
            "platform": "win32-x64",
            "searchParents": ["%ProgramFiles%/Example"],
            "installDirPattern": "Example Product *",
            "requiredFiles": [
              "bin/example-product.exe",
              "bin/example-mcp.exe"
            ]
          }
        ],
        "provides": {
          "mcpServers": [
            {
              "id": "example-product",
              "name": "example-product-mcp",
              "transport": "stdio",
              "isolation": "per-session",
              "command": "${installRoot}/bin/example-mcp.exe",
              "args": [
                "--install-root",
                "${installRoot}",
                "--install-label",
                "${installRootBasename}"
              ],
              "useProxy": true
            }
          ],
          "skills": [
            {
              "id": "example-product-skills",
              "path": "${installRoot}/skills"
            }
          ]
        }
      }
    ]
  }
}

连接器字段与发现顺序 ​

下表的“必选”表示产品内部交付的强制规范,不表示 26.7.17 provider 已经对缺失、空字符串或过滤后的空数组逐项严格报错。“当前行为”列专门记录 provider 实际执行的校验和归一化;内部审核必须执行两者中更严格的一侧。

字段必选类型或允许值当前行为
product.productId内部必填非空字符串内部交付必须提供稳定的非空产品 ID;26.7.17 provider 只验证值是 string,不验证非空或 trim。
product.displayName内部必填非空字符串内部交付必须提供非空展示名;26.7.17 provider 只验证值是 string,不验证非空或 trim。
product.envVar否字符串优先作为安装根环境变量;未提供时取第一个 environment-variable discovery 名称。
product.exampleFolder否字符串26.7.17 会读取该提示,但不使用它参与目录搜索。
connectorRuntime.commandconnectorRuntime 存在时是非空字符串用 ${pluginRoot} 展开并启动 helper;命令不存在时回退静态发现,其他启动或输出错误记为 invalid。
connectorRuntime.args否字符串数组用 ${pluginRoot} 展开;非字符串项被过滤。
connectorRuntime.env否string 到 string 的映射非字符串 value 被过滤;value 使用 helper 模板上下文展开,再覆盖到子进程环境。默认生产路径中可靠的内置变量只有 ${pluginRoot}。
connectorRuntime.timeoutMs否number默认 5000 ms;示例显式设为 10000 ms。
discovery[].kind是environment-variable、known-location 或 context-rootprovider 先确定产品安装根;静态路径随后解析 context root,再渲染 MCP 与 Skill。
environment-variable.names内部必填至少一个非空字符串的数组provider 只处理 names 为数组的项,再过滤非字符串和空字符串;过滤后为空时没有环境变量候选,不一定在读取阶段报错。
known-location.platform内部必填win32-x64当前 Windows 自动发现必须显式填写。实现也兼容 windows,但公开的 manifest 平台枚举只包含 win32-x64;缺失或与当前平台不匹配时,该 known-location 项不会被选中。
known-location.searchParents内部必填至少一个非空路径字符串的数组内部交付必须提供标准目录探测根。26.7.17 缺失或非数组时会归一化为 [],数组内只过滤非字符串、不检查空字符串;读取阶段不一定报错,宿主测试选项还可覆盖该数组。路径支持 %NAME% 环境变量展开。
known-location.installDirPattern是字符串当前实现按目录名前缀匹配;末尾 * 被作为前缀通配提示处理。
known-location.requiredFiles内部必填至少一个非空路径字符串的数组内部交付与审核必须确认每项非空且真实存在。26.7.17 先检查原数组长度,再过滤非字符串;例如 [123] 会变成空列表,后续 every([]) 可能以真值通过,因此 provider 尚未严格封堵畸形值。

内部交付必须独立确认环境变量指向的目录和 known-location 候选包含全部非空 requiredFiles;不要仅凭 provider 返回 detected 判定安装根安全。26.7.17 会调用 validRoot 检查目录与归一化后的文件列表,但上述“先检查、后过滤”限制意味着该检查不是完整的安全边界。没有环境变量时,known-location 搜索只接受唯一有效候选;零个或多个候选都会保持 missing 并给出诊断。%ProgramFiles% 是 searchParents 专用的环境变量展开形式,不是 manifest MCP 模板变量。

context-root 动态模板变量 ​

context-root 只在静态发现路径中运行。provider 先建立 { installRoot, productVersion, installRootBasename } 基础上下文,再逐项解析动态根;全部成功后,才把动态变量合并进静态连接器 MCP 的 command / args / env / endpoint 和 Skill path 模板上下文。helper 一旦返回结果(包括 missing 或 invalid),就不会处理 context-root,helper JSON 也不会再展开这些模板。

字段产品内部要求26.7.17 当前行为
context-root.variable匹配 [A-Za-z_][A-Za-z0-9_]*,同一 contribution 内唯一,且不得覆盖 installRoot、productVersion、installRootBasename不匹配正则的声明被跳过;不检查重复或保留名,后出现的动态值可以覆盖先前值乃至基础键,因此内部审核必须补上冲突检查。
context-root.envNames可选;每项 trim 后非空且不重复按顺序读取宿主覆盖环境和进程环境;过滤非字符串与空字符串,但不 trim 名称。遇到第一个已设置的值后立即校验:若该目录无效,不再尝试后续环境名或 searchRoots。
context-root.searchRoots可选;至少提供一个 env name 或搜索根;每项为非空模板路径过滤非字符串和空字符串,依次用基础上下文渲染;不能引用另一个动态 context root。第一个满足路径检查的目录胜出。
context-root.requiredPaths至少一个 trim 后非空的相对路径;逐项确认真实存在原数组长度为 0 时整项声明被跳过;之后会过滤非字符串和空字符串,但未再次确认数组非空,因此畸形数组可能退化为 every([])。内部审核必须拒绝这种输入。

解析出的根必须是目录,并且每个 requiredPaths 项都存在(文件或目录均可)。任何已接受的 context-root 找不到有效根,整个静态连接器都会变为 invalid;只有所有动态根成功后,其变量才会加入 MCP / Skill 模板上下文。静态 provider 对未知模板变量严格失败:无论未知变量出现在 context-root 的 searchRoots、MCP 还是 Skill 中,渲染异常都会使连接器为 invalid。

connector helper runtime 与 JSON 输出 ​

connectorRuntime.command 为空或没有可用 runtime 对象时,provider 直接使用静态发现;helper 启动命令返回 ENOENT 时也回退静态发现。除此之外,不存在“helper 失败后继续静态探测”:非零退出、超时、其他启动错误、无效 JSON 或 helper 返回的 invalid 都产生 invalid 结果;helper 返回 missing / unsupported 也按原状态结束,不再回退。

helper 的 command 与 args 以 ${pluginRoot} 展开;connectorRuntime.env 的 value 还可引用宿主显式传给 provider 的环境覆盖。26.7.17 helper 渲染器会把未知模板变量替换为空字符串,而不是报错;这与静态 provider 的严格失败不同。产品内部交付必须统一执行更严格审核:拒绝所有未声明变量,不得依赖“展开为空”或由此偶然触发的 ENOENT 回退。子进程环境由当前进程环境、宿主覆盖以及渲染后的 connectorRuntime.env 依次合并。

helper stdout 必须是单个 JSON 对象;当前读取以下字段:

字段类型或允许值当前行为
statusdetected、missing、invalid、unsupported其他值或缺失归一化为 invalid。
productName、productVersion可选 string用于连接器和产品版本展示;非 string 丢弃。
installRoot、launchFilestring 或 nullstring 会规范化为本地路径;其他值归一化为 null。
launchArgs可选字符串数组非字符串项被过滤。
mcpServers可选数组按上一节“内置连接器”表归一化;helper 必须输出已解析的 command / endpoint / path,不会再次展开 manifest 模板。
skills可选数组只保留具有 string id 与 path 的项,路径会规范化;可选展示字段为 displayName、description、overview,source 只保留 skill-frontmatter / manifest,其他值默认为 runtime。
message可选 string作为失败或降级诊断;非 string 丢弃。
candidates可选字符串数组用于展示多安装候选等诊断;非字符串项被过滤。

helper 的 mcpServers[].tools 还支持工具展示元数据:每项 name 必须 trim 后非空,可选 description 与对象形态的 inputSchema;source 可为 manifest、runtime、merged,否则按 runtime。这些展示字段不替代运行时 tools/list 探测,也不放宽 MCP 的 ID、transport、endpoint 或 command 审核。

这个示例只有在包被放入宿主内置插件目录、经产品打包,并被加载为 source: builtin、trustLevel: host-bundled 后,才会进入自动发现流程。把相同目录作为普通本地插件安装,不会把它提升为可信连接器,也不能获得自动授权。内置 provider 发现的 connector 不可卸载;启停状态和健康诊断仍会进入 confirmed state 与最终投影。

安全与信任边界 ​

  • 禁止安装脚本和隐式宿主命令:清单任意层级不得出现 scripts、install、postinstall、preinstall、commands。需要启动的子进程必须通过已审查的 runtime、connectorRuntime 或 MCP command 显式声明,并与权限和信任域一致。
  • 禁止第三方绑定产品运行时:activation.provider: product-runtime 只允许内置 host-bundled 插件;不可信来源会在解析或最终投影阶段 fail closed。
  • 内置信任由产品目录赋予,不由 manifest 自报:publisher.verified: true、产品 ID 或放置相似字段都不能把本地插件变为 host-bundled。只有产品打包与宿主注册代码能建立该状态。
  • 路径必须受控:普通插件的图标与 Skills 不得逃逸包根。连接器内部交付与审核必须独立确认 ${installRoot} 是预期产品目录,并逐项确认非空 requiredFiles 真实存在;26.7.17 provider 的过滤顺序尚未完全拒绝畸形列表,不能把其 validRoot 结果当作已封堵路径风险的安全保证。manifest parser 当前也没有覆盖所有运行时路径,因此集成审查必须补充真实文件与路径边界检查。
  • 声明不等于执行隔离:resourceLimits 与 packageIntegrity 在 26.7.17 主要是声明数据,目录安装器没有完整执行这些限制或摘要验证。不要据此降低打包审计要求。
  • 普通权限必须有宿主确认:清单申请、contribution 需求和 confirmed grant 缺一不可。当前没有第三方自助授权流程,任何绕过宿主确认的集成都不受支持。

内部集成流程 ​

固定执行顺序如下:

确认能力类型 → 分配稳定 ID → 准备目录 → 编写并解析清单 → 审核运行时与权限 → 放入产品 Plugins 目录 → 打包启动验证 → 验证详情与有效能力 → 回归启停和故障行为

其中,“放入产品 Plugins 目录”只适用于要随产品交付的内置包。普通插件的集成验证可以从受控目录走本地安装路径,但因为当前没有第三方授权 UI,负责集成的宿主代码仍必须建立 confirmed grant;产品内置连接器必须进入 plugins/builtin,确认打包产物中的实体路径可被 helper 和 MCP 读取。

常见拒绝原因 ​

原因阶段当前结果或处理
缺少根目录 plugin.json包校验PluginPackageValidationError:插件包缺少 plugin.json。
schema 不支持manifest 解析manifest.unsupportedSchema。只接受 mogick.plugin.v1。
必选字段缺失或为空manifest 解析manifest.missingField;部分嵌套字段以 manifest.invalidField 返回。
图标路径非法manifest 解析非包内相对路径或非 SVG/PNG/JPG/JPEG/WebP 扩展名以 manifest.invalidField 拒绝。
出现安装脚本键manifest 解析manifest.disallowedScript;检查是递归的。
contributions 为空manifest 解析manifest.invalidField。
contribution ID 重复manifest 解析前四类 contribution 跨数组重复时拒绝;exclusiveGroups 也在自身数组内查重。
未知权限 kindmanifest 解析manifest.invalidField。
平台不匹配,或普通插件填写非空 mogickAppDesktop / pluginApicontribution 准入host-incompatible,不进入有效候选。
所需权限未声明或未 confirmedcontribution 准入permission-denied;普通本地插件初始 grant 为空。
普通 MCP 缺少顶层 runtime,或 stdio MCP 不是 runtime.kind: subprocesscontribution 准入permission-denied;HTTP 也必须提供合法 runtime,内部规范使用 kind: none。
MCP 字段错误或同一 contribution 的 MCP ID 重复MCP 投影PluginManifestMcpEntryError,该 contribution 的 MCP 被拒绝并产生 plugin.manifest-mcp-invalid 诊断。
MCP 使用未知模板变量MCP 投影普通 manifest MCP 投影失败;连接器静态模板则把状态记为 invalid。
插件 ID 被宿主内置目录保留本地安装plugin.reserved-id。本地包不能覆盖产品内置 ID。
提交压缩包本地安装plugin.archive-not-supported;当前只支持目录。

集成检查清单 ​

  • [ ] 包根目录存在且只依赖一个必需文件:plugin.json。
  • [ ] schemaVersion 精确为 mogick.plugin.v1,插件、contribution、MCP 和 Skill ID 稳定且无冲突。
  • [ ] 普通插件 ID 使用团队控制的反向域名;没有占用宿主内置保留 ID。
  • [ ] 图标和普通插件 Skills 均位于插件根目录内,运行时入口与实际打包路径一致。
  • [ ] 清单任何层级都不含安装脚本键或隐式宿主命令。
  • [ ] contributions 至少有一个非空数组;exclusiveGroup 和 requiredPermissions 的值非空且受支持。
  • [ ] 普通 stdio/HTTP MCP 字段完整并存在顶层 runtime:stdio 为 subprocess,HTTP 为内部规范的 none;模板变量只使用 ${pluginRoot} / ${userWorkspace}。
  • [ ] 顶层声明权限覆盖 contribution 和 MCP 的全部需求;普通插件已有宿主 confirmed grant 与审计证据。
  • [ ] 第三方或普通本地插件没有 product-runtime activation。
  • [ ] 产品连接器只在内置目录中加载,并确认记录为 builtin / host-bundled;未用 publisher.verified 冒充信任。
  • [ ] environment-variable.names、known-location.platform、searchParents 和 requiredFiles 均满足内部强制格式,且没有依赖 provider 对空值或畸形数组的宽松归一化。
  • [ ] context-root 的 variable、envNames、searchRoots、requiredPaths 已严格审核;动态变量无重复、不覆盖基础变量,并在 MCP / Skill 投影前解析成功。
  • [ ] 连接器 MCP 使用 endpoint 而不是 url,不依赖 headers;ID 非空且唯一,transport 与 isolation 显式合法。
  • [ ] helper command / args / env 只使用已审核变量;helper JSON 的 status、路径、MCP、Skills 与诊断字段经过 schema 检查,并分别验证 ENOENT 回退及其他错误 fail closed。
  • [ ] 环境变量发现和 known-location 发现分别测试;零候选、多候选、缺文件和 helper 失败均有预期诊断。
  • [ ] 详情页显示的 MCP/Skills 与 effectiveContributions、实际 tools/list 结果一致。
  • [ ] 启用、禁用、健康降级、进程崩溃、重启和互斥选择行为完成回归。
  • [ ] 使用目录安装或产品打包验证;没有假设市场发布、压缩包安装或第三方自助授权已经存在。