Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 44 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -137,10 +137,50 @@ To expose it as an MCP server, setup MCP configuration in your MCP client.

### How it works

MCP exposes a tool-call api through natural language. The tool itself is injected on your browser runtime.
It works without requiring React Devtools extension, as we use `bippy`, which injects a `__REACT_GLOBAL_DEVTOOLS_HOOK__`
to `window`. The tool then is triggered from vite's websocket call to do corresponding actions by receiving mcp tool call
command from the mcp client you interact.
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:

#### Architecture overview

```
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)
```

#### Step by step

1. **Babel AST pass (build time)** — During Vite's `transform` hook, each `.js/.jsx/.ts/.tsx` file is parsed with `@babel/preset-react` to collect all user-defined React component names (function components, class components, `memo`/`forwardRef` wrappers, etc.). These names are stored and later injected into the page as `window.__REACT_COMPONENTS__`.

2. **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.js` is loaded as a module. It uses [bippy](https://github.com/nicholascostadev/bippy) to install a `__REACT_DEVTOOLS_GLOBAL_HOOK__` on `window`, 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.

3. **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.

4. **Tool call flow** — When an MCP client invokes a tool (e.g. `highlight-component`):
1. The MCP server receives the JSON-RPC request via the `/messages` endpoint.
2. It validates the arguments against the tool's Zod schema.
3. It sends a custom event through Vite's HMR WebSocket to the browser (e.g. `highlight-component` with the serialized args).
4. 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`).
5. The MCP server awaits this response, wraps it in a JSON-RPC result, and streams it back to the MCP client over SSE.

5. **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.

### Test

Expand Down
Loading