-
Notifications
You must be signed in to change notification settings - Fork 18
feat: add @opentiny/next-sdk #165
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
base: dev
Are you sure you want to change the base?
Conversation
WalkthroughThis change integrates the Model Context Protocol (MCP) framework into a TinyVue template by adding MCP dependencies, establishing a client-server transport mechanism in the main App component, and registering MCP server tools in specific feature components (locale and menu management) to enable remote command execution. Changes
Sequence Diagram(s)sequenceDiagram
actor Client
participant App as App.vue
participant Transport as Message Channel<br/>Transport
participant MCP as WebMcpClient
participant Tools as Tool Components<br/>(locale, menu)
participant Server as WebMcpServer<br/>per Component
Client->>App: Mount
App->>Transport: createMessageChannelPairTransport()
App->>MCP: new WebMcpClient(clientTransport)
App->>MCP: client.connect({agent: true, sessionId, url})
MCP-->>App: return sessionId
App->>+Tools: provide(serverTransport)
Tools->>Server: onMounted() creates WebMcpServer
Tools->>Server: registerTool("add-menu" | "add-i18n-entry", inputSchema, handler)
Tools->>Server: server.connect(serverTransport)
deactivate Tools
rect rgba(200, 220, 255, 0.3)
note over Client,Server: MCP Tool Execution Flow
Client->>MCP: Call remote tool
MCP->>Transport: Send command
Transport->>Server: Route to server
Server->>Server: Execute tool handler
Server-->>Transport: Return result
Transport-->>MCP: Receive result
MCP-->>Client: Tool response
end
Estimated Code Review Effort🎯 3 (Moderate) | ⏱️ ~25 minutes Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✨ 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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 6
🧹 Nitpick comments (5)
template/tinyvue/src/assets/style/global.less (1)
43-45: Consider whether!importantis necessary.The high z-index with
!importantensures.tr-containerappears on top, which is appropriate for overlays or agent UI components. However,!importantcan make future CSS adjustments more difficult. If the specificity can be managed through other means (e.g., more specific selectors or stacking context), that would be more maintainable.template/tinyvue/src/utils/base-utils.ts (1)
12-14: LGTM! Simple and effective delay utility.The sleep utility is correctly implemented and will be useful for orchestrating asynchronous MCP workflows. The function is imported and used in template/tinyvue/src/views/locale/components/add-locale.vue and template/tinyvue/src/views/menu/info/components/info-tab.vue.
Note: Consider adding a semicolon at the end of line 13 for consistency with the file's style (line 9 has a semicolon).
Optional: Add semicolon for consistency
export const sleep = (ms: number): Promise<void> => { - return new Promise((resolve) => setTimeout(resolve, ms)) + return new Promise((resolve) => setTimeout(resolve, ms)); }template/tinyvue/src/views/menu/info/components/add-menu.vue (1)
174-183: Consider simplifying property assignment.The new setMenuInfo method assigns data fields to the reactive menuInfo object. The current implementation works correctly but is verbose. Consider using
Object.assignfor a more concise approach.Simplified implementation
setMenuInfo: (data: Omit<CreateMenuDto, 'id'>) => { - menuInfo.name = data.name; - menuInfo.path = data.path; - menuInfo.component = data.component; - menuInfo.icon = data.icon; - menuInfo.menuType = data.menuType; - menuInfo.parentId = data.parentId; - menuInfo.order = data.order; - menuInfo.locale = data.locale; + Object.assign(menuInfo, data); },template/tinyvue/src/views/locale/components/add-locale.vue (1)
237-237: Consider using an enum for language IDs.Line 237 defines the language ID as a union of literals (
z.literal(1)andz.literal(2)). While functional, this approach makes the code less maintainable. Consider defining a language enum to improve readability and type safety.Enum-based approach
// Add at the top of the file or in a shared types file enum LanguageId { EN_US = 1, ZH_CN = 2 } // Then use in schema: lang: z.nativeEnum(LanguageId).describe('词条语言ID,英文 enUS 为:1,中文 zhCN 为:2'),This makes the intent clearer and provides better IntelliSense support.
package.json (1)
34-38: Consider upgrading to the latest @modelcontextprotocol/sdk version or document the pinning rationale.Version 1.16.0 exists on npm with no reported direct security vulnerabilities. However, it is significantly outdated—the latest stable release is 1.25.0 (Dec 15, 2025). If this older version is intentionally pinned for compatibility or other reasons, add a comment explaining the constraint. Additionally, run
npm auditto check for vulnerabilities in transitive dependencies.
📜 Review details
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (8)
package.jsontemplate/tinyvue/package.jsontemplate/tinyvue/src/App.vuetemplate/tinyvue/src/assets/style/global.lesstemplate/tinyvue/src/utils/base-utils.tstemplate/tinyvue/src/views/locale/components/add-locale.vuetemplate/tinyvue/src/views/menu/info/components/add-menu.vuetemplate/tinyvue/src/views/menu/info/components/info-tab.vue
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: e2e-test
🔇 Additional comments (2)
template/tinyvue/src/assets/style/global.less (1)
41-41: LGTM! Syntax fix applied correctly.The missing closing brace for the
.tiny-buttonblock has been properly added.template/tinyvue/src/App.vue (1)
9-26: LGTM! TinyRemoter configured appropriately.The TinyRemoter component is properly configured with the agent root URL and menu item visibility controls. The reactive sessionId binding will enable dynamic session tracking.
| onMounted(async () => { | ||
| const [serverTransport, clientTransport] = createMessageChannelPairTransport() | ||
| provide('serverTransport', 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: 'https://agent.opentiny.design/api/v1/webmcp-trial/mcp' // http://localhost:3005/api/v1/webmcp/mcp | ||
| }) | ||
| sessionId.value = sessionID | ||
| console.log('sessionId:', sessionId.value) | ||
| }) |
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.
Add error handling and reconsider hard-coded sessionId.
The MCP client initialization has two concerns:
-
Hard-coded sessionId on line 68 defeats the purpose of session management. The comment suggests this is for debugging with MCP Inspector, but production code should use dynamic session IDs. Consider using
crypto.randomUUID()or making the fixed ID conditional based on an environment variable. -
Missing error handling: The async operations in
onMountedlack try-catch blocks. Ifclient.connect()fails, the error will be unhandled andsessionId.valuewill remain empty, potentially breaking the TinyRemoter integration.
Proposed fix with error handling and environment-based sessionId
const sessionId = ref('')
onMounted(async () => {
- const [serverTransport, clientTransport] = createMessageChannelPairTransport()
-
- provide('serverTransport', 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: 'https://agent.opentiny.design/api/v1/webmcp-trial/mcp' // http://localhost:3005/api/v1/webmcp/mcp
- })
- sessionId.value = sessionID
- console.log('sessionId:', sessionId.value)
+ try {
+ const [serverTransport, clientTransport] = createMessageChannelPairTransport()
+
+ provide('serverTransport', serverTransport)
+
+ const client = new WebMcpClient()
+ await client.connect(clientTransport)
+
+ // Use fixed sessionId only in development for debugging
+ const fixedSessionId = import.meta.env.DEV
+ ? 'd299a869-c674-4125-a84b-bb4e24079b99'
+ : crypto.randomUUID()
+
+ const { sessionId: sessionID } = await client.connect({
+ agent: true,
+ sessionId: fixedSessionId,
+ url: 'https://agent.opentiny.design/api/v1/webmcp-trial/mcp'
+ })
+ sessionId.value = sessionID
+ console.log('sessionId:', sessionId.value)
+ } catch (error) {
+ console.error('Failed to initialize MCP client:', error)
+ // Consider notifying the user or disabling MCP features
+ }
})📝 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.
| onMounted(async () => { | |
| const [serverTransport, clientTransport] = createMessageChannelPairTransport() | |
| provide('serverTransport', 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: 'https://agent.opentiny.design/api/v1/webmcp-trial/mcp' // http://localhost:3005/api/v1/webmcp/mcp | |
| }) | |
| sessionId.value = sessionID | |
| console.log('sessionId:', sessionId.value) | |
| }) | |
| onMounted(async () => { | |
| try { | |
| const [serverTransport, clientTransport] = createMessageChannelPairTransport() | |
| provide('serverTransport', serverTransport) | |
| const client = new WebMcpClient() | |
| await client.connect(clientTransport) | |
| // Use fixed sessionId only in development for debugging | |
| const fixedSessionId = import.meta.env.DEV | |
| ? 'd299a869-c674-4125-a84b-bb4e24079b99' | |
| : crypto.randomUUID() | |
| const { sessionId: sessionID } = await client.connect({ | |
| agent: true, | |
| sessionId: fixedSessionId, | |
| url: 'https://agent.opentiny.design/api/v1/webmcp-trial/mcp' | |
| }) | |
| sessionId.value = sessionID | |
| console.log('sessionId:', sessionId.value) | |
| } catch (error) { | |
| console.error('Failed to initialize MCP client:', error) | |
| // Consider notifying the user or disabling MCP features | |
| } | |
| }) |
🤖 Prompt for AI Agents
In template/tinyvue/src/App.vue around lines 56 to 74, the onMounted block both
hard-codes a sessionId and lacks error handling; wrap the async client
creation/connect calls in a try-catch, log or surface errors, and ensure the
UI/state is updated only on success (e.g., set sessionId.value inside the try
and set an error state in the catch). Replace the fixed sessionId with a dynamic
value by default (use crypto.randomUUID()) and make the fixed ID conditional
(e.g., only use the hard-coded value when a debug flag or environment variable
is present) so production uses a generated UUID.
| onMounted(async () => { | ||
| const server = new WebMcpServer({ | ||
| name: 'i18n-management-mcp-server', | ||
| version: '1.0.0' | ||
| }) | ||
| const serverTransport = inject<any>('serverTransport') | ||
| 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'), | ||
| } | ||
| }, | ||
| async ({ key, content, lang }) => { | ||
| onOpen() | ||
| await sleep(1000) | ||
| locale.key = key | ||
| locale.content = content | ||
| locale.lang = lang | ||
| await sleep(1000) | ||
| addLocale() | ||
| return { content: [{ type: 'text', text: `收到: ${key}` }] } | ||
| } | ||
| ) | ||
| await server.connect(serverTransport) | ||
| }); |
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
🏁 Script executed:
# Check if Transport type is exported from @opentiny/next-sdk
rg -nP "export.*Transport" node_modules/@opentiny/next-sdk -A 2Repository: opentiny/tiny-pro
Length of output: 132
🏁 Script executed:
# Also check the actual file to verify it exists and code matches
if [ -f "template/tinyvue/src/views/locale/components/add-locale.vue" ]; then
echo "=== File exists. Checking lines 222-253 ==="
sed -n '222,253p' "template/tinyvue/src/views/locale/components/add-locale.vue"
else
echo "File not found at template/tinyvue/src/views/locale/components/add-locale.vue"
# Try to find it
find . -name "add-locale.vue" -type f 2>/dev/null
fiRepository: opentiny/tiny-pro
Length of output: 994
🏁 Script executed:
# Check if info-tab.vue exists and has similar MCP server setup pattern
find . -name "info-tab.vue" -type f 2>/dev/null | head -5Repository: opentiny/tiny-pro
Length of output: 388
🏁 Script executed:
# Check if @opentiny/next-sdk is in package.json
cat package.json | grep -A 5 -B 5 "@opentiny/next-sdk"Repository: opentiny/tiny-pro
Length of output: 43
🏁 Script executed:
# Check one info-tab.vue file to see similar MCP server pattern
head -n 400 "./template/tinyvue/src/views/menu/info/components/info-tab.vue" | grep -A 50 "onMounted"Repository: opentiny/tiny-pro
Length of output: 3091
🏁 Script executed:
# Check package.json file itself for @opentiny/next-sdk and dependencies section
head -n 100 package.json | tail -n 50Repository: opentiny/tiny-pro
Length of output: 2327
🏁 Script executed:
# Search for Transport type usage in the codebase
rg "Transport" --type vue --type ts --type js | head -20Repository: opentiny/tiny-pro
Length of output: 88
🏁 Script executed:
# Search for Transport type usage more broadly
rg "Transport" --type ts --type js | head -20Repository: opentiny/tiny-pro
Length of output: 880
🏁 Script executed:
# Check full package.json dependencies for @opentiny/next-sdk and related packages
rg "@opentiny" package.jsonRepository: opentiny/tiny-pro
Length of output: 172
🏁 Script executed:
# Look for WebMcpServer import to understand where Transport might come from
rg "WebMcpServer|import.*Transport" -A 2 -B 2 | head -30Repository: opentiny/tiny-pro
Length of output: 2406
🏁 Script executed:
# Check App.vue to see how serverTransport is typed and used
rg -A 10 "createMessageChannelPairTransport" "template/tinyvue/src/App.vue"Repository: opentiny/tiny-pro
Length of output: 991
🏁 Script executed:
# Look for type definitions or interfaces related to Transport in the codebase
rg "type.*Transport|interface.*Transport" --type ts -A 3Repository: opentiny/tiny-pro
Length of output: 43
🏁 Script executed:
# Check if there's a type definition file or declaration in node_modules or src
find . -name "*.d.ts" -o -name "*.ts" | xargs grep -l "Transport" 2>/dev/null | head -10Repository: opentiny/tiny-pro
Length of output: 43
Add error handling and improve type safety.
The MCP server setup mirrors the pattern in info-tab.vue and has similar concerns:
-
Type safety: Line 227 uses
anyfor serverTransport. This should use a proper type instead ofany. -
Hard-coded delays: Lines 242 and 246 use fixed 1000ms delays. Extract these as named constants.
-
Missing error handling: No try-catch blocks around async operations. Both the tool handler (lines 240-248) and server connection (line 252) should handle potential errors.
-
UI orchestration: The tool handler directly calls
onOpen()andaddLocale(), tightly coupling MCP logic to UI implementation.
These same issues appear in info-tab.vue (menu-management-mcp-server setup).
Proposed improvements
+const MCP_UI_DELAY = 1000; // ms
+
onMounted(async () => {
+ try {
const server = new WebMcpServer({
name: 'i18n-management-mcp-server',
version: '1.0.0'
})
- const serverTransport = inject<any>('serverTransport')
+ const serverTransport = inject('serverTransport')
+ if (!serverTransport) {
+ console.error('serverTransport not provided')
+ return
+ }
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'),
}
},
async ({ key, content, lang }) => {
+ try {
onOpen()
- await sleep(1000)
+ await sleep(MCP_UI_DELAY)
locale.key = key
locale.content = content
locale.lang = lang
- await sleep(1000)
+ await sleep(MCP_UI_DELAY)
addLocale()
return { content: [{ type: 'text', text: `收到: ${key}` }] }
+ } catch (error) {
+ console.error('Failed to add i18n entry via MCP:', error)
+ return {
+ content: [{
+ type: 'text',
+ text: `添加词条失败: ${error instanceof Error ? error.message : String(error)}`
+ }],
+ isError: true
+ }
+ }
}
)
await server.connect(serverTransport)
+ console.log('i18n MCP server connected successfully')
+ } catch (error) {
+ console.error('Failed to initialize i18n MCP server:', error)
+ }
});📝 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.
| onMounted(async () => { | |
| const server = new WebMcpServer({ | |
| name: 'i18n-management-mcp-server', | |
| version: '1.0.0' | |
| }) | |
| const serverTransport = inject<any>('serverTransport') | |
| 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'), | |
| } | |
| }, | |
| async ({ key, content, lang }) => { | |
| onOpen() | |
| await sleep(1000) | |
| locale.key = key | |
| locale.content = content | |
| locale.lang = lang | |
| await sleep(1000) | |
| addLocale() | |
| return { content: [{ type: 'text', text: `收到: ${key}` }] } | |
| } | |
| ) | |
| await server.connect(serverTransport) | |
| }); | |
| const MCP_UI_DELAY = 1000; // ms | |
| onMounted(async () => { | |
| try { | |
| const server = new WebMcpServer({ | |
| name: 'i18n-management-mcp-server', | |
| version: '1.0.0' | |
| }) | |
| const serverTransport = inject('serverTransport') | |
| if (!serverTransport) { | |
| console.error('serverTransport not provided') | |
| return | |
| } | |
| 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'), | |
| } | |
| }, | |
| async ({ key, content, lang }) => { | |
| try { | |
| onOpen() | |
| await sleep(MCP_UI_DELAY) | |
| locale.key = key | |
| locale.content = content | |
| locale.lang = lang | |
| await sleep(MCP_UI_DELAY) | |
| addLocale() | |
| return { content: [{ type: 'text', text: `收到: ${key}` }] } | |
| } catch (error) { | |
| console.error('Failed to add i18n entry via MCP:', error) | |
| return { | |
| content: [{ | |
| type: 'text', | |
| text: `添加词条失败: ${error instanceof Error ? error.message : String(error)}` | |
| }], | |
| isError: true | |
| } | |
| } | |
| } | |
| ) | |
| await server.connect(serverTransport) | |
| console.log('i18n MCP server connected successfully') | |
| } catch (error) { | |
| console.error('Failed to initialize i18n MCP server:', error) | |
| } | |
| }); |
🤖 Prompt for AI Agents
In template/tinyvue/src/views/locale/components/add-locale.vue around lines
222-253, replace the loosely-typed inject<any>('serverTransport') with the
correct transport type (use the MCP transport interface/type used elsewhere,
e.g., WebMcpTransport or the same type from info-tab.vue) to improve type
safety; extract the hard-coded 1000ms sleeps into named constants (e.g.,
HANDLER_DELAY_MS) at the top of the module and reference those constants; wrap
the async tool handler and the await server.connect(...) call in try-catch
blocks and log/return meaningful errors instead of letting exceptions bubble;
and decouple UI operations from the MCP handler by emitting an event or calling
a small adapter function that maps the handler result to UI actions
(onOpen/addLocale) so the handler does not directly call UI functions.
| onMounted(async () => { | ||
| Promise.all([fetchMenu(), fetchLocalItems()]).finally(() => { | ||
| treeLoading.value = false; | ||
| }); | ||
| const server = new WebMcpServer({ | ||
| name: 'menu-management-mcp-server', | ||
| version: '1.0.0' | ||
| }) | ||
| const serverTransport = inject<any>('serverTransport') |
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.
🛠️ Refactor suggestion | 🟠 Major
🧩 Analysis chain
🏁 Script executed:
# First, check if the file exists and view the relevant lines
if [ -f "template/tinyveo/src/views/menu/info/components/info-tab.vue" ]; then
sed -n '246,255p' "template/tinyveo/src/views/menu/info/components/info-tab.vue"
else
echo "Checking for correct file path..."
find . -name "info-tab.vue" -type f 2>/dev/null | head -5
fiRepository: opentiny/tiny-pro
Length of output: 178
🏁 Script executed:
# Check if @opentiny/next-sdk is in the project dependencies
find . -name "package.json" -type f | xargs grep -l "@opentiny/next-sdk" 2>/dev/null | head -5Repository: opentiny/tiny-pro
Length of output: 92
🏁 Script executed:
# Check the node_modules for Transport type export from @opentiny/next-sdk
if [ -d "node_modules/@opentiny/next-sdk" ]; then
echo "=== Checking for Transport type ==="
find node_modules/@opentiny/next-sdk -name "*.d.ts" -o -name "*.ts" | xargs grep -l "export.*Transport" 2>/dev/null | head -5
else
echo "@opentiny/next-sdk not found in node_modules"
fiRepository: opentiny/tiny-pro
Length of output: 105
🏁 Script executed:
# View the file content at the specified lines
sed -n '240,260p' template/tinyvue/src/views/menu/info/components/info-tab.vueRepository: opentiny/tiny-pro
Length of output: 525
🏁 Script executed:
# Search for Transport type definition in the codebase
rg "type.*Transport|export.*Transport" --type ts --type vue -iRepository: opentiny/tiny-pro
Length of output: 88
🏁 Script executed:
# Check imports in the info-tab.vue file to understand what's already imported
head -30 template/tinyvue/src/views/menu/info/components/info-tab.vueRepository: opentiny/tiny-pro
Length of output: 1117
🏁 Script executed:
# Check the package.json to see what version of @opentiny/next-sdk is used
rg "@opentiny/next-sdk" template/tinyvue/package.json -A 1 -B 1Repository: opentiny/tiny-pro
Length of output: 170
🏁 Script executed:
# Search for all imports from @opentiny/next-sdk to see what types are available
rg "from ['\"]@opentiny/next-sdk['\"]" -A 3 -B 1Repository: opentiny/tiny-pro
Length of output: 1562
🏁 Script executed:
# Search for Transport or transport-related types in the codebase
rg -i "transport" template/tinyvue/src/views/menu/info/components/info-tab.vue -B 2 -A 2Repository: opentiny/tiny-pro
Length of output: 243
🏁 Script executed:
# Check if there are any type definitions or .d.ts files in the repo that might define Transport
find . -name "*.d.ts" -type f | xargs grep -l "Transport" 2>/dev/null | head -10Repository: opentiny/tiny-pro
Length of output: 43
🏁 Script executed:
# Search for how serverTransport is actually used after injection
sed -n '255,280p' template/tinyvue/src/views/menu/info/components/info-tab.vueRepository: opentiny/tiny-pro
Length of output: 880
🏁 Script executed:
# Check how createMessageChannelPairTransport is used in App.vue to understand transport types
sed -n '1,50p' template/tinyvue/src/App.vue | grep -A 10 "createMessageChannelPairTransport"Repository: opentiny/tiny-pro
Length of output: 469
🏁 Script executed:
# Search for server.connect usage patterns
rg "server\.connect" -B 3 -A 3Repository: opentiny/tiny-pro
Length of output: 1058
🏁 Script executed:
# Search for any transport-related type definitions or interfaces in the codebase
rg "transport" template/tinyvue/src -i --type ts --type vue | head -30Repository: opentiny/tiny-pro
Length of output: 88
🏁 Script executed:
# Check if there's a web_search to verify what's exported from @opentiny/next-sdk
# We'll search the package documentationRepository: opentiny/tiny-pro
Length of output: 43
🌐 Web query:
@opentiny/next-sdk Transport type export documentation
💡 Result:
You can import the Transport type from the package. Example:
import type { Transport } from '@opentiny/next-sdk'
(See the WebMcpClient connect signature which uses Transport in the OpenTiny NEXT docs.) [1]
Sources
- OpenTiny NEXT — WebMcpClient (connect signature uses Transport). [1]
Replace any type for serverTransport with proper Transport type.
Line 255 uses any for the injected serverTransport, which bypasses TypeScript's type checking. Import the Transport type from @opentiny/next-sdk and apply it to maintain type safety.
Proposed fix
+import type { Transport } from '@opentiny/next-sdk'
onMounted(async () => {
// ...
const server = new WebMcpServer({
name: 'menu-management-mcp-server',
version: '1.0.0'
})
- const serverTransport = inject<any>('serverTransport')
+ const serverTransport = inject<Transport>('serverTransport')
+ if (!serverTransport) {
+ console.error('serverTransport not provided')
+ return
+ }📝 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.
| onMounted(async () => { | |
| Promise.all([fetchMenu(), fetchLocalItems()]).finally(() => { | |
| treeLoading.value = false; | |
| }); | |
| const server = new WebMcpServer({ | |
| name: 'menu-management-mcp-server', | |
| version: '1.0.0' | |
| }) | |
| const serverTransport = inject<any>('serverTransport') | |
| import type { Transport } from '@opentiny/next-sdk' | |
| onMounted(async () => { | |
| Promise.all([fetchMenu(), fetchLocalItems()]).finally(() => { | |
| treeLoading.value = false; | |
| }); | |
| const server = new WebMcpServer({ | |
| name: 'menu-management-mcp-server', | |
| version: '1.0.0' | |
| }) | |
| const serverTransport = inject<Transport>('serverTransport') | |
| if (!serverTransport) { | |
| console.error('serverTransport not provided') | |
| return | |
| } |
🤖 Prompt for AI Agents
In template/tinyvue/src/views/menu/info/components/info-tab.vue around lines 246
to 255, the injected serverTransport is typed as any which disables TypeScript
checks; import the Transport type from '@opentiny/next-sdk' (add: import type {
Transport } from '@opentiny/next-sdk') and replace
inject<any>('serverTransport') with inject<Transport>('serverTransport') (or
const serverTransport = inject<Transport>('serverTransport') || /* handle
undefined */) so the injected value is strongly typed and handle the possible
undefined return from inject.
| server.registerTool( | ||
| 'add-menu', | ||
| { | ||
| title: '添加菜单', | ||
| description: '添加菜单', | ||
| inputSchema: { | ||
| name: z.string().describe('名称'), | ||
| order: z.number().describe('优先级').default(0), | ||
| parentId: z.number().describe('父菜单ID').optional(), | ||
| icon: z.string().describe('图标').optional().default(''), | ||
| component: z.string().describe('组件'), | ||
| path: z.string().describe('路径'), | ||
| locale: z.string().describe('国际化'), | ||
| } | ||
| }, | ||
| async ({ name, order, parentId, icon, component, path, locale }) => { | ||
| handleAddMenu() | ||
| await sleep(1000) | ||
| addMenu.value.setMenuInfo({ | ||
| name, | ||
| order, | ||
| parentId, | ||
| icon, | ||
| component, | ||
| menuType: "/", | ||
| path, | ||
| locale, | ||
| }) | ||
| await sleep(1000) | ||
| onClickAdd() | ||
| return { content: [{ type: 'text', text: `收到: ${name}` }] } | ||
| } | ||
| ) |
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.
Add error handling and make delays configurable.
The MCP tool registration has several concerns:
-
Hard-coded delays: Lines 274 and 285 use fixed 1000ms delays. These magic numbers make the code harder to maintain and test. Consider extracting them as named constants or making them configurable.
-
Missing error handling: The tool handler lacks try-catch blocks. If
handleAddMenu(),setMenuInfo(), oronClickAdd()fail, the error will propagate uncaught and the tool will return an incomplete response. -
UI orchestration in tool handler: The handler directly manipulates UI state (
handleAddMenu(),onClickAdd()), which tightly couples the MCP tool to the component's internal implementation. This pattern may be difficult to test and maintain.
Proposed improvements
+const MCP_UI_DELAY = 1000; // ms - delay for UI updates to be visible
+
onMounted(async () => {
// ... server setup ...
server.registerTool(
'add-menu',
{
title: '添加菜单',
description: '添加菜单',
inputSchema: {
name: z.string().describe('名称'),
order: z.number().describe('优先级').default(0),
parentId: z.number().describe('父菜单ID').optional(),
icon: z.string().describe('图标').optional().default(''),
component: z.string().describe('组件'),
path: z.string().describe('路径'),
locale: z.string().describe('国际化'),
}
},
async ({ name, order, parentId, icon, component, path, locale }) => {
+ try {
handleAddMenu()
- await sleep(1000)
+ await sleep(MCP_UI_DELAY)
addMenu.value.setMenuInfo({
name,
order,
parentId,
icon,
component,
menuType: "/",
path,
locale,
})
- await sleep(1000)
+ await sleep(MCP_UI_DELAY)
onClickAdd()
return { content: [{ type: 'text', text: `收到: ${name}` }] }
+ } catch (error) {
+ console.error('Failed to add menu via MCP:', error)
+ return {
+ content: [{
+ type: 'text',
+ text: `添加菜单失败: ${error instanceof Error ? error.message : String(error)}`
+ }],
+ isError: true
+ }
+ }
}
)Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
template/tinyvue/src/views/menu/info/components/info-tab.vue lines 257-289: the
MCP tool handler uses hard-coded 1000ms sleeps, has no error handling, and
directly manipulates component UI state; extract the delays into named constants
or a configurable prop (e.g., ADD_MENU_DELAY_MS) and replace literal 1000 with
that constant/prop, wrap the entire handler body in a try-catch that
logs/returns a clear error response on failure, and stop tightly coupling to
internal UI functions by emitting component events or calling injected callbacks
instead of directly invoking handleAddMenu() and onClickAdd() so the tool only
triggers abstracted actions and returns success/failure after setMenuInfo and
the delay complete.
| } | ||
| ) | ||
| await server.connect(serverTransport) |
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.
Add error handling for server connection.
The server.connect() call on line 291 lacks error handling. If the connection fails, the MCP server won't be available and tools won't be accessible, but the component will continue functioning normally without any indication of the problem.
Add try-catch for server connection
- await server.connect(serverTransport)
+ try {
+ await server.connect(serverTransport)
+ console.log('MCP server connected successfully')
+ } catch (error) {
+ console.error('Failed to connect MCP server:', error)
+ // Consider showing a user notification or disabling MCP features
+ }
});📝 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.
| await server.connect(serverTransport) | |
| try { | |
| await server.connect(serverTransport) | |
| console.log('MCP server connected successfully') | |
| } catch (error) { | |
| console.error('Failed to connect MCP server:', error) | |
| // Consider showing a user notification or disabling MCP features | |
| } |
🤖 Prompt for AI Agents
In template/tinyvue/src/views/menu/info/components/info-tab.vue around line 291,
the await server.connect(serverTransport) call lacks error handling; wrap the
connect call in a try-catch, catch and log the error (use existing logger or
console.error), update component state to reflect connection failure (e.g., set
a boolean like serverConnected = false or set an error message reactive
property), and surface a user-visible notification or disable dependent UI/tools
so users know the MCP server is unavailable.
| }, | ||
| async ({ key, content, lang }) => { | ||
| onOpen() | ||
| await sleep(1000) |
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.
为什么这里要用 sleep 等待?为什么只等待 1 秒?
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.
这里主要是为了让整个操作不要太快了,让人能看清楚这个弹窗里的表单
|
@coderabbitai generate docstrings |
✅ Actions performedInitiated docstring generation; will generate only if new commits exist. |
|
Note Docstrings generation - SUCCESS |
Docstrings generation was requested by @kagol. * #165 (comment) The following files were modified: * `template/tinyvue/src/utils/base-utils.ts`
| url: 'https://agent.opentiny.design/api/v1/webmcp-trial/mcp' // http://localhost:3005/api/v1/webmcp/mcp | ||
| }) | ||
| sessionId.value = sessionID | ||
| console.log('sessionId:', sessionId.value) |
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.
| console.log('sessionId:', sessionId.value) |
PR
主要更新:
@opentiny/next-sdk/@opentiny/next-remoter,并接入 WebAgent 服务,用于实现智能化,可通过自然语言,让AI帮助人完成添加词条、添加菜单等操作add-i18n-entry)、添加菜单(add-menu)效果如下:
添加词条:
添加菜单:
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
Bug Fixes
Chores
✏️ Tip: You can customize this high-level summary in your review settings.