Porthole
No description available
Ask AI about Porthole
Powered by Claude Β· Grounded in docs
I know everything about Porthole. Ask me about installation, configuration, usage, or troubleshooting.
0/500
Reviews
Documentation
Porthole
MCP generative UI for Claude Code β charts, diagrams, dashboards rendered in a persistent browser canvas.
What is this?
Porthole is a single-process MCP server that gives Claude Code a visual output channel. When Claude calls show_widget, the result appears instantly in your browser β no copy-paste, no screenshots.
One process, three roles:
| Role | What it does |
|---|---|
| MCP stdio server | Exposes show_widget + visualize_read_me tools to Claude Code |
| HTTP :8080 | Serves the canvas UI |
| WebSocket /ws | Pushes widgets to the browser in real-time |
Features
- Chart.js charts β bar, line, pie, radar, scatter with dark theme
- SVG diagrams β flow charts, architecture diagrams, trees
- Metric dashboards β cards, tables, badges, grids
- Interactive widgets β tabs, sliders, buttons that send data back to Claude
- Widget history β sidebar tracks all widgets, click to revisit
- Hook notifications β toast alerts when Claude finishes or needs attention
- Design system β CSS variables, consistent dark theme, no hardcoded colors
Quick start
git clone https://github.com/robbinfan/porthole.git
cd porthole
npm install
npm run build
Register as MCP server
claude mcp add --scope user porthole \
-- node /path/to/porthole/dist/index.js
Or edit ~/.claude.json directly:
{
"mcpServers": {
"porthole": {
"command": "node",
"args": ["/path/to/porthole/dist/index.js"]
}
}
}
Open the canvas
open http://localhost:8080
That's it. Every show_widget call from Claude now renders in your browser.
Tell Claude to use it
Add to your CLAUDE.md (project root or ~/.claude/CLAUDE.md):
# Visualization
You have access to `show_widget` and `visualize_read_me` MCP tools.
Use them automatically when producing:
- Metrics comparisons, benchmarks, performance data
- Charts, time-series, A/B analysis
- Architecture or flow diagrams
- Any table with >3 numeric columns
Call procedure:
1. visualize_read_me(modules) β silent, don't mention to user
- numbers/metrics β ["chart", "mockup"]
- diagrams β ["diagram"]
- interactive β ["interactive", "chart"]
2. show_widget(i_have_seen_read_me: true, title, widget_code, ...)
3. Brief text summary after β no markdown tables duplicating the widget.
Widget sizes: comparison=900Γ560, single chart=720Γ480, dashboard=1100Γ680
How it works
Claude Code ββstdioβββΆ Porthole MCP Server ββwsβββΆ Browser Canvas
β
HTTP :8080
serves UI
- Claude calls
visualize_read_me(["chart"])β gets design guidelines - Claude calls
show_widget({ title, widget_code })β server broadcasts via WebSocket - Browser receives widget and renders Chart.js / SVG / HTML
Widget code is a raw HTML fragment: <style> β content β <script> (streaming-friendly order).
Project structure
porthole/
βββ src/
β βββ index.ts # Entry point
β βββ server.ts # HTTP + WebSocket + MCP server
β βββ guidelines.ts # Design system guidelines
βββ public/
β βββ canvas.html # Browser UI
βββ assets/
β βββ porthole-logo.svg
β βββ porthole-wordmark.svg
βββ dist/ # Compiled output
βββ package.json
βββ tsconfig.json
HTTP API
| Endpoint | Method | Description |
|---|---|---|
/ | GET | Canvas UI |
/health | GET | Server status JSON |
/logo.svg | GET | Porthole logo |
/hook | POST | Receive hook notifications (toasts) |
/api/widget | POST | Push widget (testing, non-MCP) |
Hook notifications (optional)
Notifications appear as toasts in the canvas UI.
~/.claude/hooks/notify-porthole.sh:
#!/usr/bin/env bash
INPUT="$(cat)"
curl -sf --max-time 2 -X POST \
-H 'Content-Type: application/json' \
-d "$INPUT" \
http://localhost:8080/hook > /dev/null 2>&1 || true
exit 0
~/.claude/settings.json:
{
"hooks": {
"Stop": [{ "hooks": [{ "type": "command", "command": "bash ~/.claude/hooks/notify-porthole.sh" }] }],
"Notification": [{ "hooks": [{ "type": "command", "command": "bash ~/.claude/hooks/notify-porthole.sh" }] }]
}
}
Environment variables
| Variable | Default | Description |
|---|---|---|
CANVAS_PORT | 8080 | HTTP + WebSocket port |
Run on Coder
git clone https://github.com/robbinfan/porthole.git ~/porthole
cd ~/porthole
npm install && npm run build
claude mcp add --scope user porthole \
-- node /home/coder/porthole/dist/index.js
# Expose port
coder port-forward <workspace-name> --tcp 8080:8080
Keep-alive with PM2 (optional)
npm install -g pm2
pm2 start dist/index.js --name porthole
pm2 save && pm2 startup
License
MIT
