@opentiny/next-sdk 0.2.10 → 0.3.0-alpha.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/README.md CHANGED
@@ -11,6 +11,9 @@
11
11
  <a href="#️-参与开发">🛠️ 参与开发</a>
12
12
  </p>
13
13
 
14
+ > [!IMPORTANT]
15
+ > **WebMCP 兼容性说明**:OpenTiny WebMCP 方案**全面兼容 Google Chrome 浏览器由 Google 官方定义的内置 WebMCP 协议**。这意味着您通过此 SDK 开发的 WebSkills 可以无缝接入浏览器原生的 AI 能力。
16
+
14
17
  ---
15
18
 
16
19
  **OpenTiny NEXT-SDKs** 是一套前端智能应用开发工具包,旨在简化 WebAgent 的集成与使用,支持多种编程语言和前端框架,帮助开发者快速实现智能化功能。
@@ -31,7 +34,7 @@
31
34
  ## ✨ 主要特性
32
35
 
33
36
  - 🎯 **简化集成**:提供简洁的 API 封装,简化与 WebAgent 服务的连接、认证等逻辑
34
- - 🔌 **MCP 协议**:完整实现 Model Context Protocol(MCP)的 Web 版本,支持浏览器端工具调用
37
+ - 🔌 **MCP 协议**:完整实现 Model Context Protocol(MCP)的 Web 版本,**全面兼容 Google Chrome 内置的 WebMCP 协议**,支持浏览器端工具调用
35
38
  - 🤖 **AI 对话组件**:提供开箱即用的 AI 对话框组件(`@opentiny/next-remoter`)
36
39
  - 🔄 **适配器层**:可将任意前端 AI 对话框组件快速接入 WebAgent 服务
37
40
  - 🌐 **多模态支持**:支持文字、语音等多模态输入,抹平不同 LLM 之间的差异
@@ -88,21 +91,27 @@ const server = new WebMcpServer({
88
91
  })
89
92
 
90
93
  // 注册工具
91
- server.registerTool('demo-tool', {
92
- title: '演示工具',
93
- description: '这是一个演示工具',
94
- inputSchema: {
95
- foo: z.string().describe('输入参数')
94
+ server.registerTool(
95
+ 'demo-tool',
96
+ {
97
+ title: '演示工具',
98
+ description: '这是一个演示工具',
99
+ inputSchema: {
100
+ foo: z.string().describe('输入参数')
101
+ }
96
102
  },
97
- }, async (params) => {
98
- console.log('接收到参数:', params)
99
- return {
100
- content: [{
101
- type: 'text',
102
- text: `已处理: ${params.foo}`
103
- }]
103
+ async (params) => {
104
+ console.log('接收到参数:', params)
105
+ return {
106
+ content: [
107
+ {
108
+ type: 'text',
109
+ text: `已处理: ${params.foo}`
110
+ }
111
+ ]
112
+ }
104
113
  }
105
- })
114
+ )
106
115
 
107
116
  // 连接 Server Transport
108
117
  await server.connect(serverTransport)
@@ -157,10 +166,7 @@ const sessionId = 'your-session-id'
157
166
  </script>
158
167
 
159
168
  <template>
160
- <tiny-remoter
161
- :session-id="sessionId"
162
- title="我的智能助手"
163
- />
169
+ <tiny-remoter :session-id="sessionId" title="我的智能助手" />
164
170
  </template>
165
171
  ```
166
172
 
@@ -185,33 +191,33 @@ const sessionId = 'your-session-id'
185
191
  </head>
186
192
  <body>
187
193
  <script>
188
- (async () => {
189
- const { WebMcpServer, createMessageChannelPairTransport, z, WebMcpClient } = WebMCP;
190
- const [serverTransport, clientTransport] = createMessageChannelPairTransport();
194
+ ;(async () => {
195
+ const { WebMcpServer, createMessageChannelPairTransport, z, WebMcpClient } = WebMCP
196
+ const [serverTransport, clientTransport] = createMessageChannelPairTransport()
191
197
 
192
- const client = new WebMcpClient();
193
- await client.connect(clientTransport);
198
+ const client = new WebMcpClient()
199
+ await client.connect(clientTransport)
194
200
  const { sessionId } = await client.connect({
195
201
  agent: true,
196
- url: "https://agent.opentiny.design/api/v1/webmcp-trial/mcp",
197
- });
202
+ url: 'https://agent.opentiny.design/api/v1/webmcp-trial/mcp'
203
+ })
198
204
 
199
- const server = new WebMcpServer();
205
+ const server = new WebMcpServer()
200
206
  server.registerTool(
201
- "demo-tool",
207
+ 'demo-tool',
202
208
  {
203
- title: "演示工具",
204
- description: "一个简单工具",
205
- inputSchema: { foo: z.string() },
209
+ title: '演示工具',
210
+ description: '一个简单工具',
211
+ inputSchema: { foo: z.string() }
206
212
  },
207
213
  async (params) => {
208
- console.log("params:", params);
209
- return { content: [{ type: "text", text: `收到: ${params.foo}` }] };
214
+ console.log('params:', params)
215
+ return { content: [{ type: 'text', text: `收到: ${params.foo}` }] }
210
216
  }
211
- );
217
+ )
212
218
 
213
- await server.connect(serverTransport);
214
- })();
219
+ await server.connect(serverTransport)
220
+ })()
215
221
  </script>
216
222
  </body>
217
223
  </html>
@@ -265,14 +271,20 @@ const server = new WebMcpServer({
265
271
  })
266
272
 
267
273
  // 注册工具
268
- server.registerTool('tool-name', {
269
- title: '工具标题',
270
- description: '工具描述',
271
- inputSchema: { /* Zod schema */ }
272
- }, async (params) => {
273
- // 工具处理逻辑
274
- return { content: [{ type: 'text', text: '结果' }] }
275
- })
274
+ server.registerTool(
275
+ 'tool-name',
276
+ {
277
+ title: '工具标题',
278
+ description: '工具描述',
279
+ inputSchema: {
280
+ /* Zod schema */
281
+ }
282
+ },
283
+ async (params) => {
284
+ // 工具处理逻辑
285
+ return { content: [{ type: 'text', text: '结果' }] }
286
+ }
287
+ )
276
288
  ```
277
289
 
278
290
  ### WebMcpClient
@@ -590,17 +602,21 @@ const { sessionId } = await client.connect({
590
602
  使用 `server.registerTool()` 注册自定义工具:
591
603
 
592
604
  ```typescript
593
- server.registerTool('my-tool', {
594
- title: '我的工具',
595
- description: '工具描述',
596
- inputSchema: {
597
- param1: z.string(),
598
- param2: z.number()
605
+ server.registerTool(
606
+ 'my-tool',
607
+ {
608
+ title: '我的工具',
609
+ description: '工具描述',
610
+ inputSchema: {
611
+ param1: z.string(),
612
+ param2: z.number()
613
+ }
614
+ },
615
+ async (params) => {
616
+ // 实现工具逻辑
617
+ return { content: [{ type: 'text', text: '执行结果' }] }
599
618
  }
600
- }, async (params) => {
601
- // 实现工具逻辑
602
- return { content: [{ type: 'text', text: '执行结果' }] }
603
- })
619
+ )
604
620
  ```
605
621
 
606
622
  ### 3. 支持哪些大语言模型?
@@ -222,12 +222,18 @@ export class AgentModelProvider {
222
222
  )
223
223
  }
224
224
 
225
- /** 创建所有的 mcpClients,并更新它们的tools */
226
- async initClientsAndTools() {
227
- await this._createMpcClients()
228
- await this._createMpcTools()
229
- this.onUpdatedTools?.()
230
- }
225
+ /** 创建所有的 mcpClients,并更新它们的tools */
226
+ async initClientsAndTools() {
227
+ await this._createMpcClients()
228
+ await this._createMpcTools()
229
+ this.onUpdatedTools?.()
230
+ }
231
+
232
+ /** 仅刷新已连接 clients 的 tools(不重建 client,适合工具目录动态变化场景) */
233
+ async refreshTools() {
234
+ await this._createMpcTools()
235
+ this.onUpdatedTools?.()
236
+ }
231
237
 
232
238
  /** 全量更新所有的 mcpServers */
233
239
  async updateMcpServers(mcpServers?: Record<string, McpServerConfig>) {
@@ -820,22 +826,61 @@ export class AgentModelProvider {
820
826
  }
821
827
 
822
828
  // 否则使用原有的 function calling 模式
823
- if (!this.llm) {
824
- throw new Error('LLM is not initialized')
825
- }
826
-
827
- await this.initClientsAndTools()
828
-
829
- const allTools = this._tempMergeTools(options.tools, false)
830
-
831
- const chatOptions = {
832
- // @ts-ignore ProviderV2 是所有llm的父类, 在每一个具体的llm 类都有一个选择model的函数用法
833
- model: this.llm(model),
834
- stopWhen: stepCountIs(maxSteps),
835
- ...options,
836
- tools: allTools,
837
- activeTools: this._getActiveToolNames(allTools),
838
- }
829
+ if (!this.llm) {
830
+ throw new Error('LLM is not initialized')
831
+ }
832
+
833
+ await this.initClientsAndTools()
834
+
835
+ const extraTools = (options.tools || {}) as ToolSet
836
+ const allTools = this._tempMergeTools(extraTools, false)
837
+
838
+ // tools 保持“同一个对象引用”并在每个 step 前同步:
839
+ // - prepareStep 只能控制 activeTools,不能直接返回 tools
840
+ // - 因此需要在进入每个 step 前,把最新 mcpTools 合并回当前 tools 对象
841
+ // - 这样 activeTools 新增的工具名在当步就能命中真实工具定义
842
+ const syncToolsForStep = () => {
843
+ const latestTools = this._tempMergeTools(extraTools, false)
844
+
845
+ // upsert
846
+ Object.entries(latestTools).forEach(([name, tool]) => {
847
+ ;(allTools as any)[name] = tool
848
+ })
849
+
850
+ // remove stale
851
+ Object.keys(allTools).forEach((name) => {
852
+ if (!(name in latestTools)) {
853
+ delete (allTools as any)[name]
854
+ }
855
+ })
856
+ }
857
+
858
+ const userPrepareStep = (options as any).prepareStep
859
+ const wrappedPrepareStep = async (stepOptions: any) => {
860
+ syncToolsForStep()
861
+
862
+ const latestActiveTools = this._getActiveToolNames(allTools)
863
+ const userStepPatch = typeof userPrepareStep === 'function' ? await userPrepareStep(stepOptions) : undefined
864
+ const safeUserStepPatch =
865
+ userStepPatch && typeof userStepPatch === 'object' ? userStepPatch : ({} as Record<string, any>)
866
+
867
+ return {
868
+ ...safeUserStepPatch,
869
+ activeTools: Array.isArray(safeUserStepPatch.activeTools)
870
+ ? safeUserStepPatch.activeTools.filter((name: string) => latestActiveTools.includes(name))
871
+ : latestActiveTools
872
+ }
873
+ }
874
+
875
+ const chatOptions = {
876
+ // @ts-ignore ProviderV2 是所有llm的父类, 在每一个具体的llm 类都有一个选择model的函数用法
877
+ model: this.llm(model),
878
+ stopWhen: stepCountIs(maxSteps),
879
+ ...options,
880
+ tools: allTools,
881
+ prepareStep: wrappedPrepareStep,
882
+ activeTools: this._getActiveToolNames(allTools)
883
+ }
839
884
 
840
885
  // 保存最后一条 user 消息,用于后续缓存
841
886
  let lastUserMessage: any = null
package/core.ts ADDED
@@ -0,0 +1,28 @@
1
+ // 核心导出,仅包含无 DOM 依赖(Node.js / Service Worker 安全)的内容
2
+ import Ajv from 'ajv'
3
+ export { Ajv }
4
+ export { z } from 'zod'
5
+ export { AuthClientProvider } from '@opentiny/next'
6
+ export { ResourceTemplate } from '@modelcontextprotocol/sdk/server/mcp.js'
7
+ export { UriTemplate } from '@modelcontextprotocol/sdk/shared/uriTemplate.js'
8
+ export { completable } from '@modelcontextprotocol/sdk/server/completable.js'
9
+ export { getDisplayName } from '@modelcontextprotocol/sdk/shared/metadataUtils.js'
10
+ export { InMemoryTransport } from '@modelcontextprotocol/sdk/inMemory.js'
11
+ export type * from 'zod'
12
+ export type * from '@opentiny/next'
13
+ export type * from '@modelcontextprotocol/sdk/types.js'
14
+ export type * from '@modelcontextprotocol/sdk/shared/protocol.js'
15
+ export type * from '@modelcontextprotocol/sdk/shared/transport.js'
16
+ export type * from '@modelcontextprotocol/sdk/client/sse.js'
17
+ export type * from '@modelcontextprotocol/sdk/client/streamableHttp.js'
18
+ export type * from '@modelcontextprotocol/sdk/server/mcp.js'
19
+
20
+ export * from './WebMcpServer'
21
+ export * from './WebMcpClient'
22
+
23
+ // 浏览器扩展自定义传输层(不包含对 window 依赖的模块)
24
+ export * from './transport/ExtensionClientTransport'
25
+
26
+ export { AgentModelProvider } from './agent/AgentModelProvider'
27
+ export { getAISDKTools } from './agent/utils/getAISDKTools'
28
+ export type * from './agent/type'