-
Notifications
You must be signed in to change notification settings - Fork 24
feat: add next-sdk web skills #195
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,25 @@ | ||||||||||||||||||||||||||||||||||||||
| import { createMessageChannelPairTransport, WebMcpServer, withPageTools } from '@opentiny/next-sdk' | ||||||||||||||||||||||||||||||||||||||
| import registerLocaleManagementTools from './locale/tools' | ||||||||||||||||||||||||||||||||||||||
coderabbitai[bot] marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||||||||||||||||||||||||||
| import registerMenuManagementTools from './menu/tools' | ||||||||||||||||||||||||||||||||||||||
| import registerPermissionManagementTools from './permission/tools' | ||||||||||||||||||||||||||||||||||||||
| import registerRoleManagementTools from './role/tools' | ||||||||||||||||||||||||||||||||||||||
| import registerUserManagementTools from './user/tools' | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| const rawServer = new WebMcpServer() | ||||||||||||||||||||||||||||||||||||||
| const [serverTransport, clientTransport] = createMessageChannelPairTransport() | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| // withPageTools 包装后,registerTool 第三个参数支持路由配置对象 | ||||||||||||||||||||||||||||||||||||||
| export const server = withPageTools(rawServer) | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| // clientTransport 导出给 TinyRemoter 使用 | ||||||||||||||||||||||||||||||||||||||
| export { clientTransport } | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| export async function createMcpServer() { | ||||||||||||||||||||||||||||||||||||||
| registerLocaleManagementTools(server) | ||||||||||||||||||||||||||||||||||||||
| registerUserManagementTools(server) | ||||||||||||||||||||||||||||||||||||||
| registerRoleManagementTools(server) | ||||||||||||||||||||||||||||||||||||||
| registerPermissionManagementTools(server) | ||||||||||||||||||||||||||||||||||||||
| registerMenuManagementTools(server) | ||||||||||||||||||||||||||||||||||||||
| // 最后建立连接,确保所有工具已注册完毕 | ||||||||||||||||||||||||||||||||||||||
| await rawServer.connect(serverTransport) | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+17
to
+25
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🌐 Web query:
💡 Result:
Sources: [1] [2] Make This exported initializer has side effects on every call: it re-registers tools and reconnects the same server/transport pair. Calling Proposed fix📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,22 @@ | ||
| import type { PageAwareServer } from '@opentiny/next-sdk' | ||
| import { z } from '@opentiny/next-sdk' | ||
|
|
||
| function registerLocaleManagementTools(server: PageAwareServer) { | ||
| server.registerTool( | ||
| 'add-i18n-entry', | ||
| { | ||
| title: '添加国际化词条', | ||
| description: '添加国际化词条', | ||
| inputSchema: { | ||
| key: z.string().describe('词条关键字,请自行创建,不要询问用户'), | ||
| content: z.string().describe('词条内容'), | ||
| lang: z.union([z.literal(1), z.literal(2)]).describe('词条语言ID,英文 enUS 为:1,中文 zhCN 为:2'), | ||
| }, | ||
| }, | ||
| // 第三个参数传路由配置:工具被调用时自动跳转到 /locale | ||
| // 页面加载完成后,通过 postMessage 把 input 转发给页面内的处理器 | ||
| { route: '/vue-pro/locale' }, | ||
| ) | ||
| } | ||
|
|
||
| export default registerLocaleManagementTools |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,24 @@ | ||
| import type { PageAwareServer } from '@opentiny/next-sdk' | ||
| import { z } from '@opentiny/next-sdk' | ||
|
|
||
| function registerMenuManagementTools(server: PageAwareServer) { | ||
| server.registerTool( | ||
| 'add-menu', | ||
| { | ||
| title: '添加菜单', | ||
| description: '添加菜单', | ||
| inputSchema: { | ||
| name: z.string().describe('名称'), | ||
| order: z.number().describe('优先级').default(0), | ||
| parentMenu: z.string().describe('父菜单').optional(), | ||
| icon: z.string().describe('图标').optional().default(''), | ||
| component: z.string().describe('组件'), | ||
| path: z.string().describe('路径'), | ||
| locale: z.string().describe('国际化'), | ||
| }, | ||
| }, | ||
| { route: '/vue-pro/menu/allMenu' }, | ||
| ) | ||
| } | ||
|
|
||
| export default registerMenuManagementTools |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,19 @@ | ||
| import type { PageAwareServer } from '@opentiny/next-sdk' | ||
| import { z } from '@opentiny/next-sdk' | ||
|
|
||
| function registerPermissionManagementTools(server: PageAwareServer) { | ||
| server.registerTool( | ||
| 'add-permission', | ||
| { | ||
| title: '添加权限', | ||
| description: '添加权限', | ||
| inputSchema: { | ||
| name: z.string().describe('权限名称'), | ||
| desc: z.string().describe('权限描述'), | ||
| }, | ||
| }, | ||
| { route: '/vue-pro/permission/allPermission' }, | ||
| ) | ||
| } | ||
|
|
||
| export default registerPermissionManagementTools |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,33 @@ | ||
| import type { PageAwareServer } from '@opentiny/next-sdk' | ||
| import { z } from '@opentiny/next-sdk' | ||
|
|
||
| function registerRoleManagementTools(server: PageAwareServer) { | ||
| server.registerTool( | ||
| 'add-role', | ||
| { | ||
| title: '添加角色', | ||
| description: '添加角色,不需要生成角色卡片', | ||
| inputSchema: { | ||
| name: z.string().describe('角色名称'), | ||
| // TODO: 用户的语言可能是添加用户和删除用户的权限,而不是 user::add 和 user::remove 权限或者权限 ID 为 2 和 3,需要做下转换 | ||
| permissions: z.array(z.number()).describe('角色拥有的权限'), | ||
| }, | ||
| }, | ||
| { route: '/vue-pro/role/allRole' }, | ||
| ) | ||
|
|
||
| server.registerTool( | ||
| 'bind-menu-for-role', | ||
| { | ||
| title: '绑定菜单', | ||
| description: '给某个角色绑定菜单', | ||
| inputSchema: { | ||
| role: z.string().describe('需要绑定菜单的角色名称'), | ||
| menu: z.string().describe('需要绑定的菜单名称'), | ||
| }, | ||
| }, | ||
| { route: '/vue-pro/role/allRole' }, | ||
| ) | ||
| } | ||
|
|
||
| export default registerRoleManagementTools |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,29 @@ | ||||||||||||||||||
| import type { PageAwareServer } from '@opentiny/next-sdk' | ||||||||||||||||||
| import { z } from '@opentiny/next-sdk' | ||||||||||||||||||
|
|
||||||||||||||||||
| function registerUserManagementTools(server: PageAwareServer) { | ||||||||||||||||||
| server.registerTool( | ||||||||||||||||||
| 'add-user', | ||||||||||||||||||
| { | ||||||||||||||||||
| title: '添加用户', | ||||||||||||||||||
| description: '添加用户,可选参数不需要用户提供,也不用创建表单卡片,直接根据用户提供的信息添加用户即可', | ||||||||||||||||||
| inputSchema: { | ||||||||||||||||||
| email: z.string().describe('邮箱'), | ||||||||||||||||||
| password: z.string().describe('密码'), | ||||||||||||||||||
| name: z.string().describe('用户名'), | ||||||||||||||||||
| address: z.string().describe('地址').optional(), | ||||||||||||||||||
| department: z.string().describe('所属部门').optional(), | ||||||||||||||||||
| roleIds: z.array(z.number()).describe('职位').optional(), | ||||||||||||||||||
| employeeType: z.string().describe('招聘类型').optional(), | ||||||||||||||||||
| probationDate: z.array(z.date()).describe('试用期起止时间').optional(), | ||||||||||||||||||
| probationDuration: z.string().describe('试用期时长').optional(), | ||||||||||||||||||
| protocolStart: z.date().describe('劳动合同开始日期').optional(), | ||||||||||||||||||
| protocolEnd: z.date().describe('劳动合同结束日期').optional(), | ||||||||||||||||||
|
Comment on lines
+18
to
+21
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🌐 Web query:
💡 Result:
If you need to accept ISO strings, use a string schema like 🏁 Script executed: Repository: opentiny/tiny-pro Length of output: 92 🏁 Script executed: Repository: opentiny/tiny-pro Length of output: 1375 🏁 Script executed: Repository: opentiny/tiny-pro Length of output: 518 Replace The schema uses Suggested fix📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||
| status: z.string().describe('状态').optional(), | ||||||||||||||||||
| }, | ||||||||||||||||||
| }, | ||||||||||||||||||
| { route: '/vue-pro/userManager/allInfo' }, | ||||||||||||||||||
| ) | ||||||||||||||||||
| } | ||||||||||||||||||
|
|
||||||||||||||||||
| export default registerUserManagementTools | ||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| export const skills = import.meta.glob('./**/*', { | ||
| query: '?raw', // 以原始文本形式导入,不经过模块解析 | ||
| import: 'default', // 取模块的 default 导出(即文件内容字符串) | ||
| eager: true // 同步加载,避免异步等待 | ||
| }) as Record<string, string> |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,36 @@ | ||
| --- | ||
| name: tiny-pro-operator | ||
| description: TinyPro 项目的前端页面操作指南。当用户需要新建页面、配置菜单、设置权限(v-permission)、国际化(i18n)管理或进行 TinyPro 相关页面的操作时触发。该技能提供从菜单管理、词条管理、用户管理、权限管理的完整指引。 | ||
| --- | ||
|
|
||
| # TinyPro 操作指南 (tiny-pro-operator) | ||
|
|
||
| 本技能旨在帮助开发者在 TinyPro 框架下高效地完成页面操作,比如:菜单管理、权限管理和国际化配置。 | ||
|
|
||
| 以下是系统管理包含的功能,每个功能都有对应的路由和 MCP 工具。 | ||
|
|
||
| - 菜单管理(路由:`menu/allMenu`): | ||
| - ✅️创建菜单 `add-menu` | ||
| - ❌修改菜单 | ||
| - ❌删除菜单 | ||
| - 权限管理(路由:`permission/allPermission`): | ||
| - ✅️添加权限 `add-permission` | ||
| - ❌编辑权限 | ||
| - ❌删除权限 | ||
| - 角色管理(路由:`role/allRole`): | ||
| - ✅️添加角色 `add-role` | ||
| - ❌编辑角色(名称、权限) | ||
| - ❌删除角色 | ||
| - ✅️绑定目录 bind-menu-for-role | ||
| - 用户管理(路由:`userManager/allInfo`): | ||
| - ✅️添加用户 `add-user` | ||
| - ❌编辑用户 | ||
| - ❌修改密码 | ||
| - ❌删除用户(包含批量删除用户) | ||
| - 国际化词条管理(路由:`locale`): | ||
| - ✅️添加词条 `add-i18n-entry` | ||
| - ❌删除词条(包含批量删除词条) | ||
|
|
||
| 当用户询问相关操作时,需要跳转到对应的路由,调用对应的 MCP 工具。 | ||
|
|
||
| 例如:“帮我添加权限:good::add,描述是:创建商品”,则需要调用 MCP 工具 `add-permission`。 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🌐 Web query:
In Vue 3 Composition API, are child components mounted before the parent component'sonMounted()hook runs, and is the initial render committed before an asynconMounted()callback finishes?💡 Result:
Yes.
Child components mount before the parent’s
onMounted()runs (for synchronous children): Vue considers a component “mounted” only after all of its synchronous child components have been mounted and the component’s DOM has been created/inserted, so the parent’smounted/onMountednecessarily happens after those children. [1]The initial render is already committed before an async
onMounted(async () => …)finishes:onMountedruns after the component has finished the initial rendering and created the DOM nodes, and Vue does not delay/“await” client rendering on a Promise returned frommounted/onMounted. (Vue only has an explicit “wait for the Promise” lifecycle hook for SSR:serverPrefetch.) [2] [3]Sources: [1] Vue Options API lifecycle:
mountedsemantics. [2] Vue Guide:onMountedruns after initial render + DOM creation. [3] Vue Options API lifecycle:serverPrefetchis awaited by the server renderer.Gate
TinyRemoteron successful MCP startup.createMcpServer()is async and runs inonMounted(), butTinyRemoteris rendered unconditionally during initial render—which happens beforeonMountedruns. If bootstrap is still in flight or throws, the remoter is exposed to an uninitialized local server with an unhandled error.🐛 Proposed fix
📝 Committable suggestion
🤖 Prompt for AI Agents