@llui/mcp
v0.12.5
Published
LLui MCP server — LLM debug tools via Model Context Protocol
Readme
@llui/mcp
Model Context Protocol server for LLui. Exposes debug tools for LLM-assisted development.
pnpm add -D @llui/mcpUsage
The MCP server has two transports and two usage patterns.
Plugin-launched (recommended): one-terminal dev
Install @llui/mcp as a dev dependency. The Vite plugin auto-detects the package and spawns llui-mcp --http as a child of the dev server. One pnpm dev starts everything; no second terminal, no stdio fuss.
// vite.config.ts
import llui from '@llui/vite-plugin'
export default defineConfig({ plugins: [llui()] })Point your MCP client (e.g. Claude Code) at the HTTP endpoint. In .mcp.json:
{
"mcpServers": {
"llui": {
"type": "http",
"url": "http://127.0.0.1:5200/mcp"
}
}
}The MCP protocol runs on POST /mcp; the browser-relay WebSocket bridge shares the same port via upgrade on /bridge.
Stdio (manual spawn): traditional MCP client
If your MCP client spawns servers over stdio (the older pattern), run the CLI without --http:
{
"mcpServers": {
"llui": {
"command": "npx",
"args": ["llui-mcp"]
}
}
}The server talks stdio to the client and stands up its own WebSocket bridge on port 5200 for the browser relay. With this pattern, set mcpPort: 5200 explicitly in the Vite plugin so it wires to the externally-managed server instead of spawning its own:
export default defineConfig({ plugins: [llui({ mcpPort: 5200 })] })Troubleshooting: llui-mcp doctor
If a tool call returns a bridge-unavailable error or Claude simply can't talk to a running app, run the doctor to see what's wrong:
npx llui-mcp doctorIt checks, in order:
- Is the active-marker file at
node_modules/.cache/llui-mcp/active.jsonpresent? - Is the marker JSON parseable?
- Has the Vite plugin stamped its
devUrlinto the marker? - Is the bridge port listening on 127.0.0.1?
- Is the PID recorded in the marker still alive?
Each check prints ✓ or ✗ with a one-line detail. Exit code is 0 when everything passes, 1 when any check fails.
Tools
State Inspection
| Tool | Description |
| ---------------- | ---------------------------------- |
| get_state | Get current component state |
| describe_state | Describe state shape and types |
| search_state | Search state tree by path or value |
Messaging
| Tool | Description |
| ------------------ | ---------------------------------------- |
| send_message | Dispatch a message to the component |
| validate_message | Check if a message matches the Msg union |
History and Replay
| Tool | Description |
| --------------------- | -------------------------------------- |
| get_message_history | List all dispatched messages |
| export_trace | Export message trace for replayTrace |
| replay_trace | Replay a trace and compare states |
Bindings and DOM
| Tool | Description |
| ---------------- | ---------------------------------------------- |
| get_bindings | List all active bindings and their masks |
| why_did_update | Explain which state change triggered a binding |
| trace_element | Trace a DOM element back to its binding |
Bitmask Debugging
| Tool | Description |
| ------------- | -------------------------------------- |
| decode_mask | Decode a bitmask into state path names |
| mask_legend | Show the full bit-to-path mapping |
Snapshots
| Tool | Description |
| ---------------- | ----------------------------------- |
| snapshot_state | Save a named state snapshot |
| restore_state | Restore a previously saved snapshot |
Multi-Mount
| Tool | Description |
| ------------------ | ------------------------------------------ |
| list_components | List all mounted component instances |
| select_component | Select a component for subsequent commands |
View and DOM
| Tool | Description |
| ------------------- | ------------------------------------------------------------------------ |
| inspect_element | Rich report: tag, attrs, classes, data-*, text, computed, box, bindings |
| get_rendered_html | outerHTML of a selector (default = mount root), truncatable |
| dom_diff | Compare expected HTML against rendered HTML |
| dispatch_event | Synthesize a browser event; returns Msgs produced + resulting state |
| get_focus | Active element info: selector, tag, selection range |
Bindings and Scope
| Tool | Description |
| -------------------- | -------------------------------------------------------------- |
| force_rerender | Re-evaluate all bindings; returns indices that changed |
| each_diff | Per-each-site add/remove/move/reuse per update |
| scope_tree | Scope hierarchy with kind (root/show/each/branch/child/portal) |
| disposer_log | Recent scope disposals with cause |
| list_dead_bindings | Bindings that are dead or have never changed value |
| binding_graph | state path -> binding indices (inverts compiler mask legend) |
Effects
| Tool | Description |
| ----------------- | ---------------------------------------------------------------------- |
| pending_effects | Queued and in-flight effects |
| effect_timeline | Phased log: dispatched -> in-flight -> resolved/cancelled |
| mock_effect | Register match->response mock; next matching effect resolves with mock |
| resolve_effect | Manually resolve a specific pending effect |
Time Travel and Utilities
| Tool | Description |
| ---------------- | -------------------------------------------------------------- |
| step_back | Rewind N messages by replaying from init (pure mode default) |
| coverage | Per-Msg variant fire counts + list of never-fired variants |
| diff_state | Structured JSON diff between two state values |
| assert | Evaluate eq/neq/exists/gt/lt/in against a state path |
| search_history | Filter history by type, statePath change, effectType, or range |
Eval
| Tool | Description |
| ------ | --------------------------------------------------------------------- |
| eval | Arbitrary JS in page context; returns result + observability envelope |
