About Porthole
Porthole is an MCP server in the Other category: agent-controlled LilyGo T-Display-S3 desk display with MCP, Cloudflare relay, themes, rotation, QR codes, and WS2812 effects.
Install
git clone https://github.com/robbinfan/portholeThis server has no ConduID identity, so agent calls to it are not receipted. Pin the version you install and review the source before granting it credentials.
Tools (2)
show_widgetRender a visual widget in the Claude Canvas browser tab at localhost:
Parameters not published.
visualize_read_meLoad design guidelines before calling show_widget. Silent — do NOT mention to user.
Parameters not published.
Ask AI
Ask AI about Porthole
Powered by Claude · Grounded in docs
Security checks
- ✓README presentThe repository ships documentation.
5 checks have not been run yet.
README
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
README copy captured when this server was indexed; it may lag the repository. The original is authoritative.