rsbuild-plugin-vue-mcp
by BrightX
README.md
# Rsbuild plugin VueDevtools MCP
Language / θ―θ¨: [English](README.md) | [δΈζ](README_zh.md)
> Rsbuild/Rspack MCP plugin based on Vue DevTools.
>
> Supports `Rsbuild 1.x/2.x` and `Rspack 1.x/2.x`.
Through the [Model Context Protocol (MCP)](https://modelcontextprotocol.io), AI tools (such as IDE assistants and
agents) can read and manipulate your Vue application state in real time, truly enabling "AI that understands your
application."
This plugin bridges your dev server and the Vue DevTools running inside your app page via **birpc over WebSocket**,
exposing a set of MCP tools that let AI inspect components, router, and Pinia stores, and even edit component state
directly.
## Features
- π **Zero-config MCP server** β automatically mounted on your existing dev server (no separate process).
- π³ **Inspect the component tree** of the running app, in tree.
- π§© **Read & edit Vue component state** (reactive data, props, computed, refsβ¦).
- π¦ **Highlight a component** in the page to visually locate it.
- π§ **Read the Vue Router** info (current route, matched records, params, queryβ¦).
- ποΈ **Inspect Pinia** β browse the store tree and read individual store state.
- β‘οΈ Works with both **Rsbuild** and **Rspack** dev servers.
## Usage
### Install
```bash
# For npm
npm add rsbuild-plugin-vue-mcp -D
# For yarn
yarn add rsbuild-plugin-vue-mcp -D
# For pnpm
pnpm add rsbuild-plugin-vue-mcp -D
```
### Rsbuild
```js
// rsbuild.config.js
import { defineConfig } from '@rsbuild/core';
import { pluginVue } from '@rsbuild/plugin-vue';
import { pluginVueMcp } from "rsbuild-plugin-vue-mcp";
export default defineConfig({
plugins: [
pluginVue(),
pluginVueMcp(),
],
});
```
### Rspack
```js
// rspack.config.js
import { defineConfig } from '@rspack/cli';
import { rspack } from "@rspack/core";
import { VueLoaderPlugin } from 'rspack-vue-loader';
import { VueMcpPlugin } from 'rsbuild-plugin-vue-mcp/rspack';
export default defineConfig({
plugins: [
new rspack.HtmlRspackPlugin(),
new VueLoaderPlugin(),
new VueMcpPlugin(),
],
module: {
rules: [
{
test: /\.vue$/,
loader: 'rspack-vue-loader',
options: {
experimentalInlineMatchResource: true,
},
},
],
},
});
```
The MCP server (Streamable HTTP transport) will be available at `http://localhost:[port]/__mcp/mcp`.
> Requirements: requires `@rsbuild/core >= 1.2.9` (for Rsbuild) or `@rspack/core >= 1.3.0` (for Rspack) so the plugin
> can attach to the underlying HTTP server.
### Connect an MCP client
Start the dev server (usually `npm run dev`). It prints the MCP service URL in the console, e.g.:
```
β MCP: Server is running at http://localhost:5173/__mcp/mcp
```
Add that URL to your client's MCP configuration (Cursor, Claude Desktop, VS Code, etc.):
```json
{
"mcpServers": {
"vue-devtools": {
"type": "streamable-http",
"url": "http://localhost:<YourPort>/__mcp/mcp",
"description": "Vue DevTools MCP - Rsbuild/Rspack MCP plugin based on Vue DevTools",
"disabled": false
}
}
}
```
> [!IMPORTANT]
> To actually call the MCP tools and debug your app, **two things are required**:
> 1. The dev server is running (so the MCP server is up).
> 2. You have **opened your app page in a browser** (e.g. `http://localhost:5173`). The injected `overlay.js` then
connects to the dev server via WebSocket and exposes the Vue DevTools runtime.
>
> The tools reach the *live* app through that WebSocket connection β if no app page is open, the tool calls will fail or
> time out.
Once the page is open, the AI assistant can call the tools listed below against your running dev app.
## MCP Tools
The plugin registers the following tools on the MCP server. Each tool talks to the app page through birpc, so the data
always reflects the **live** application. All tools return their results as **JSON-formatted text** (not markdown).
| Tool | Description | Inputs |
|-----------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------|
| `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. | β |
| `toggle-app` | Toggle the activeAppRecord by its id. All operations on Components/Router/Pinia are based on the activeAppRecord. | `id: string` |
| `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) |
| `get-component-state` | Get a component's state as JSON (data, props, computed, refsβ¦). | `componentName: string` |
| `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'` |
| `highlight-component` | Highlight a component on the page (auto-clears after 5s). | `componentName: string` |
| `scroll-to-component` | Scroll the page to bring a component into view. | `componentName: string` |
| `get-component-bounds` | Get a component's bounding rect (x/y/width/height, plus viewport size and in-viewport flag). | `componentName: string` |
| `get-component-dom` | Get the rendered DOM (outerHTML) of a component (truncated to 10000 chars). | `componentName: string` |
| `get-component-render-code` | Get the compiled render function code of a component (truncated to 10000 chars). | `componentName: string` |
| `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). | β |
| `cancel-pick-component` | Cancel the pick mode started by `pick-component`. | β |
| `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) |
| `get-router-info` | Get the current Vue Router info as JSON (route, matched records, params, queryβ¦). | β |
| `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>` |
| `get-pinia-tree` | Get the Pinia store tree as JSON. | β |
| `get-pinia-state` | Get a single Pinia store's state as JSON. | `storeName: string` |
| `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'` |
### Example AI workflow
- "Show me the component tree of the current page."
- "What is the state of the `UserCard` component?"
- "Set `count` in `Counter` to `10`." β calls `edit-component-state` and the UI updates instantly.
- "Highlight the `Navbar` component." β the element flashes in the browser.
- "Scroll to the `Footer` component." β calls `scroll-to-component`.
- "Open `Footer`'s source in my editor." β calls `open-component-in-editor`.
- "Navigate to `/users/1` and show me its component tree." β calls `navigate-router`.
- "I don't know which component this button belongs to." β calls `pick-component` and the user clicks it.
- "What route are we on and what are its params?" β calls `get-router-info`.
- "Show me the `cart` Pinia store state and set `count` to `10`." β calls `get-pinia-state` + `edit-pinia-state`.
## How it works
The plugin uses **birpc** as the RPC layer and **WebSocket** as the transport between the dev server and the app page.
```mermaid
graph TD
subgraph A["MCP Host (AI Client)"]
A1[MCP Client]
A2[MCP Client]
end
subgraph B["MCP Server (Rsbuild/Rspack Dev Server)"]
B1[MCP Tools<br/>get-component-tree / get-component-state / ...]
B2[birpc group<br/>createRPCServer]
B3[WebSocket Server<br/>/__vue-devtools-mcp-ws]
end
subgraph C["Vue App (Browser)"]
C1[overlay.js injected]
C2[birpc client]
C3[Vue DevTools Kit<br/>devtools.api / ctx]
end
A <-- " streamable-http / SSE " --> B1
B1 --> B2
B2 <== " birpc over WebSocket " ==> B3
B3 --> C1 --> C2 --> C3
```
1. **Injection** β When the dev server starts, the plugin injects `overlay.js` into the app's HTML (or, when `appendTo`
is configured, appends an import to matching source modules). `overlay.js` initializes `@vue/devtools-kit` and opens
a `WebSocket` to the dev server at `/__vue-devtools-mcp-ws`.
2. **RPC bridge** β The dev server creates a birpc group (`createRPCServer`) over the WebSocket connections.
`overlay.js` creates a birpc client (`createBirpc`). Requests from the server are forwarded to the app; responses
come back via hook callbacks (`onInspectorTreeUpdated`, `onInspectorStateUpdated`, β¦).
3. **MCP layer** β MCP tool handlers (`src/mcp/server.ts`) call the birpc client to reach the app, wait for the response
through `hookable` hooks, and return it as the tool result.
This two-hop design (MCP β birpc β DevTools) means every tool call inspects or mutates the **actual running application
**, not a static snapshot.
## Configuration
Both `pluginVueMcp(options)` (Rsbuild) and `new VueMcpPlugin(options)` (Rspack) accept the same options:
```ts
interface PluginVueMcpOptions {
/** Host to listen on. Default: `localhost`. */
host?: string
/** Print the MCP server URL in the console. Default: `true`. */
printUrl?: boolean
/** Custom MCP server info (name/version). Ignored when `mcpServer` is provided. */
mcpServerInfo?: { name?: string, version?: string, ... }
/**
* Customize or replace the MCP server instance. Called whenever a server is created.
* You may register extra tools, or return a new McpServer to replace the default one.
*/
mcpServerSetup?: (server: McpServer, api: RsbuildPluginAPI | Compiler) => void | Promise<void | McpServer>
/** Path prefix for the MCP endpoint. Default: `/__mcp` (so the endpoint is `/__mcp/mcp`). */
mcpPath?: string
/**
* Instead of injecting a <script> into HTML, append an import to modules whose id
* matches this regex. Useful for projects without an HTML entry.
* WARNING: only set this if you know exactly what it does.
*/
appendTo?: string | RegExp
}
```
### Examples
Register extra MCP tools alongside the defaults:
```js
pluginVueMcp({
mcpServerSetup(server, api) {
server.registerTool('ping', { description: 'Ping the dev server' }, async () => ({
content: [{ type: 'text', text: 'pong' }],
}))
},
})
```
Use a custom MCP endpoint path:
```js
pluginVueMcp({ mcpPath: '/my-mcp' })
// β http://localhost:<port>/my-mcp/mcp
```
## Requirements
- Node.js `>= 18`
- `@rsbuild/core >= 1.2.9 || >= 2.0.0` (optional peer, for the Rsbuild plugin)
- `@rspack/core >= 1.3.0 || >= 2.0.0` (optional peer, for the Rspack plugin)
- A Vue 3 application instrumented with `@vue/devtools-kit` (handled automatically by the injected overlay).
## Debugging
You can inspect the MCP server with the official [MCP Inspector](https://modelcontextprotocol.io/docs/tools/inspector):
```bash
npx @modelcontextprotocol/inspector
```
Then point it at `http://localhost:<YourPort>/__mcp/mcp` with the Streamable HTTP transport.
## Reference / Credits
- Inspired by [vite-plugin-vue-mcp](https://github.com/webfansplz/vite-plugin-vue-mcp) β the original idea of bridging
Vue DevTools and MCP.
- [Model Context Protocol](https://modelcontextprotocol.io)
- [`birpc`](https://github.com/antfu/birpc) β the RPC layer used between dev server and app page
- [`@vue/devtools-kit`](https://github.com/vuejs/devtools-next) β Vue DevTools core API
## License
[MIT](./LICENSE)
This server cannot be deployed
Maintenance
ActivityMaintained
ResponsivenessNo issues