@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.
- package/WebMcp.ts +6 -0
- package/WebMcpClient.ts +16 -21
- package/agent/type.ts +93 -69
- package/core.ts +1 -0
- package/dist/SimulatorMask-BHVXyogh-BFEGpD5S.js +1048 -0
- package/dist/SimulatorMask-BHVXyogh-CCYbrb84.js +801 -0
- package/dist/WebMcp.d.ts +3 -0
- package/dist/WebMcpClient.d.ts +7 -13
- package/dist/agent/type.d.ts +10 -0
- package/dist/core.d.ts +1 -0
- package/dist/core.js +16 -15
- package/dist/index.d.ts +3 -2
- package/dist/index.es.dev.js +8384 -2700
- package/dist/index.es.js +21608 -16893
- package/dist/index.js +4033 -1850
- package/dist/index.umd.dev.js +9739 -3110
- package/dist/index.umd.js +500 -66
- package/dist/initialize-builtin-WebMCP-HgObT902.js +6279 -0
- package/dist/mcpsdk@1.25.3.dev.js +253 -94
- package/dist/mcpsdk@1.25.3.es.dev.js +253 -94
- package/dist/mcpsdk@1.25.3.es.js +5689 -5531
- package/dist/mcpsdk@1.25.3.js +32 -27
- package/dist/page-tools/a11y-tree.d.ts +103 -0
- package/dist/page-tools/bridge.d.ts +0 -6
- package/dist/page-tools/initialize-builtin-WebMCP.d.ts +1 -0
- package/dist/page-tools/page-agent-tool.d.ts +9 -0
- package/dist/page-tools/page-state-cache.d.ts +36 -0
- package/dist/skills/index.d.ts +7 -12
- package/dist/webagent.dev.js +3772 -1919
- package/dist/webagent.es.dev.js +3528 -1571
- package/dist/webagent.es.js +16380 -14585
- package/dist/webagent.js +74 -66
- package/dist/webmcp-full.dev.js +7670 -747
- package/dist/webmcp-full.es.dev.js +6482 -608
- package/dist/webmcp-full.es.js +12180 -7618
- package/dist/webmcp-full.js +631 -29
- package/dist/webmcp.dev.js +8047 -53
- package/dist/webmcp.es.dev.js +6977 -31
- package/dist/webmcp.es.js +6187 -860
- package/dist/webmcp.js +602 -1
- package/index.ts +2 -15
- package/package.json +11 -4
- package/page-tools/a11y-tree.ts +532 -0
- package/page-tools/bridge.ts +48 -14
- package/page-tools/initialize-builtin-WebMCP.ts +20 -0
- package/page-tools/page-agent-prompt.md +123 -0
- package/page-tools/page-agent-tool.ts +223 -0
- package/page-tools/page-state-cache.ts +78 -0
- package/remoter/createRemoter.ts +2 -1
- package/remoter/svgs/logo.svg +45 -0
- package/skills/index.ts +50 -31
- package/utils/builtinProxy.ts +42 -20
- package/vite-env.d.ts +5 -0
- 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(
|
|
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
|
|
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(
|
|
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
|
-
|
|
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(
|
|
76
|
-
|
|
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(
|
|
117
|
-
|
|
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(
|
|
137
|
-
|
|
138
|
-
|
|
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
|
-
|
|
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: {
|
|
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
|
|
package/utils/builtinProxy.ts
CHANGED
|
@@ -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 (
|
|
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: {
|
|
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 (
|
|
33
|
+
} else if (method === 'notifications/initialized') {
|
|
23
34
|
// Ignore
|
|
24
|
-
} else if (
|
|
35
|
+
} else if (method === 'ping' || method === 'custom_ping') {
|
|
25
36
|
await transport.send({ jsonrpc: '2.0', id, result: {} })
|
|
26
|
-
} else if (
|
|
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 (
|
|
59
|
-
const nativeCtx =
|
|
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
|
-
|
|
71
|
-
|
|
72
|
-
|
|
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
|
-
//
|
|
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: ${
|
|
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({
|
|
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
|
}
|