@opentiny/next-remoter 0.2.2 → 0.2.4

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/README.md CHANGED
@@ -1,62 +1,171 @@
1
- # next-remoter
1
+ # OpenTiny Next Remoter
2
2
 
3
- 目前是提供给`Vue的开发用户`的一个开箱即用的组件。
3
+ 一个基于 `@opentiny/tiny-robot` 开发的 Vue3 AI 对话组件,提供开箱即用的智能对话能力。
4
4
 
5
- 核心代码在 `components, composable` 中, `TinyRobotChat.vue` 是真正的组件
5
+ ## 主要功能
6
6
 
7
- 1. `index.ts` 是用于导出 Vue 组件的包,供用户使用
8
- 2. `App.vue` 是部署在服务器上,扫码访问的页面
7
+ - 🤖 **AI 对话**:支持与大语言模型进行智能对话
8
+ - 🎨 **自定义界面**:支持自定义欢迎界面和提示建议
9
+ - 🔌 **MCP 插件市场**:内置插件市场,支持添加和管理 MCP 服务
10
+ - 📱 **扫码添加应用**:支持扫码快速添加应用和工具
11
+ - 🔄 **多模型切换**:支持在多个大语言模型之间切换
12
+ - 🎯 **技能系统**:支持自定义技能和提示词
13
+ - 🎭 **多角色展示**:支持多角色消息展示,支持 Markdown 和工具调用渲染
14
+ - 🎪 **生成式 UI**:支持生成式 UI 渲染(可选)
15
+ - 🌐 **国际化支持**:支持中英文切换
9
16
 
10
- ## 设想`Vue的开发用户`的使用流程
17
+ ## 📦 安装
11
18
 
12
- ```html
13
- <tiny-remoter
14
- ref="remoterRef"
15
- v-model:show="showAiChat"
16
- :sessionId="sessionId"
17
- :title="项目名字"
18
- :llmConfig="llmConfig"
19
- :custom-market-mcp-servers="customMarketMcpServers"
20
- >
21
- <template #welcome>
22
- <!-- 自定义标题+图标
23
- 自定义的Promts, 点击后调用 sendMessage()
24
- 其它任意欢迎界面元素 -->
25
- </template>
26
- <template #suggestions>
27
- <!-- 输入框上面的提示词。 有丰富的用法,并不仅是点击,插入一个模板,详见官网。 所以用插槽代替 -->
28
- </template>
29
- </tiny-remoter>
19
+ ```bash
20
+ npm install @opentiny/next-remoter
21
+ #
22
+ pnpm add @opentiny/next-remoter
30
23
  ```
31
24
 
32
- ```typescript
33
- import { createRemoter } from 'next-sdk'
34
- import { TinyRemoter } from 'remoter'
25
+ ## 🏗️ 项目结构
26
+
27
+ ```
28
+ next-remoter/
29
+ ├── src/
30
+ │ ├── components/ # 组件实现
31
+ │ │ ├── TinyRobotChat.vue # 核心对话组件
32
+ │ │ └── QrCodeScan.vue # 二维码扫码组件
33
+ │ ├── composable/ # 组合式函数
34
+ │ │ └── usePlugin.ts # 插件管理逻辑
35
+ │ ├── index.ts # 导出 Vue 组件,供用户使用
36
+ │ └── App.vue # 部署在服务器上的扫码访问页面
37
+ ├── package.json
38
+ └── README.md
39
+ ```
40
+
41
+ **说明:**
42
+
43
+ - `index.ts`:导出 `TinyRemoter` 组件供外部使用
44
+ - `App.vue`:可部署到服务器,提供扫码访问的独立页面
45
+ - `components/TinyRobotChat.vue`:核心对话组件实现
46
+ - `composable/usePlugin.ts`:MCP 插件管理的核心逻辑
47
+
48
+ ## 🚀 快速开始
49
+
50
+ ### 基本使用
51
+
52
+ ```vue
53
+ <template>
54
+ <TinyRemoter
55
+ v-model:show="showAiChat"
56
+ :sessionId="sessionId"
57
+ title="我的AI助手"
58
+ />
59
+ </template>
60
+
61
+ <script setup>
62
+ import { ref } from 'vue'
63
+ import { TinyRemoter } from '@opentiny/next-remoter'
64
+
65
+ const showAiChat = ref(false)
66
+ const sessionId = ref('your-session-id')
67
+ </script>
68
+ ```
69
+
70
+ ### 完整使用示例
71
+
72
+ ```vue
73
+ <template>
74
+ <TinyRemoter
75
+ ref="remoterRef"
76
+ v-model:show="showAiChat"
77
+ :sessionId="sessionId"
78
+ :title="项目名称"
79
+ :llmConfig="llmConfig"
80
+ :customMarketMcpServers="customMarketMcpServers"
81
+ >
82
+ <!-- 自定义欢迎界面 -->
83
+ <template #welcome>
84
+ <div class="welcome-content">
85
+ <h2>欢迎使用 AI 助手</h2>
86
+ <p>我可以帮您完成各种任务</p>
87
+ <button @click="sendPrompt('帮我分析数据')">分析数据</button>
88
+ </div>
89
+ </template>
90
+
91
+ <!-- 自定义输入框上方的提示建议 -->
92
+ <template #suggestions>
93
+ <div class="suggestions">
94
+ <button @click="sendPrompt('查询天气')">查询天气</button>
95
+ <button @click="sendPrompt('生成代码')">生成代码</button>
96
+ </div>
97
+ </template>
98
+ </TinyRemoter>
99
+ </template>
100
+
101
+ <script setup>
102
+ import { ref } from 'vue'
103
+ import { TinyRemoter } from '@opentiny/next-remoter'
35
104
 
36
105
  const showAiChat = ref(false)
37
106
  const remoterRef = ref()
107
+ const sessionId = ref('your-session-id')
38
108
 
39
- // 配置自定义LLM(可选)
109
+ // 配置自定义 LLM
40
110
  const llmConfig = {
41
111
  apiKey: 'your-api-key',
42
112
  baseURL: 'https://api.openai.com/v1',
43
- providerType: 'openai'
113
+ providerType: 'openai',
114
+ model: 'gpt-4o'
115
+ }
116
+
117
+ // 自定义 MCP 服务器
118
+ const customMarketMcpServers = [
119
+ {
120
+ id: 'custom-server',
121
+ name: '自定义服务器',
122
+ description: '我的自定义 MCP 服务',
123
+ url: 'https://your-server.com/mcp',
124
+ type: 'sse',
125
+ enabled: false,
126
+ addState: 'idle',
127
+ tools: []
128
+ }
129
+ ]
130
+
131
+ // 发送预设提示词
132
+ const sendPrompt = (message) => {
133
+ remoterRef.value?.sendMessage(message)
44
134
  }
135
+ </script>
136
+ ```
45
137
 
46
- // 1、 创建 server,client
138
+ ## 📖 使用场景
139
+
140
+ ### 场景一:创建 WebMCP 连接
141
+
142
+ ```typescript
143
+ import { WebMcpClient, WebMcpServer, createMessageChannelPairTransport } from '@opentiny/next-sdk'
144
+
145
+ // 1. 创建消息通道
47
146
  const [serverTransport, clientTransport] = createMessageChannelPairTransport()
48
- const server = new WebMcpServer({ name: 'demo-server', version: '1.0.0' })
49
- const client = new WebMcpClient({ name: 'demo-client', version: '1.0.0' })
50
147
 
148
+ // 2. 创建并连接 MCP 服务端
149
+ const server = new WebMcpServer({ name: 'demo-server', version: '1.0.0' })
51
150
  await server.connect(serverTransport)
151
+
152
+ // 3. 创建并连接 MCP 客户端
153
+ const client = new WebMcpClient({ name: 'demo-client', version: '1.0.0' })
52
154
  await client.connect(clientTransport)
53
155
 
156
+ // 4. 连接到远程 MCP 服务
54
157
  const { sessionId } = await client.connect({
55
158
  url: 'https://agent.opentiny.design/api/v1/webmcp-trial/mcp',
56
159
  agent: true
57
160
  })
161
+ ```
58
162
 
59
- // 2. 创建页面的浮动块
163
+ ### 场景二:创建页面浮动遥控器
164
+
165
+ ```typescript
166
+ import { createRemoter } from '@opentiny/next-sdk'
167
+
168
+ // 创建页面右下角的浮动遥控器
60
169
  createRemoter({
61
170
  sessionId,
62
171
  qrCodeUrl: 'https://ai.opentiny.design/next-remoter',
@@ -64,94 +173,748 @@ createRemoter({
64
173
  showAiChat.value = true
65
174
  }
66
175
  })
176
+ ```
67
177
 
68
- // 3. remoterRef实例
69
- // v-model:fullscreen 双向绑定是否全屏
70
- // v-model:show 双向绑定是否显示,内部关闭是emit('update:show',false)
71
- // sessionId 必须传
72
- // title 左上角的 container.title
73
- // agentRoot 后端代理的地址,有默认值
178
+ ## ⚙️ 组件属性
74
179
 
75
- // remoterRef实例需要:
76
- // expose({ sendMessage, abortRequest, messages, messageState,senderRef})
180
+ ### 基础属性
77
181
 
78
- // currentTemplate, clearTemplate, 模板相关的功能先去掉,方便跨UI chat 框架适配。
182
+ | 属性 | 类型 | 必填 | 默认值 | 说明 |
183
+ |------|------|------|--------|------|
184
+ | `v-model:show` | `boolean` | 是 | - | 双向绑定是否显示对话框 |
185
+ | `sessionId` | `string` | 是 | - | 会话 ID |
186
+ | `title` | `string` | 否 | - | 对话框左上角标题 |
187
+ | `v-model:fullscreen` | `boolean` | 否 | `false` | 双向绑定是否全屏 |
188
+ | `agentRoot` | `string` | 否 | - | 后端代理地址 |
189
+ | `locale` | `'zh-CN' \| 'en-US'` | 否 | `'zh-CN'` | 国际化语言 |
190
+ | `mode` | `'remoter' \| 'chat-dialog'` | 否 | `'chat-dialog'` | 展示模式 |
191
+ | `systemPrompt` | `string` | 否 | - | 系统提示词 |
192
+ | `llmConfig` | `object` | 否 | - | 大语言模型配置 |
193
+ | `llmConfigs` | `array` | 否 | - | 多模型配置数组 |
194
+ | `v-model:selectedModelId` | `string` | 否 | - | 当前选中的模型 ID |
195
+ | `customMarketMcpServers` | `array` | 否 | - | 自定义 MCP 市场服务列表 |
196
+ | `skills` | `array` | 否 | - | 技能配置数组 |
197
+ | `inBrowserExt` | `boolean` | 否 | `false` | 是否运行在浏览器扩展中 |
198
+ | `genUiAble` | `boolean` | 否 | `false` | 是否支持生成式 UI |
199
+ | `genUiComponents` | `object` | 否 | - | 生成式 UI 组件 |
79
200
 
80
- ## LLM配置
201
+ ### LLM 配置(llmConfig)
81
202
 
82
- TinyRemoter组件支持自定义大语言模型配置,统一通过 `llmConfig` 传入:
203
+ TinyRemoter 组件支持自定义大语言模型配置,统一通过 `llmConfig` 传入:
83
204
 
84
- ### 1. 使用 llmConfig.providerType 配置对象
205
+ #### 方式一:使用 providerType 配置
85
206
 
86
207
  ```typescript
87
208
  const llmConfig = {
88
209
  apiKey: 'your-api-key',
89
210
  baseURL: 'https://api.openai.com/v1',
90
- providerType: 'openai' // 支持 'openai' | 'deepseek' 或自定义Provider函数
211
+ providerType: 'openai', // 支持 'openai' | 'deepseek'
212
+ model: 'gpt-4o',
213
+ maxSteps: 10
91
214
  }
92
215
  ```
93
216
 
94
- ### 2. 使用自定义Provider实例(通过 llmConfig.llm)
217
+ #### 方式二:使用自定义 Provider 实例
95
218
 
96
219
  ```typescript
97
220
  import { createOpenAI } from '@ai-sdk/openai'
98
221
 
99
- const customProvider = createOpenAI({
100
- apiKey: process.env.OPENAI_API_KEY,
101
- baseURL: 'https://api.openai.com/v1'
222
+ const llmConfig = {
223
+ llm: createOpenAI({
224
+ apiKey: process.env.OPENAI_API_KEY,
225
+ baseURL: 'https://api.openai.com/v1'
226
+ }),
227
+ model: 'gpt-4o'
228
+ }
229
+ ```
230
+
231
+ #### 支持的 LLM 提供商
232
+
233
+ - ✅ OpenAI (`providerType: 'openai'`)
234
+ - ✅ DeepSeek (`providerType: 'deepseek'`)
235
+ - ✅ Anthropic Claude(通过自定义 Provider)
236
+ - ✅ 其他 ai-sdk 兼容的提供商
237
+
238
+ ### 插槽
239
+
240
+ | 插槽名 | 说明 |
241
+ |--------|------|
242
+ | `#welcome` | 无对话消息时展示的欢迎界面,可自定义标题、图标、预设提示词等 |
243
+ | `#suggestions` | 输入框上方的提示建议区域 |
244
+ | `#operations` | 容器头部右侧的操作区域(新建会话、历史会话等) |
245
+ | `#header-actions` | MCP 插件市场头部的操作区域 |
246
+
247
+ ### 导出方法和属性
248
+
249
+ ```typescript
250
+ defineExpose({
251
+ agent, // 大模型代理实例
252
+ welcomeIcon, // 欢迎图标
253
+ messages, // 对话消息列表
254
+ messageState, // 对话消息状态
255
+ roles, // 对话角色配置
256
+ inputMessage, // 输入框文本
257
+ senderRef, // 输入框组件实例
258
+ abortRequest, // 取消发送函数
259
+ sendMessage, // 发送消息函数
260
+ loadMcpServerToPlugin, // 加载 MCP 服务到插件
261
+ handleClientDisconnected, // 处理客户端断开
262
+ addMessage // 添加消息函数
102
263
  })
264
+ ```
265
+
266
+ ## 💡 使用示例
267
+
268
+ ### 示例 1:使用 OpenAI
269
+
270
+ ```vue
271
+ <template>
272
+ <TinyRemoter
273
+ v-model:show="show"
274
+ :sessionId="sessionId"
275
+ title="OpenAI 助手"
276
+ :llmConfig="openAIConfig"
277
+ />
278
+ </template>
279
+
280
+ <script setup>
281
+ import { ref } from 'vue'
282
+ import { TinyRemoter } from '@opentiny/next-remoter'
283
+
284
+ const show = ref(false)
285
+ const sessionId = ref('session-001')
286
+
287
+ const openAIConfig = {
288
+ apiKey: import.meta.env.VITE_OPENAI_API_KEY,
289
+ baseURL: 'https://api.openai.com/v1',
290
+ providerType: 'openai',
291
+ model: 'gpt-4o',
292
+ maxSteps: 10
293
+ }
294
+ </script>
295
+ ```
296
+
297
+ ### 示例 2:使用 DeepSeek
298
+
299
+ ```vue
300
+ <template>
301
+ <TinyRemoter
302
+ v-model:show="show"
303
+ :sessionId="sessionId"
304
+ title="DeepSeek 助手"
305
+ :llmConfig="deepSeekConfig"
306
+ />
307
+ </template>
308
+
309
+ <script setup>
310
+ import { ref } from 'vue'
311
+ import { TinyRemoter } from '@opentiny/next-remoter'
312
+
313
+ const show = ref(false)
314
+ const sessionId = ref('session-002')
315
+
316
+ const deepSeekConfig = {
317
+ apiKey: import.meta.env.VITE_DEEPSEEK_API_KEY,
318
+ baseURL: 'https://api.deepseek.com',
319
+ providerType: 'deepseek',
320
+ model: 'deepseek-chat',
321
+ maxSteps: 15
322
+ }
323
+ </script>
324
+ ```
325
+
326
+ ### 示例 3:使用 Anthropic Claude
327
+
328
+ ```vue
329
+ <template>
330
+ <TinyRemoter
331
+ v-model:show="show"
332
+ :sessionId="sessionId"
333
+ title="Claude 助手"
334
+ :llmConfig="claudeConfig"
335
+ />
336
+ </template>
337
+
338
+ <script setup>
339
+ import { ref } from 'vue'
340
+ import { TinyRemoter } from '@opentiny/next-remoter'
341
+ import { createAnthropic } from '@ai-sdk/anthropic'
342
+
343
+ const show = ref(false)
344
+ const sessionId = ref('session-003')
345
+
346
+ const claudeConfig = {
347
+ llm: createAnthropic({
348
+ apiKey: import.meta.env.VITE_ANTHROPIC_API_KEY
349
+ }),
350
+ model: 'claude-3-5-sonnet-20241022'
351
+ }
352
+ </script>
353
+ ```
354
+
355
+ ### 示例 4:多模型切换
356
+
357
+ ```vue
358
+ <template>
359
+ <TinyRemoter
360
+ v-model:show="show"
361
+ v-model:selected-model-id="selectedModelId"
362
+ :sessionId="sessionId"
363
+ title="智能助手"
364
+ :llmConfigs="modelConfigs"
365
+ />
366
+ </template>
367
+
368
+ <script setup>
369
+ import { ref, watch } from 'vue'
370
+ import { TinyRemoter } from '@opentiny/next-remoter'
371
+ import IconOpenAI from './icons/openai.svg'
372
+ import IconDeepSeek from './icons/deepseek.svg'
373
+
374
+ const show = ref(false)
375
+ const sessionId = ref('session-004')
376
+ const selectedModelId = ref('gpt-4o')
377
+
378
+ const modelConfigs = [
379
+ {
380
+ id: 'gpt-4o',
381
+ label: 'GPT-4o',
382
+ icon: IconOpenAI,
383
+ isDefault: true,
384
+ apiKey: import.meta.env.VITE_OPENAI_API_KEY,
385
+ baseURL: 'https://api.openai.com/v1',
386
+ providerType: 'openai',
387
+ model: 'gpt-4o'
388
+ },
389
+ {
390
+ id: 'deepseek-v3',
391
+ label: 'DeepSeek V3',
392
+ icon: IconDeepSeek,
393
+ apiKey: import.meta.env.VITE_DEEPSEEK_API_KEY,
394
+ baseURL: 'https://api.deepseek.com',
395
+ providerType: 'deepseek',
396
+ model: 'deepseek-chat'
397
+ }
398
+ ]
399
+
400
+ // 监听模型切换
401
+ watch(selectedModelId, (newModelId) => {
402
+ console.log('当前选中的模型:', newModelId)
403
+ })
404
+ </script>
405
+ ```
406
+
407
+ ### 示例 5:配置技能列表
408
+
409
+ ```vue
410
+ <template>
411
+ <TinyRemoter
412
+ v-model:show="show"
413
+ :sessionId="sessionId"
414
+ title="技能助手"
415
+ :llmConfig="llmConfig"
416
+ :skills="skills"
417
+ />
418
+ </template>
419
+
420
+ <script setup>
421
+ import { ref } from 'vue'
422
+ import { TinyRemoter } from '@opentiny/next-remoter'
423
+
424
+ const show = ref(false)
425
+ const sessionId = ref('session-005')
103
426
 
104
427
  const llmConfig = {
105
- llm: customProvider
428
+ apiKey: import.meta.env.VITE_OPENAI_API_KEY,
429
+ baseURL: 'https://api.openai.com/v1',
430
+ providerType: 'openai',
431
+ model: 'gpt-4o'
106
432
  }
433
+
434
+ // 在输入框输入 @ 可唤起技能列表
435
+ const skills = [
436
+ {
437
+ label: '办公助手',
438
+ value: '你是一个办公助手,擅长处理文档、邮件和日程安排'
439
+ },
440
+ {
441
+ label: '代码专家',
442
+ value: '你是一个代码专家,擅长编写和优化代码',
443
+ tools: ['codeAnalysis', 'codeGeneration']
444
+ },
445
+ {
446
+ label: '数据分析师',
447
+ value: '你是一个数据分析师,擅长数据分析和可视化'
448
+ }
449
+ ]
450
+ </script>
107
451
  ```
108
452
 
109
- 然后在组件中使用:
453
+ ### 示例 6:自定义插槽
454
+
455
+ ```vue
456
+ <template>
457
+ <TinyRemoter
458
+ ref="robotRef"
459
+ v-model:show="show"
460
+ :sessionId="sessionId"
461
+ title="自定义助手"
462
+ :llmConfig="llmConfig"
463
+ >
464
+ <!-- 自定义欢迎界面 -->
465
+ <template #welcome>
466
+ <div class="custom-welcome">
467
+ <img src="./logo.png" alt="Logo" />
468
+ <h2>欢迎使用智能助手</h2>
469
+ <p>选择一个快速开始的功能</p>
470
+ <div class="quick-actions">
471
+ <button
472
+ v-for="action in quickActions"
473
+ :key="action.id"
474
+ @click="handleQuickAction(action)"
475
+ >
476
+ {{ action.label }}
477
+ </button>
478
+ </div>
479
+ </div>
480
+ </template>
481
+
482
+ <!-- 自定义提示建议 -->
483
+ <template #suggestions>
484
+ <div class="suggestion-pills">
485
+ <span
486
+ v-for="suggestion in suggestions"
487
+ :key="suggestion"
488
+ @click="handleSuggestion(suggestion)"
489
+ >
490
+ {{ suggestion }}
491
+ </span>
492
+ </div>
493
+ </template>
494
+
495
+ <!-- 自定义头部操作 -->
496
+ <template #operations>
497
+ <button @click="exportChat">导出对话</button>
498
+ <button @click="clearHistory">清空历史</button>
499
+ </template>
500
+ </TinyRemoter>
501
+ </template>
502
+
503
+ <script setup>
504
+ import { ref } from 'vue'
505
+ import { TinyRemoter } from '@opentiny/next-remoter'
506
+
507
+ const show = ref(false)
508
+ const robotRef = ref()
509
+ const sessionId = ref('session-006')
110
510
 
111
- ```html
112
- <tiny-remoter
113
- :llmConfig="llmConfig"
114
- :sessionId="sessionId"
115
- />
511
+ const llmConfig = {
512
+ apiKey: import.meta.env.VITE_OPENAI_API_KEY,
513
+ baseURL: 'https://api.openai.com/v1',
514
+ providerType: 'openai',
515
+ model: 'gpt-4o'
516
+ }
517
+
518
+ const quickActions = [
519
+ { id: 1, label: '📊 数据分析', prompt: '帮我分析这份数据' },
520
+ { id: 2, label: '💻 代码生成', prompt: '帮我生成代码' },
521
+ { id: 3, label: '📝 文档撰写', prompt: '帮我撰写文档' }
522
+ ]
523
+
524
+ const suggestions = ['天气查询', '日程安排', '邮件撰写', '数据可视化']
525
+
526
+ const handleQuickAction = (action) => {
527
+ robotRef.value?.sendMessage(action.prompt)
528
+ }
529
+
530
+ const handleSuggestion = (suggestion) => {
531
+ robotRef.value?.sendMessage(suggestion)
532
+ }
533
+
534
+ const exportChat = () => {
535
+ const messages = robotRef.value?.messages
536
+ console.log('导出对话:', messages)
537
+ }
538
+
539
+ const clearHistory = () => {
540
+ console.log('清空历史')
541
+ }
542
+ </script>
543
+
544
+ <style scoped>
545
+ .custom-welcome {
546
+ text-align: center;
547
+ padding: 40px 20px;
548
+ }
549
+
550
+ .quick-actions {
551
+ display: flex;
552
+ gap: 10px;
553
+ justify-content: center;
554
+ margin-top: 20px;
555
+ }
556
+
557
+ .suggestion-pills {
558
+ display: flex;
559
+ gap: 8px;
560
+ flex-wrap: wrap;
561
+ padding: 10px;
562
+ }
563
+
564
+ .suggestion-pills span {
565
+ padding: 6px 12px;
566
+ background: #f0f0f0;
567
+ border-radius: 16px;
568
+ cursor: pointer;
569
+ transition: all 0.2s;
570
+ }
571
+
572
+ .suggestion-pills span:hover {
573
+ background: #e0e0e0;
574
+ }
575
+ </style>
576
+ ```
577
+
578
+ ## 🔌 自定义 MCP 插件
579
+
580
+ `TinyRemoter` 通过 `customMarketMcpServers` 属性支持自定义 MCP 插件。传入的插件会与组件内置的 `DEFAULT_SERVERS` 合并,显示在插件市场中。
581
+
582
+ ### 插件配置说明
583
+
584
+ ```typescript
585
+ interface PluginInfo {
586
+ id: string // 插件唯一标识(最终会拼成 plugin-${id})
587
+ name: string // 插件名称
588
+ description: string // 插件描述
589
+ icon?: string // 插件图标 URL
590
+ url: string // MCP 服务器地址
591
+ type: 'sse' | 'StreamableHTTP' // 协议类型
592
+ enabled: boolean // 是否已启用
593
+ addState: 'idle' | 'adding' | 'added' | 'error' // 添加状态
594
+ tools: any[] // 工具列表
595
+ }
116
596
  ```
117
597
 
118
- ### 支持的提供商
598
+ ### 使用示例
119
599
 
120
- - OpenAI (`providerType: 'openai'`)
121
- - DeepSeek (`providerType: 'deepseek'`)
122
- - 其他ai-sdk兼容的提供商(通过自定义Provider函数)
600
+ ```vue
601
+ <template>
602
+ <TinyRemoter
603
+ v-model:show="show"
604
+ :sessionId="sessionId"
605
+ :customMarketMcpServers="customServers"
606
+ />
607
+ </template>
123
608
 
124
- ## 自定义 MCP 插件(customMcpServers)
609
+ <script setup>
610
+ import { ref } from 'vue'
611
+ import { TinyRemoter } from '@opentiny/next-remoter'
125
612
 
126
- `TinyRemoter` 暴露 `customMarketMcpServers` 属性,用于在插件市场中追加自定义 MCP 插件。传入的数组会和组件内置的 `DEFAULT_SERVERS` 合并,示例:
613
+ const show = ref(false)
614
+ const sessionId = ref('session-007')
127
615
 
128
- ```ts
129
- const customMarketMcpServers = [
616
+ const customServers = [
130
617
  {
131
618
  id: 'ppt-mcp',
132
- name: 'PPT文档MCP服务器',
133
- description: '可以创建、编辑、保存PPT文档',
134
- icon: 'https://agent.opentiny.design/public-assets/icons/icon-ppt.png',
135
- url: 'https://agent.opentiny.design/servers/ppt-mcp/sse',
619
+ name: 'PPT 文档服务',
620
+ description: '可以创建、编辑、保存 PPT 文档',
621
+ icon: 'https://example.com/icons/ppt.png',
622
+ url: 'https://your-server.com/servers/ppt-mcp/sse',
136
623
  type: 'sse',
137
624
  enabled: false,
138
625
  addState: 'idle',
139
626
  tools: []
627
+ },
628
+ {
629
+ id: 'excel-mcp',
630
+ name: 'Excel 数据处理',
631
+ description: '支持 Excel 文件读取、分析和生成',
632
+ icon: 'https://example.com/icons/excel.png',
633
+ url: 'https://your-server.com/servers/excel-mcp/sse',
634
+ type: 'sse',
635
+ enabled: false,
636
+ addState: 'idle',
637
+ tools: []
638
+ },
639
+ {
640
+ id: 'database-mcp',
641
+ name: '数据库查询',
642
+ description: '支持多种数据库的查询操作',
643
+ url: 'https://your-server.com/servers/db-mcp',
644
+ type: 'StreamableHTTP',
645
+ enabled: false,
646
+ addState: 'idle',
647
+ tools: []
140
648
  }
141
649
  ]
650
+ </script>
651
+ ```
652
+
653
+ ### 字段说明
654
+
655
+ - **id**:插件唯一标识,需保持全局唯一性
656
+ - **type**:需与后端 MCP 服务器协议类型保持一致(`sse` 或 `StreamableHTTP`)
657
+ - **enabled**:标记插件是否已启用,用于 UI 状态显示
658
+ - **addState**:插件添加状态,驱动 UI 按钮和进度显示
659
+ - `idle`:未添加
660
+ - `adding`:添加中
661
+ - `added`:已添加
662
+ - `error`:添加失败
663
+ - **tools**:MCP 服务提供的工具列表,连接成功后自动填充
664
+
665
+ ### 在浏览器扩展中使用
666
+
667
+ 在浏览器扩展场景中,可以通过配置文件自动聚合自定义插件:
668
+
669
+ ```typescript
670
+ // packages/next-wxt/entrypoints/sidepanel/useCustomMarketMcpServers.ts
671
+ import { customServers } from './meta'
672
+
673
+ const customMarketMcpServers = useCustomMarketMcpServers(customServers)
674
+ ```
675
+
676
+ ## 🛠️ 参与开发
677
+
678
+ ### 环境要求
679
+
680
+ - **Node.js** >= 18
681
+ - **pnpm** >= 8
682
+ - **Vue** 3.x
683
+
684
+ ### 开发流程
685
+
686
+ #### 1. 克隆项目
687
+
688
+ ```bash
689
+ git clone https://github.com/opentiny/next-sdk.git
690
+ cd next-sdk
691
+ ```
692
+
693
+ #### 2. 安装依赖
694
+
695
+ ```bash
696
+ pnpm install
142
697
  ```
143
698
 
144
- - `id`:需要保持唯一性,对应插件注册名称(会拼成 `plugin-${id}`)
145
- - `type`:与后端 MCP 服务器协议保持一致,如 `sse`、`StreamableHTTP`
146
- - `enabled/addState/tools`:驱动 TinyRemoter 市场 UI 的状态字段
699
+ #### 3. 开发模式
147
700
 
148
- 在浏览器扩展侧,可通过 `packages/next-wxt/entrypoints/sidepanel/useCustomMarketMcpServers.ts` 自动聚合 `meta.ts` 定义的 `customMarketMcpServers`,再传入 `TinyRemoter`。
701
+ ```bash
702
+ # 进入 next-remoter 包目录
703
+ cd packages/next-remoter
704
+
705
+ # 启动开发服务器
706
+ pnpm dev
707
+
708
+ # 浏览器访问 http://localhost:5173
709
+ ```
149
710
 
150
- ## 构建发包
711
+ #### 4. 构建项目
151
712
 
152
- ```shell
153
- pnpm i
713
+ ```bash
714
+ # 构建生产版本
715
+ pnpm build
716
+
717
+ # 构建运行时版本
718
+ pnpm build:runtime
719
+
720
+ # 构建站点版本
721
+ pnpm build:site
722
+ ```
723
+
724
+ ### 项目结构详解
725
+
726
+ ```
727
+ packages/next-remoter/
728
+ ├── src/
729
+ │ ├── components/ # 组件目录
730
+ │ │ ├── TinyRobotChat.vue # 核心对话组件(689 行)
731
+ │ │ └── QrCodeScan.vue # 二维码扫描组件
732
+ │ ├── composable/ # 组合式函数
733
+ │ │ └── usePlugin.ts # 插件管理逻辑(418 行)
734
+ │ ├── index.ts # 组件导出入口
735
+ │ └── App.vue # 独立部署页面
736
+ ├── vite.config.ts # Vite 配置
737
+ ├── vite.runtime.config.ts # 运行时构建配置
738
+ ├── vite.remoter.config.ts # Remoter 构建配置
739
+ ├── package.json # 包配置
740
+ └── README.md # 本文档
741
+ ```
742
+
743
+ ### 核心文件说明
744
+
745
+ #### TinyRobotChat.vue
746
+
747
+ - 核心对话组件实现
748
+ - 包含消息展示、输入处理、工具调用等核心功能
749
+ - 支持插槽自定义和主题配置
750
+
751
+ #### usePlugin.ts
752
+
753
+ - MCP 插件管理的核心逻辑
754
+ - 处理插件的添加、删除、启用/禁用
755
+ - 管理工具列表和调用
756
+
757
+ #### index.ts
758
+
759
+ - 导出 `TinyRemoter` 组件
760
+ - 作为 npm 包的入口文件
761
+
762
+ #### App.vue
763
+
764
+ - 可独立部署的扫码访问页面
765
+ - 用于移动端或独立部署场景
766
+
767
+ ### 构建脚本说明
768
+
769
+ ```json
770
+ {
771
+ "scripts": {
772
+ "dev": "vite", // 开发服务器
773
+ "build": "vite build", // 构建 npm 包
774
+ "build:runtime": "vite build --config vite.runtime.config.ts", // 构建运行时
775
+ "build:site": "vite build --config vite.remoter.config.ts", // 构建站点
776
+ "build:visualizer": "vite build --config vite.remoter.config.ts --mode visualizer", // 构建分析
777
+ "preview": "vite preview" // 预览构建结果
778
+ }
779
+ }
780
+ ```
781
+
782
+ ### 发布流程
783
+
784
+ ```bash
785
+ # 1. 更新版本号
786
+ # 编辑 packages/next-remoter/package.json 中的 version 字段
787
+
788
+ # 2. 安装依赖(如果还没安装)
789
+ pnpm install
790
+
791
+ # 3. 构建项目
154
792
  pnpm -F @opentiny/next-remoter build
793
+
794
+ # 4. 进入包目录
155
795
  cd packages/next-remoter
796
+
797
+ # 5. 发布到 npm(需要 npm 登录)
156
798
  npm publish
157
799
  ```
800
+
801
+ ### 调试技巧
802
+
803
+ #### 1. 使用开发工具
804
+
805
+ ```bash
806
+ # 启动开发服务器,支持热重载
807
+ pnpm dev
808
+ ```
809
+
810
+ #### 2. 查看构建产物
811
+
812
+ ```bash
813
+ # 使用 visualizer 模式查看打包分析
814
+ pnpm build:visualizer
815
+ ```
816
+
817
+ #### 3. 本地测试 npm 包
818
+
819
+ ```bash
820
+ # 在 next-remoter 目录下创建本地链接
821
+ pnpm link
822
+
823
+ # 在测试项目中使用
824
+ cd /path/to/test-project
825
+ pnpm link @opentiny/next-remoter
826
+ ```
827
+
828
+ ### 贡献指南
829
+
830
+ 我们欢迎所有形式的贡献!以下是参与贡献的方式:
831
+
832
+ #### 报告问题
833
+
834
+ - 在 [GitHub Issues](https://github.com/opentiny/next-sdk/issues) 提交 Bug 报告
835
+ - 提供详细的复现步骤和环境信息
836
+
837
+ #### 提交代码
838
+
839
+ 1. Fork 项目到你的 GitHub 账号
840
+ 2. 创建特性分支:`git checkout -b feature/amazing-feature`
841
+ 3. 提交改动:`git commit -m 'Add some amazing feature'`
842
+ 4. 推送到分支:`git push origin feature/amazing-feature`
843
+ 5. 提交 Pull Request
844
+
845
+ #### 代码规范
846
+
847
+ - 遵循 Vue3 组合式 API 最佳实践
848
+ - 使用 TypeScript 编写类型安全的代码
849
+ - 添加必要的注释和文档
850
+ - 确保代码通过 ESLint 检查
851
+
852
+ ## 📚 文档和资源
853
+
854
+ - [官方文档](https://docs.opentiny.design/next-sdk/guide/tiny-robot-remoter.html)
855
+ - [GitHub 仓库](https://github.com/opentiny/next-sdk)
856
+ - [问题反馈](https://github.com/opentiny/next-sdk/issues)
857
+ - [OpenTiny 官网](https://opentiny.design)
858
+
859
+ ## ❓ 常见问题
860
+
861
+ ### 1. 如何获取 sessionId?
862
+
863
+ ```typescript
864
+ import { WebMcpClient } from '@opentiny/next-sdk'
865
+
866
+ const client = new WebMcpClient({ name: 'my-client', version: '1.0.0' })
867
+ const { sessionId } = await client.connect({
868
+ url: 'https://your-mcp-server.com/mcp',
869
+ agent: true
870
+ })
871
+ ```
872
+
873
+ ### 2. 如何切换大语言模型?
874
+
875
+ 使用 `llmConfigs` 属性配置多个模型,通过 `v-model:selectedModelId` 切换:
876
+
877
+ ```vue
878
+ <TinyRemoter
879
+ v-model:selected-model-id="modelId"
880
+ :llmConfigs="models"
881
+ />
882
+ ```
883
+
884
+ ### 3. 如何自定义欢迎界面?
885
+
886
+ 使用 `#welcome` 插槽:
887
+
888
+ ```vue
889
+ <TinyRemoter>
890
+ <template #welcome>
891
+ <div>自定义内容</div>
892
+ </template>
893
+ </TinyRemoter>
894
+ ```
895
+
896
+ ### 4. 如何调用组件方法?
897
+
898
+ 通过 ref 引用组件实例:
899
+
900
+ ```vue
901
+ <script setup>
902
+ const robotRef = ref()
903
+ // 发送消息
904
+ robotRef.value?.sendMessage('你好')
905
+ // 取消请求
906
+ robotRef.value?.abortRequest()
907
+ </script>
908
+ ```
909
+
910
+ ## 📄 许可证
911
+
912
+ MIT
913
+
914
+ ## 🙏 致谢
915
+
916
+ 感谢所有为本项目做出贡献的开发者!
917
+
918
+ ---
919
+
920
+ 如有任何问题或建议,欢迎提交 [Issue](https://github.com/opentiny/next-sdk/issues) 或 [Pull Request](https://github.com/opentiny/next-sdk/pulls)。