rsbuild-plugin-vue-mcp 0.1.2 → 0.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -130,15 +130,26 @@ Once the page is open, the AI assistant can call the tools listed below against
130
130
  The plugin registers the following tools on the MCP server. Each tool talks to the app page through birpc, so the data
131
131
  always reflects the **live** application. All tools return their results as **JSON-formatted text** (not markdown).
132
132
 
133
- | Tool | Description | Inputs |
134
- |------------------------|-----------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------|
135
- | `get-component-tree` | Get the Vue component tree. The result is returned as a **JSON** text payload. | — |
136
- | `get-component-state` | Get a component's state as JSON (data, props, computed, refs…). | `componentName: string` |
137
- | `edit-component-state` | Edit a value inside a component's state (live, reactive). | `componentName: string`, `path: string[]`, `value: string`, `valueType: 'string' \| 'number' \| 'boolean' \| 'object' \| 'array'` |
138
- | `highlight-component` | Highlight a component on the page (auto-clears after 5s). | `componentName: string` |
139
- | `get-router-info` | Get the current Vue Router info as JSON (route, matched records, params, query…). | — |
140
- | `get-pinia-tree` | Get the Pinia store tree as JSON. | — |
141
- | `get-pinia-state` | Get a single Pinia store's state as JSON. | `storeName: string` |
133
+ | Tool | Description | Inputs |
134
+ |-----------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------|
135
+ | `get-app-record-status` | Get the current DevTools App status (appRecords and activeAppRecord). Must be called first before any other tool. An empty result usually means the app page needs to be opened. | — |
136
+ | `toggle-app` | Toggle the activeAppRecord by its id. All operations on Components/Router/Pinia are based on the activeAppRecord. | `id: string` |
137
+ | `get-component-tree` | Get the Vue component tree. The result is returned as a **JSON** text payload. | `componentName?: string` (optional, query a single component by name) |
138
+ | `get-component-state` | Get a component's state as JSON (data, props, computed, refs…). | `componentName: string` |
139
+ | `edit-component-state` | Edit a value inside a component's state (live, reactive). | `componentName: string`, `path: string[]`, `value: string`, `valueType: 'string' \| 'number' \| 'boolean' \| 'object' \| 'array'` |
140
+ | `highlight-component` | Highlight a component on the page (auto-clears after 5s). | `componentName: string` |
141
+ | `scroll-to-component` | Scroll the page to bring a component into view. | `componentName: string` |
142
+ | `get-component-bounds` | Get a component's bounding rect (x/y/width/height, plus viewport size and in-viewport flag). | `componentName: string` |
143
+ | `get-component-dom` | Get the rendered DOM (outerHTML) of a component (truncated to 10000 chars). | `componentName: string` |
144
+ | `get-component-render-code` | Get the compiled render function code of a component (truncated to 10000 chars). | `componentName: string` |
145
+ | `pick-component` | Enter pick mode: the user clicks a component on the page, then its id and name are returned (blocks until click, ~30s timeout). | — |
146
+ | `cancel-pick-component` | Cancel the pick mode started by `pick-component`. | — |
147
+ | `open-component-in-editor` | Open the component's source file in the local editor via the dev server `__open-in-editor` endpoint (SFC only). | `componentName: string`, `baseUrl?: string` (defaults to the page origin) |
148
+ | `get-router-info` | Get the current Vue Router info as JSON (route, matched records, params, query…). | — |
149
+ | `navigate-router` | Navigate the router (like `router.push`) and return the new route info. Requires vue-router. | `path?: string`, `name?: string`, `params?: Record<string, string>`, `query?: Record<string, string>` |
150
+ | `get-pinia-tree` | Get the Pinia store tree as JSON. | — |
151
+ | `get-pinia-state` | Get a single Pinia store's state as JSON. | `storeName: string` |
152
+ | `edit-pinia-state` | Edit a value inside a Pinia store's state (live, reactive). | `storeName: string`, `path: string[]`, `value: string`, `valueType: 'string' \| 'number' \| 'boolean' \| 'object' \| 'array'` |
142
153
 
143
154
  ### Example AI workflow
144
155
 
@@ -146,8 +157,12 @@ always reflects the **live** application. All tools return their results as **JS
146
157
  - "What is the state of the `UserCard` component?"
147
158
  - "Set `count` in `Counter` to `10`." → calls `edit-component-state` and the UI updates instantly.
148
159
  - "Highlight the `Navbar` component." → the element flashes in the browser.
160
+ - "Scroll to the `Footer` component." → calls `scroll-to-component`.
161
+ - "Open `Footer`'s source in my editor." → calls `open-component-in-editor`.
162
+ - "Navigate to `/users/1` and show me its component tree." → calls `navigate-router`.
163
+ - "I don't know which component this button belongs to." → calls `pick-component` and the user clicks it.
149
164
  - "What route are we on and what are its params?" → calls `get-router-info`.
150
- - "Show me the state of the `cart` Pinia store."
165
+ - "Show me the `cart` Pinia store state and set `count` to `10`." → calls `get-pinia-state` + `edit-pinia-state`.
151
166
 
152
167
  ## How it works
153
168
 
package/README_zh.md CHANGED
@@ -126,15 +126,26 @@ MCP 服务(基于 Streamable HTTP 传输)将在 `http://localhost:[port]/__m
126
126
  插件会在 MCP 服务上注册以下工具。每个工具都通过 birpc 与 App 页面通信,因此数据始终是**实时**的应用状态。所有工具返回的结果均为
127
127
  **JSON 格式的文本**(不是 markdown)。
128
128
 
129
- | 工具 | 说明 | 入参 |
130
- |------------------------|-----------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------|
131
- | `get-component-tree` | 获取 Vue 组件树。返回结果为 **JSON** 文本。 | — |
132
- | `get-component-state` | 以 JSON 格式获取组件状态(data、props、computed、refs…)。 | `componentName: string` |
133
- | `edit-component-state` | 编辑组件状态中的某个值(实时、响应式)。 | `componentName: string`、`path: string[]`、`value: string`、`valueType: 'string' \| 'number' \| 'boolean' \| 'object' \| 'array'` |
134
- | `highlight-component` | 在页面中高亮某个组件(5 秒后自动取消)。 | `componentName: string` |
135
- | `get-router-info` | 以 JSON 格式获取当前 Vue Router 信息(路由、匹配记录、params、query…)。 | — |
136
- | `get-pinia-tree` | 以 JSON 格式获取 Pinia store 树。 | — |
137
- | `get-pinia-state` | 以 JSON 格式获取单个 Pinia store 的状态。 | `storeName: string` |
129
+ | 工具 | 说明 | 入参 |
130
+ |-----------------------------|-------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------|
131
+ | `get-app-record-status` | 获取当前 DevTools App 状态(appRecords 与 activeAppRecord)。调用任何其他工具前必须先调用它。空结果通常表示需要打开应用页面。 | — |
132
+ | `toggle-app` | 通过 appRecord 的 id 切换 activeAppRecord。组件/路由/Pinia 等操作都基于 activeAppRecord。 | `id: string` |
133
+ | `get-component-tree` | 获取 Vue 组件树。返回结果为 **JSON** 文本。 | `componentName?: string`(可选,按组件名查询单个组件) |
134
+ | `get-component-state` | 以 JSON 格式获取组件状态(data、props、computed、refs…)。 | `componentName: string` |
135
+ | `edit-component-state` | 编辑组件状态中的某个值(实时、响应式)。 | `componentName: string`、`path: string[]`、`value: string`、`valueType: 'string' \| 'number' \| 'boolean' \| 'object' \| 'array'` |
136
+ | `highlight-component` | 在页面中高亮某个组件(5 秒后自动取消)。 | `componentName: string` |
137
+ | `scroll-to-component` | 滚动页面使指定组件进入视口。 | `componentName: string` |
138
+ | `get-component-bounds` | 获取组件的包围盒(x/y/width/height,含视口尺寸与是否在视口内)。 | `componentName: string` |
139
+ | `get-component-dom` | 获取组件渲染后的 DOM(outerHTML,超过 10000 字符会截断)。 | `componentName: string` |
140
+ | `get-component-render-code` | 获取组件编译后的渲染函数代码(超过 10000 字符会截断)。 | `componentName: string` |
141
+ | `pick-component` | 进入组件拾取模式:用户在页面上点击某个组件后返回其 id 与名称(阻塞等待点击,约 30 秒超时)。 | — |
142
+ | `cancel-pick-component` | 取消 `pick-component` 开启的拾取模式。 | — |
143
+ | `open-component-in-editor` | 通过 dev server 的 `__open-in-editor` 端点在本地编辑器中打开组件源码(仅支持 SFC)。 | `componentName: string`、`baseUrl?: string`(默认为页面 origin) |
144
+ | `get-router-info` | 以 JSON 格式获取当前 Vue Router 信息(路由、匹配记录、params、query…)。 | — |
145
+ | `navigate-router` | 执行路由导航(类似 `router.push`),返回导航后的路由信息。需要应用安装 vue-router。 | `path?: string`、`name?: string`、`params?: Record<string, string>`、`query?: Record<string, string>` |
146
+ | `get-pinia-tree` | 以 JSON 格式获取 Pinia store 树。 | — |
147
+ | `get-pinia-state` | 以 JSON 格式获取单个 Pinia store 的状态。 | `storeName: string` |
148
+ | `edit-pinia-state` | 编辑 Pinia store 状态中的某个值(实时、响应式)。 | `storeName: string`、`path: string[]`、`value: string`、`valueType: 'string' \| 'number' \| 'boolean' \| 'object' \| 'array'` |
138
149
 
139
150
  ### AI 使用示例
140
151
 
@@ -142,8 +153,12 @@ MCP 服务(基于 Streamable HTTP 传输)将在 `http://localhost:[port]/__m
142
153
  - "`UserCard` 组件现在的状态是什么?"
143
154
  - "把 `Counter` 组件的 `count` 改成 `10`。" → 调用 `edit-component-state`,页面 UI 立即更新。
144
155
  - "高亮一下 `Navbar` 组件。" → 浏览器中对应元素闪烁。
156
+ - "滚到 `Footer` 组件那里。" → 调用 `scroll-to-component`。
157
+ - "把 `Footer` 的源码在编辑器里打开。" → 调用 `open-component-in-editor`。
158
+ - "导航到 `/users/1` 页面看看组件树。" → 调用 `navigate-router`。
159
+ - "我不知道这个按钮对应哪个组件。" → 调用 `pick-component`,让用户在页面上点一下。
145
160
  - "当前路由是什么,参数是什么?" → 调用 `get-router-info`。
146
- - "看看 `cart` 这个 Pinia store 的状态。"
161
+ - "看看 `cart` 这个 Pinia store 的状态,把 `count` 改成 `10`。" → 调用 `get-pinia-state` + `edit-pinia-state`。
147
162
 
148
163
  ## 工作原理
149
164
 
package/dist/396.js CHANGED
@@ -28,7 +28,7 @@ function createRPCServer(ws, functions, options = {}) {
28
28
  return group.broadcast;
29
29
  }
30
30
  var package_namespaceObject = {
31
- rE: "0.1.2"
31
+ rE: "0.1.3"
32
32
  };
33
33
  function stringify(value) {
34
34
  return "string" == typeof value ? value : JSON.stringify(value);
@@ -40,9 +40,23 @@ function createMcpServerDefault(options, ctx) {
40
40
  version: package_namespaceObject.rE,
41
41
  ...options.mcpServerInfo
42
42
  });
43
+ server.registerPrompt('app-record-guide', {
44
+ title: 'App Record Guide',
45
+ description: 'How to read the App Record status and switch the active app instance before using other MCP tools.'
46
+ }, async ()=>({
47
+ messages: [
48
+ {
49
+ role: 'user',
50
+ content: {
51
+ type: 'text',
52
+ text: "Before using any other MCP tools, call `get-app-record-status` to read the current Vue DevTools App status (appRecords and activeAppRecord).\n\nHow to interpret the result:\n- Empty content: no web page with an app is open. The user needs to open the page in a browser first.\n- Multiple content items: the user has multiple app pages open. Close all but one to avoid interference between them.\n- A content item containing multiple appRecords: multiple Vue App instances exist on the same page. Use `toggle-app` (with the appRecord `id`) to switch the activeAppRecord.\n\nAll MCP tool operations on Components, Router, Pinia, etc. are performed based on the activeAppRecord. When you need to target a different app instance, call `toggle-app` first."
53
+ }
54
+ }
55
+ ]
56
+ }));
43
57
  server.registerTool('get-app-record-status', {
44
58
  title: 'App Record Status',
45
- description: "Get the current Devtools App status (appRecords and activeAppRecord). Must be called first before any other MCP tools. An empty content response usually means the user needs to open a web page with an app to debug. If multiple content items are returned, the user has multiple app pages open and should close all but one to avoid interference. If a content contains multiple appRecords, it means multiple Vue App instances exist on the page. `toggle-app` to switch activeAppRecord. "
59
+ description: "Get the current Devtools App status (appRecords and activeAppRecord). Must be called first before any other MCP tools. See the `app-record-guide` prompt for how to interpret the result."
46
60
  }, async ()=>{
47
61
  const res = await ctx.rpcServer.getAppRecordStatus();
48
62
  return {
@@ -54,7 +68,7 @@ function createMcpServerDefault(options, ctx) {
54
68
  });
55
69
  server.registerTool('toggle-app', {
56
70
  title: 'Toggle App',
57
- description: 'Toggle the activeAppRecord by the id of the appRecord. All MCP tool operations on Components, Router, Pinia, etc. are performed based on the activeAppRecord.',
71
+ description: 'Toggle the activeAppRecord by the id of the appRecord. All MCP tool operations on Components, Router, Pinia, etc. are performed based on the activeAppRecord. See the `app-record-guide` prompt for details.',
58
72
  inputSchema: {
59
73
  id: z.string()
60
74
  }
@@ -151,6 +165,116 @@ function createMcpServerDefault(options, ctx) {
151
165
  ]
152
166
  };
153
167
  });
168
+ server.registerTool('scroll-to-component', {
169
+ description: 'Scroll the page to bring the Vue component into view.',
170
+ inputSchema: {
171
+ componentName: z.string().describe('by componentName (the component\'s name, not uid)')
172
+ }
173
+ }, async ({ componentName })=>{
174
+ await ctx.rpcServer.scrollToComponent({
175
+ componentName
176
+ });
177
+ return {
178
+ content: [
179
+ {
180
+ type: 'text',
181
+ text: 'ok'
182
+ }
183
+ ]
184
+ };
185
+ });
186
+ server.registerTool('get-component-bounds', {
187
+ description: 'Get the bounding rect of the Vue component (x/y/width/height relative to the viewport, plus viewport size and whether it is currently in the viewport).',
188
+ inputSchema: {
189
+ componentName: z.string().describe('by componentName (the component\'s name, not uid)')
190
+ }
191
+ }, async ({ componentName })=>{
192
+ const res = await ctx.rpcServer.getComponentBounds({
193
+ componentName
194
+ });
195
+ return {
196
+ content: res.map((r)=>({
197
+ type: 'text',
198
+ text: stringify(r)
199
+ }))
200
+ };
201
+ });
202
+ server.registerTool('get-component-dom', {
203
+ description: 'Get the rendered DOM (outerHTML) of the Vue component. The output is truncated to 10000 characters.',
204
+ inputSchema: {
205
+ componentName: z.string().describe('by componentName (the component\'s name, not uid)')
206
+ }
207
+ }, async ({ componentName })=>{
208
+ const res = await ctx.rpcServer.getComponentDom({
209
+ componentName
210
+ });
211
+ return {
212
+ content: res.map((r)=>({
213
+ type: 'text',
214
+ text: stringify(r)
215
+ }))
216
+ };
217
+ });
218
+ server.registerTool('get-component-render-code', {
219
+ description: 'Get the compiled render function code of the Vue component. The output is truncated to 10000 characters.',
220
+ inputSchema: {
221
+ componentName: z.string().describe('by componentName (the component\'s name, not uid)')
222
+ }
223
+ }, async ({ componentName })=>{
224
+ const res = await ctx.rpcServer.getComponentRenderCode({
225
+ componentName
226
+ });
227
+ return {
228
+ content: res.map((r)=>({
229
+ type: 'text',
230
+ text: stringify(r)
231
+ }))
232
+ };
233
+ });
234
+ server.registerTool('pick-component', {
235
+ description: "Enter component pick mode: the user clicks a component on the page, then the tool returns its id and name. It blocks until the user clicks (may time out, birpc timeout is 30s). Use `cancel-pick-component` to cancel. Useful when you don't know the name of the target component."
236
+ }, async ()=>{
237
+ const res = await ctx.rpcServer.pickComponent();
238
+ return {
239
+ content: res.map((r)=>({
240
+ type: 'text',
241
+ text: stringify(r)
242
+ }))
243
+ };
244
+ });
245
+ server.registerTool('cancel-pick-component', {
246
+ description: 'Cancel the component pick mode started by `pick-component`.'
247
+ }, async ()=>{
248
+ await ctx.rpcServer.cancelPickComponent();
249
+ return {
250
+ content: [
251
+ {
252
+ type: 'text',
253
+ text: 'ok'
254
+ }
255
+ ]
256
+ };
257
+ });
258
+ server.registerTool('open-component-in-editor', {
259
+ description: "Open the Vue component's source file in the local editor via the dev server `__open-in-editor` endpoint. Only works for SFC components (requires `__file` in the compiled component).",
260
+ inputSchema: {
261
+ componentName: z.string().describe('by componentName (the component\'s name, not uid)'),
262
+ baseUrl: z.string().optional().describe('dev server base url, defaults to the page origin')
263
+ }
264
+ }, async ({ componentName, baseUrl })=>{
265
+ await ctx.rpcServer.openComponentInEditor({
266
+ componentName,
267
+ baseUrl
268
+ });
269
+ return {
270
+ content: [
271
+ {
272
+ type: 'text',
273
+ text: 'ok'
274
+ }
275
+ ]
276
+ };
277
+ });
154
278
  server.registerTool('get-router-info', {
155
279
  description: 'Get the Vue router info in JSON structure format.'
156
280
  }, async ()=>{
@@ -162,6 +286,28 @@ function createMcpServerDefault(options, ctx) {
162
286
  }))
163
287
  };
164
288
  });
289
+ server.registerTool('navigate-router', {
290
+ description: 'Navigate the Vue Router (like router.push) and return the new route info. Requires vue-router to be installed in the app.',
291
+ inputSchema: {
292
+ path: z.string().optional().describe('target path, e.g. `/users/1`'),
293
+ name: z.string().optional().describe('target route name (used when `path` is not provided)'),
294
+ params: z.record(z.string(), z.string()).optional().describe('route params for named routes'),
295
+ query: z.record(z.string(), z.string()).optional().describe('query params, e.g. `{ "tab": "1" }`')
296
+ }
297
+ }, async ({ path, name, params, query })=>{
298
+ const res = await ctx.rpcServer.navigateRouter({
299
+ path,
300
+ name,
301
+ params,
302
+ query
303
+ });
304
+ return {
305
+ content: res.map((r)=>({
306
+ type: 'text',
307
+ text: stringify(r)
308
+ }))
309
+ };
310
+ });
165
311
  server.registerTool('get-pinia-state', {
166
312
  description: 'Get the Pinia state in JSON structure format.',
167
313
  inputSchema: {
@@ -178,6 +324,36 @@ function createMcpServerDefault(options, ctx) {
178
324
  }))
179
325
  };
180
326
  });
327
+ server.registerTool('edit-pinia-state', {
328
+ description: 'Edit a value inside a Pinia store\'s state (live, reactive).',
329
+ inputSchema: {
330
+ storeName: z.string(),
331
+ path: z.array(z.string()).describe('path to the value inside the store state, e.g. `["items", "0", "name"]`'),
332
+ value: z.string(),
333
+ valueType: z["enum"]([
334
+ 'string',
335
+ 'number',
336
+ 'boolean',
337
+ 'object',
338
+ 'array'
339
+ ])
340
+ }
341
+ }, async ({ storeName, path, value, valueType })=>{
342
+ await ctx.rpcServer.editPiniaState({
343
+ storeName,
344
+ path,
345
+ value,
346
+ valueType
347
+ });
348
+ return {
349
+ content: [
350
+ {
351
+ type: 'text',
352
+ text: 'ok'
353
+ }
354
+ ]
355
+ };
356
+ });
181
357
  server.registerTool('get-pinia-tree', {
182
358
  description: 'Get the Pinia tree in JSON structure format.'
183
359
  }, async ()=>{
package/dist/index.cjs CHANGED
@@ -76,7 +76,7 @@ function createRPCServer(ws, functions, options = {}) {
76
76
  }
77
77
  const mcp_js_namespaceObject = require("@modelcontextprotocol/sdk/server/mcp.js");
78
78
  var package_namespaceObject = {
79
- rE: "0.1.2"
79
+ rE: "0.1.3"
80
80
  };
81
81
  const external_zod_namespaceObject = require("zod");
82
82
  function stringify(value) {
@@ -89,9 +89,23 @@ function createMcpServerDefault(options, ctx) {
89
89
  version: package_namespaceObject.rE,
90
90
  ...options.mcpServerInfo
91
91
  });
92
+ server.registerPrompt('app-record-guide', {
93
+ title: 'App Record Guide',
94
+ description: 'How to read the App Record status and switch the active app instance before using other MCP tools.'
95
+ }, async ()=>({
96
+ messages: [
97
+ {
98
+ role: 'user',
99
+ content: {
100
+ type: 'text',
101
+ text: "Before using any other MCP tools, call `get-app-record-status` to read the current Vue DevTools App status (appRecords and activeAppRecord).\n\nHow to interpret the result:\n- Empty content: no web page with an app is open. The user needs to open the page in a browser first.\n- Multiple content items: the user has multiple app pages open. Close all but one to avoid interference between them.\n- A content item containing multiple appRecords: multiple Vue App instances exist on the same page. Use `toggle-app` (with the appRecord `id`) to switch the activeAppRecord.\n\nAll MCP tool operations on Components, Router, Pinia, etc. are performed based on the activeAppRecord. When you need to target a different app instance, call `toggle-app` first."
102
+ }
103
+ }
104
+ ]
105
+ }));
92
106
  server.registerTool('get-app-record-status', {
93
107
  title: 'App Record Status',
94
- description: "Get the current Devtools App status (appRecords and activeAppRecord). Must be called first before any other MCP tools. An empty content response usually means the user needs to open a web page with an app to debug. If multiple content items are returned, the user has multiple app pages open and should close all but one to avoid interference. If a content contains multiple appRecords, it means multiple Vue App instances exist on the page. `toggle-app` to switch activeAppRecord. "
108
+ description: "Get the current Devtools App status (appRecords and activeAppRecord). Must be called first before any other MCP tools. See the `app-record-guide` prompt for how to interpret the result."
95
109
  }, async ()=>{
96
110
  const res = await ctx.rpcServer.getAppRecordStatus();
97
111
  return {
@@ -103,7 +117,7 @@ function createMcpServerDefault(options, ctx) {
103
117
  });
104
118
  server.registerTool('toggle-app', {
105
119
  title: 'Toggle App',
106
- description: 'Toggle the activeAppRecord by the id of the appRecord. All MCP tool operations on Components, Router, Pinia, etc. are performed based on the activeAppRecord.',
120
+ description: 'Toggle the activeAppRecord by the id of the appRecord. All MCP tool operations on Components, Router, Pinia, etc. are performed based on the activeAppRecord. See the `app-record-guide` prompt for details.',
107
121
  inputSchema: {
108
122
  id: external_zod_namespaceObject.z.string()
109
123
  }
@@ -200,6 +214,116 @@ function createMcpServerDefault(options, ctx) {
200
214
  ]
201
215
  };
202
216
  });
217
+ server.registerTool('scroll-to-component', {
218
+ description: 'Scroll the page to bring the Vue component into view.',
219
+ inputSchema: {
220
+ componentName: external_zod_namespaceObject.z.string().describe('by componentName (the component\'s name, not uid)')
221
+ }
222
+ }, async ({ componentName })=>{
223
+ await ctx.rpcServer.scrollToComponent({
224
+ componentName
225
+ });
226
+ return {
227
+ content: [
228
+ {
229
+ type: 'text',
230
+ text: 'ok'
231
+ }
232
+ ]
233
+ };
234
+ });
235
+ server.registerTool('get-component-bounds', {
236
+ description: 'Get the bounding rect of the Vue component (x/y/width/height relative to the viewport, plus viewport size and whether it is currently in the viewport).',
237
+ inputSchema: {
238
+ componentName: external_zod_namespaceObject.z.string().describe('by componentName (the component\'s name, not uid)')
239
+ }
240
+ }, async ({ componentName })=>{
241
+ const res = await ctx.rpcServer.getComponentBounds({
242
+ componentName
243
+ });
244
+ return {
245
+ content: res.map((r)=>({
246
+ type: 'text',
247
+ text: stringify(r)
248
+ }))
249
+ };
250
+ });
251
+ server.registerTool('get-component-dom', {
252
+ description: 'Get the rendered DOM (outerHTML) of the Vue component. The output is truncated to 10000 characters.',
253
+ inputSchema: {
254
+ componentName: external_zod_namespaceObject.z.string().describe('by componentName (the component\'s name, not uid)')
255
+ }
256
+ }, async ({ componentName })=>{
257
+ const res = await ctx.rpcServer.getComponentDom({
258
+ componentName
259
+ });
260
+ return {
261
+ content: res.map((r)=>({
262
+ type: 'text',
263
+ text: stringify(r)
264
+ }))
265
+ };
266
+ });
267
+ server.registerTool('get-component-render-code', {
268
+ description: 'Get the compiled render function code of the Vue component. The output is truncated to 10000 characters.',
269
+ inputSchema: {
270
+ componentName: external_zod_namespaceObject.z.string().describe('by componentName (the component\'s name, not uid)')
271
+ }
272
+ }, async ({ componentName })=>{
273
+ const res = await ctx.rpcServer.getComponentRenderCode({
274
+ componentName
275
+ });
276
+ return {
277
+ content: res.map((r)=>({
278
+ type: 'text',
279
+ text: stringify(r)
280
+ }))
281
+ };
282
+ });
283
+ server.registerTool('pick-component', {
284
+ description: "Enter component pick mode: the user clicks a component on the page, then the tool returns its id and name. It blocks until the user clicks (may time out, birpc timeout is 30s). Use `cancel-pick-component` to cancel. Useful when you don't know the name of the target component."
285
+ }, async ()=>{
286
+ const res = await ctx.rpcServer.pickComponent();
287
+ return {
288
+ content: res.map((r)=>({
289
+ type: 'text',
290
+ text: stringify(r)
291
+ }))
292
+ };
293
+ });
294
+ server.registerTool('cancel-pick-component', {
295
+ description: 'Cancel the component pick mode started by `pick-component`.'
296
+ }, async ()=>{
297
+ await ctx.rpcServer.cancelPickComponent();
298
+ return {
299
+ content: [
300
+ {
301
+ type: 'text',
302
+ text: 'ok'
303
+ }
304
+ ]
305
+ };
306
+ });
307
+ server.registerTool('open-component-in-editor', {
308
+ description: "Open the Vue component's source file in the local editor via the dev server `__open-in-editor` endpoint. Only works for SFC components (requires `__file` in the compiled component).",
309
+ inputSchema: {
310
+ componentName: external_zod_namespaceObject.z.string().describe('by componentName (the component\'s name, not uid)'),
311
+ baseUrl: external_zod_namespaceObject.z.string().optional().describe('dev server base url, defaults to the page origin')
312
+ }
313
+ }, async ({ componentName, baseUrl })=>{
314
+ await ctx.rpcServer.openComponentInEditor({
315
+ componentName,
316
+ baseUrl
317
+ });
318
+ return {
319
+ content: [
320
+ {
321
+ type: 'text',
322
+ text: 'ok'
323
+ }
324
+ ]
325
+ };
326
+ });
203
327
  server.registerTool('get-router-info', {
204
328
  description: 'Get the Vue router info in JSON structure format.'
205
329
  }, async ()=>{
@@ -211,6 +335,28 @@ function createMcpServerDefault(options, ctx) {
211
335
  }))
212
336
  };
213
337
  });
338
+ server.registerTool('navigate-router', {
339
+ description: 'Navigate the Vue Router (like router.push) and return the new route info. Requires vue-router to be installed in the app.',
340
+ inputSchema: {
341
+ path: external_zod_namespaceObject.z.string().optional().describe('target path, e.g. `/users/1`'),
342
+ name: external_zod_namespaceObject.z.string().optional().describe('target route name (used when `path` is not provided)'),
343
+ params: external_zod_namespaceObject.z.record(external_zod_namespaceObject.z.string(), external_zod_namespaceObject.z.string()).optional().describe('route params for named routes'),
344
+ query: external_zod_namespaceObject.z.record(external_zod_namespaceObject.z.string(), external_zod_namespaceObject.z.string()).optional().describe('query params, e.g. `{ "tab": "1" }`')
345
+ }
346
+ }, async ({ path, name, params, query })=>{
347
+ const res = await ctx.rpcServer.navigateRouter({
348
+ path,
349
+ name,
350
+ params,
351
+ query
352
+ });
353
+ return {
354
+ content: res.map((r)=>({
355
+ type: 'text',
356
+ text: stringify(r)
357
+ }))
358
+ };
359
+ });
214
360
  server.registerTool('get-pinia-state', {
215
361
  description: 'Get the Pinia state in JSON structure format.',
216
362
  inputSchema: {
@@ -227,6 +373,36 @@ function createMcpServerDefault(options, ctx) {
227
373
  }))
228
374
  };
229
375
  });
376
+ server.registerTool('edit-pinia-state', {
377
+ description: 'Edit a value inside a Pinia store\'s state (live, reactive).',
378
+ inputSchema: {
379
+ storeName: external_zod_namespaceObject.z.string(),
380
+ path: external_zod_namespaceObject.z.array(external_zod_namespaceObject.z.string()).describe('path to the value inside the store state, e.g. `["items", "0", "name"]`'),
381
+ value: external_zod_namespaceObject.z.string(),
382
+ valueType: external_zod_namespaceObject.z["enum"]([
383
+ 'string',
384
+ 'number',
385
+ 'boolean',
386
+ 'object',
387
+ 'array'
388
+ ])
389
+ }
390
+ }, async ({ storeName, path, value, valueType })=>{
391
+ await ctx.rpcServer.editPiniaState({
392
+ storeName,
393
+ path,
394
+ value,
395
+ valueType
396
+ });
397
+ return {
398
+ content: [
399
+ {
400
+ type: 'text',
401
+ text: 'ok'
402
+ }
403
+ ]
404
+ };
405
+ });
230
406
  server.registerTool('get-pinia-tree', {
231
407
  description: 'Get the Pinia tree in JSON structure format.'
232
408
  }, async ()=>{