@opentiny/next-sdk 0.3.1 → 0.3.3-alpha.1

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.
Files changed (54) hide show
  1. package/WebMcp.ts +6 -0
  2. package/WebMcpClient.ts +16 -21
  3. package/agent/type.ts +93 -69
  4. package/core.ts +1 -0
  5. package/dist/SimulatorMask-BHVXyogh-BFEGpD5S.js +1048 -0
  6. package/dist/SimulatorMask-BHVXyogh-CCYbrb84.js +801 -0
  7. package/dist/WebMcp.d.ts +3 -0
  8. package/dist/WebMcpClient.d.ts +7 -13
  9. package/dist/agent/type.d.ts +10 -0
  10. package/dist/core.d.ts +1 -0
  11. package/dist/core.js +16 -15
  12. package/dist/index.d.ts +3 -2
  13. package/dist/index.es.dev.js +8384 -2700
  14. package/dist/index.es.js +21608 -16893
  15. package/dist/index.js +4033 -1850
  16. package/dist/index.umd.dev.js +9739 -3110
  17. package/dist/index.umd.js +500 -66
  18. package/dist/initialize-builtin-WebMCP-HgObT902.js +6279 -0
  19. package/dist/mcpsdk@1.25.3.dev.js +253 -94
  20. package/dist/mcpsdk@1.25.3.es.dev.js +253 -94
  21. package/dist/mcpsdk@1.25.3.es.js +5689 -5531
  22. package/dist/mcpsdk@1.25.3.js +32 -27
  23. package/dist/page-tools/a11y-tree.d.ts +103 -0
  24. package/dist/page-tools/bridge.d.ts +0 -6
  25. package/dist/page-tools/initialize-builtin-WebMCP.d.ts +1 -0
  26. package/dist/page-tools/page-agent-tool.d.ts +9 -0
  27. package/dist/page-tools/page-state-cache.d.ts +36 -0
  28. package/dist/skills/index.d.ts +7 -12
  29. package/dist/webagent.dev.js +3772 -1919
  30. package/dist/webagent.es.dev.js +3528 -1571
  31. package/dist/webagent.es.js +16380 -14585
  32. package/dist/webagent.js +74 -66
  33. package/dist/webmcp-full.dev.js +7670 -747
  34. package/dist/webmcp-full.es.dev.js +6482 -608
  35. package/dist/webmcp-full.es.js +12180 -7618
  36. package/dist/webmcp-full.js +631 -29
  37. package/dist/webmcp.dev.js +8047 -53
  38. package/dist/webmcp.es.dev.js +6977 -31
  39. package/dist/webmcp.es.js +6187 -860
  40. package/dist/webmcp.js +602 -1
  41. package/index.ts +2 -15
  42. package/package.json +11 -4
  43. package/page-tools/a11y-tree.ts +532 -0
  44. package/page-tools/bridge.ts +48 -14
  45. package/page-tools/initialize-builtin-WebMCP.ts +20 -0
  46. package/page-tools/page-agent-prompt.md +123 -0
  47. package/page-tools/page-agent-tool.ts +223 -0
  48. package/page-tools/page-state-cache.ts +78 -0
  49. package/remoter/createRemoter.ts +2 -1
  50. package/remoter/svgs/logo.svg +45 -0
  51. package/skills/index.ts +50 -31
  52. package/utils/builtinProxy.ts +42 -20
  53. package/vite-env.d.ts +5 -0
  54. package/dist/AgentModelProvider-BnMj6YSC.js +0 -4182
package/skills/index.ts CHANGED
@@ -26,9 +26,15 @@ export interface SkillMeta {
26
26
  /**
27
27
  * 从主 SKILL.md 的 YAML front matter 中用正则提取 name、description
28
28
  */
29
- export function parseSkillFrontMatter(content: string): { name: string; description: string } | null {
29
+ export async function parseSkillFrontMatter(
30
+ content: string | (() => Promise<string>)
31
+ ): Promise<{ name: string; description: string } | null> {
32
+ if (typeof content !== 'string' && typeof content !== 'function') return null
33
+
30
34
  // 先提取 --- 之间的文本块
31
- const blockMatch = content.match(FRONT_MATTER_BLOCK_REG)
35
+ const realContent = typeof content === 'string' ? content : await content()
36
+ const blockMatch = realContent.match(FRONT_MATTER_BLOCK_REG)
37
+
32
38
  if (!blockMatch?.[1]) return null
33
39
  const block = blockMatch[1]
34
40
 
@@ -47,14 +53,17 @@ export function parseSkillFrontMatter(content: string): { name: string; descript
47
53
  * 以便 getSkillMdContent / getMainSkillPathByName 等能正确按 path 查找。
48
54
  * 兼容任意引入位置:./skills/xxx、../skills/xxx、src/skills/xxx 等,取最后一个 skills/ 后的部分并加上 ./
49
55
  */
50
- function normalizeSkillModuleKeys(modules: Record<string, string>): Record<string, string> {
56
+ async function normalizeSkillModuleKeys(
57
+ modules: Record<string, string | (() => Promise<string>)>
58
+ ): Promise<Record<string, string>> {
51
59
  const result: Record<string, string> = {}
52
60
  for (const [key, content] of Object.entries(modules)) {
53
61
  const normalizedKey = key.replace(/\\/g, '/')
54
62
  const skillsIndex = normalizedKey.lastIndexOf('skills/')
55
63
  const relativePath = skillsIndex >= 0 ? normalizedKey.slice(skillsIndex + 7) : normalizedKey
56
64
  const standardPath = relativePath.startsWith('./') ? relativePath : `./${relativePath}`
57
- result[standardPath] = content
65
+ const realContent = typeof content === 'string' ? content : await content()
66
+ result[standardPath] = realContent
58
67
  }
59
68
  return result
60
69
  }
@@ -63,8 +72,8 @@ function normalizeSkillModuleKeys(modules: Record<string, string>): Record<strin
63
72
  * 获取所有「主 SKILL.md」的路径(一级子目录下的 SKILL.md)
64
73
  * - 对传入的 modules 先做 normalize,兼容任意 import.meta.glob 写法
65
74
  */
66
- export function getMainSkillPaths(modules: Record<string, string>): string[] {
67
- const normalized = normalizeSkillModuleKeys(modules)
75
+ export async function getMainSkillPaths(modules: Record<string, string | (() => Promise<string>)>): Promise<string[]> {
76
+ const normalized = await normalizeSkillModuleKeys(modules)
68
77
  return Object.keys(normalized).filter((path) => MAIN_SKILL_PATH_REG.test(path))
69
78
  }
70
79
 
@@ -72,14 +81,16 @@ export function getMainSkillPaths(modules: Record<string, string>): string[] {
72
81
  * 获取所有技能的概况列表(name、description、path),用于 systemPrompt 或列表展示
73
82
  * - 内部统一对 modules 做 normalize,避免调用方关心路径细节
74
83
  */
75
- export function getSkillOverviews(modules: Record<string, string>): SkillMeta[] {
76
- const normalized = normalizeSkillModuleKeys(modules)
84
+ export async function getSkillOverviews(
85
+ modules: Record<string, string | (() => Promise<string>)>
86
+ ): Promise<SkillMeta[]> {
87
+ const normalized = await normalizeSkillModuleKeys(modules)
77
88
  const mainPaths = Object.keys(normalized).filter((path) => MAIN_SKILL_PATH_REG.test(path))
78
89
  const list: SkillMeta[] = []
79
90
  for (const path of mainPaths) {
80
91
  const content = normalized[path]
81
92
  if (!content) continue
82
- const parsed = parseSkillFrontMatter(content)
93
+ const parsed = await parseSkillFrontMatter(content)
83
94
  if (!parsed) continue
84
95
  list.push({
85
96
  name: parsed.name,
@@ -100,21 +111,15 @@ export function formatSkillsForSystemPrompt(skills: SkillMeta[]): string {
100
111
  return `## 可用技能\n\n${lines.join('\n')}\n\n当需要用到某技能时,请使用 get_skill_content 工具获取该技能的完整文档内容。`
101
112
  }
102
113
 
103
- /**
104
- * 获取所有已加载的技能文件路径(含主 SKILL.md 与 reference 下的 .md/.json/.xml 等)
105
- * - 对 modules 做 normalize 后再返回 key 列表
106
- */
107
- export function getSkillMdPaths(modules: Record<string, string>): string[] {
108
- const normalized = normalizeSkillModuleKeys(modules)
109
- return Object.keys(normalized)
110
- }
111
-
112
114
  /**
113
115
  * 根据相对路径获取某个技能文档的原始内容(支持 .md、.json、.xml 等文本格式)
114
116
  * - 自动对 modules 做 normalize,再按 path 查找
115
117
  */
116
- export function getSkillMdContent(modules: Record<string, string>, path: string): string | undefined {
117
- const normalized = normalizeSkillModuleKeys(modules)
118
+ export async function getSkillMdContent(
119
+ modules: Record<string, string | (() => Promise<string>)>,
120
+ path: string
121
+ ): Promise<string | undefined> {
122
+ const normalized = await normalizeSkillModuleKeys(modules)
118
123
 
119
124
  // 1. 尝试原有的严格匹配
120
125
  const exactMatch = normalized[path]
@@ -133,9 +138,12 @@ export function getSkillMdContent(modules: Record<string, string>, path: string)
133
138
  * 支持匹配目录名(如 ecommerce)或 SKILL.md 内 frontmatter 定义的 name
134
139
  * - 依赖 getMainSkillPaths,内部已做 normalize
135
140
  */
136
- export function getMainSkillPathByName(modules: Record<string, string>, name: string): string | undefined {
137
- const normalizedModules = normalizeSkillModuleKeys(modules)
138
- const paths = getMainSkillPaths(normalizedModules)
141
+ export async function getMainSkillPathByName(
142
+ modules: Record<string, string | (() => Promise<string>)>,
143
+ name: string
144
+ ): Promise<string | undefined> {
145
+ const normalizedModules = await normalizeSkillModuleKeys(modules)
146
+ const paths = await getMainSkillPaths(normalizedModules)
139
147
 
140
148
  // 1. 先尝试按目录名精确匹配 (兼容老逻辑)
141
149
  const dirMatch = paths.find((p) => p.startsWith(`./${name}/SKILL.md`))
@@ -145,7 +153,7 @@ export function getMainSkillPathByName(modules: Record<string, string>, name: st
145
153
  for (const p of paths) {
146
154
  const content = normalizedModules[p]
147
155
  if (content) {
148
- const parsed = parseSkillFrontMatter(content)
156
+ const parsed = await parseSkillFrontMatter(content)
149
157
  if (parsed && parsed.name === name) {
150
158
  return p
151
159
  }
@@ -186,15 +194,26 @@ const SKILL_INPUT_SCHEMA = z.object({
186
194
  * - get_skill_content: 按技能名或路径获取完整文档内容,便于大模型自动识别并加载技能
187
195
  * remoter 可将返回的 tools 合并进 extraTools 注入 agent
188
196
  */
189
- export function createSkillTools(modules: Record<string, string>): SkillToolsSet {
190
- const normalizedModules = normalizeSkillModuleKeys(modules)
197
+ export function createSkillTools(modules: Record<string, string | (() => Promise<string>)>): SkillToolsSet {
198
+ let isNormalizeSkillModuleKeys = false
199
+ let normalizeSkillModuleKeysResult: Record<string, string>
191
200
 
192
201
  // @ts-ignore ai package 的 tool() 函数类型推断存在"类型实例化过深"的已知限制,无法正确推断包含复杂 Zod 链的 schema
193
202
  const getSkillContent = tool({
194
203
  description:
195
204
  '根据技能名称或文档路径获取该技能的完整文档内容。如果你想根据相对路径查阅文件,请务必同时提供你当前所在的文件路径 currentPath。',
196
205
  inputSchema: SKILL_INPUT_SCHEMA,
197
- execute: (args: { skillName?: string; path?: string; currentPath?: string }): Record<string, unknown> => {
206
+ execute: async (args: {
207
+ skillName?: string
208
+ path?: string
209
+ currentPath?: string
210
+ }): Promise<Record<string, unknown>> => {
211
+ if (!isNormalizeSkillModuleKeys) {
212
+ normalizeSkillModuleKeysResult = await normalizeSkillModuleKeys(modules)
213
+ isNormalizeSkillModuleKeys = true
214
+ }
215
+
216
+ const normalizedModules = normalizeSkillModuleKeysResult
198
217
  const { skillName, path: pathArg, currentPath: currentPathArg } = args
199
218
  let content: string | undefined
200
219
  let resolvedPath = ''
@@ -215,7 +234,7 @@ export function createSkillTools(modules: Record<string, string>): SkillToolsSet
215
234
  const dummyBase = `http://localhost/${basePathContext}/`
216
235
  const url = new URL(pathArg, dummyBase)
217
236
  resolvedPath = '.' + url.pathname
218
- content = getSkillMdContent(normalizedModules, resolvedPath)
237
+ content = await getSkillMdContent(normalizedModules, resolvedPath)
219
238
 
220
239
  // 尝试 2:如果大模型忘了传正确的 currentPath,或者是强行传错,做个智能根目录回退
221
240
  if (content === undefined && (pathArg.startsWith('./') || pathArg.startsWith('../')) && currentPathArg) {
@@ -225,7 +244,7 @@ export function createSkillTools(modules: Record<string, string>): SkillToolsSet
225
244
  const fallbackDummyBase = `http://localhost/${skillRoot}/`
226
245
  const fallbackUrl = new URL(pathArg, fallbackDummyBase)
227
246
  const fallbackPath = '.' + fallbackUrl.pathname
228
- content = getSkillMdContent(normalizedModules, fallbackPath)
247
+ content = await getSkillMdContent(normalizedModules, fallbackPath)
229
248
  if (content) {
230
249
  resolvedPath = fallbackPath
231
250
  }
@@ -241,10 +260,10 @@ export function createSkillTools(modules: Record<string, string>): SkillToolsSet
241
260
  }
242
261
  }
243
262
  } else if (skillName) {
244
- const mainPath = getMainSkillPathByName(normalizedModules, skillName)
263
+ const mainPath = await getMainSkillPathByName(normalizedModules, skillName)
245
264
  if (mainPath) {
246
265
  resolvedPath = mainPath
247
- content = getSkillMdContent(normalizedModules, mainPath)
266
+ content = await getSkillMdContent(normalizedModules, mainPath)
248
267
  }
249
268
  }
250
269
 
@@ -5,29 +5,37 @@ import type { Transport } from '@modelcontextprotocol/sdk/shared/transport.js'
5
5
  * 将发向 remote 的 MCP 请求拦截,代理给所在浏览器的 navigator.modelContextTesting 上执行。
6
6
  */
7
7
  export const setupBuiltinProxy = (transport: Transport) => {
8
+ const getNativeCtx = () => {
9
+ if (typeof navigator === 'undefined') return null
10
+ const nav = navigator as any
11
+ return nav.modelContextTesting || null
12
+ }
13
+
8
14
  transport.onmessage = async (message: any) => {
9
15
  if (!message || typeof message !== 'object') return
10
16
  const id = message.id
17
+ const method = message.method
18
+
11
19
  try {
12
- if (message.method === 'initialize') {
20
+ if (method === 'initialize') {
13
21
  await transport.send({
14
22
  jsonrpc: '2.0',
15
23
  id,
16
24
  result: {
17
25
  protocolVersion: '2024-11-05',
18
- capabilities: { tools: {} },
26
+ capabilities: {
27
+ tools: { listChanged: true },
28
+ logging: {}
29
+ },
19
30
  serverInfo: { name: 'browser-builtin-webmcp-proxy', version: '1.0.0' }
20
31
  }
21
32
  })
22
- } else if (message.method === 'notifications/initialized') {
33
+ } else if (method === 'notifications/initialized') {
23
34
  // Ignore
24
- } else if (message.method === 'ping' || message.method === 'custom_ping') {
35
+ } else if (method === 'ping' || method === 'custom_ping') {
25
36
  await transport.send({ jsonrpc: '2.0', id, result: {} })
26
- } else if (message.method === 'tools/list') {
27
- const nativeCtx =
28
- typeof navigator !== 'undefined'
29
- ? (navigator as any).modelContextTesting || (navigator as any).modelContext
30
- : null
37
+ } else if (method === 'tools/list') {
38
+ const nativeCtx = getNativeCtx()
31
39
  if (nativeCtx && nativeCtx.listTools) {
32
40
  const rawTools = await nativeCtx.listTools()
33
41
  const tools = rawTools.map((t: any) => {
@@ -55,35 +63,49 @@ export const setupBuiltinProxy = (transport: Transport) => {
55
63
  } else {
56
64
  await transport.send({ jsonrpc: '2.0', id, result: { tools: [] } })
57
65
  }
58
- } else if (message.method === 'tools/call') {
59
- const nativeCtx = typeof navigator !== 'undefined' ? navigator.modelContextTesting || navigator.modelContext : null
66
+ } else if (method === 'tools/call') {
67
+ const nativeCtx = getNativeCtx()
60
68
  if (nativeCtx && nativeCtx.executeTool) {
69
+ if (!message.params || typeof message.params !== 'object' || !message.params.name) {
70
+ const error: any = new Error('Invalid params: "name" is required and params must be an object')
71
+ error.code = -32602 // Invalid params
72
+ throw error
73
+ }
61
74
  const { name, arguments: args } = message.params
62
75
  const result = await nativeCtx.executeTool(name, JSON.stringify(args || {}))
63
- // 如果结果已经是 MCP 格式 ({ content: [...] }),直接转发;否则包装为 text
64
76
  const finalResult =
65
77
  result && typeof result === 'object' && 'content' in result
66
78
  ? result
67
79
  : { content: [{ type: 'text', text: typeof result === 'string' ? result : JSON.stringify(result) }] }
68
80
  await transport.send({ jsonrpc: '2.0', id, result: finalResult })
69
81
  } else {
70
- await transport.send({
71
- jsonrpc: '2.0',
72
- id,
73
- error: { code: -32601, message: 'Browser built-in WebMCP not available' }
74
- })
82
+ const error: any = new Error('executeTool not implemented in Browser built-in WebMCP')
83
+ error.code = -32601 // Method not found
84
+ throw error
75
85
  }
86
+ } else if (method === 'logging/setLevel') {
87
+ // 浏览器内置 WebMCP 不是标准 MCP Server,不支持日志级别设置。
88
+ // 直接 mock 返回成功,避免严格的 MCP 客户端因未实现该方法而报错。
89
+ await transport.send({ jsonrpc: '2.0', id, result: {} })
90
+ } else if (method === 'prompts/list') {
91
+ await transport.send({ jsonrpc: '2.0', id, result: { prompts: [] } })
92
+ } else if (method === 'resources/list') {
93
+ await transport.send({ jsonrpc: '2.0', id, result: { resources: [] } })
76
94
  } else if (id !== undefined) {
77
- // 符合协议:对于未处理且带有 id 的请求,返回 Method not found
95
+ // 对于其他不支持但带 id 的请求,返回规范要求的 -32601 Method not found
78
96
  await transport.send({
79
97
  jsonrpc: '2.0',
80
98
  id,
81
- error: { code: -32601, message: `Method not found: ${message.method}` }
99
+ error: { code: -32601, message: `Method not found: ${method}` }
82
100
  })
83
101
  }
84
102
  } catch (err: any) {
85
103
  if (id !== undefined) {
86
- await transport.send({ jsonrpc: '2.0', id, error: { code: -32000, message: err.message || String(err) } })
104
+ await transport.send({
105
+ jsonrpc: '2.0',
106
+ id,
107
+ error: { code: err.code || -32000, message: err.message || String(err) }
108
+ })
87
109
  }
88
110
  }
89
111
  }
package/vite-env.d.ts CHANGED
@@ -2,4 +2,9 @@
2
2
  declare module '*.svg?url' {
3
3
  const src: string;
4
4
  export default src;
5
+ }
6
+
7
+ declare module '*.md?raw' {
8
+ const content: string;
9
+ export default content;
5
10
  }