A Vite plugin that creates an MCP server to help LLMs understand your React App context
-
highlight-component- description: Highlight React component based on the component name.
- params:
componentName: string
-
get-component-states- description: Get the React component props, states, and contexts in JSON structure format.
- params:
componentName: string
-
get-component-tree- description: Get the React component tree of the current page in ASCII format.
- params:
allComponent: boolean, if truthy, return a tree for all components instead of your self-defined components only.
-
get-unnecessary-rerenders- description: Get the wasted re-rendered components of the current page.
- params:
timeframe: number, if present, only get unnecessary renders within the lasttimeframeseconds. If not, get all unnecessary renders happened on the current page.allComponent: boolean, if truthy, get unnecessary renders for all components instead of self-defined components only.
-
Custom Tools
You can now define your own tool functions in JS/TS in your Vite project, and inject it to the plugin.
pnpm install vite-react-mcp -DYou also need @babel/preset-react installed, as this plugins traverses AST to collect your React components names.
pnpm install @babel/preset-react// vite.config.ts
import ReactMCP from 'vite-react-mcp'
export default defineConfig({
plugins: [ReactMCP()],
})Define your own tool in your Vite project, e.g.
// any ts file in your Vite project
import type { ToolResultValue } from 'vite-react-mcp';
export default function myCustomTool(args: { message: string }): ToolResultValue {
const { message } = args;
console.log(`[custom-tool/log1] ${message}`);
return {
success: true,
message: `Log1 received: ${message}`,
};
}// vite.config.ts
import ReactMCP from 'vite-react-mcp'
import log1 from 'path/to/your/module'
export default defineConfig({
plugins: [ReactMCP({
customTools: [
{
name: 'log1',
description: 'Log1',
schema: z.object({
message: z.string(),
}),
clientFunction: log1,
}
]
})],
})Then run your app in dev.
Note: vite-react-mcp is meant to be used in dev environment only
At this point, you already can access window.__VITE_REACT_MCP_TOOLS__ to use the tools in Developer panel on your browser.
To expose it as an MCP server, setup MCP configuration in your MCP client.
-
For Cursor, create a
./cursor/mcp.jsonat the root level of your react project.{ "mcpServers": { "vite-react-mcp": { "url": "http://localhost:3000/sse" } } }Make sure the port is the same as your react app
-
For Claude Desktop, it requires a bit of workaround. If you are interested, you can take a look at this thread.
The reason is Claude MCP Client does execution based on command, while what we have here is HTTP based API. You need to write a script acting as a bridge to make it look like execution based.
This plugin bridges an MCP server with your React app's runtime, enabling LLMs to inspect and interact with your components in a live browser session. Here's the full picture:
MCP Client (Cursor, etc.)
│
│ SSE + HTTP POST (/sse, /messages)
▼
Vite Dev Server (Node.js)
├── MCP Server (handles tool listing & calls)
└── HMR WebSocket
│
│ custom events via Vite HMR
▼
Browser
├── overlay.js (tool implementations + WebSocket listeners)
├── bippy (React fiber access via __REACT_DEVTOOLS_GLOBAL_HOOK__)
└── window.__VITE_REACT_MCP_TOOLS__ (tool registry)
-
Babel AST pass (build time) — During Vite's
transformhook, each.js/.jsx/.ts/.tsxfile is parsed with@babel/preset-reactto collect all user-defined React component names (function components, class components,memo/forwardRefwrappers, etc.). These names are stored and later injected into the page aswindow.__REACT_COMPONENTS__. -
Browser-side injection (runtime) — At dev startup, scripts are injected into the HTML
<head>:- The collected component names are set on
window.__REACT_COMPONENTS__. overlay.jsis loaded as a module. It uses bippy to install a__REACT_DEVTOOLS_GLOBAL_HOOK__onwindow, giving direct access to the React fiber tree without requiring the React DevTools browser extension. It also hooks into React's commit lifecycle (onCommitFiberRoot) to continuously track fiber roots and detect unnecessary re-renders.- The overlay exposes all built-in tool functions (highlight, tree, states, re-renders) on
window.__VITE_REACT_MCP_TOOLS__and registers Vite HMR listeners (import.meta.hot.on(...)) for each tool.
- The collected component names are set on
-
MCP server (SSE transport) — When the Vite dev server starts, the plugin attaches two HTTP endpoints:
GET /sse— Establishes a long-lived Server-Sent Events connection with the MCP client.POST /messages?sessionId=<id>— Receives JSON-RPC tool-call requests from the MCP client.
The MCP server advertises all built-in and custom tools (with JSON Schema descriptions derived from their Zod schemas) so any MCP-compatible client can discover and invoke them.
-
Tool call flow — When an MCP client invokes a tool (e.g.
highlight-component):- The MCP server receives the JSON-RPC request via the
/messagesendpoint. - It validates the arguments against the tool's Zod schema.
- It sends a custom event through Vite's HMR WebSocket to the browser (e.g.
highlight-componentwith the serialized args). - The browser-side HMR listener picks up the event, executes the tool function against the live React fiber tree, and sends the result back via another HMR event (e.g.
highlight-component-response). - The MCP server awaits this response, wraps it in a JSON-RPC result, and streams it back to the MCP client over SSE.
- The MCP server receives the JSON-RPC request via the
-
Custom tools — User-defined tools follow the same WebSocket round-trip. Their handler functions are registered in the browser at startup via dynamic
import()or inline injection, and corresponding HMR listeners are created automatically.
pnpm run playgroundThe playground contains a simple user profile application to test React component interactions.
This project is inspired by vite-plugin-vue-mcp. Thanks for the awesome idea bridging mcp and devtools.
MIT




