Skip to content

MCP 扩展添加与详情 ​

McpExtensionForm 收集一条 MCP 连接配置,支持表单和 JSON 两种输入方式;McpExtensionDetail 展示已加载的 MCP 信息与工具开关。两者都是可嵌入页面或弹窗的内容组件,适合在应用处理扩展管理操作后分别呈现添加和详情界面。

概览 ​

适用场景 ​

  • 用 McpExtensionForm 让用户填写一条 SSE 或 Streamable HTTP 连接,并把校验后的配置交给应用。
  • 用 McpExtensionDetail 展示名称、描述和工具列表,让应用接收工具开关意图。
  • 与 ExtensionManager 组合时,由应用根据用户的添加或查看操作打开相应界面。

职责边界 ​

表单负责收集和校验连接配置,详情组件展示应用传入的信息与工具状态。两者都不会替应用加载详情或保存数据,也不拥有弹窗标题和关闭行为。应用负责调用服务、保存结果、更新界面,并处理取消和关闭操作。

MCP 存储工具是独立的公开入口,表单不会自动调用它们;具体接口见文末。已有的 McpAddForm 也是独立公共组件。

用法示例 ​

添加一条 MCP 连接 ​

为必填的 model-value 提供初始值。用户输入可转换为配置时,v-model 会更新;点击“确定”通过校验后,应用收到完整配置和输入方式。示例只在页面显示收到的结果,真实保存由应用实现。

loading

使用 JSON 配置 ​

mode 是受控值:提供它时,应用应处理 update:mode 并同步新值。未提供 mode 时,组件自行切换方式,初始方式由 default-mode 决定。切换方式会从最近可表示的 model-value 建立新草稿;暂时无效的 JSON 留在当前编辑器内,不会覆盖绑定值。

代码方式要求顶层包含 mcpServers,且恰好只有一个服务器。sse、http 和 streamableHttp 可以解析,http 会规范化为 streamableHttp;不支持 stdio。示例可以改坏 JSON 后点击“确定”查看错误,再恢复示例配置。

loading

显示并切换工具 ​

McpExtensionDetail 按传入的 tools 展示开关。用户操作时,它只触发 tool-toggle,不会自行修改 tools。示例由应用更新数组,因此开关状态会在事件后保持;禁用项不能触发切换。

loading

可访问性与设计约束 ​

表单使用有名称的输入框和单选组。提交未通过校验时,错误会关联到对应字段,并把焦点移到第一个无效字段;修正字段后,相应错误会清除。缩略图 URL 加载失败时会显示本地默认图和可见错误提示。代码编辑器也会在提交失败时获得焦点。

工具开关使用带工具名称的原生复选框,并提供 switch 语义;禁用项不进入可操作状态。详情摘要中的名称和描述会按可用宽度换行,单个工具的名称和描述则以单行省略;应用应为详情区域提供合适的宽度。弹窗焦点管理、标题、关闭按钮与返回焦点由宿主负责。

API ​

组件和以下类型均从 @opentiny/tiny-robot 导入;示例使用 TrMcpExtensionForm、TrMcpExtensionDetail 及对应的 <tr-*> 标签。两个组件都没有公开 Slots 或 Expose 方法。

McpExtensionForm ​

Props ​

属性名说明类型默认值必填
model-value当前连接配置;草稿可转换时通过 update:model-value 请求更新,父组件需同步。McpExtensionFormValue—是
mode受控的添加方式;提供时优先于 default-mode,父组件需处理 update:mode。McpExtensionFormMode—否
default-mode未提供 mode 时使用的初始添加方式。McpExtensionFormMode'form'否

Events ​

事件名触发时机回调参数
update:model-value表单草稿可转换为配置,或代码草稿可解析时;可能早于完整提交校验。父组件需更新绑定值。(value: McpExtensionFormValue) => void
update:mode用户选择另一种添加方式时;提供 mode 的父组件需同步新值。(mode: McpExtensionFormMode) => void
submit点击“确定”且当前方式校验通过后;组件先更新绑定值,再交由应用保存。(value: McpExtensionFormValue, meta: McpExtensionFormSubmitMeta) => void
cancel用户点击“取消”时;组件不清空草稿或关闭宿主。() => void

Types ​

类型名类型或签名说明
McpExtensionFormModeunion'form' | 'code'。
McpExtensionFormValueinterface一条经过表单或代码编辑的连接配置。
McpExtensionFormPropsinterface表单的公开 Props。
McpExtensionFormSubmitMetainterface提交来源,source 为当前输入方式。
McpExtensionFormEmitsinterface表单的四个公开事件签名。

以下是 McpExtensionFormValue 与 McpExtensionFormSubmitMeta 的完整定义:

ts
interface McpExtensionFormValue {
  name: string
  description?: string
  type: 'sse' | 'streamableHttp'
  url: string
  headers?: Record<string, string>
  thumbnail?: string | null
}

interface McpExtensionFormSubmitMeta {
  source: McpExtensionFormMode
}

name 和 url 在提交时必填,url 必须为 HTTP 或 HTTPS 地址。表单中的 headers 输入是 JSON 对象;可转换的数字、布尔值会成为字符串。空白的可选字段在提交结果中省略。thumbnail 是图片 URL 或空值,图片加载失败时仅回退预览,不自动替换应用保存的 URL。

McpExtensionDetail ​

Props ​

属性名说明类型默认值必填
idMCP 标识;详情组件不根据它加载数据。string—是
name展示的 MCP 名称。string—是
tools应用提供的工具列表;事件不会直接修改它。McpExtensionTool[]—是
description描述;为空时显示“暂无描述”。string—否
updated-at已格式化的更新时间文本;未提供时不显示。string—否

Events ​

事件名触发时机回调参数
tool-toggle用户切换可用工具时;仅提供下一状态,应用需更新 tools。(event: McpExtensionToolToggleEvent) => void

Types ​

类型名类型或签名说明
McpExtensionToolinterface工具标识、显示信息与开关状态。
McpExtensionDetailPropsinterface详情的公开 Props。
McpExtensionToolToggleEventinterface被切换工具的 ID 和目标状态。
McpExtensionDetailEmitsinterfacetool-toggle 的公开事件签名。

以下是 McpExtensionTool 与 McpExtensionToolToggleEvent 的完整定义:

ts
interface McpExtensionTool {
  id: string
  name: string
  description?: string
  enabled: boolean
  disabled?: boolean
}

interface McpExtensionToolToggleEvent {
  toolId: string
  enabled: boolean
}

id 应在列表中稳定且唯一;disabled: true 时不能切换。工具数量直接来自 tools.length,updated-at 只展示传入文本,不会自行格式化日期。

公开 MCP 存储工具 ​

MCP 存储是工具函数,不是组件。McpExtensionStorage 在一个实例中分别管理连接定义 data 和配置 options。source + id 是稳定身份;名称只用于显示,因此不同来源可以同名。卸载时调用 deleteData,工具开关和应用配置仍保留;再次安装同一身份会恢复这些配置。

ts
import { createMemoryMcpExtensionStorage } from '@opentiny/tiny-robot'

const storage = createMemoryMcpExtensionStorage({
  parseBusinessOptions(value) {
    if (typeof value !== 'object' || value === null || !('enabled' in value) || typeof value.enabled !== 'boolean') {
      throw new Error('启用配置无效')
    }
    return { enabled: value.enabled }
  },
})
const identity = { source: 'remote', id: 'weather' }
await storage.upsertData({
  ...identity,
  version: 1,
  name: '天气服务',
  type: 'streamableHttp',
  url: 'https://example.com/mcp',
  tools: [{ id: 'forecast', name: '天气预报', enabled: true }],
})
await storage.setOptions(identity, {
  toolPolicy: { default: 'enabled', overrides: {} },
  business: { enabled: false },
})
await storage.deleteData(identity) // 配置仍可用 getOptions(identity) 读取

createMcpExtensionStorage() 默认使用浏览器 localStorage,也可传入 DOM Storage 或异步 adapter。createMemoryMcpExtensionStorage() 每次返回独立实例;页面刷新后清空。表单只发出提交数据,应用需要在保存成功后更新界面。

公开入口签名用途
createMcpExtensionStorage<TBusiness>(options?: McpExtensionStorageOptions<TBusiness>) => McpExtensionStorage<TBusiness>Web Storage 或异步文档适配器。
createMemoryMcpExtensionStorage<TBusiness>(options?: Pick<McpExtensionStorageOptions<TBusiness>, 'parseBusinessOptions'>) => McpExtensionStorage<TBusiness>独立的内存实例。

所有方法均返回 Promise。identity 是 { source, id }。

方法返回值行为
listData()Promise<McpExtensionData[]>读取已保存的定义。
getData(identity)Promise<McpExtensionData | undefined>按稳定身份读取定义。
upsertData(input)Promise<McpExtensionData>保存定义;较高 version 更新,较低版本保持原定义,同版本内容冲突时报错;不修改 options。
deleteData(identity)Promise<void>删除定义,保留 options;定义不存在时报错。
getOptions(identity)Promise<McpExtensionOptions<TBusiness> | undefined>配置可以独立于定义存在。
setOptions(identity, options)Promise<McpExtensionOptions<TBusiness>>保存工具策略及应用配置。
deleteOptions(identity)Promise<void>显式清除配置。
setToolEnabled(identity, toolId, enabled)Promise<McpExtensionOptions<TBusiness>>只修改工具覆盖值,保留其他配置;没有定义时也可调用。
create(input) / createFromConfig(config)Promise<McpExtensionData>手动添加:生成 manual 来源的 UUID、版本 1;配置文本仅接受单个服务器。
update(identity, input) / updateFromConfig(identity, config)Promise<McpExtensionData>编辑已保存的连接字段并递增版本;保留身份和 options。

以下为本节存储类型的完整字段;McpExtensionStorage<TBusiness> 的全部方法列在上表。McpExtensionTool 的字段见前文详情组件 API。

ts
type McpExtensionTransportType = 'sse' | 'streamableHttp'

interface McpExtensionInput {
  name: string
  description?: string
  type: McpExtensionTransportType
  url: string
  headers?: Record<string, unknown>
  thumbnail?: string | null
}

interface McpExtensionIdentity {
  source: string
  id: string
}

interface McpExtensionDataInput extends McpExtensionIdentity, McpExtensionInput {
  version: number
  tools: McpExtensionTool[]
}

interface McpExtensionData extends McpExtensionIdentity {
  version: number
  name: string
  description: string
  type: McpExtensionTransportType
  url: string
  headers: Record<string, string>
  thumbnail: string | null
  tools: McpExtensionTool[]
  createdAt: string
  updatedAt: string
}

interface McpExtensionToolPolicy {
  default: 'enabled' | 'disabled'
  overrides: Record<string, boolean>
}

interface McpExtensionOptions<TBusiness = never> {
  toolPolicy: McpExtensionToolPolicy
  business?: TBusiness
}

interface McpExtensionStorageAdapter {
  read(key: string): Promise<string | null>
  write(key: string, value: string): Promise<void>
}

interface McpExtensionStorageOptions<TBusiness = never> {
  namespace?: string
  storage?: Storage
  adapter?: McpExtensionStorageAdapter
  parseBusinessOptions?: (value: unknown) => TBusiness
}

version 是非负整数,用于比较同一身份的定义。options 中的 toolPolicy 是组件提供的基础字段;business 由应用定义,读取时必须提供 parseBusinessOptions 校验。storage 与 adapter 只能二选一。异步适配器以完整文档读写;同一实例会串行执行修改,跨客户端并发控制由实际后端负责。Web Storage 会把请求头以普通 JSON 文本保存;有凭据时应选择合适的应用存储。