- Notifications
You must be signed in to change notification settings - Fork 434
feat: enable generated applications to support MCP tool invocation #1683
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: develop
Are you sure you want to change the base?
Conversation
WalkthroughAdds MCP (Model Context Protocol) support across generator and UI: new genMcpPlugin, MCP templates and server tooling, generator plumbing to propagate pluginConfig.mcp, conditional dependency injection, UI toggle and caching for code generation, plus mock schema/version updates. Changes
Sequence Diagram(s)sequenceDiagram participant UI as FileSelector participant Main as Main.vue participant Gen as generateApp() participant Plugins as Plugin Chain participant MCP as genMcpPlugin participant Templates as MCP Templates participant Out as Generated Files UI->>Main: emit update:enableMcp(enabled) Main->>Main: handleMcpToggle(enabled) / use appSchemaCache Main->>Gen: generateAppCode(schema, { pluginConfig: { mcp: { enabled } } }) Gen->>Plugins: run(schema, context) Plugins->>Plugins: read context.pluginConfig.mcp -> mcpEnabled Plugins->>Gen: pass mcpEnabled into SFC/deps generation alt mcpEnabled == true Gen->>MCP: genMcpPlugin.run(schema, context) MCP->>Templates: generate MCP files (base, server, templates, tools) MCP->>Out: emit MCP runtime files else Gen->>Out: skip MCP-specific files end Plugins->>Out: emit standard generated files Main->>UI: update saveFilesInfo (file list) Estimated code review effort🎯 4 (Complex) | ⏱️ ~60–75 minutes Areas needing extra attention:
Poem
Pre-merge checks and finishing touches✅ Passed checks (3 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: 8
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
packages/vue-generator/src/generator/generateApp.js (1)
24-38: Add 'mcp' to the defaultPlugins union type in index.d.ts.The implementation in generateApp.js includes the mcp plugin, but the TypeScript type definition is missing the corresponding union member. Update
packages/vue-generator/src/index.d.tsby adding| 'mcp'to the defaultPlugins union.
🧹 Nitpick comments (18)
packages/vue-generator/src/templates/vue-template/templateFiles/src/mcp/App.vue.mcp-style.ts (1)
1-6: Consider making the font-size configurable.The hardcoded
30pxfont-size for.next-sdk-trigger-btnmay not be appropriate for all use cases. Consider accepting options to allow customization.Example:
-export default () => { +export default (schema, options = {}) => { + const { triggerBtnFontSize = '30px' } = options + return ` :deep(.next-sdk-trigger-btn) { - font-size: 30px; + font-size: ${triggerBtnFontSize}; }` }packages/vue-generator/src/plugins/genPagePlugin.js (1)
21-21: Pass context safely; prefer nullish-coalescing for boolean configContext handling looks good. Tiny nit: use ?? false over || false to avoid treating non-boolean falsy values (e.g., '') as false unexpectedly. Also, confirm genSFCWithDefaultPlugin consumers read config.mcpEnabled; if not, this flag won’t take effect.
Apply:
- const mcpEnabled = context?.pluginConfig?.mcp?.enabled || false + const mcpEnabled = context?.pluginConfig?.mcp?.enabled ?? falseAnd verify mcpEnabled is actually consumed in SFC hooks or downstream generators.
Also applies to: 26-34
packages/vue-generator/src/plugins/genDependenciesPlugin.js (3)
24-28: parseSchema now context-aware: default param for resilienceGood extension. To harden, default mcpConfig to an empty object to simplify call sites and narrow guards.
-const parseSchema = (schema, mcpConfig) => { +const parseSchema = (schema, mcpConfig = {}) => {
60-66: MCP deps injection: avoid accidental downgrades and keep ranges consistentInjecting MCP deps only when enabled is correct. Two suggestions:
- Don’t overwrite an existing version if one already exists (prevents downgrades).
- Use consistent semver ranges (all pinned or all caret/tilde) unless you have a reason to mix.
- resDeps['@opentiny/next-remoter'] = '0.0.2' - resDeps['@opentiny/next-sdk'] = '^0.1.0' - resDeps['@opentiny/tiny-robot'] = '^0.3.0-alpha.16' - resDeps['@opentiny/tiny-vue-mcp'] = '~0.0.3' + resDeps['@opentiny/next-remoter'] ||= '0.0.2' + resDeps['@opentiny/next-sdk'] ||= '^0.1.0' + resDeps['@opentiny/tiny-robot'] ||= '^0.3.0-alpha.16' + resDeps['@opentiny/tiny-vue-mcp'] ||= '~0.0.3'Also consider promoting pre-releases to explicit pins if stability is required.
Please confirm these versions are current and compatible with your target TinyVue version matrix.
84-106: Ensure file type when creating package.json; add minimal JSON formattingWhen package.json doesn’t exist, you return a file object without fileType. Other paths use addFile with fileType: 'json'. For consistency and downstream processors, include fileType and optionally pretty-print for readability.
- if (!originPackageItem) { - return { - fileName, - path, - fileContent: JSON.stringify({ dependencies }) - } - } + if (!originPackageItem) { + return { + fileType: 'json', + fileName, + path, + fileContent: JSON.stringify({ dependencies }, null, 2) + } + }packages/toolbars/generate-code/src/FileSelector.vue (2)
12-16: MCP toggle wiring looks good; minor a11y/UX tweaksSolid prop/emit design. Consider:
- Add an accessible label for the switch (aria-label or via id) so screen readers announce it properly.
- Optionally expose v-model:enableMcp for symmetry with update:enableMcp.
Example:
- <span class="mcp-switch-label">启用 MCP 集成:</span> - <tiny-switch v-model="mcpEnabled" @change="handleMcpChange"></tiny-switch> + <label class="mcp-switch-label" :for="'mcp-switch'">启用 MCP 集成:</label> + <tiny-switch :id="'mcp-switch'" v-model="mcpEnabled" aria-label="启用 MCP 集成" @change="handleMcpChange" />Also applies to: 41-54, 56-64
96-105: Selection reset on data updates may override user choiceThe deep watch on props.data re-expands and re-selects all rows. If data updates while the dialog is open, it can erase the user’s selection.
- Gate the auto-select to openDialog only, or
- Detect first render vs subsequent updates, or
- Preserve current selections before refresh and restore them after.
Also applies to: 138-146
packages/vue-generator/src/templates/vue-template/templateFiles/src/mcp/tools/navigationTools.ts (1)
28-33: Normalize error messagesInterpolating unknown errors can yield “[object Object]”. Prefer error instanceof Error ? error.message : String(error).
- content: [{ type: "text", text: `导航失败:${error}` }], + content: [{ type: "text", text: `导航失败:${error instanceof Error ? error.message : String(error)}` }],Apply similarly to other handlers.
Also applies to: 49-54, 70-75, 94-99
packages/vue-generator/src/templates/vue-template/templateFiles/src/mcp/App.vue.mcp-setup.ts (1)
25-50: Call order: connect client proxy before connecting serversinitMcpServer connects servers to serverTransport. Ensure onMounted logic calls createProxyTransport() and awaits it before initMcpServer(), otherwise client/server sides may race.
Example onMounted sequence:
await createProxyTransport(); await initMcpServer();Confirm App.vue.mcp-onMounted.ts wires this order.
Also applies to: 52-63
packages/toolbars/generate-code/src/Main.vue (4)
55-58: Type the reactive state and consider shallow cache.Add an explicit state type and prefer shallowRef/shallowReactive for large schemas to avoid deep proxy/memory overhead.
-const state = reactive({ +interface GenState { + dirHandle: any | null + generating: boolean + showDialogbox: boolean + saveFilesInfo: Array<{ fileContent: string; filePath: string; fileType?: string }> + enableMcp: boolean + appSchemaCache: unknown | null +} +const state = reactive<GenState>({ dirHandle: null, generating: false, showDialogbox: false, saveFilesInfo: [], enableMcp: false, // MCP 开关状态 - appSchemaCache: null // 缓存应用 schema,用于重新生成代码 + appSchemaCache: null // 缓存应用 schema,用于重新生成代码 })
188-201: Pass-through of MCP option is good. Consider co-locating defaults.This correctly forwards mcp.enabled. If you later pass more MCP config, centralize defaults in one place to avoid divergence with the generator.
203-222: Deduplicate file path normalization.The mapping logic is duplicated. Extract a helper to avoid drift and ease testing.
+const normalizeGenResult = (genResult = []) => { + return genResult.map(({ fileContent, fileName, path, fileType }) => { + const slash = path.endsWith('/') || path === '.' ? '' : '/' + let filePath = `${path}${slash}` + if (filePath.startsWith('./')) filePath = filePath.slice(2) + if (filePath.startsWith('.')) filePath = filePath.slice(1) + if (filePath.startsWith('/')) filePath = filePath.slice(1) + return { fileContent, filePath: `${filePath}${fileName}`, fileType } + }) +} -const { genResult = [] } = res || {} -const fileRes = genResult.map(({ fileContent, fileName, path, fileType }) => { ... }) +const { genResult = [] } = res || {} +const fileRes = normalizeGenResult(genResult)Apply similarly in handleMcpToggle.
Also applies to: 325-343 --- `298-360`: **Add a lightweight re-entry guard for toggle.** Rapid toggles could race and reorder results. Guard with a local flag. ```diff -const handleMcpToggle = async (enabled) => { +let toggling = false +const handleMcpToggle = async (enabled) => { + if (toggling) return + toggling = true state.enableMcp = enabled if (!state.appSchemaCache) { - return + toggling = false + return } try { // ... } catch (error) { // ... } finally { + toggling = false } }packages/vue-generator/src/plugins/genMcpPlugin.js (3)
251-261: Broaden onMounted regex to avoid duplicate blocks.Current regex misses async/spacing variants and appends a new onMounted.
- if (existingScript.includes('onMounted(')) { - newScript = newScript.replace(/onMounted\(\(\) => \{([\s\S]*?)\}\)/, (_, content) => { + if (existingScript.includes('onMounted(')) { + newScript = newScript.replace(/onMounted\s*\(\s*(?:async\s*)?\(\)\s*=>\s*\{([\s\S]*?)\}\s*\)/, (_, content) => { return `onMounted(() => {${content}\n${mcpOnMountedCode}\n})` })
338-376: Parity with new-script path: add i18n imports/provide when missing.modifyExistingScriptSetup doesn’t add i18n, unlike createNewScriptSetup, causing inconsistencies.
// 添加必要的 imports newScript = addRequiredImports(newScript, existingScript) newScript = `${mcpImports}\n${newScript}` + if (!existingScript.includes("I18nInjectionKey")) { + newScript = `import { I18nInjectionKey } from 'vue-i18n'\n${newScript}` + } + if (!existingScript.match(/import\s+i18n\s+from\s+['"]\.\/i18n['"]/)) { + newScript = `import i18n from './i18n'\n${newScript}` + } // 添加 router 变量(如果不存在) if (!existingScript.includes('const router')) { newScript += `\nconst router = useRouter();` } + if (!existingScript.includes('provide(I18nInjectionKey')) { + newScript += `\nprovide(I18nInjectionKey, i18n)` + }
167-170: customTools are validated but unused.Either implement generation for customTools or drop validation to avoid confusion.
Also applies to: 625-657
packages/vue-generator/src/templates/vue-template/templateFiles/src/mcp/server.ts (2)
48-57: Register main-server tools only once.If init is called twice, duplicate registrations may occur. Consider guarding with a flag.
+ private initialized = false // 初始化管理器 init(router: Router, config: any) { + if (this.initialized) return this.router = router this.capabilities = config.capabilities // ... this.registerServerToolsByMode('main', this.mainServer) + this.initialized = true }
150-166: Dispose should also disconnect, not just null transports.Call server.disconnect() if available to clean RPC state.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (19)
designer-demo/public/mock/bundle.json(1 hunks)mockServer/src/mock/get/app-center/v1/apps/schema/1.json(8 hunks)packages/toolbars/generate-code/src/FileSelector.vue(4 hunks)packages/toolbars/generate-code/src/Main.vue(4 hunks)packages/vue-generator/src/generator/generateApp.js(5 hunks)packages/vue-generator/src/generator/vue/sfc/genSetupSFC.js(2 hunks)packages/vue-generator/src/plugins/genBlockPlugin.js(2 hunks)packages/vue-generator/src/plugins/genDependenciesPlugin.js(3 hunks)packages/vue-generator/src/plugins/genMcpPlugin.js(1 hunks)packages/vue-generator/src/plugins/genPagePlugin.js(1 hunks)packages/vue-generator/src/plugins/index.js(1 hunks)packages/vue-generator/src/templates/vue-template/templateFiles/src/mcp/App.vue.mcp-imports.ts(1 hunks)packages/vue-generator/src/templates/vue-template/templateFiles/src/mcp/App.vue.mcp-onMounted.ts(1 hunks)packages/vue-generator/src/templates/vue-template/templateFiles/src/mcp/App.vue.mcp-setup.ts(1 hunks)packages/vue-generator/src/templates/vue-template/templateFiles/src/mcp/App.vue.mcp-style.ts(1 hunks)packages/vue-generator/src/templates/vue-template/templateFiles/src/mcp/App.vue.mcp-template.ts(1 hunks)packages/vue-generator/src/templates/vue-template/templateFiles/src/mcp/base.ts(1 hunks)packages/vue-generator/src/templates/vue-template/templateFiles/src/mcp/server.ts(1 hunks)packages/vue-generator/src/templates/vue-template/templateFiles/src/mcp/tools/navigationTools.ts(1 hunks)
🧰 Additional context used
🧬 Code graph analysis (10)
packages/vue-generator/src/generator/vue/sfc/genSetupSFC.js (1)
packages/vue-generator/src/plugins/genMcpPlugin.js (2)
handleTinyMcpConfigAttrHook(737-820)handleTinyMcpConfigAttrHook(737-820)
packages/vue-generator/src/plugins/genPagePlugin.js (2)
packages/vue-generator/src/plugins/genDependenciesPlugin.js (2)
schema(10-10)schema(25-25)packages/vue-generator/src/generator/vue/sfc/genSetupSFC.js (3)
res(52-58)genSFCWithDefaultPlugin(219-279)genSFCWithDefaultPlugin(219-279)
packages/vue-generator/src/plugins/genBlockPlugin.js (1)
packages/vue-generator/src/generator/vue/sfc/genSetupSFC.js (3)
res(52-58)genSFCWithDefaultPlugin(219-279)genSFCWithDefaultPlugin(219-279)
packages/vue-generator/src/plugins/genMcpPlugin.js (5)
packages/vue-generator/src/templates/vue-template/templateFiles/src/mcp/App.vue.mcp-setup.ts (1)
schema(1-64)packages/vue-generator/src/templates/vue-template/templateFiles/src/mcp/base.ts (1)
schema(1-6)packages/vue-generator/src/templates/vue-template/templateFiles/src/mcp/server.ts (1)
schema(1-214)packages/vue-generator/src/templates/vue-template/templateFiles/src/mcp/tools/navigationTools.ts (1)
schema(1-102)packages/vue-generator/src/generator/vue/sfc/genSetupSFC.js (3)
config(46-51)config(220-220)globalHooks(104-200)
packages/vue-generator/src/templates/vue-template/templateFiles/src/mcp/base.ts (1)
packages/vue-generator/src/templates/vue-template/templateFiles/src/mcp/App.vue.mcp-setup.ts (1)
schema(1-64)
packages/vue-generator/src/templates/vue-template/templateFiles/src/mcp/server.ts (2)
packages/vue-generator/src/templates/vue-template/templateFiles/src/mcp/App.vue.mcp-setup.ts (1)
schema(1-64)packages/vue-generator/src/templates/vue-template/templateFiles/src/mcp/tools/navigationTools.ts (1)
schema(1-102)
packages/vue-generator/src/templates/vue-template/templateFiles/src/mcp/tools/navigationTools.ts (2)
packages/vue-generator/src/templates/vue-template/templateFiles/src/mcp/server.ts (1)
schema(1-214)packages/vue-generator/src/plugins/genMcpPlugin.js (2)
routes(36-42)routes(58-58)
packages/vue-generator/src/templates/vue-template/templateFiles/src/mcp/App.vue.mcp-setup.ts (2)
packages/vue-generator/src/templates/vue-template/templateFiles/src/mcp/base.ts (1)
schema(1-6)packages/vue-generator/src/templates/vue-template/templateFiles/src/mcp/server.ts (1)
schema(1-214)
packages/vue-generator/src/generator/generateApp.js (2)
packages/vue-generator/src/plugins/genGlobalState.js (1)
globalState(9-9)packages/vue-generator/src/index.d.ts (1)
defaultPlugins(2-13)
packages/vue-generator/src/plugins/genDependenciesPlugin.js (1)
packages/vue-generator/src/plugins/genMcpPlugin.js (2)
mcpConfig(465-465)mcpConfig(747-747)
🔇 Additional comments (18)
mockServer/src/mock/get/app-center/v1/apps/schema/1.json (1)
1729-1729: Mock data version consolidation looks good.The bulk version updates to 3.26.0 for OpenTiny Vue components appear intentional for establishing a consistent test baseline. Non-OpenTiny packages and specialized components (element-plus, TinyPlus, TinySearch, TinyDropdown) correctly retain their original versions.
However, confirm that this version consolidation is deliberate for MCP feature testing and aligns with the plugin's component generation validation scope.
Also applies to: 1743-1743, 1750-1750, 1764-1764, 1771-1771, 1778-1778, 1820-1820, 1827-1827, 1841-1841, 1848-1848, 1855-1855, 1869-1869, 1876-1876, 1883-1883, 1890-1890, 1897-1897, 1904-1904, 1911-1911, 1918-1918, 1932-1932, 1939-1939, 1946-1946, 1953-1953, 1960-1960, 1967-1967, 1974-1974, 1988-1988, 1995-1995, 2002-2002, 2009-2009, 2016-2016
designer-demo/public/mock/bundle.json (1)
10973-11005: Add defaultValue to guide users; verify conditional visibility and schema validation support before implementing.The
defaultValuesuggestion is sound and follows proven patterns in bundle.json. However, the conditional visibility (visibleWhen) and JSON schema validation (jsonSchema) suggestions lack evidence of support in the codebase—no matching patterns were found in schema processing or CodeConfigurator usage.Recommended action:
- ✓ Add
defaultValuewith a default MCP config skeleton- ⚠ Before implementing conditional visibility or JSON schema: verify with the forms/schema engine team that these features are supported
Suggested diff:
"content": [ { "property": "tiny_mcp_config", "label": { "text": { "zh_CN": "组件MCP化配置" } }, "required": true, "readOnly": false, "disabled": false, "cols": 12, "widget": { "component": "CodeConfigurator", "props": { "language": "json" } }, + "defaultValue": { + "enabled": false, + "agentRoot": "", + "sessionId": "", + "capabilities": [], + "tools": { + "navigation": true, + "application": true, + "customTools": [] + } + }, "description": { - "zh_CN": "设置表格的基础 mcp 信息" + "zh_CN": "设置表格的基础 MCP 信息(与代码生成插件默认值保持一致)" }, "labelPosition": "top" } ]packages/vue-generator/src/plugins/index.js (1)
13-13: LGTM!The export follows the established pattern and correctly exposes the new MCP plugin.
packages/vue-generator/src/generator/vue/sfc/genSetupSFC.js (2)
27-27: LGTM!The import correctly brings in the MCP config attribute handler for use in the SFC generation pipeline.
223-238: LGTM!The hook is correctly positioned after
handlePrimitiveAttributeHookand beforehandleExpressionAttrHook, which ensures that MCP config attributes are processed at the appropriate stage of attribute transformation.packages/vue-generator/src/templates/vue-template/templateFiles/src/mcp/App.vue.mcp-onMounted.ts (1)
1-4: LGTM!The generated code correctly calls
createProxyTransport()andinitMcpServer()which are defined in the setup phase (as seen in App.vue.mcp-setup.ts).packages/vue-generator/src/templates/vue-template/templateFiles/src/mcp/App.vue.mcp-imports.ts (2)
9-9: Verify the import path for MCP server module.Similar to the base module import, verify that
"./mcp/server"is the correct relative path from where this generated code will be inserted.
8-8: The review comment is based on an incorrect assumption about the file structure.The verification shows:
- base.ts is generated to
./src/base.ts(not./src/mcp/base.tsas the review assumes)- server.ts is generated to
./src/mcp/server.ts- App.vue is at
./src/App.vueThe imports are injected into App.vue's script, making the relative paths:
"./base"correctly refers to./src/base.ts✓"./mcp/server"correctly refers to./src/mcp/server.ts✓The import path is correct as-is. No changes needed.
Likely an incorrect or invalid review comment.
packages/vue-generator/src/templates/vue-template/templateFiles/src/mcp/App.vue.mcp-template.ts (1)
1-3: LGTM!The template correctly uses
SESSION_IDwhich is imported via the MCP imports template, and binds it to the TinyRemoter component.packages/vue-generator/src/plugins/genBlockPlugin.js (3)
21-21: LGTM!The signature change correctly accepts the context parameter to enable MCP configuration propagation.
31-32: LGTM!The MCP configuration is safely extracted with optional chaining and a sensible default value of
false.
36-40: LGTM!The
mcpEnabledflag is correctly passed to the SFC generator, enabling MCP features to be conditionally applied during block code generation.packages/vue-generator/src/plugins/genDependenciesPlugin.js (2)
2-2: Check alias resolution for '@/...' importThis file mixes relative imports with an alias import. Ensure the '@' alias is configured for this package’s build/test tooling; otherwise, this will fail in plain Node resolution.
Run your bundler/test config to confirm alias mapping for '@/generator/vue/sfc/parseImport'.
55-58: Nice: align @opentiny/vue-icon and @opentiny/vue-common with @opentiny/vueAdding vue-common alongside vue-icon prevents runtime import gaps. LGTM.
packages/vue-generator/src/templates/vue-template/templateFiles/src/mcp/App.vue.mcp-setup.ts (1)
6-20: Type imports and provide dependencies must exist in imports templateThis snippet relies on Transport, WebMcpClient, createMessageChannelPairTransport, provide, router, and mcpServerManager being in scope. Ensure App.vue.mcp-imports.ts adds:
- import { provide } from 'vue'
- import { WebMcpClient, createMessageChannelPairTransport } from '@opentiny/next-sdk'
- import type { Transport } from '@opentiny/next-sdk'
- router and mcpServerManager definitions/imports
Please confirm these imports are present to avoid TS/rollup errors.
packages/toolbars/generate-code/src/Main.vue (1)
8-10: Prop/event wiring for MCP looks correct.Kebab-case prop and model event align with Vue conventions. No issues.
packages/vue-generator/src/templates/vue-template/templateFiles/src/mcp/server.ts (1)
22-26: Import set looks correct for generated code.Assuming dependencies are injected when MCP is enabled, these imports should resolve.
Please confirm genDependenciesPlugin adds @opentiny/next-sdk, @opentiny/tiny-vue-mcp, and @opentiny/vue-common only when mcp.enabled === true.
packages/vue-generator/src/plugins/genMcpPlugin.js (1)
736-820: No action required: mcpEnabled is correctly passed through config chains.The review comment's concern about a flag mismatch is unfounded. Both caller sites (genPagePlugin and genBlockPlugin) explicitly extract
context?.pluginConfig?.mcp?.enabledand pass it asmcpEnabledin the config object togenSFCWithDefaultPlugin. The config flows correctly through the chain:genSFCWithDefaultPlugin→generateSFCFile→genTemplateByHook→ hook invocation with the config parameter intact. The hook receivesconfig.mcpEnabledas intended, and the naming is consistent throughout.Likely an incorrect or invalid review comment.
...ages/vue-generator/src/templates/vue-template/templateFiles/src/mcp/tools/navigationTools.ts Outdated Show resolved Hide resolved
...ages/vue-generator/src/templates/vue-template/templateFiles/src/mcp/tools/navigationTools.ts Show resolved Hide resolved
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: 8
♻️ Duplicate comments (4)
packages/vue-generator/src/plugins/genMcpPlugin.js (4)
12-28: Hardcoded defaults for agentRoot and sessionId are already flagged.This issue was identified in previous review comments. The hardcoded values should be removed and required when enabled.
198-232: De-duplication logic is already correct.The function checks for both style imports on lines 200-201 and returns early if both exist (line 204). It then inserts only missing imports. The concern raised in previous comments appears to be addressed in the current code.
513-578: Validation issues already flagged.The validation logic has been identified in previous review comments as using truthy checks that miss invalid values and not requiring agentRoot/sessionId when enabled.
669-684: Transform timing issue already flagged.This section performs transformTinyMcpConfig after pages are likely generated. This has been identified in previous review comments.
🧹 Nitpick comments (2)
packages/vue-generator/src/plugins/genMcpPlugin.js (2)
636-667: Tool generation failures are silently ignored for configured tools.Lines 648-656 catch errors during tool generation and log a warning, but don't provide any indication to the user that their configured tools failed to generate. This could lead to runtime errors when the application expects tool files that don't exist.
Consider:
- Throwing an error if a configured tool fails (fail-fast)
- Or collecting failures and returning them as part of the result
- At minimum, make the log more visible (e.g.,
type: 'error'instead of'warning')} catch (error) { if (this.addLog) { this.addLog({ - type: 'warning', + type: 'error', message: `${toolName} 工具生成失败: ${error.message}`, plugin: 'genMcpPlugin' }) } + // Re-throw to fail fast on tool generation errors + throw new Error(`Failed to generate ${toolName} tool: ${error.message}`) }
788-827: Large code block should use template for consistency.Lines 805-826 embed a large multi-line code snippet directly in the hook. This is inconsistent with the template-driven approach used elsewhere in the file (e.g.,
mcpSetupTemplate,mcpImportsTemplate).For consistency and maintainability, extract this to a template:
// In templates/vue-template/templateFiles/src/mcp/page-server-init.ts export default (schema, options) => { const { pageId } = options return ` // 使用统一管理的页面服务器 const { server, connect, disconnect } = usePageMcpServer('${pageId}', { business: { id: '${pageId}', description: '${pageId}页面' } }) // ... rest of code ` }Then use it:
globalHooks.addStatement({ position: INSERT_POSITION.AFTER_METHODS, value: pageServerInitTemplate(null, { pageId }), key: 'mcpServerInit' })
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
packages/vue-generator/src/plugins/genMcpPlugin.js(1 hunks)packages/vue-generator/src/templates/vue-template/templateFiles/src/mcp/base.ts(1 hunks)packages/vue-generator/src/templates/vue-template/templateFiles/src/mcp/tools/navigationTools.ts(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- packages/vue-generator/src/templates/vue-template/templateFiles/src/mcp/tools/navigationTools.ts
🧰 Additional context used
🧬 Code graph analysis (2)
packages/vue-generator/src/plugins/genMcpPlugin.js (8)
packages/vue-generator/src/plugins/genBlockPlugin.js (3)
defaultOption(4-6)realOptions(9-9)realOptions(11-11)packages/vue-generator/src/plugins/genPagePlugin.js (3)
defaultOption(4-6)realOptions(9-9)realOptions(11-11)packages/vue-generator/src/plugins/genDependenciesPlugin.js (5)
defaultOption(4-7)schema(10-10)schema(25-25)realOptions(72-72)realOptions(74-74)packages/vue-generator/src/templates/vue-template/templateFiles/src/mcp/base.ts (1)
schema(1-10)packages/vue-generator/src/templates/vue-template/templateFiles/src/mcp/tools/navigationTools.ts (1)
schema(1-102)packages/vue-generator/src/templates/vue-template/templateFiles/src/mcp/server.ts (1)
schema(1-214)packages/vue-generator/src/templates/vue-template/templateFiles/src/mcp/App.vue.mcp-setup.ts (1)
schema(1-64)packages/vue-generator/src/generator/vue/sfc/genSetupSFC.js (3)
config(46-51)config(220-220)globalHooks(104-200)
packages/vue-generator/src/templates/vue-template/templateFiles/src/mcp/base.ts (1)
packages/vue-generator/src/templates/vue-template/templateFiles/src/mcp/App.vue.mcp-setup.ts (1)
schema(1-64)
⏰ 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: push-check
packages/vue-generator/src/templates/vue-template/templateFiles/src/mcp/base.ts Show resolved Hide resolved
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: 3
♻️ Duplicate comments (8)
packages/vue-generator/src/plugins/genMcpPlugin.js (8)
240-252: Fragile import manipulation; prefer parsing or robust update.
String replace assumes a specific format forimport { provide } from 'vue'.Suggested approach:
// Pseudocode using regex const vueImport = existingScript.match(/import\s*\{([^}]*)\}\s*from\s*['"]vue['"]/) if (vueImport) { const names = vueImport[1].split(',').map(s => s.trim()) if (!names.includes('onMounted')) names.push('onMounted') if (!names.includes('provide')) names.push('provide') newScript = newScript.replace(vueImport[0], `import { ${[...new Set(names)].join(', ')} } from 'vue'`) } else { newScript = `import { provide, onMounted } from "vue";\n${newScript}` }Best: use @babel/parser/traverse to edit ImportDeclaration safely.
261-272: onMounted regex is brittle; handle async/function forms.
Covers onlyonMounted(() => {...}).Apply this diff:
- if (existingScript.includes('onMounted(')) { - // 已有 onMounted,在其中添加 MCP 初始化 - newScript = newScript.replace(/onMounted\(\(\) => \{([\s\S]*?)\}\)/, (_, content) => { - return `onMounted(() => {${content}\n${mcpOnMountedCode}\n})` - }) + if (existingScript.includes('onMounted(')) { + newScript = newScript.replace( + /onMounted\(\s*(async\s*)?(?:\(\)\s*=>|function\s*\(\))\s*\{([\s\S]*?)\}\s*\)/, + (m, asyncK = '', body) => `onMounted(${asyncK}() => {${body}\n${mcpOnMountedCode}\n})` + )Parsing with an AST would be more robust.
669-686: Confirm transform timing relative to page/SFC generation.
If genMcpPlugin.run executes after pages/blocks, transforms won’t affect SFCs. PR summary claims it runs earlier—please verify.#!/bin/bash # Verify plugin order in generateApp.js (genMcpPlugin should run before genPagePlugin/genBlockPlugin) rg -n -C3 "genMcpPlugin\(|genPagePlugin\(|genBlockPlugin\(" packages/vue-generator/src/generator/generateApp.js # Also check defaultPlugins or pipeline assembly rg -n -C3 "defaultPlugins|plugins\s*=" packages/vue-generator/src/generator/generateApp.js
12-28: Do not ship hardcoded agentRoot/sessionId; require when enabled.
Defaults leak a static endpoint/UUID. Set empty defaults and read env; validation should enforce presence when enabled.Apply this diff:
const defaultOption = { enabled: false, // 默认禁用 MCP,用户需要显式启用 - agentRoot: 'https://agent.opentiny.design/api/v1/webmcp-trial/', - sessionId: '78b66563-95c0-4839-8007-e8af634dd658', + agentRoot: '', + sessionId: '', capabilities: {Also merge env before user options:
- const realOptions = mergeOptions(defaultOption, options) + const envDefaults = { + agentRoot: process.env.MCP_AGENT_ROOT || '', + sessionId: process.env.MCP_SESSION_ID || '' + } + const realOptions = mergeOptions(defaultOption, envDefaults, options)
39-42: Preserve empty route ('') and guard non-string route values.
Current|| 'home'turns '' into 'home' and.startsWithcrashes on non-strings.Apply this diff:
- const routeField = page.meta.route || page.meta.router - const routePath = routeField.startsWith('/') ? routeField.slice(1) : routeField - return routePath || 'home' + const routeField = page.meta.route || page.meta.router + if (typeof routeField !== 'string') return 'home' + const routePath = routeField.startsWith('/') ? routeField.slice(1) : routeField + return routePath ?? 'home'
67-161: Generated code uses unvalidated identifiers and unescaped keys.
Risk of malformed TS and runtime errors; property assignment should use bracket notation. Validate identifiers first.Apply this diff (add validation and safer assignment):
function generateApplicationTools(schema) { // 基于全局状态和数据源生成自定义工具 const globalStates = schema.globalState || [] + const isValidIdent = (s) => typeof s === 'string' && /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(s) + globalStates.forEach((state) => { + if (state.id && !isValidIdent(state.id)) { + throw new Error(`Invalid globalState id: ${state.id}`) + } + if (state.state && typeof state.state === 'object') { + Object.keys(state.state).forEach((k) => { + if (!isValidIdent(k)) { + throw new Error(`Invalid state key on ${state.id}: ${k}`) + } + }) + } + }) @@ - ${state.id}Store.${key} = value + ${state.id}Store[${JSON.stringify(key)}] = valueConsider switching to an AST builder for robustness in a follow-up.
312-321: Remove hardcoded i18n injection; it’s not guaranteed to exist.
Unconditionally importing/providing i18n will break projects without vue-i18n or ./i18n.Apply this diff:
const mcpScript = `<script setup lang="ts"> ${mcpImports} import { useRouter } from "vue-router"; -import { I18nInjectionKey } from 'vue-i18n' -import { provide, onMounted } from 'vue' -import i18n from './i18n' - -provide(I18nInjectionKey, i18n) +import { provide, onMounted } from 'vue' const router = useRouter();If i18n is required, gate injection on actual file presence and dependency.
513-578: Tighten validation; require agentRoot/sessionId when enabled; null-safe object checks.
Current truthy checks miss invalid values and don’t enforce required fields when enabled.Apply this diff:
- if (config.enabled && typeof config.enabled !== 'boolean') { + if ('enabled' in config && typeof config.enabled !== 'boolean') { errors.push('enabled 必须是布尔值') } - if (config.agentRoot && typeof config.agentRoot !== 'string') { + if ('agentRoot' in config && typeof config.agentRoot !== 'string') { errors.push('agentRoot 必须是字符串') } - if (config.sessionId && typeof config.sessionId !== 'string') { + if ('sessionId' in config && typeof config.sessionId !== 'string') { errors.push('sessionId 必须是字符串') } - if (config.agentRoot && config.agentRoot !== '') { + if (typeof config.agentRoot === 'string' && config.agentRoot !== '') { try { new URL(config.agentRoot) } catch (e) { errors.push('agentRoot 必须是有效的 URL') } } - if (config.tools && typeof config.tools !== 'object') { + if (config.tools && (typeof config.tools !== 'object' || config.tools === null || Array.isArray(config.tools))) { errors.push('tools 必须是对象') } else if (config.tools) { @@ - if (config.customTools && !Array.isArray(config.customTools)) { + if (config.customTools && !Array.isArray(config.customTools)) { errors.push('customTools 必须是数组') } + if (config.enabled === true) { + if (!config.agentRoot) errors.push('启用 MCP 时必须配置 agentRoot') + if (!config.sessionId) errors.push('启用 MCP 时必须配置 sessionId') + } - if (config.capabilities && typeof config.capabilities !== 'object') { + if (config.capabilities && (typeof config.capabilities !== 'object' || config.capabilities === null)) { errors.push('capabilities 必须是对象') }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
packages/vue-generator/src/plugins/genMcpPlugin.js(1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
packages/vue-generator/src/plugins/genMcpPlugin.js (5)
packages/vue-generator/src/templates/vue-template/templateFiles/src/mcp/tools/navigationTools.ts (1)
schema(1-102)packages/vue-generator/src/templates/vue-template/templateFiles/src/mcp/App.vue.mcp-setup.ts (1)
schema(1-64)packages/vue-generator/src/templates/vue-template/templateFiles/src/mcp/base.ts (1)
schema(1-10)packages/vue-generator/src/templates/vue-template/templateFiles/src/mcp/server.ts (1)
schema(1-214)packages/vue-generator/src/generator/vue/sfc/genSetupSFC.js (3)
config(46-51)config(220-220)globalHooks(104-200)
⏰ 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: push-check
🔇 Additional comments (2)
packages/vue-generator/src/plugins/genMcpPlugin.js (2)
198-232: Style import de-dupe logic LGTM.
Both robot and remoter imports are conditionally inserted; idempotent on reruns.
468-506: Escaping fix for id/description LGTM.
Using JSON.stringify prevents broken literals in generated JS_EXPRESSION.
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: 1
♻️ Duplicate comments (10)
packages/vue-generator/src/plugins/genMcpPlugin.js (10)
240-252: Fragile import manipulation remains unchanged.Line 244's string replacement approach (
newScript.replace('import { provide', 'import { provide, onMounted')) still assumes a specific import format. A previous review recommended using an AST parser or more robust regex to handle multi-line imports, comments, or aliases.
261-272: Regex for onMounted modification remains fragile.Line 264's pattern (
/onMounted\(\(\) => \{([\s\S]*?)\}\)/) was previously flagged—it doesn't handleasync () => {},function() {}, or nested braces reliably. Consider using an AST parser for robust modification.
669-712: Transform timing issue persists.
transformTinyMcpConfigis called at line 682 withingenMcpPlugin.run. A previous review flagged that if MCP runs after page/block generation, these transformations won't affect SFC generation. The PR objectives state MCP runs before page generation, but verify the actual plugin ordering ingenerateApp.jsto ensure transforms are applied at the correct phase.#!/bin/bash # Verify plugin execution order in generateApp.js rg -n -A10 -B5 'genMcpPlugin|defaultPlugins' packages/vue-generator/src/generator/generateApp.js
12-28: Hardcoded credentials remain unaddressed.The static
agentRootendpoint andsessionIdUUID are still present in defaults. This was flagged in a previous review and remains a security and operational risk.
35-50: Empty string route handling still incorrect.Line 41 uses
||which converts an intentional empty route ('') to'home'. A previous review recommended using??or explicit checks to preserve empty strings.
67-161: Code generation still lacks input validation and escaping.
state.idandkeyare directly interpolated into generated TypeScript code without validation or escaping. This was flagged previously and remains unresolved—if these contain quotes, backticks, or invalid identifier characters, the generated code will be malformed.
312-335: Hardcoded i18n imports will cause build errors.Lines 316-318 unconditionally import
vue-i18nand./i18n, but no i18n file is generated. This critical issue from a previous review remains unresolved and will break builds when MCP is enabled.
513-578: Validation approach still uses truthy checks.The validation logic (lines 518-556) was previously flagged for using truthy checks (
config.enabled &&,config.agentRoot &&) instead of checking property presence. This misses invalid values likenullor0, and doesn't enforce thatagentRootandsessionIdare required whenenabled === true.
620-625: base.ts path is still incorrect.Line 623 writes
base.tsto'./src'instead of'./src/mcp'. This critical issue from a previous review breaks imports from MCP templates that expect./src/mcp/base.
635-667: customTools generation still missing.
customToolsare validated (lines 558-565) but never generated or registered. A previous review flagged this gap between validation and implementation—the loop at lines 636-667 only handles built-in tools fromtoolGenerators.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
packages/vue-generator/src/plugins/genMcpPlugin.js(1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
packages/vue-generator/src/plugins/genMcpPlugin.js (5)
packages/vue-generator/src/templates/vue-template/templateFiles/src/mcp/tools/navigationTools.ts (1)
schema(1-102)packages/vue-generator/src/templates/vue-template/templateFiles/src/mcp/base.ts (1)
schema(1-10)packages/vue-generator/src/templates/vue-template/templateFiles/src/mcp/App.vue.mcp-setup.ts (1)
schema(1-64)packages/vue-generator/src/templates/vue-template/templateFiles/src/mcp/server.ts (1)
schema(1-214)packages/vue-generator/src/generator/vue/sfc/genSetupSFC.js (3)
config(46-51)config(220-220)globalHooks(104-200)
⏰ 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: push-check
English | 简体中文
PR Checklist
Please check if your PR fulfills the following requirements:
PR Type
What kind of change does this PR introduce?
Background and solution
What is the current behavior?
Issue Number: N/A
What is the new behavior?
自定义 MCP 出码功能
一、出码流程概览
1.1 整体流程图
1.2 生成的文件结构
1.3 关键依赖包
{ "@opentiny/next-remoter": "0.0.2", // MCP UI 组件(聊天界面) "@opentiny/next-sdk": "^0.1.0", // MCP SDK(客户端/服务器) "@opentiny/tiny-robot": "^0.3.0-alpha.16", // AI 机器人组件 "@opentiny/tiny-vue-mcp": "~0.0.3", // TinyVue MCP 集成 "@opentiny/vue-common": "与 @opentiny/vue 版本一致" // Vue 通用工具 }依赖添加逻辑(genDependenciesPlugin.js):
mcpConfig.enabled === true时才添加 MCP 相关依赖二、出码流程解析
2.1 配置阶段
2.1.1 默认配置(genMcpPlugin.js)
2.1.2 配置验证
插件会验证配置的有效性:
enabled必须是布尔值agentRoot必须是有效的 URLtools中的工具类型必须是已知类型customTools必须是数组且每个工具有 name 和 implementation2.2 插件执行阶段
2.2.1 插件生命周期(generateApp.js)
2.2.2 MCP 插件执行流程(采用模板驱动)
三、 默认出码流程 vs MCP 出码流程对比
3.1 架构对比
3.2 代码生成流程对比
默认流程
MCP 流程(新增部分)
四、相关界面与过程截图
4.1 基本使用流程(以 tiny-grid 组件为例
给 tiny-grid 添加 mcp 相关配置

开启 mcp 出码相关配置、生成代码
自定义出码 MCP 工具
如何在 MCP 插件中添加新的工具生成器。通过配置驱动的方式,添加新工具只需两步:
toolGenerators中注册1. 编写工具生成函数
在
genMcpPlugin.js中添加生成函数:2. 注册到工具生成器
在
toolGenerators对象中添加:3. 配置启用工具
用户在配置中启用:
Does this PR introduce a breaking change?
Other information
Summary by CodeRabbit
New Features
Chores