Skip to content

Commit 5f431a7

Browse files
authored
feat: add next-sdk web skills (#195)
* feat: add next-sdk skills * feat: add next-sdk web skills
1 parent f70e1c1 commit 5f431a7

18 files changed

Lines changed: 1408 additions & 999 deletions

File tree

pnpm-lock.yaml

Lines changed: 861 additions & 743 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

template/tinyvue/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,8 @@
2929
"@babel/core": "^7.25.2",
3030
"@gaonengwww/mock-server": "^1.0.5",
3131
"@opentiny/icons": "^0.1.3",
32-
"@opentiny/next-remoter": "^0.0.10",
33-
"@opentiny/next-sdk": "^0.1.15",
32+
"@opentiny/next-remoter": "0.2.7",
33+
"@opentiny/next-sdk": "0.2.7",
3434
"@opentiny/vue": "^3.28.0",
3535
"@opentiny/vue-huicharts": "~3.28.0",
3636
"@opentiny/vue-icon": "~3.28.0",

template/tinyvue/src/App.vue

Lines changed: 15 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
<script lang="ts" setup>
22
import { TinyRemoter } from '@opentiny/next-remoter'
3-
import { createMessageChannelPairTransport, WebMcpClient, WebMcpServer, z } from '@opentiny/next-sdk'
43
import { TinyConfigProvider } from '@opentiny/vue'
54
import TinyThemeTool from '@opentiny/vue-theme/theme-tool'
6-
import { onMounted, provide, ref } from 'vue'
7-
import { useRoute, useRouter } from 'vue-router'
5+
import { onMounted } from 'vue'
86
import GlobalSetting from '@/components/global-setting/index.vue'
97
import { useTheme } from './hooks/useTheme'
8+
import { clientTransport, createMcpServer } from './mcp-servers'
9+
import { skills } from './skills'
1010
import '@opentiny/next-remoter/dist/style.css'
1111
1212
const theme = new TinyThemeTool()
@@ -25,51 +25,18 @@ const design = {
2525
},
2626
}
2727
28-
const sessionId = ref('')
29-
const [serverTransport, clientTransport] = createMessageChannelPairTransport()
30-
provide('serverTransport', serverTransport)
31-
32-
const AGENT_URL = 'https://agent.opentiny.design/api/v1/webmcp-trial/'
28+
// 将本地 MCP Server 注册到 TinyRemoter
29+
// key 为服务器名称(自定义),type: 'local' 表示浏览器本地运行
30+
const mcpServers = {
31+
'my-mcp-server': {
32+
type: 'local',
33+
transport: clientTransport,
34+
},
35+
}
3336
37+
// 启动 MCP Server(注册工具 + 建立通信通道)
3438
onMounted(async () => {
35-
const server = new WebMcpServer()
36-
const $router = useRouter()
37-
const $route = useRoute()
38-
39-
// TODO: 参数需要优化,用户不会知道具体的路由路径,用户的语言可能是:帮我打开菜单管理页面,这时应该根据名称获取路由路径,再做路由跳转
40-
// 进一步优化:用户可能在任意页面直接提需求:帮我创建 xx 菜单,这时 AI 应该先跳转菜单管理页面,然后调佣创建菜单的工具
41-
server.registerTool(
42-
'switch-router',
43-
{
44-
title: '切换路由',
45-
description: '切换路由',
46-
inputSchema: {
47-
routerPath: z.string().describe('路由路径'),
48-
},
49-
},
50-
async ({ routerPath }) => {
51-
if ($route.path === routerPath) {
52-
return { content: [{ type: 'text', text: routerPath }] }
53-
}
54-
55-
$router.push(import.meta.env.VITE_CONTEXT + routerPath)
56-
return { content: [{ type: 'text', text: routerPath }] }
57-
},
58-
)
59-
await server.connect(serverTransport)
60-
61-
// 创建 WebMcpClient ,并与 WebAgent 连接
62-
const client = new WebMcpClient()
63-
await client.connect(clientTransport)
64-
const { sessionId: sessionID } = await client.connect({
65-
agent: true,
66-
67-
// sessionId 为可选参数。若传入该参数,系统将使用指定值作为会话标识;若未传入,WebAgent 服务将自动生成一个随机的字符串作为 sessionId。为便于通过 MCP Inspector 工具进行调试,此处采用了固定的 sessionId。用户亦可通过浏览器原生提供的 crypto.randomUUID() 方法生成随机字符串作为会话标识。
68-
sessionId: 'd299a869-c674-4125-a84b-bb4e24079b99',
69-
70-
url: `${AGENT_URL}mcp`,
71-
})
72-
sessionId.value = sessionID
39+
await createMcpServer()
7340
})
7441
</script>
7542

@@ -82,22 +49,8 @@ onMounted(async () => {
8249
<GlobalSetting />
8350
</div>
8451
<TinyRemoter
85-
:agent-root="AGENT_URL"
86-
:session-id="sessionId"
87-
:menu-items="[
88-
{
89-
action: 'qr-code',
90-
show: false,
91-
},
92-
{
93-
action: 'remote-control',
94-
show: false,
95-
},
96-
{
97-
action: 'remote-url',
98-
show: false,
99-
},
100-
]"
52+
:skills="skills"
53+
:mcp-servers="mcpServers"
10154
/>
10255
</template>
10356

template/tinyvue/src/main.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import TinySearchBox from '@opentiny/vue-search-box'
22
import { createApp } from 'vue'
3+
import { setNavigator } from '@opentiny/next-sdk'
34
import globalComponents from '@/components'
45
import App from './App.vue'
56
import directive from './directive'
@@ -22,3 +23,6 @@ app.use(directive)
2223
app.use(TinySearchBox)
2324

2425
app.mount('#app')
26+
27+
// 必须在 router 注册后调用,让 SDK 持有 router.push 的引用
28+
setNavigator((route) => router.push(route))
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import { createMessageChannelPairTransport, WebMcpServer, withPageTools } from '@opentiny/next-sdk'
2+
import registerLocaleManagementTools from './locale/tools'
3+
import registerMenuManagementTools from './menu/tools'
4+
import registerPermissionManagementTools from './permission/tools'
5+
import registerRoleManagementTools from './role/tools'
6+
import registerUserManagementTools from './user/tools'
7+
8+
const rawServer = new WebMcpServer()
9+
const [serverTransport, clientTransport] = createMessageChannelPairTransport()
10+
11+
// withPageTools 包装后,registerTool 第三个参数支持路由配置对象
12+
export const server = withPageTools(rawServer)
13+
14+
// clientTransport 导出给 TinyRemoter 使用
15+
export { clientTransport }
16+
17+
export async function createMcpServer() {
18+
registerLocaleManagementTools(server)
19+
registerUserManagementTools(server)
20+
registerRoleManagementTools(server)
21+
registerPermissionManagementTools(server)
22+
registerMenuManagementTools(server)
23+
// 最后建立连接,确保所有工具已注册完毕
24+
await rawServer.connect(serverTransport)
25+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import type { PageAwareServer } from '@opentiny/next-sdk'
2+
import { z } from '@opentiny/next-sdk'
3+
4+
function registerLocaleManagementTools(server: PageAwareServer) {
5+
server.registerTool(
6+
'add-i18n-entry',
7+
{
8+
title: '添加国际化词条',
9+
description: '添加国际化词条',
10+
inputSchema: {
11+
key: z.string().describe('词条关键字,请自行创建,不要询问用户'),
12+
content: z.string().describe('词条内容'),
13+
lang: z.union([z.literal(1), z.literal(2)]).describe('词条语言ID,英文 enUS 为:1,中文 zhCN 为:2'),
14+
},
15+
},
16+
// 第三个参数传路由配置:工具被调用时自动跳转到 /locale
17+
// 页面加载完成后,通过 postMessage 把 input 转发给页面内的处理器
18+
{ route: '/vue-pro/locale' },
19+
)
20+
}
21+
22+
export default registerLocaleManagementTools
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import type { PageAwareServer } from '@opentiny/next-sdk'
2+
import { z } from '@opentiny/next-sdk'
3+
4+
function registerMenuManagementTools(server: PageAwareServer) {
5+
server.registerTool(
6+
'add-menu',
7+
{
8+
title: '添加菜单',
9+
description: '添加菜单',
10+
inputSchema: {
11+
name: z.string().describe('名称'),
12+
order: z.number().describe('优先级').default(0),
13+
parentMenu: z.string().describe('父菜单').optional(),
14+
icon: z.string().describe('图标').optional().default(''),
15+
component: z.string().describe('组件'),
16+
path: z.string().describe('路径'),
17+
locale: z.string().describe('国际化'),
18+
},
19+
},
20+
{ route: '/vue-pro/menu/allMenu' },
21+
)
22+
}
23+
24+
export default registerMenuManagementTools
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import type { PageAwareServer } from '@opentiny/next-sdk'
2+
import { z } from '@opentiny/next-sdk'
3+
4+
function registerPermissionManagementTools(server: PageAwareServer) {
5+
server.registerTool(
6+
'add-permission',
7+
{
8+
title: '添加权限',
9+
description: '添加权限',
10+
inputSchema: {
11+
name: z.string().describe('权限名称'),
12+
desc: z.string().describe('权限描述'),
13+
},
14+
},
15+
{ route: '/vue-pro/permission/allPermission' },
16+
)
17+
}
18+
19+
export default registerPermissionManagementTools
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import type { PageAwareServer } from '@opentiny/next-sdk'
2+
import { z } from '@opentiny/next-sdk'
3+
4+
function registerRoleManagementTools(server: PageAwareServer) {
5+
server.registerTool(
6+
'add-role',
7+
{
8+
title: '添加角色',
9+
description: '添加角色,不需要生成角色卡片',
10+
inputSchema: {
11+
name: z.string().describe('角色名称'),
12+
// TODO: 用户的语言可能是添加用户和删除用户的权限,而不是 user::add 和 user::remove 权限或者权限 ID 为 2 和 3,需要做下转换
13+
permissions: z.array(z.number()).describe('角色拥有的权限'),
14+
},
15+
},
16+
{ route: '/vue-pro/role/allRole' },
17+
)
18+
19+
server.registerTool(
20+
'bind-menu-for-role',
21+
{
22+
title: '绑定菜单',
23+
description: '给某个角色绑定菜单',
24+
inputSchema: {
25+
role: z.string().describe('需要绑定菜单的角色名称'),
26+
menu: z.string().describe('需要绑定的菜单名称'),
27+
},
28+
},
29+
{ route: '/vue-pro/role/allRole' },
30+
)
31+
}
32+
33+
export default registerRoleManagementTools
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import type { PageAwareServer } from '@opentiny/next-sdk'
2+
import { z } from '@opentiny/next-sdk'
3+
4+
function registerUserManagementTools(server: PageAwareServer) {
5+
server.registerTool(
6+
'add-user',
7+
{
8+
title: '添加用户',
9+
description: '添加用户,可选参数不需要用户提供,也不用创建表单卡片,直接根据用户提供的信息添加用户即可',
10+
inputSchema: {
11+
email: z.string().describe('邮箱'),
12+
password: z.string().describe('密码'),
13+
name: z.string().describe('用户名'),
14+
address: z.string().describe('地址').optional(),
15+
department: z.string().describe('所属部门').optional(),
16+
roleIds: z.array(z.number()).describe('职位').optional(),
17+
employeeType: z.string().describe('招聘类型').optional(),
18+
probationDate: z.array(z.date()).describe('试用期起止时间').optional(),
19+
probationDuration: z.string().describe('试用期时长').optional(),
20+
protocolStart: z.date().describe('劳动合同开始日期').optional(),
21+
protocolEnd: z.date().describe('劳动合同结束日期').optional(),
22+
status: z.string().describe('状态').optional(),
23+
},
24+
},
25+
{ route: '/vue-pro/userManager/allInfo' },
26+
)
27+
}
28+
29+
export default registerUserManagementTools

0 commit comments

Comments
 (0)