npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@coze/app-sdk

v0.0.1-alpha.b378f6

Published

MCP Apps View SDK with Coze Host integrations

Downloads

194

Readme

@coze/app-sdk

@coze/app-sdk 是基于官方 @modelcontextprotocol/ext-apps 的 MCP Apps View SDK。 它完整继承并重导出官方 View API,同时为 Coze Host 增加 Composer 草稿写入和 Panel 交互能力。

该包面向所有开发者发布到公网 npm,同时保留字节内部 BNPM 和仓库内 workspace 的使用方式。

安装

公网项目从 npm 安装:

npm install @coze/app-sdk

字节内部项目仍可从 BNPM 安装:

bnpm install @coze/app-sdk

包入口

业务 View 使用根入口:

Panel 与 Host 之间的消息协议统一收录在 Panel 消息协议

import { App, PostMessageTransport } from '@coze/app-sdk';

包提供七个职责分离的入口:

import { App } from '@coze/app-sdk';
import type { AppApi, LaunchContext } from '@coze/app-sdk/contracts';
import { parsePluginDevelopmentPackage } from '@coze/app-sdk/development';
import { App as BusinessApp } from '@coze/app-sdk/business';
import {
  definePanel,
  definePluginApp,
  usePanelRuntime,
} from '@coze/app-sdk/react';
import { PanelRuntimeProvider } from '@coze/app-sdk/react/host';
import type { RuntimeContext } from '@coze/app-sdk/internal';
  • 根入口:第三方 MCP Apps View 与 Iframe Transport。
  • /contracts:不加载 React 的 AppApi、LaunchContext 与 Panel Runtime 合同。
  • /development:标准 Plugin Manifest / UI Resources 的开发态解析与校验工具。
  • /business:受信 Host 自定义扩展使用的类型化 request 与错误模型;普通业务 HTTP 不走此入口。
  • /react:一方 React Panel 的定义、Runtime Hook 与 definePluginApp
  • /react/host:Web、桌面和移动端 Host 共用的 Provider 与入口 marker guard。
  • /internal:统一 Bootstrap 与受信 Host 使用的 RuntimeContext、容器 Adapter 和内部方法;不属于业务 API。

API 稳定性与权限

以上七个入口都会随 npm 包发布。面向开发者的公共 API 遵循 SemVer;/business/internal 仅供受信业务或 Host 集成使用。包可以被下载或导入不代表获得对应的 Host 权限,运行时仍以 Host 声明的 capability 和信任校验为准。/internal 不提供 SemVer 兼容性承诺。

Panel 开发

一方 Panel 默认导出零 Host props 组件,并从 Runtime Hook 读取 Host 注入:

import { definePanel, usePanelRuntime } from '@coze/app-sdk/react';

function FilesPanel() {
  const { app, launchContext, activation, hostContext, signal, close } =
    usePanelRuntime();
  // launchContext also exposes the current userId and accountId when provided by Host.
  // 业务只依赖统一 Runtime,不判断 iframe、Electron 或 Capacitor。
}

export default definePanel(FilesPanel);

PC Web 的 default Native Panel 可以声明当前激活周期的默认工作区布局。该配置只表达初始偏好, Host 仍会保护对话区最小宽度;用户本次拖动或重新展开左栏不会写入全局布局偏好:

export default definePanel(FilesPanel, {
  layout: {
    collapseLeftPanel: true,
    width: { type: 'ratio', value: 0.5, min: 360, max: 960 },
  },
});

fixedvalue/min/max 使用 CSS 像素,ratio.value 取值为 (0, 1]。iframe、Sandbox、 Composer、Dialog 和移动端不会消费该配置。

close() 精确返回 Host teardown Promise;signal.aborted 才表示当前实例已经失效。公共 Runtime 不暴露容器类型;业务不应按 iframe、WebView 或 in-process 分叉。平台响应式信息由 hostContext.platform 表达。

一个业务产物可以用 definePluginApp() 声明 Host 可选择的全部 Panel。Panel ID 由 Host 的 内部 RuntimeContext 下发,不从 URL 推导;Dialog 只是 Host 展示 Panel 的一种方式,不需要第二套组件定义:

export default definePluginApp({
  panels: {
    main: definePanel(FilesPanel),
    picker: definePanel(FilePickerPanel),
  },
});

开发态标准 Manifest

本地调试 Host 直接消费 agent-plugins.org v1.0.0 的 plugin.json,Panel 声明位于 extensions["cn.coze.plugin"].panels;Sandbox Panel 还必须通过 resourceUri 精确引用 cn.coze.plugin/resources.json。开发态只保留这两份与 Panel 有关的标准 JSON,不再定义一套 私有 Catalog。

最小 plugin.json

{
  "$schema": "https://coze.cn/schemas/1.0.0/plugin.schema.json",
  "name": "coze-plugin-example",
  "extensions": {
    "cn.coze.plugin": {
      "panels": [
        {
          "id": "main",
          "name": "Main",
          "runtime": "sandbox",
          "resourceUri": "ui://coze-plugin-example/main"
        },
        {
          "id": "composer",
          "name": "Composer",
          "runtime": "sandbox",
          "placement": "composer",
          "resourceUri": "ui://coze-plugin-example/composer"
        },
        {
          "id": "message",
          "name": "Message card",
          "runtime": "sandbox",
          "placement": "message",
          "resourceUri": "ui://coze-plugin-example/message"
        }
      ]
    }
  }
}

对应的 cn.coze.plugin/resources.json

{
  "resources": [
    {
      "uri": "ui://coze-plugin-example/main",
      "name": "main",
      "path": "./dist/main.html"
    },
    {
      "uri": "ui://coze-plugin-example/composer",
      "name": "composer",
      "path": "./dist/composer.html"
    },
    {
      "uri": "ui://coze-plugin-example/message",
      "name": "message",
      "path": "./dist/message.html"
    }
  ]
}

parsePluginDevelopmentManifest()parsePluginDevelopmentResources()parsePluginDevelopmentPackage() 会校验 schema 版本、稳定 Plugin name、Panel ID、重复项、 Resource namespace、包内路径,以及 Sandbox Panel 到 UI Resource 的引用关系,并返回深冻结副本。 开发服务器在固定路径 /__coze_plugin_dev__/plugin.json/__coze_plugin_dev__/cn.coze.plugin/resources.json 暴露它们;revision 使用两份规范化响应体的 共同 HTTP ETag,不得写入 Manifest JSON。

Manifest name 是稳定 Plugin name,不是 plg_* 内部安装 ID;内部 ID、开发 URL、信任级别、 Session/Agent/Project 归属与凭证均由 Host 管理。placement: "composer" 表示由输入框区域承载, placement: "message" 表示只能由 Host 投影到消息流,placement: "dialog" 表示只能作为 Dialog 目标;这些 placement 都不进入普通 Panel 列表。

当前 Panel 可以通过 app.coze.dialog.open({ panelId, payload }),把同一插件内声明为 placement: "dialog" 的另一个 Panel 交给 Host 以 Modal 展示。目标组件仍使用 usePanelRuntime(),打开参数位于 activation?.payload

受信的一方 Panel 可以通过 app.coze.commerce.openUpgradeDialog({ benefitType, targetLevel, contentScene, tracking }) 请求 Web Host 展示统一的功能升级弹窗。调用前使用 app.coze.commerce.canOpenUpgradeDialog 判断当前 Host 是否开放该能力;该请求只确认 Host 接收, 不返回弹窗关闭或支付结果。

仓库内开发

仓库内使用方需要在自己的 package.json 中声明 workspace 依赖:

{
  "dependencies": {
    "@coze/app-sdk": "workspace:*"
  }
}

依赖变更按仓库 Rush 工作流处理,不要在仓库根目录或包目录直接执行 npm、pnpm 或 yarn 安装。

快速开始

import { App, PostMessageTransport } from '@coze/app-sdk';

const app = new App(
  { name: 'example-app', version: '1.0.0' },
  { availableDisplayModes: ['inline', 'fullscreen'] },
  { autoResize: true, strict: true },
);

// 建议在连接前订阅事件,避免遗漏初始化阶段的通知。
const offActivation = app.coze.panel.onActivation(activation => {
  console.log('Panel activation:', activation.payload);
});

await app.connect(new PostMessageTransport(window.parent, window.parent));

// 不再需要事件时取消订阅。
offActivation();

// View 销毁时关闭连接。
await app.close();

App 的构造参数、标准方法和生命周期与官方 MCP Apps App 保持一致。已有 View 通常只需要把 导入来源改为 @coze/app-sdk

View 需要关闭自身时,使用与官方源码相同的 API。该通知只表达关闭意图,是否关闭由 Host 决定:

await app.requestTeardown();

Host 接受后会继续发送标准 ui/resource-teardown,View 仍通过 app.onteardown 完成保存和清理。

一方 HTTP 与当前对话事件

一方 HTTP 业务直接调用生成 IDL Service,不通过 app.coze.requests 或 Host registrar。 当前对话消息 Event 是公共 App SDK 协议,一方与三方插件使用相同入口:

import { conversationStream } from '@coze/app-sdk';
import { clawApi } from '@coze-space/services/claw';

const result = await clawApi.ListMessage({
  session_id: launchContext.sessionId,
});
const unsubscribe = await app.coze.events.subscribe(
  conversationStream,
  frame => consume(frame),
);
await unsubscribe();

调用方必须从可信 LaunchContext 构造请求目标字段,并把现有 AbortSignal 传给 IDL 方法第二个参数。events 位于根入口,调用方不能传 sessionIdconversationId, Host 始终绑定当前 App 实例的对话上下文。订阅前可通过 app.coze.events.canSubscribe(conversationStream) 检查当前 Host 是否支持。listener 直接接收 Base Agent StreamFrame;完整事件类型从 @coze/app-sdk/events 导出。Host 根据 App 信任级别决定可见事件集合:trusted App 接收全部合法 Frame, third-party App 只接收 agent.message。业务方在 listener 中自行过滤所需事件类型。

app.coze.requests 仅供确实需要 Host 自定义实现的受信扩展使用,不作为业务 HTTP 代理,也不作为普通 Panel 的默认接入方式。

Drive 云盘

Drive 在 App SDK 中提供一等 API。已迁移的受信一方 Panel 直接调用并按方法检查 capability:

const capabilities = await app.coze.drive.getCapabilities();
if (capabilities.available && app.coze.drive.canListEntries) {
  const { tree } = await app.coze.drive.listEntries({
    path: capabilities.workspace_path,
    depth: 2,
  });
}

完整的路径作用域、11 个方法、二进制传输和统一错误约定见 Drive 协议

Web、Desktop 与 Mobile 统一使用该入口,不再维护 Business Drive Definition 或并行链路。

下载文件

下载使用 MCP Apps 标准 ui/download-file。连接完成后先做 capability 检查;不支持时应隐藏或禁用入口, 不要自行增加私有下载协议:

if (app.getHostCapabilities()?.downloadFile) {
  await app.downloadFile({
    contents: [
      {
        type: 'resource_link',
        uri: 'https://example.com/report.pdf',
        name: 'report.pdf',
        mimeType: 'application/pdf',
      },
    ],
  });
}

Coze Host 的业务策略只接受标准 ResourceLinkEmbeddedResource。链接资源由 Host 在当前插件身份 下解析;第三方 HTTP(S) 请求不携带 Coze Cookie 或 token。多个资源可能由 Coze Host 打包为 ZIP, 但这是 Host 增强行为,View 不能假设其他 MCP Apps Host 也会自动打包。

如果下载对象是当前 Agent / 项目 Workspace 内的云盘文件,并且需要遵循 Drive 的本地优先选路, 应使用 app.coze.drive.downloadFile({ path }),参见 Drive 协议。 它与这里的标准 ui/download-file 是两项独立能力:前者只接受 Workspace 文件路径,后者接受标准 Resource。

Desktop 本地文件上传

app.coze.desktop.uploadFile() 是受信任一方 Panel 的 Desktop-only 能力。当前只提供接口,尚未实现桌面端本地上传逻辑;完整定义见 Desktop 消息协议

发送消息

立即发送

app.sendMessage() 直接使用标准 MCP Apps ui/message

const result = await app.sendMessage({
  role: 'user',
  content: [{ type: 'text', text: '立即发送到会话' }],
});

if (result.isError) {
  // Host 已接收请求,但消息发送失败。
}

参数类型与 MCP Apps 官方 McpUiMessageRequest['params'] 完全一致,不接受 Coze 私有消息字段、 Coze Skill 或通用 Composer Reference。结构化草稿统一使用 Composer API。

Composer 共享草稿

Composer 是当前会话的共享可见草稿。调用前逐叶检查 capability;每个操作都可以独立开放:

if (app.coze.composer.canReplace) {
  await app.coze.composer.replace({
    text: '请分析',
    focus: true,
  });
}

if (app.coze.composer.canAddReferences) {
  await app.coze.composer.addReferences({
    references: [
      {
        kind: 'selection',
        title: '已选 29 个元素',
        details: {
          targetTitle: '已选 29 个元素',
        },
        locator: { selectionId: 'selection-1' },
      },
    ],
  });
}

if (app.coze.composer.canUpdateReference) {
  await app.coze.composer.updateReference({
    referenceId: 'reference-1',
    reference: {
      kind: 'selection',
      title: '已选 30 个元素',
      details: {
        targetTitle: '已选 30 个元素',
      },
      locator: { selectionId: 'selection-1' },
    },
  });
}

if (app.coze.composer.canInsertChip) {
  await app.coze.composer.insertChip({
    chip: {
      entityId: 'asset-1',
      kind: 'video-asset',
      label: '城市黄昏素材',
      metadata: {
        coverUrl: 'https://example.com/asset-cover.png',
        fileUri: 'file:///assets/city-sunset.mp4',
      },
    },
    focus: true,
  });
}

const snapshot = app.coze.composer.canGet
  ? await app.coze.composer.get()
  : undefined;

const deviceInfo = app.coze.composer.canGetDeviceInfo
  ? await app.coze.composer.getDeviceInfo()
  : undefined;

const subscription = app.coze.composer.canSubscribe
  ? await app.coze.composer.subscribe(nextSnapshot => {
      renderComposerMirror(nextSnapshot);
    })
  : undefined;

renderComposerMirror(subscription?.snapshot ?? snapshot);
await subscription?.unsubscribe();

const removeSubscription = app.coze.composer.canSubscribeReferenceRemove
  ? await app.coze.composer.subscribeReferenceRemove(event => {
      handleRemovedReferences(event.references, event.reason);
    })
  : undefined;

const submitSubscription = app.coze.composer.canSubscribeSubmit
  ? await app.coze.composer.subscribeSubmit(event => {
      handleSubmitOutcome(event.outcome);
    })
  : undefined;

await removeSubscription?.unsubscribe();
await submitSubscription?.unsubscribe();

if (app.coze.composer.canSubmit) {
  await app.coze.composer.submit();
}

compose(insert) 在当前光标插入有序 content,并追加 references;compose(replace) 完整重建文本、 技能、文件、通用引用和回复引用。get/subscribe 始终返回完整快照,不返回差量或公开节点 ID。 技能展示名与图标、引用 provenance 都由 Host 注入,调用方只提供 skillId 和引用业务字段。 subscribeSubmitfailed outcome 表示已进入业务提交逻辑但提交失败。

Panel API

所有 Coze 扩展集中在稳定的 app.coze 命名空间下:

await app.coze.panel.open({
  target: { panelId: 'detail' },
  history: 'push',
  payload: { itemId: '123' },
});

await app.coze.panel.open({
  target: { pluginId: 'plg-builtin-files', panelId: 'main' },
  history: 'replace',
});

await app.coze.panel.back();
await app.coze.panel.forward();

省略 target.pluginId 表示当前插件;传入时按目标插件的稳定 ID 路由。default Panel 的 history 可选 pushreplace,省略时等价于 pushback() / forward() 在边界返回 { moved: false }。一方与三方插件使用同一合同和权限,目标必须已存在于当前 Session 的可用 Panel 目录中。canOpen 只表示 Host 提供导航能力,不保证某个具体目标存在。

订阅 Panel 事件:

const offActivation = app.coze.panel.onActivation(activation => {
  console.log(activation.payload);
  console.log(activation.navigation.action);
});

offActivation();

onActivation 会保留尚未消费的最新一次激活。业务加载、错误、只读和重试状态由 Panel 应用内部管理, 不通过 Host 协议上报。

Panel Header

移动端等 App Host 会在 Panel 之外渲染 Header。标题和显隐沿用既有 setHeader patch;传 title: null 恢复 Manifest 中的 Panel 名称:

if (app.coze.panel.canSetHeader) {
  await app.coze.panel.setHeader({ visible: true, title: '文件详情' });
  await app.coze.panel.setHeader({ title: null });
}

自定义按钮也归入同一个 Panel Header API。按钮由 Host 渲染,点击通知不缓冲,因此先监听再展示:

const offHeaderAction = app.coze.panel.onHeaderAction(({ actionId }) => {
  if (actionId === 'refresh') {
    void reload();
  }
});

if (app.coze.panel.canSetHeaderActions) {
  await app.coze.panel.setHeaderActions({
    actions: [
      { id: 'refresh', label: '刷新', icon: 'custom-refresh' },
      { id: 'more', label: '更多' },
    ],
  });
}

setHeaderActions 是全量替换,传 { actions: [] } 清空。每项只包含唯一 idlabel 和可选的 非空字符串 icon;SDK 不限制按钮数量、文本长度或 icon 取值。Panel 销毁或不再需要监听时调用 offHeaderAction()

安全区

安全区直接使用 MCP Apps 标准 hostContext.safeAreaInsets,不经过 Coze 自定义请求:

const insets = app.getHostContext()?.safeAreaInsets;
const offHostContext = app.subscribeHostContext(context => {
  applySafeArea(context?.safeAreaInsets);
});

Panel 销毁或不再需要监听时调用 offHostContext()

Composer 显隐

移动端会话输入框就是 Composer。隐藏它只影响可见性,不清空草稿:

if (app.coze.composer.canSetVisibility) {
  await app.coze.composer.setVisibility({ visible: false });
}

Capability 检查

Coze 扩展是渐进增强能力。连接成功后,可以读取当前 Host 实际声明的细粒度 capability:

app.coze.composer.canInsert;
app.coze.composer.canInsertChip;
app.coze.composer.canReplace;
app.coze.composer.canAddReferences;
app.coze.composer.canUpdateReference;
app.coze.composer.canRemoveReferences;
app.coze.composer.canFocus;
app.coze.composer.canGet;
app.coze.composer.canSubscribe;
app.coze.composer.canSubscribeReferenceRemove;
app.coze.composer.canSubscribeSubmit;
app.coze.composer.canSubmit;
app.coze.composer.canSetVisibility;
app.coze.panel.canOpen;
app.coze.panel.canSetHeader;
app.coze.panel.canSetHeaderActions;
app.coze.desktop.canUploadFile;
app.getHostCapabilities()?.downloadFile;
app.getHostCapabilities()?.openLinks;

这些值在连接前和连接关闭后均为 false。调用未声明的 Coze 能力不会自动降级,而是抛出 CozeCapabilityUnavailableError

错误处理

import {
  CozeCapabilityUnavailableError,
  CozeHostError,
  CozeInvalidArgumentError,
} from '@coze/app-sdk';

try {
  await app.coze.composer.compose({
    mode: 'insert',
    content: [{ type: 'text', text: '写入草稿' }],
  });
} catch (error) {
  if (error instanceof CozeCapabilityUnavailableError) {
    console.log('当前 Host 不支持该能力:', error.capability);
  } else if (error instanceof CozeInvalidArgumentError) {
    console.log('本地参数不合法:', error.message);
  } else if (error instanceof CozeHostError) {
    console.log('Host 请求失败:', error.method, error.code, error.data);
  }
}

标准 MCP Apps API 的返回值和异常保持官方语义;只有 Coze 扩展请求会映射为上述 Coze 错误。 所有请求方法均支持传入官方 RequestOptions,SDK 不会自动重试或静默切换到其他发送方式。

License

本项目使用 MIT License。第三方组件及许可证见 THIRD-PARTY-LICENSES.md