Conversation
WalkthroughReplaces remote agent MCP wiring with a local page-aware MCP server and page-tool registration; pins two SDK/remoter deps to 0.2.7; adds MCP server modules, skill discovery and docs; updates Vue components to register/deregister page tools and sets SDK navigator. Changes
Sequence DiagramsequenceDiagram
participant App as App.vue (Bootstrap)
participant MCP as MCP Server (mcp-servers)
participant Transport as MessageChannel Transport
participant TinyRemoter as TinyRemoter (Component)
participant Page as Page Component (locale/menu/role/user/permission)
App->>MCP: call createMcpServer()
MCP->>MCP: create WebMcpServer + withPageTools
MCP->>Transport: create serverTransport <-> clientTransport
MCP->>MCP: register locale, menu, permission, role, user tools
MCP->>Transport: rawServer.connect(serverTransport)
MCP-->>App: export server & clientTransport
App->>TinyRemoter: provide :mcp-servers, :skills, clientTransport
Page->>TinyRemoter: user triggers tool action
TinyRemoter->>Page: deliver tool input -> Page handler
Page-->>TinyRemoter: return confirmation/result
Page->>Page: onUnmounted -> invoke cleanupPageTool()
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 7
🧹 Nitpick comments (10)
template/tinyvue/src/views/locale/components/add-locale.vue (1)
3-3: Remove unused importsWebMcpServerandz.After migrating to the
registerPageToolpattern,WebMcpServerandzare no longer used in this component. These should be removed to keep the imports clean.Proposed fix
-import { registerPageTool, WebMcpServer, z } from '@opentiny/next-sdk' +import { registerPageTool } from '@opentiny/next-sdk'🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@template/tinyvue/src/views/locale/components/add-locale.vue` at line 3, The import line includes unused symbols WebMcpServer and z; update the import that currently reads the named imports (registerPageTool, WebMcpServer, z) to only import registerPageTool so remove WebMcpServer and z from the import list (keep registerPageTool) to eliminate unused imports in the add-locale component.template/tinyvue/src/skills/tiny-pro-operator/reference/front-end-guide.md (2)
13-19: Add language identifier to fenced code block.The code block showing the directory structure should have a language identifier for proper rendering. Since it's a directory tree, use
textorplaintext.Proposed fix
-``` +```text web src views test-page index.vue</details> <details> <summary>🤖 Prompt for AI Agents</summary>Verify each finding against the current code and only fix it if needed.
In
@template/tinyvue/src/skills/tiny-pro-operator/reference/front-end-guide.md
around lines 13 - 19, The fenced code block in front-end-guide.md showing the
directory tree currently lacks a language identifier; update the triple-backtick
fence for the directory tree (the block containing "web / src / views /
test-page / index.vue") to include a language identifier such as text or
plaintext (e.g., changetotext) so the directory structure renders
correctly.</details> --- `32-181`: **Add alt text to images for accessibility.** Multiple images throughout this document are missing alternate text. While this doesn't break functionality, adding descriptive alt text improves accessibility and helps when images fail to load. Example fix for line 32: ```markdown  ``` <details> <summary>🤖 Prompt for AI Agents</summary> ``` Verify each finding against the current code and only fix it if needed. In `@template/tinyvue/src/skills/tiny-pro-operator/reference/front-end-guide.md` around lines 32 - 181, The markdown images in front-end-guide.md are missing alt text which hurts accessibility; update each image markdown (e.g., the occurrences of ./images/点击添加词条.png, ./images/选择语言.png, ./images/添加菜单.png, ./images/绑定菜单.png, ./images/左侧测试页面%20-%20中文.png, ./images/最终页面效果.png and all other ./images/* entries) to include concise, descriptive alt text (for example "添加词条对话框", "选择语言", "添加菜单", "绑定菜单成功", "左侧测试页面(中文)", "最终页面效果", etc.) so every  becomes ; ensure descriptions are brief and context-appropriate for each image in the document. ``` </details> </blockquote></details> <details> <summary>template/tinyvue/src/skills/index.ts (1)</summary><blockquote> `1-6`: **Remove debug `console.log` and consider excluding self from glob.** 1. The `console.log` on line 6 should be removed before merging to avoid polluting the console in production. 2. The glob pattern `./**/*` will include this `index.ts` file itself in the output. Consider excluding it with a negative pattern if that's unintended. <details> <summary>Proposed fix</summary> ```diff export const skills = import.meta.glob('./**/*', { query: '?raw', // 以原始文本形式导入,不经过模块解析 import: 'default', // 取模块的 default 导出(即文件内容字符串) eager: true // 同步加载,避免异步等待 }) as Record<string, string> -console.log('skills:', skills); ``` If you need to exclude `index.ts`: ```typescript export const skills = import.meta.glob(['./**/*', '!./index.ts'], { // ... }) as Record<string, string> ``` </details> <details> <summary>🤖 Prompt for AI Agents</summary> ``` Verify each finding against the current code and only fix it if needed. In `@template/tinyvue/src/skills/index.ts` around lines 1 - 6, Remove the debug console.log by deleting the line that logs the skills constant and update the import.meta.glob call that builds the skills record to exclude this file itself (so index.ts is not imported into skills); specifically modify the import.meta.glob invocation that creates the exported skills variable to use an exclusion pattern for './index.ts' (or similar negative glob) while retaining query:'?raw', import:'default', eager:true. ``` </details> </blockquote></details> <details> <summary>template/tinyvue/src/views/role/info/components/info-tab.vue (1)</summary><blockquote> `2-2`: **Remove unused imports.** `WebMcpServer` and `z` are imported but never used in this file. <details> <summary>♻️ Proposed fix</summary> ```diff -import { registerPageTool, WebMcpServer, z } from '@opentiny/next-sdk' +import { registerPageTool } from '@opentiny/next-sdk' ``` </details> <details> <summary>🤖 Prompt for AI Agents</summary> ``` Verify each finding against the current code and only fix it if needed. In `@template/tinyvue/src/views/role/info/components/info-tab.vue` at line 2, The import line currently brings in unused symbols WebMcpServer and z; remove WebMcpServer and z from the import so only used symbols (e.g., registerPageTool) remain to avoid unused-import lint errors—locate the import statement that includes registerPageTool, WebMcpServer, z and delete the WebMcpServer and z identifiers from that import. ``` </details> </blockquote></details> <details> <summary>template/tinyvue/src/views/userManager/info/components/info-tab.vue (1)</summary><blockquote> `3-3`: **Remove unused imports.** `WebMcpServer` and `z` are imported but never used in this file. Similarly, `inject` on line 21 is imported but not used. <details> <summary>♻️ Proposed fix</summary> ```diff -import { registerPageTool, WebMcpServer, z } from '@opentiny/next-sdk' +import { registerPageTool } from '@opentiny/next-sdk' ``` </details> <details> <summary>🤖 Prompt for AI Agents</summary> ``` Verify each finding against the current code and only fix it if needed. In `@template/tinyvue/src/views/userManager/info/components/info-tab.vue` at line 3, The import statement includes unused symbols: remove WebMcpServer and z from the import line "import { registerPageTool, WebMcpServer, z } from '@opentiny/next-sdk'" and also remove the unused "inject" import (the import that includes inject on line 21) so only actually used imports remain (e.g., keep registerPageTool if used); update the import declarations accordingly to eliminate unused imports and avoid linter errors. ``` </details> </blockquote></details> <details> <summary>template/tinyvue/src/App.vue (1)</summary><blockquote> `3-3`: **Remove unused imports.** `createMessageChannelPairTransport`, `WebMcpClient`, `WebMcpServer`, and `z` are imported but the code only uses them in ways that are either redundant or can be cleaned up (given the orphaned transport issue above). <details> <summary>🤖 Prompt for AI Agents</summary> ``` Verify each finding against the current code and only fix it if needed. In `@template/tinyvue/src/App.vue` at line 3, The import line unnecessarily includes createMessageChannelPairTransport, WebMcpClient, WebMcpServer, and z which are not used; update the import statement to remove these unused symbols so only the actually used exports from '@opentiny/next-sdk' remain (leave any required exports intact and remove createMessageChannelPairTransport, WebMcpClient, WebMcpServer, and z). ``` </details> </blockquote></details> <details> <summary>template/tinyvue/src/views/permission/info/components/info-tab.vue (1)</summary><blockquote> `4-4`: **Remove unused imports.** `WebMcpServer` and `z` are imported but never used in this file. Similarly, `inject` on line 20 is imported but not used. <details> <summary>♻️ Proposed fix</summary> ```diff -import { registerPageTool, WebMcpServer, z } from '@opentiny/next-sdk' +import { registerPageTool } from '@opentiny/next-sdk' ``` ```diff -import { computed, reactive, ref, onMounted, onUnmounted, inject } from 'vue' +import { computed, reactive, ref, onMounted, onUnmounted } from 'vue' ``` </details> <details> <summary>🤖 Prompt for AI Agents</summary> ``` Verify each finding against the current code and only fix it if needed. In `@template/tinyvue/src/views/permission/info/components/info-tab.vue` at line 4, Remove the unused imports: delete WebMcpServer and z from the import statement that currently reads "import { registerPageTool, WebMcpServer, z } from '@opentiny/next-sdk'" and remove the unused "inject" import (wherever imported on line ~20), leaving only the actually used symbol(s) such as registerPageTool; ensure no other references to WebMcpServer, z, or inject remain in the file after removal. ``` </details> </blockquote></details> <details> <summary>template/tinyvue/src/views/menu/info/components/info-tab.vue (1)</summary><blockquote> `7-7`: **Remove unused imports.** `WebMcpServer` and `z` are imported but never used in this file. <details> <summary>♻️ Proposed fix</summary> ```diff -import { registerPageTool, WebMcpServer, z } from '@opentiny/next-sdk' +import { registerPageTool } from '@opentiny/next-sdk' ``` </details> <details> <summary>🤖 Prompt for AI Agents</summary> ``` Verify each finding against the current code and only fix it if needed. In `@template/tinyvue/src/views/menu/info/components/info-tab.vue` at line 7, The import statement pulls in WebMcpServer and z which are unused; modify the import from '@opentiny/next-sdk' to only import the used symbol registerPageTool (leave registerPageTool intact and remove WebMcpServer and z) so the file no longer contains unused imports. ``` </details> </blockquote></details> <details> <summary>template/tinyvue/src/mcp-servers/role/tools.ts (1)</summary><blockquote> `12-12`: **TODO comment noted - permission name resolution.** The comment raises a valid UX concern about users providing human-readable permission names vs. IDs. Consider tracking this as a follow-up task. Would you like me to open an issue to track the permission name-to-ID conversion enhancement? <details> <summary>🤖 Prompt for AI Agents</summary> ``` Verify each finding against the current code and only fix it if needed. In `@template/tinyvue/src/mcp-servers/role/tools.ts` at line 12, Implement permission-name resolution in template/tinyvue/src/mcp-servers/role/tools.ts by adding a resolver (e.g., resolvePermissionIdentifiers) that accepts mixed permission inputs (human-readable names like "添加用户"/"删除用户" or numerical IDs) and returns a canonical array of numeric permission IDs; use an authoritative lookup (static map or PermissionService.getAll / PermissionRepository) to map names to IDs, validate unknown names and throw or return clear errors, and update callers such as assignRolePermissions / removeRolePermissions to call resolvePermissionIdentifiers before persisting changes (cache the mapping for performance). ``` </details> </blockquote></details> </blockquote></details> <details> <summary>🤖 Prompt for all review comments with AI agents</summary>Verify each finding against the current code and only fix it if needed.
Inline comments:
In@template/tinyvue/src/App.vue:
- Around line 31-32: The local transport created by
createMessageChannelPairTransport() (the serverTransport variable) is orphaned
and never connected to the MCP server; remove the
createMessageChannelPairTransport() call and the provide('serverTransport',
serverTransport) line, and instead provide the functional transport exported by
your MCP server module (use the server-side transport exported from
./mcp-servers or export one there if missing) so that components receive the
same transport used in the mcpServers config along with the existing
clientTransport.In
@template/tinyvue/src/mcp-servers/index.ts:
- Line 2: The bootstrap only registers registerLocaleManagementTools(server) so
the other MCP skill modules (menu, permission, role, user) never get wired;
import their registration functions and call them in the same place you call
registerLocaleManagementTools(server) (e.g., registerMenu...,
registerPermission..., registerRole..., registerUser... with the server
argument) so each module is registered at runtime; locate calls to
registerLocaleManagementTools in index.ts and add the analogous
registerXxxManagementTools(server) calls for the menu, permission, role and user
modules.- Around line 13-17: createMcpServer currently re-registers tools and calls
rawServer.connect(serverTransport) on every invocation; make it idempotent by
adding an initialization guard and a safe connect check: introduce a
module-level boolean (e.g., mcpServerInitialized) and return early if true, call
registerLocaleManagementTools only when first initializing, and before calling
rawServer.connect(serverTransport) check whether the WebMcpServer is already
connected (or wrap connect call in try/catch and set the initialized flag only
after a successful connection); update createMcpServer to set the flag after
successful registration/connection so repeated calls are no-ops.In
@template/tinyvue/src/mcp-servers/menu/tools.ts:
- Around line 4-24: The function is misnamed: rename
registerLocaleManagementTools to registerMenuManagementTools and update the
default export to export the same name to avoid collision with the locale tools;
specifically change the function declaration identifier
(registerLocaleManagementTools) and the export default to
registerMenuManagementTools, and search for any local references to the old name
in this file (e.g., the registerTool call remains unchanged) to update them
accordingly so imports elsewhere use registerMenuManagementTools.In
@template/tinyvue/src/mcp-servers/user/tools.ts:
- Around line 18-21: The date fields in the schema (probationDate,
protocolStart, protocolEnd) are using z.date(), which rejects ISO date strings;
update those validators to z.coerce.date() (and for the array use
z.array(z.coerce.date())) so incoming ISO string dates are coerced to Date
objects at validation time; keep probationDuration as string. Locate the symbols
probationDate, probationDuration, protocolStart, protocolEnd and replace
z.date() with z.coerce.date() accordingly.In
@template/tinyvue/src/views/permission/info/components/info-tab.vue:
- Around line 205-212: The handler in info-tab.vue is registered under the wrong
tool id ('add-menu') so it will never be invoked by the permission server (which
uses 'add-permission') and also conflicts with the menu tool; change the
registered id to 'add-permission' and keep the existing logic that calls
handleAddPermission(), assigns state.permissionAddData.name/desc, and then calls
handlePermissionAddSubmit() so the permission tool can trigger this flow; ensure
you update the identifier in the handler registration (replace 'add-menu' with
'add-permission') and remove/avoid duplicate registrations for 'add-menu' to
prevent conflicts with menu/tools.ts.In
@template/tinyvue/src/views/userManager/info/components/info-tab.vue:
- Around line 360-373: The handler registered via registerPageTool in
cleanupPageTool is using the wrong tool ID ('add-role'), so replace that ID with
the correct user tool name ('add-user') to match the mcp-server registration;
leave the handler body (calls to handleAddUser,
addUserFormRef.value.setUserInfo, and addUserFormRef.value.handleSubmit) intact
so the user flow triggers properly when the 'add-user' tool is invoked.
Nitpick comments:
In@template/tinyvue/src/App.vue:
- Line 3: The import line unnecessarily includes
createMessageChannelPairTransport, WebMcpClient, WebMcpServer, and z which are
not used; update the import statement to remove these unused symbols so only the
actually used exports from '@opentiny/next-sdk' remain (leave any required
exports intact and remove createMessageChannelPairTransport, WebMcpClient,
WebMcpServer, and z).In
@template/tinyvue/src/mcp-servers/role/tools.ts:
- Line 12: Implement permission-name resolution in
template/tinyvue/src/mcp-servers/role/tools.ts by adding a resolver (e.g.,
resolvePermissionIdentifiers) that accepts mixed permission inputs
(human-readable names like "添加用户"/"删除用户" or numerical IDs) and returns a
canonical array of numeric permission IDs; use an authoritative lookup (static
map or PermissionService.getAll / PermissionRepository) to map names to IDs,
validate unknown names and throw or return clear errors, and update callers such
as assignRolePermissions / removeRolePermissions to call
resolvePermissionIdentifiers before persisting changes (cache the mapping for
performance).In
@template/tinyvue/src/skills/index.ts:
- Around line 1-6: Remove the debug console.log by deleting the line that logs
the skills constant and update the import.meta.glob call that builds the skills
record to exclude this file itself (so index.ts is not imported into skills);
specifically modify the import.meta.glob invocation that creates the exported
skills variable to use an exclusion pattern for './index.ts' (or similar
negative glob) while retaining query:'?raw', import:'default', eager:true.In
@template/tinyvue/src/skills/tiny-pro-operator/reference/front-end-guide.md:
- Around line 13-19: The fenced code block in front-end-guide.md showing the
directory tree currently lacks a language identifier; update the triple-backtick
fence for the directory tree (the block containing "web / src / views /
test-page / index.vue") to include a language identifier such as text or
plaintext (e.g., changetotext) so the directory structure renders
correctly.- Around line 32-181: The markdown images in front-end-guide.md are missing alt
text which hurts accessibility; update each image markdown (e.g., the
occurrences of ./images/点击添加词条.png, ./images/选择语言.png, ./images/添加菜单.png,
./images/绑定菜单.png, ./images/左侧测试页面%20-%20中文.png, ./images/最终页面效果.png and all
other ./images/* entries) to include concise, descriptive alt text (for example
"添加词条对话框", "选择语言", "添加菜单", "绑定菜单成功", "左侧测试页面(中文)", "最终页面效果", etc.) so every
becomes
; ensure descriptions are brief and
context-appropriate for each image in the document.In
@template/tinyvue/src/views/locale/components/add-locale.vue:
- Line 3: The import line includes unused symbols WebMcpServer and z; update the
import that currently reads the named imports (registerPageTool, WebMcpServer,
z) to only import registerPageTool so remove WebMcpServer and z from the import
list (keep registerPageTool) to eliminate unused imports in the add-locale
component.In
@template/tinyvue/src/views/menu/info/components/info-tab.vue:
- Line 7: The import statement pulls in WebMcpServer and z which are unused;
modify the import from '@opentiny/next-sdk' to only import the used symbol
registerPageTool (leave registerPageTool intact and remove WebMcpServer and z)
so the file no longer contains unused imports.In
@template/tinyvue/src/views/permission/info/components/info-tab.vue:
- Line 4: Remove the unused imports: delete WebMcpServer and z from the import
statement that currently reads "import { registerPageTool, WebMcpServer, z }
from '@opentiny/next-sdk'" and remove the unused "inject" import (wherever
imported on line ~20), leaving only the actually used symbol(s) such as
registerPageTool; ensure no other references to WebMcpServer, z, or inject
remain in the file after removal.In
@template/tinyvue/src/views/role/info/components/info-tab.vue:
- Line 2: The import line currently brings in unused symbols WebMcpServer and z;
remove WebMcpServer and z from the import so only used symbols (e.g.,
registerPageTool) remain to avoid unused-import lint errors—locate the import
statement that includes registerPageTool, WebMcpServer, z and delete the
WebMcpServer and z identifiers from that import.In
@template/tinyvue/src/views/userManager/info/components/info-tab.vue:
- Line 3: The import statement includes unused symbols: remove WebMcpServer and
z from the import line "import { registerPageTool, WebMcpServer, z } from
'@opentiny/next-sdk'" and also remove the unused "inject" import (the import
that includes inject on line 21) so only actually used imports remain (e.g.,
keep registerPageTool if used); update the import declarations accordingly to
eliminate unused imports and avoid linter errors.</details> --- <details> <summary>ℹ️ Review info</summary> <details> <summary>⚙️ Run configuration</summary> **Configuration used**: Organization UI **Review profile**: CHILL **Plan**: Pro **Run ID**: `63bb48e5-010f-4096-a535-f37bf2d8d36d` </details> <details> <summary>📥 Commits</summary> Reviewing files that changed from the base of the PR and between 0cf5a244dbd1386805347f7a6b4b9f5b0a2b0ef1 and 551357eb2d85fe6b72bbfd9e6ce2317579d2a3f5. </details> <details> <summary>⛔ Files ignored due to path filters (1)</summary> * `pnpm-lock.yaml` is excluded by `!**/pnpm-lock.yaml` </details> <details> <summary>📒 Files selected for processing (17)</summary> * `template/tinyvue/package.json` * `template/tinyvue/src/App.vue` * `template/tinyvue/src/main.ts` * `template/tinyvue/src/mcp-servers/index.ts` * `template/tinyvue/src/mcp-servers/locale/tools.ts` * `template/tinyvue/src/mcp-servers/menu/tools.ts` * `template/tinyvue/src/mcp-servers/permission/tools.ts` * `template/tinyvue/src/mcp-servers/role/tools.ts` * `template/tinyvue/src/mcp-servers/user/tools.ts` * `template/tinyvue/src/skills/index.ts` * `template/tinyvue/src/skills/tiny-pro-operator/SKILL.md` * `template/tinyvue/src/skills/tiny-pro-operator/reference/front-end-guide.md` * `template/tinyvue/src/views/locale/components/add-locale.vue` * `template/tinyvue/src/views/menu/info/components/info-tab.vue` * `template/tinyvue/src/views/permission/info/components/info-tab.vue` * `template/tinyvue/src/views/role/info/components/info-tab.vue` * `template/tinyvue/src/views/userManager/info/components/info-tab.vue` </details> </details> <!-- This is an auto-generated comment by CodeRabbit for review status -->
| export async function createMcpServer() { | ||
| registerLocaleManagementTools(server) | ||
| // 最后建立连接,确保所有工具已注册完毕 | ||
| await rawServer.connect(serverTransport) | ||
| } |
There was a problem hiding this comment.
🧩 Analysis chain
🌐 Web query:
In @opentiny/next-sdk0.2.6-beta.0, isWebMcpServer.connect()safe to call multiple times on the same instance, and doesregisterTool() allow duplicate registrations of the same tool name?
💡 Result:
-
WebMcpServer.connect()(in@opentiny/next-sdkv0.2.6-beta.0) is not designed to be safely called multiple times on the same instance. The implementation always assignsthis.transportand then callsawait this.server.connect(this.transport)with no “already connected” guard or automaticclose()/disconnect first—so a second call will attempt to connect again (behavior then depends on the underlying@modelcontextprotocol/sdkserver, and may error or lead to inconsistent state). [1] -
registerTool()does not allow two tools with the same name to coexist. In this SDK it simply delegates tothis.server.registerTool(name, ...)(so the tool name is the identity key). [1] The MCP TS SDK docs also describeregisterTool()as a way tools can be “added … or updated at runtime” (and that it triggers tool-list-changed notifications), implying re-registering the same name is treated as an update/replace, not a duplicate entry. [2]
Sources: [1] [2]
Make createMcpServer() idempotent.
This exported initializer has side effects on every call: it re-registers tools and reconnects the same server/transport pair. Calling connect() multiple times on the same WebMcpServer instance is unsafe and can cause errors or inconsistent state. A remount or HMR path could trigger this repeatedly.
Proposed fix
+let mcpServerReady: Promise<void> | undefined
+
export async function createMcpServer() {
- registerLocaleManagementTools(server)
- // 最后建立连接,确保所有工具已注册完毕
- await rawServer.connect(serverTransport)
+ if (!mcpServerReady) {
+ mcpServerReady = (async () => {
+ registerLocaleManagementTools(server)
+ // 最后建立连接,确保所有工具已注册完毕
+ await rawServer.connect(serverTransport)
+ })()
+ }
+
+ await mcpServerReady
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| export async function createMcpServer() { | |
| registerLocaleManagementTools(server) | |
| // 最后建立连接,确保所有工具已注册完毕 | |
| await rawServer.connect(serverTransport) | |
| } | |
| let mcpServerReady: Promise<void> | undefined | |
| export async function createMcpServer() { | |
| if (!mcpServerReady) { | |
| mcpServerReady = (async () => { | |
| registerLocaleManagementTools(server) | |
| // 最后建立连接,确保所有工具已注册完毕 | |
| await rawServer.connect(serverTransport) | |
| })() | |
| } | |
| await mcpServerReady | |
| } |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@template/tinyvue/src/mcp-servers/index.ts` around lines 13 - 17,
createMcpServer currently re-registers tools and calls
rawServer.connect(serverTransport) on every invocation; make it idempotent by
adding an initialization guard and a safe connect check: introduce a
module-level boolean (e.g., mcpServerInitialized) and return early if true, call
registerLocaleManagementTools only when first initializing, and before calling
rawServer.connect(serverTransport) check whether the WebMcpServer is already
connected (or wrap connect call in try/catch and set the initialized flag only
after a successful connection); update createMcpServer to set the flag after
successful registration/connection so repeated calls are no-ops.
| probationDate: z.array(z.date()).describe('试用期起止时间').optional(), | ||
| probationDuration: z.string().describe('试用期时长').optional(), | ||
| protocolStart: z.date().describe('劳动合同开始日期').optional(), | ||
| protocolEnd: z.date().describe('劳动合同结束日期').optional(), |
There was a problem hiding this comment.
🧩 Analysis chain
🌐 Web query:
Does Zod z.date() validate ISO date strings or only Date objects?
💡 Result:
z.date() validates only JavaScript Date instances—it will fail on ISO date/datetime strings. For example, "2022-01-12T06:15:00.000Z" does not pass z.date(). [1]
If you need to accept ISO strings, use a string schema like z.iso.date() (for YYYY-MM-DD) / z.iso.datetime() (for datetimes), or coerce into a Date with z.coerce.date() (then validate as a Date). [1]
🏁 Script executed:
cd template/tinyvue && find . -name "tools.ts" | grep userRepository: opentiny/tiny-pro
Length of output: 92
🏁 Script executed:
cat -n template/tinyvue/src/mcp-servers/user/tools.ts | head -100Repository: opentiny/tiny-pro
Length of output: 1375
🏁 Script executed:
# Search for any date coercion or preprocessing related to this schema
rg -i "coerce|date" template/tinyvue/src/mcp-servers/user/tools.ts -A 2 -B 2Repository: opentiny/tiny-pro
Length of output: 518
Replace z.date() with z.coerce.date() for date fields.
The schema uses z.date() on lines 18, 20, and 21, which validates only JavaScript Date instances and will reject ISO date strings (e.g., "2022-01-12T06:15:00.000Z"). Since dates from the message transport arrive as ISO strings, validation will fail at runtime. Use z.coerce.date() instead to accept ISO strings and automatically coerce them to Date objects:
Suggested fix
- probationDate: z.array(z.date()).describe('试用期起止时间').optional(),
+ probationDate: z.array(z.coerce.date()).describe('试用期起止时间').optional(),
- protocolStart: z.date().describe('劳动合同开始日期').optional(),
+ protocolStart: z.coerce.date().describe('劳动合同开始日期').optional(),
- protocolEnd: z.date().describe('劳动合同结束日期').optional(),
+ protocolEnd: z.coerce.date().describe('劳动合同结束日期').optional(),📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| probationDate: z.array(z.date()).describe('试用期起止时间').optional(), | |
| probationDuration: z.string().describe('试用期时长').optional(), | |
| protocolStart: z.date().describe('劳动合同开始日期').optional(), | |
| protocolEnd: z.date().describe('劳动合同结束日期').optional(), | |
| probationDate: z.array(z.coerce.date()).describe('试用期起止时间').optional(), | |
| probationDuration: z.string().describe('试用期时长').optional(), | |
| protocolStart: z.coerce.date().describe('劳动合同开始日期').optional(), | |
| protocolEnd: z.coerce.date().describe('劳动合同结束日期').optional(), |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@template/tinyvue/src/mcp-servers/user/tools.ts` around lines 18 - 21, The
date fields in the schema (probationDate, protocolStart, protocolEnd) are using
z.date(), which rejects ISO date strings; update those validators to
z.coerce.date() (and for the array use z.array(z.coerce.date())) so incoming ISO
string dates are coerced to Date objects at validation time; keep
probationDuration as string. Locate the symbols probationDate,
probationDuration, protocolStart, protocolEnd and replace z.date() with
z.coerce.date() accordingly.
template/tinyvue/src/views/permission/info/components/info-tab.vue
Outdated
Show resolved
Hide resolved
551357e to
3ff1e3d
Compare
There was a problem hiding this comment.
Actionable comments posted: 1
♻️ Duplicate comments (2)
template/tinyvue/src/App.vue (1)
29-30:⚠️ Potential issue | 🟠 MajorProvide the connected
serverTransport, not a new one.Lines 29-30 create a second transport pair, but
createMcpServer()only connects the pair fromtemplate/tinyvue/src/mcp-servers/index.ts. Any descendant injectingserverTransporthere will be wired to a dead channel.#!/bin/bash set -euo pipefail # Verify whether any descendants consume the injected transport key. rg -n -C2 "createMessageChannelPairTransport|provide\(['\"]serverTransport['\"]|inject\(['\"]serverTransport['\"]" template/tinyvue/srcSuggested fix
template/tinyvue/src/App.vue-import { createMessageChannelPairTransport } from '@opentiny/next-sdk' import { TinyConfigProvider } from '@opentiny/vue' import TinyThemeTool from '@opentiny/vue-theme/theme-tool' import { onMounted, provide } from 'vue' @@ -import { createMcpServer, clientTransport } from './mcp-servers' +import { createMcpServer, clientTransport, serverTransport } from './mcp-servers' @@ -const [serverTransport] = createMessageChannelPairTransport() provide('serverTransport', serverTransport)
template/tinyvue/src/mcp-servers/index.ts-// clientTransport 导出给 TinyRemoter 使用 -export { clientTransport } +// transports 导出给 TinyRemoter / 页面工具使用 +export { clientTransport, serverTransport }If the search finds no
inject('serverTransport')consumers, removing theprovide('serverTransport', ...)line entirely is the better cleanup.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@template/tinyvue/src/App.vue` around lines 29 - 30, The code is providing a freshly created transport via createMessageChannelPairTransport() and calling provide('serverTransport', serverTransport), but createMcpServer() connects a different transport pair, so descendants injecting 'serverTransport' receive an unconnected channel; fix by providing the already-connected server transport from createMcpServer() (or remove the provide call if no inject('serverTransport') consumers exist). Locate the provide('serverTransport', ...) call in App.vue and replace it with the connected transport returned/managed by createMcpServer() (or delete the provide line if ripgrep shows no inject('serverTransport') usage).template/tinyvue/src/mcp-servers/index.ts (1)
17-24:⚠️ Potential issue | 🟠 MajorGuard
createMcpServer()against repeated initialization.Line 24 reconnects the same
rawServer/serverTransportpair on every invocation. A remount or HMR path can turn that into duplicate registration or an unsafe secondconnect().Search the official `@opentiny/next-sdk` 0.2.7 documentation or source: is WebMcpServer.connect() safe to call multiple times on the same instance, and what happens if registerTool() is called again with the same tool name?Suggested fix
+let mcpServerReady: Promise<void> | undefined + export async function createMcpServer() { - registerLocaleManagementTools(server) - registerUserManagementTools(server) - registerRoleManagementTools(server) - registerPermissionManagementTools(server) - registerMenuManagementTools(server) - // 最后建立连接,确保所有工具已注册完毕 - await rawServer.connect(serverTransport) + if (!mcpServerReady) { + mcpServerReady = (async () => { + registerLocaleManagementTools(server) + registerUserManagementTools(server) + registerRoleManagementTools(server) + registerPermissionManagementTools(server) + registerMenuManagementTools(server) + // 最后建立连接,确保所有工具已注册完毕 + await rawServer.connect(serverTransport) + })().catch((error) => { + mcpServerReady = undefined + throw error + }) + } + + await mcpServerReady }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@template/tinyvue/src/mcp-servers/index.ts` around lines 17 - 24, createMcpServer() currently calls registration helpers and then always calls rawServer.connect(serverTransport), which can cause duplicate tool registration or unsafe reconnects on remount/HMR; guard the function so it runs initialization only once by tracking an initialized flag (e.g., module-level boolean or a property on server) and return early if already initialized, and only call rawServer.connect(serverTransport) when not connected yet; set the flag immediately after successful connect (or after all registrations) and ensure the guard references createMcpServer, rawServer.connect, and the register*ManagementTools functions to prevent repeated registration/connect.
🧹 Nitpick comments (1)
template/tinyvue/src/mcp-servers/role/tools.ts (1)
10-14: Good documentation of known limitation.The TODO comment appropriately flags the permission ID conversion issue. Users may describe permissions in natural language (e.g., "add and delete user permissions") rather than as numeric IDs. This semantic gap will need to be handled, either:
- In the LLM prompt/system instructions
- By a permission name-to-ID lookup in the handler
- By changing the schema to accept string identifiers
Consider tracking this as a follow-up issue if not addressed in this PR.
Would you like me to open an issue to track the permission name-to-ID conversion enhancement?
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@template/tinyvue/src/mcp-servers/role/tools.ts` around lines 10 - 14, The inputSchema currently requires permissions as z.array(z.number()) which doesn't handle natural-language permission names; either accept string identifiers in the schema or convert names to IDs in the handler: update inputSchema.permissions to accept z.array(z.union([z.number(), z.string()])) (or similar), implement a helper convertPermissionNamesToIds(permissionInputs) and call it from the processRoleInput (or the role handler that consumes inputSchema) to map any string names to numeric IDs using your permission lookup, and add unit tests covering numeric, string, and mixed inputs; ensure errors are thrown when unknown names are encountered.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@template/tinyvue/src/views/role/info/components/info-tab.vue`:
- Around line 226-239: The code assumes rowData exists after calling
getAllRoleDetail() and immediately accesses rowData.menus and rowData.id; add a
defensive null-check after finding rowData (the result of
data.roleInfo.items.find) and handle the missing case (e.g., return a helpful
response or throw) before calling roleTableRef.value.openMenuModal,
menuDrawerRef.value.treeRef.setChecked, or menuDrawerRef.value.onConfirm;
alternatively, call getAllRoleDetail with a larger limit or paginated fetch
until the role is found (update the call site of getAllRoleDetail and the
variable rowData) so you never dereference undefined.
---
Duplicate comments:
In `@template/tinyvue/src/App.vue`:
- Around line 29-30: The code is providing a freshly created transport via
createMessageChannelPairTransport() and calling provide('serverTransport',
serverTransport), but createMcpServer() connects a different transport pair, so
descendants injecting 'serverTransport' receive an unconnected channel; fix by
providing the already-connected server transport from createMcpServer() (or
remove the provide call if no inject('serverTransport') consumers exist). Locate
the provide('serverTransport', ...) call in App.vue and replace it with the
connected transport returned/managed by createMcpServer() (or delete the provide
line if ripgrep shows no inject('serverTransport') usage).
In `@template/tinyvue/src/mcp-servers/index.ts`:
- Around line 17-24: createMcpServer() currently calls registration helpers and
then always calls rawServer.connect(serverTransport), which can cause duplicate
tool registration or unsafe reconnects on remount/HMR; guard the function so it
runs initialization only once by tracking an initialized flag (e.g.,
module-level boolean or a property on server) and return early if already
initialized, and only call rawServer.connect(serverTransport) when not connected
yet; set the flag immediately after successful connect (or after all
registrations) and ensure the guard references createMcpServer,
rawServer.connect, and the register*ManagementTools functions to prevent
repeated registration/connect.
---
Nitpick comments:
In `@template/tinyvue/src/mcp-servers/role/tools.ts`:
- Around line 10-14: The inputSchema currently requires permissions as
z.array(z.number()) which doesn't handle natural-language permission names;
either accept string identifiers in the schema or convert names to IDs in the
handler: update inputSchema.permissions to accept z.array(z.union([z.number(),
z.string()])) (or similar), implement a helper
convertPermissionNamesToIds(permissionInputs) and call it from the
processRoleInput (or the role handler that consumes inputSchema) to map any
string names to numeric IDs using your permission lookup, and add unit tests
covering numeric, string, and mixed inputs; ensure errors are thrown when
unknown names are encountered.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 5a008282-f401-4f7b-9347-6a0c2350511c
⛔ Files ignored due to path filters (1)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (15)
template/tinyvue/package.jsontemplate/tinyvue/src/App.vuetemplate/tinyvue/src/mcp-servers/index.tstemplate/tinyvue/src/mcp-servers/locale/tools.tstemplate/tinyvue/src/mcp-servers/menu/tools.tstemplate/tinyvue/src/mcp-servers/permission/tools.tstemplate/tinyvue/src/mcp-servers/role/tools.tstemplate/tinyvue/src/mcp-servers/user/tools.tstemplate/tinyvue/src/skills/index.tstemplate/tinyvue/src/skills/tiny-pro-operator/SKILL.mdtemplate/tinyvue/src/views/locale/components/add-locale.vuetemplate/tinyvue/src/views/menu/info/components/info-tab.vuetemplate/tinyvue/src/views/permission/info/components/info-tab.vuetemplate/tinyvue/src/views/role/info/components/info-tab.vuetemplate/tinyvue/src/views/userManager/info/components/info-tab.vue
🚧 Files skipped from review as they are similar to previous changes (5)
- template/tinyvue/package.json
- template/tinyvue/src/mcp-servers/menu/tools.ts
- template/tinyvue/src/mcp-servers/user/tools.ts
- template/tinyvue/src/skills/tiny-pro-operator/SKILL.md
- template/tinyvue/src/skills/index.ts
3ff1e3d to
25dc53c
Compare
There was a problem hiding this comment.
Actionable comments posted: 4
🧹 Nitpick comments (4)
template/tinyvue/src/mcp-servers/role/tools.ts (1)
10-14: Address TODO: Permission format conversion is unimplemented.The comment indicates users may specify permissions in natural language (e.g., "添加用户的权限") rather than numeric IDs. The current schema expects
z.array(z.number()), so the AI or an intermediate layer must handle this conversion.Consider one of:
- Accept string identifiers (e.g.,
user::add) and resolve them server-side.- Document the expected format clearly in the description so the AI can prompt for or infer IDs.
- Add a mapping layer before invoking the tool.
Would you like me to open an issue to track this conversion requirement, or help draft a solution?
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@template/tinyvue/src/mcp-servers/role/tools.ts` around lines 10 - 14, The inputSchema currently requires permissions as z.array(z.number()) but the TODO notes users may supply natural-language or string identifiers; update the schema and add a normalization layer: change inputSchema.permissions to accept numbers or strings (e.g., z.array(z.union([z.number(), z.string()]))) and implement a normalizePermissions function (or PERMISSION_MAP lookup) in the same module that converts user-facing strings (natural language or token like "user::add") to numeric IDs before the tool uses them; call this normalizer where the tool processes incoming input (refer to inputSchema.permissions and add/introduce normalizePermissions or PERMISSION_MAP in tools.ts) and ensure unknown strings produce a clear validation error.template/tinyvue/package.json (1)
32-33: Pinned dependencies are set to the latest available versions.The exact versions pinned (
0.2.7for both packages) are confirmed to exist on npm and are currently the latest available. This locks the dependencies to stable releases, which ensures consistent behavior but requires manual updates for future releases.If automatic compatibility updates are desired, consider using caret ranges (
^0.2.7) instead to receive patch and minor version updates while the major version remains fixed.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@template/tinyvue/package.json` around lines 32 - 33, The package.json currently pins "@opentiny/next-remoter" and "@opentiny/next-sdk" to exact versions "0.2.7"; update those dependency entries to use caret ranges ("^0.2.7") so the project accepts compatible minor/patch updates automatically (change the values for "@opentiny/next-remoter" and "@opentiny/next-sdk" to "^0.2.7").template/tinyvue/src/views/role/info/components/info-tab.vue (1)
213-224: Consider replacing hardcodedsleep()with event-driven synchronization.The
sleep(1000)calls assume UI operations complete within 1 second. On slower networks or devices, this may cause race conditions where the form isn't ready or the confirm action fires prematurely.If this is for demo purposes only, the current approach is acceptable. For production use, consider waiting for specific DOM states or emitting/awaiting component-ready events.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@template/tinyvue/src/views/role/info/components/info-tab.vue` around lines 213 - 224, The handler for 'add-role' currently uses fixed sleep(1000) delays around onAdd() and before calling addRoleFormRef.value.onConfirm(), which can race on slow devices; replace these sleeps with event-driven waits: have onAdd() open the form/modal and emit or return a Promise when the form is fully initialized (e.g., an "opened" or "ready" event), then await that Promise or wait for addRoleFormRef.value to be non-null and for a readiness flag/method (e.g., addRoleFormRef.value.ready or an emitted "ready" event) before calling setRoleInfo and before calling onConfirm; update the add-role handler to await those specific readiness signals instead of sleep to ensure deterministic sequencing for setRoleInfo and onConfirm.template/tinyvue/src/App.vue (1)
38-39: Make local MCP bootstrap idempotent.
createMcpServer()re-registers every tool and reconnects the same module-level server on each call. Calling it from theonMounted()lifecycle hook inApp.vuemakes remount and HMR scenarios unnecessarily risky; caching the first init promise intemplate/tinyvue/src/mcp-servers/index.tswould make this setup much safer.♻️ Proposed fix in
template/tinyvue/src/mcp-servers/index.ts+let initPromise: Promise<void> | null = null + export async function createMcpServer() { - registerLocaleManagementTools(server) - registerUserManagementTools(server) - registerRoleManagementTools(server) - registerPermissionManagementTools(server) - registerMenuManagementTools(server) - // 最后建立连接,确保所有工具已注册完毕 - await rawServer.connect(serverTransport) + if (!initPromise) { + initPromise = (async () => { + registerLocaleManagementTools(server) + registerUserManagementTools(server) + registerRoleManagementTools(server) + registerPermissionManagementTools(server) + registerMenuManagementTools(server) + // 最后建立连接,确保所有工具已注册完毕 + await rawServer.connect(serverTransport) + })() + } + + return initPromise }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@template/tinyvue/src/App.vue` around lines 38 - 39, createMcpServer() is currently non-idempotent and is called onMounted in App.vue, causing duplicate tool registration and reconnects on remount/HMR; make the initialization idempotent by adding a module-level cache in template/tinyvue/src/mcp-servers/index.ts (e.g., let initPromise: Promise<void> | null) and modify the exported createMcpServer function to return the cached promise if present, otherwise set initPromise to the actual async init logic, perform registrations/connect once, and return it; also ensure any teardown or error resets initPromise so failed inits can retry.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@template/tinyvue/src/App.vue`:
- Around line 37-39: The TinyRemoter component is rendered before
createMcpServer() completes in onMounted(), exposing it to an uninitialized
server; modify the component to track a ready state (e.g., mcpReady) updated
after await createMcpServer() (and set false on error) and conditionally
render/gate TinyRemoter based on that flag, handling bootstrap errors by not
mounting TinyRemoter when createMcpServer() throws; refer to createMcpServer,
onMounted and TinyRemoter to locate where to add the boolean state and
conditional rendering.
In `@template/tinyvue/src/views/locale/components/add-locale.vue`:
- Around line 141-150: The handler 'add-i18n-entry' returns success before the
async work completes because addLocale() does not return its internal promise
and the handler uses fixed sleep() waits; change addLocale to return the promise
from its internal chain (including the createLocalItem call) so callers can
await its completion and errors, and remove brittle sleep(1000) usages in both
addLocale and the handler—use proper awaits (e.g., await the mutation promise or
Vue's nextTick / an explicit resolved state) and then in the handler await
addLocale(), handle errors with try/catch and only return the success response
after the awaited addLocale() resolves or propagate the error; reference
functions/vars: addLocale, createLocalItem, onOpen, locale, and the
'add-i18n-entry' handler.
In `@template/tinyvue/src/views/menu/info/components/info-tab.vue`:
- Around line 261-276: The handler for 'add-menu' (the async arrow function that
calls handleAddMenu, sleep, getIdByLabel, addMenu.value.setMenuInfo, and
onClickAdd) can dereference addMenu.value before the component is mounted;
replace the fixed sleep with a safe wait: after calling handleAddMenu() await
nextTick() (or loop with small sleeps and a timeout) to ensure addMenu.value is
non-null, then proceed to call addMenu.value.setMenuInfo({ name, order,
parentId, icon, component, menuType: '/', path, locale: menuLocale }); if
addMenu.value remains null after the timeout, log or return early instead of
calling setMenuInfo. Ensure references to handleAddMenu, getIdByLabel,
addMenu.value.setMenuInfo, and onClickAdd are preserved.
In `@template/tinyvue/src/views/permission/info/components/info-tab.vue`:
- Around line 205-212: The 'add-permission' tool handler calls
handlePermissionAddSubmit() without awaiting it and returns success immediately;
update the handler to await handlePermissionAddSubmit() and only return the
success message after the awaited call completes, ensure you still call
handleAddPermission() and set state.permissionAddData.name/desc before awaiting;
additionally modify the flow to catch and rethrow or return an error when
handlePermissionAddSubmit() fails (and remove or surface the empty catch inside
handlePermissionAddSubmit if present) so validation or API errors are propagated
back to the tool caller instead of silently returning "收到: ${name}".
---
Nitpick comments:
In `@template/tinyvue/package.json`:
- Around line 32-33: The package.json currently pins "@opentiny/next-remoter"
and "@opentiny/next-sdk" to exact versions "0.2.7"; update those dependency
entries to use caret ranges ("^0.2.7") so the project accepts compatible
minor/patch updates automatically (change the values for
"@opentiny/next-remoter" and "@opentiny/next-sdk" to "^0.2.7").
In `@template/tinyvue/src/App.vue`:
- Around line 38-39: createMcpServer() is currently non-idempotent and is called
onMounted in App.vue, causing duplicate tool registration and reconnects on
remount/HMR; make the initialization idempotent by adding a module-level cache
in template/tinyvue/src/mcp-servers/index.ts (e.g., let initPromise:
Promise<void> | null) and modify the exported createMcpServer function to return
the cached promise if present, otherwise set initPromise to the actual async
init logic, perform registrations/connect once, and return it; also ensure any
teardown or error resets initPromise so failed inits can retry.
In `@template/tinyvue/src/mcp-servers/role/tools.ts`:
- Around line 10-14: The inputSchema currently requires permissions as
z.array(z.number()) but the TODO notes users may supply natural-language or
string identifiers; update the schema and add a normalization layer: change
inputSchema.permissions to accept numbers or strings (e.g.,
z.array(z.union([z.number(), z.string()]))) and implement a normalizePermissions
function (or PERMISSION_MAP lookup) in the same module that converts user-facing
strings (natural language or token like "user::add") to numeric IDs before the
tool uses them; call this normalizer where the tool processes incoming input
(refer to inputSchema.permissions and add/introduce normalizePermissions or
PERMISSION_MAP in tools.ts) and ensure unknown strings produce a clear
validation error.
In `@template/tinyvue/src/views/role/info/components/info-tab.vue`:
- Around line 213-224: The handler for 'add-role' currently uses fixed
sleep(1000) delays around onAdd() and before calling
addRoleFormRef.value.onConfirm(), which can race on slow devices; replace these
sleeps with event-driven waits: have onAdd() open the form/modal and emit or
return a Promise when the form is fully initialized (e.g., an "opened" or
"ready" event), then await that Promise or wait for addRoleFormRef.value to be
non-null and for a readiness flag/method (e.g., addRoleFormRef.value.ready or an
emitted "ready" event) before calling setRoleInfo and before calling onConfirm;
update the add-role handler to await those specific readiness signals instead of
sleep to ensure deterministic sequencing for setRoleInfo and onConfirm.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 1548403f-faf9-44bb-9015-b0a9cb7b9e14
⛔ Files ignored due to path filters (1)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (15)
template/tinyvue/package.jsontemplate/tinyvue/src/App.vuetemplate/tinyvue/src/mcp-servers/index.tstemplate/tinyvue/src/mcp-servers/locale/tools.tstemplate/tinyvue/src/mcp-servers/menu/tools.tstemplate/tinyvue/src/mcp-servers/permission/tools.tstemplate/tinyvue/src/mcp-servers/role/tools.tstemplate/tinyvue/src/mcp-servers/user/tools.tstemplate/tinyvue/src/skills/index.tstemplate/tinyvue/src/skills/tiny-pro-operator/SKILL.mdtemplate/tinyvue/src/views/locale/components/add-locale.vuetemplate/tinyvue/src/views/menu/info/components/info-tab.vuetemplate/tinyvue/src/views/permission/info/components/info-tab.vuetemplate/tinyvue/src/views/role/info/components/info-tab.vuetemplate/tinyvue/src/views/userManager/info/components/info-tab.vue
🚧 Files skipped from review as they are similar to previous changes (6)
- template/tinyvue/src/mcp-servers/locale/tools.ts
- template/tinyvue/src/skills/index.ts
- template/tinyvue/src/skills/tiny-pro-operator/SKILL.md
- template/tinyvue/src/mcp-servers/permission/tools.ts
- template/tinyvue/src/views/userManager/info/components/info-tab.vue
- template/tinyvue/src/mcp-servers/index.ts
| // 启动 MCP Server(注册工具 + 建立通信通道) | ||
| onMounted(async () => { | ||
| const server = new WebMcpServer() | ||
| const $router = useRouter() | ||
| const $route = useRoute() | ||
|
|
||
| // TODO: 参数需要优化,用户不会知道具体的路由路径,用户的语言可能是:帮我打开菜单管理页面,这时应该根据名称获取路由路径,再做路由跳转 | ||
| // 进一步优化:用户可能在任意页面直接提需求:帮我创建 xx 菜单,这时 AI 应该先跳转菜单管理页面,然后调佣创建菜单的工具 | ||
| server.registerTool( | ||
| 'switch-router', | ||
| { | ||
| title: '切换路由', | ||
| description: '切换路由', | ||
| inputSchema: { | ||
| routerPath: z.string().describe('路由路径'), | ||
| }, | ||
| }, | ||
| async ({ routerPath }) => { | ||
| if ($route.path === routerPath) { | ||
| return { content: [{ type: 'text', text: routerPath }] } | ||
| } | ||
|
|
||
| $router.push(import.meta.env.VITE_CONTEXT + routerPath) | ||
| return { content: [{ type: 'text', text: routerPath }] } | ||
| }, | ||
| ) | ||
| await server.connect(serverTransport) | ||
|
|
||
| // 创建 WebMcpClient ,并与 WebAgent 连接 | ||
| const client = new WebMcpClient() | ||
| await client.connect(clientTransport) | ||
| const { sessionId: sessionID } = await client.connect({ | ||
| agent: true, | ||
|
|
||
| // sessionId 为可选参数。若传入该参数,系统将使用指定值作为会话标识;若未传入,WebAgent 服务将自动生成一个随机的字符串作为 sessionId。为便于通过 MCP Inspector 工具进行调试,此处采用了固定的 sessionId。用户亦可通过浏览器原生提供的 crypto.randomUUID() 方法生成随机字符串作为会话标识。 | ||
| sessionId: 'd299a869-c674-4125-a84b-bb4e24079b99', | ||
|
|
||
| url: `${AGENT_URL}mcp`, | ||
| }) | ||
| sessionId.value = sessionID | ||
| await createMcpServer() |
There was a problem hiding this comment.
🧩 Analysis chain
🌐 Web query:
In Vue 3 Composition API, are child components mounted before the parent component's onMounted()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: mounted semantics. [2] Vue Guide: onMounted runs after initial render + DOM creation. [3] Vue Options API lifecycle: serverPrefetch is awaited by the server renderer.
Gate TinyRemoter on successful MCP startup.
createMcpServer() is async and runs in onMounted(), but TinyRemoter is rendered unconditionally during initial render—which happens before onMounted runs. If bootstrap is still in flight or throws, the remoter is exposed to an uninitialized local server with an unhandled error.
🐛 Proposed fix
-import { onMounted } from 'vue'
+import { onMounted, ref } from 'vue'
@@
const mcpServers = {
'my-mcp-server': {
type: 'local',
transport: clientTransport,
},
}
+const mcpReady = ref(false)
+
// 启动 MCP Server(注册工具 + 建立通信通道)
onMounted(async () => {
- await createMcpServer()
+ try {
+ await createMcpServer()
+ mcpReady.value = true
+ } catch (error) {
+ console.error('Failed to start local MCP server', error)
+ }
})
@@
<TinyRemoter
+ v-if="mcpReady"
:skills="skills"
:mcp-servers="mcpServers"
/>📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| // 启动 MCP Server(注册工具 + 建立通信通道) | |
| onMounted(async () => { | |
| const server = new WebMcpServer() | |
| const $router = useRouter() | |
| const $route = useRoute() | |
| // TODO: 参数需要优化,用户不会知道具体的路由路径,用户的语言可能是:帮我打开菜单管理页面,这时应该根据名称获取路由路径,再做路由跳转 | |
| // 进一步优化:用户可能在任意页面直接提需求:帮我创建 xx 菜单,这时 AI 应该先跳转菜单管理页面,然后调佣创建菜单的工具 | |
| server.registerTool( | |
| 'switch-router', | |
| { | |
| title: '切换路由', | |
| description: '切换路由', | |
| inputSchema: { | |
| routerPath: z.string().describe('路由路径'), | |
| }, | |
| }, | |
| async ({ routerPath }) => { | |
| if ($route.path === routerPath) { | |
| return { content: [{ type: 'text', text: routerPath }] } | |
| } | |
| $router.push(import.meta.env.VITE_CONTEXT + routerPath) | |
| return { content: [{ type: 'text', text: routerPath }] } | |
| }, | |
| ) | |
| await server.connect(serverTransport) | |
| // 创建 WebMcpClient ,并与 WebAgent 连接 | |
| const client = new WebMcpClient() | |
| await client.connect(clientTransport) | |
| const { sessionId: sessionID } = await client.connect({ | |
| agent: true, | |
| // sessionId 为可选参数。若传入该参数,系统将使用指定值作为会话标识;若未传入,WebAgent 服务将自动生成一个随机的字符串作为 sessionId。为便于通过 MCP Inspector 工具进行调试,此处采用了固定的 sessionId。用户亦可通过浏览器原生提供的 crypto.randomUUID() 方法生成随机字符串作为会话标识。 | |
| sessionId: 'd299a869-c674-4125-a84b-bb4e24079b99', | |
| url: `${AGENT_URL}mcp`, | |
| }) | |
| sessionId.value = sessionID | |
| await createMcpServer() | |
| // 启动 MCP Server(注册工具 + 建立通信通道) | |
| onMounted(async () => { | |
| try { | |
| await createMcpServer() | |
| mcpReady.value = true | |
| } catch (error) { | |
| console.error('Failed to start local MCP server', error) | |
| } | |
| }) |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@template/tinyvue/src/App.vue` around lines 37 - 39, The TinyRemoter component
is rendered before createMcpServer() completes in onMounted(), exposing it to an
uninitialized server; modify the component to track a ready state (e.g.,
mcpReady) updated after await createMcpServer() (and set false on error) and
conditionally render/gate TinyRemoter based on that flag, handling bootstrap
errors by not mounting TinyRemoter when createMcpServer() throws; refer to
createMcpServer, onMounted and TinyRemoter to locate where to add the boolean
state and conditional rendering.
| 'add-i18n-entry': async ({ key, content, lang: langId }) => { | ||
| onOpen() | ||
| await sleep(1000) | ||
| locale.key = key | ||
| locale.content = content | ||
| locale.lang = langId | ||
| await sleep(1000) | ||
| addLocale() | ||
| return { content: [{ type: 'text', text: `收到: ${key}` }] } | ||
| }, |
There was a problem hiding this comment.
Handler returns success before operation completes.
The addLocale() function (lines 101-126) does not return a promise—it initiates an internal promise chain but returns undefined. This means:
- The
awaiton line 148 resolves immediately - The success response is returned before knowing if
createLocalItemsucceeded - Errors from the API call won't propagate to the MCP tool caller
Additionally, the sleep(1000) calls are fragile—they assume UI state settles within a fixed time rather than awaiting actual state changes.
🔧 Proposed fix: refactor addLocale to return a promise
First, modify addLocale to return the promise chain (around line 101):
-function addLocale() {
- localeForm.value
+async function addLocale() {
+ await localeForm.value
.validate()
- .then(() => {
- createLocalItem(locale)
- .then(({ data }) => {
+ try {
+ const { data } = await createLocalItem(locale)
locale.key = ''
locale.content = ''
locale.lang = '' as any
locales.pushLocale(data)
i18.mergeLocaleMessage(data.lang.name, {
[data.key]: data.content,
})
emits('localChange')
- })
- .catch((reason) => {
- Notify({
- type: 'error',
- message: reason.response.data.message,
- })
- })
- .finally(() => {
- onClose()
- })
- })
+ } catch (reason: any) {
+ Notify({
+ type: 'error',
+ message: reason.response?.data?.message ?? 'Failed to add locale',
+ })
+ throw reason
+ } finally {
+ onClose()
+ }
}Then update the handler to catch errors:
'add-i18n-entry': async ({ key, content, lang: langId }) => {
onOpen()
await sleep(1000)
locale.key = key
locale.content = content
locale.lang = langId
await sleep(1000)
- addLocale()
- return { content: [{ type: 'text', text: `收到: ${key}` }] }
+ try {
+ await addLocale()
+ return { content: [{ type: 'text', text: `添加成功: ${key}` }] }
+ } catch (e) {
+ return { content: [{ type: 'text', text: `添加失败: ${key}` }], isError: true }
+ }
},🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@template/tinyvue/src/views/locale/components/add-locale.vue` around lines 141
- 150, The handler 'add-i18n-entry' returns success before the async work
completes because addLocale() does not return its internal promise and the
handler uses fixed sleep() waits; change addLocale to return the promise from
its internal chain (including the createLocalItem call) so callers can await its
completion and errors, and remove brittle sleep(1000) usages in both addLocale
and the handler—use proper awaits (e.g., await the mutation promise or Vue's
nextTick / an explicit resolved state) and then in the handler await
addLocale(), handle errors with try/catch and only return the success response
after the awaited addLocale() resolves or propagate the error; reference
functions/vars: addLocale, createLocalItem, onOpen, locale, and the
'add-i18n-entry' handler.
| 'add-menu': async ({ name, order, parentMenu, icon, component, path, locale: menuLocale }) => { | ||
| handleAddMenu() | ||
| await sleep(1000) | ||
| const parentId = getIdByLabel(i18nMenuData.value, parentMenu) | ||
| addMenu.value.setMenuInfo({ | ||
| name, | ||
| order, | ||
| parentId, | ||
| icon, | ||
| component, | ||
| menuType: '/', | ||
| path, | ||
| locale: menuLocale, | ||
| }) | ||
| await sleep(1000) | ||
| onClickAdd() |
There was a problem hiding this comment.
Potential null reference when accessing addMenu.value.
After handleAddMenu() opens the modal and sleep(1000) completes, addMenu.value is accessed without a null check. While Vue should have rendered the component by then, timing is not guaranteed—especially under heavy load or slower devices.
Consider adding a defensive check or using nextTick to ensure the component is mounted:
🛡️ Proposed defensive check
'add-menu': async ({ name, order, parentMenu, icon, component, path, locale: menuLocale }) => {
handleAddMenu()
await sleep(1000)
+ if (!addMenu.value) {
+ return { content: [{ type: 'text', text: 'Error: Menu form not ready' }] }
+ }
const parentId = getIdByLabel(i18nMenuData.value, parentMenu)
addMenu.value.setMenuInfo({📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| 'add-menu': async ({ name, order, parentMenu, icon, component, path, locale: menuLocale }) => { | |
| handleAddMenu() | |
| await sleep(1000) | |
| const parentId = getIdByLabel(i18nMenuData.value, parentMenu) | |
| addMenu.value.setMenuInfo({ | |
| name, | |
| order, | |
| parentId, | |
| icon, | |
| component, | |
| menuType: '/', | |
| path, | |
| locale: menuLocale, | |
| }) | |
| await sleep(1000) | |
| onClickAdd() | |
| 'add-menu': async ({ name, order, parentMenu, icon, component, path, locale: menuLocale }) => { | |
| handleAddMenu() | |
| await sleep(1000) | |
| if (!addMenu.value) { | |
| return { content: [{ type: 'text', text: 'Error: Menu form not ready' }] } | |
| } | |
| const parentId = getIdByLabel(i18nMenuData.value, parentMenu) | |
| addMenu.value.setMenuInfo({ | |
| name, | |
| order, | |
| parentId, | |
| icon, | |
| component, | |
| menuType: '/', | |
| path, | |
| locale: menuLocale, | |
| }) | |
| await sleep(1000) | |
| onClickAdd() |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@template/tinyvue/src/views/menu/info/components/info-tab.vue` around lines
261 - 276, The handler for 'add-menu' (the async arrow function that calls
handleAddMenu, sleep, getIdByLabel, addMenu.value.setMenuInfo, and onClickAdd)
can dereference addMenu.value before the component is mounted; replace the fixed
sleep with a safe wait: after calling handleAddMenu() await nextTick() (or loop
with small sleeps and a timeout) to ensure addMenu.value is non-null, then
proceed to call addMenu.value.setMenuInfo({ name, order, parentId, icon,
component, menuType: '/', path, locale: menuLocale }); if addMenu.value remains
null after the timeout, log or return early instead of calling setMenuInfo.
Ensure references to handleAddMenu, getIdByLabel, addMenu.value.setMenuInfo, and
onClickAdd are preserved.
| 'add-permission': async ({ name, desc }) => { | ||
| handleAddPermission() | ||
| await sleep(1000) | ||
| state.permissionAddData.name = name | ||
| state.permissionAddData.desc = desc | ||
| await sleep(1000) | ||
| handlePermissionAddSubmit() | ||
| return { content: [{ type: 'text', text: `收到: ${name}` }] } |
There was a problem hiding this comment.
Missing await on async submission causes premature return.
handlePermissionAddSubmit() is an async function that performs the API call to create the permission. Without await, the tool returns "收到: ${name}" before the permission is actually created, creating a race condition where the caller may assume success prematurely.
Additionally, if form validation fails (line 191's empty catch), the tool still returns a success message without any indication of failure.
🐛 Proposed fix
'add-permission': async ({ name, desc }) => {
handleAddPermission()
await sleep(1000)
state.permissionAddData.name = name
state.permissionAddData.desc = desc
await sleep(1000)
- handlePermissionAddSubmit()
- return { content: [{ type: 'text', text: `收到: ${name}` }] }
+ await handlePermissionAddSubmit()
+ return { content: [{ type: 'text', text: `权限已添加: ${name}` }] }
},Note: Consider also adding error handling to propagate submission failures back to the tool caller, since the current
catch(() => { })inhandlePermissionAddSubmitsilently swallows validation errors.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| 'add-permission': async ({ name, desc }) => { | |
| handleAddPermission() | |
| await sleep(1000) | |
| state.permissionAddData.name = name | |
| state.permissionAddData.desc = desc | |
| await sleep(1000) | |
| handlePermissionAddSubmit() | |
| return { content: [{ type: 'text', text: `收到: ${name}` }] } | |
| 'add-permission': async ({ name, desc }) => { | |
| handleAddPermission() | |
| await sleep(1000) | |
| state.permissionAddData.name = name | |
| state.permissionAddData.desc = desc | |
| await sleep(1000) | |
| await handlePermissionAddSubmit() | |
| return { content: [{ type: 'text', text: `权限已添加: ${name}` }] } | |
| }, |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@template/tinyvue/src/views/permission/info/components/info-tab.vue` around
lines 205 - 212, The 'add-permission' tool handler calls
handlePermissionAddSubmit() without awaiting it and returns success immediately;
update the handler to await handlePermissionAddSubmit() and only return the
success message after the awaited call completes, ensure you still call
handleAddPermission() and set state.permissionAddData.name/desc before awaiting;
additionally modify the flow to catch and rethrow or return an error when
handlePermissionAddSubmit() fails (and remove or surface the empty catch inside
handlePermissionAddSubmit if present) so validation or API errors are propagated
back to the tool caller instead of silently returning "收到: ${name}".
PR
演示动画:
演示动画的提示词:
PR Checklist
Please check if your PR fulfills the following requirements:
PR Type
What kind of change does this PR introduce?
What is the current behavior?
Issue Number: N/A
What is the new behavior?
Does this PR introduce a breaking change?
Other information
Summary by CodeRabbit
New Features
Documentation
Chores