162 lines
7.3 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# GHL × json-render Spike
Proof-of-concept: replace hand-coded HTML MCP apps with AI-generated UIs using Vercel's [json-render](https://github.com/vercel-labs/json-render) library.
## What This Is
We have 11 GHL MCP apps built as self-contained HTML files (~400-540 lines each, ~5,000 lines total). Each is hand-coded with bespoke markup. This spike tests whether we can:
1. **Define a component catalog** (Zod schemas) that constrains what AI can generate
2. **Build React components** matching the existing visual quality
3. **Have AI produce JSON trees** that the Renderer turns into polished UIs
4. **Serve via MCP Apps SDK** (`@modelcontextprotocol/ext-apps`) for rendering in AI chat
## How It Works
```
User Prompt → AI + Catalog (guardrailed) → JSON Tree → React Renderer → Polished UI
┌──────────────┐ ┌───────────────┐ ┌──────────────┐ ┌──────────────┐
│ "Show me the │────▶│ Claude + GHL │────▶│ JSON Tree │────▶│ React with │
│ pipeline" │ │ Catalog │ │ (validated) │ │ Tailwind UI │
└──────────────┘ └───────────────┘ └──────────────┘ └──────────────┘
```
**Key insight:** One HTML app bundle serves ALL views. The JSON tree in `structuredContent` determines what renders.
## Quick Start
```bash
cd ghl-json-render-spike
npm install
npm run dev
# Open http://localhost:3000
```
## Demo Pages
| Page | URL | Components Used |
|------|-----|-----------------|
| Landing | `/` | Overview + links to demos |
| Contact Grid | `/contact-grid` | PageHeader · SearchBar · FilterChips · DataTable |
| Pipeline Board | `/pipeline` | PageHeader (gradient) · KanbanBoard |
| Campaign Stats | `/campaign` | PageHeader · StatsGrid · MetricCard · ProgressBar · Card |
| Invoice Preview | `/invoice` | DetailHeader · SplitLayout · InfoBlock · LineItemsTable · KeyValueList · ActionBar |
| Playground | `/playground` | Paste any JSON tree → see it render live |
## Project Structure
```
src/
├── catalog/
│ └── ghl-catalog.ts # Component catalog (Zod schemas + action defs)
├── components/ # React implementations of each catalog component
│ ├── registry.tsx # Maps type names → React components
│ ├── PageHeader.tsx
│ ├── DataTable.tsx
│ ├── KanbanBoard.tsx
│ ├── MetricCard.tsx
│ ├── LineItemsTable.tsx
│ └── ... (20 components total)
├── examples/ # JSON trees showing what AI would generate
│ ├── contact-grid.json
│ ├── pipeline-board.json
│ ├── campaign-stats.json
│ └── invoice-preview.json
├── lib/
│ └── GHLRenderer.tsx # Wraps DataProvider + ActionProvider + Renderer
├── mcp/
│ ├── generate-ui.ts # System prompts + catalog → prompt generation
│ └── mcp-tool-handler.ts # Full MCP Apps SDK integration example
└── app/ # Next.js pages (demo harness)
```
## Component Catalog (20 Components)
### Layout
- **PageHeader** — title, subtitle, status badge, meta stats, gradient variant
- **Card** — container with optional header, padding variants
- **StatsGrid** — responsive grid of metric cards
- **SplitLayout** — two-column layout (50/50, 33/67, 67/33)
- **Section** — titled section wrapper
### Data Display
- **DataTable** — sortable table with column formats, row selection, pagination
- **KanbanBoard** — columns with draggable cards (pipeline view)
- **MetricCard** — big number + label + trend indicator
- **StatusBadge** — colored badge by status variant
- **Timeline** — chronological event list
- **ProgressBar** — percentage bar with benchmark markers
### Detail Views
- **DetailHeader** — entity name, ID, status badge
- **KeyValueList** — label/value pairs (totals, metadata)
- **LineItemsTable** — invoice-style table with quantities and totals
- **InfoBlock** — labeled block of info (From/To on invoices)
### Interactive
- **SearchBar** — search input with focus ring
- **FilterChips** — toggleable filter tags
- **TabGroup** — tab navigation
### Actions
- **ActionButton** — primary/secondary/danger/ghost variants
- **ActionBar** — row of action buttons
### Action Definitions (12 Actions)
`view_contact`, `edit_contact`, `delete_contact`, `move_opportunity`, `send_invoice`, `mark_paid`, `void_invoice`, `download_pdf`, `pause_campaign`, `resume_campaign`, `export_data`, `refresh_data`
## MCP Apps Integration
The spike includes a complete example of how this integrates with the official MCP Apps SDK:
```
registerAppTool → returns structuredContent with UITree
registerAppResource → serves single-file HTML bundle
HTML app receives UITree via ontoolresult → feeds to json-render Renderer
```
**Key pattern:** One `registerAppResource` call serves one HTML bundle. ALL GHL views render through it. The `structuredContent.uiTree` JSON tree determines the UI.
See `src/mcp/mcp-tool-handler.ts` for the full server example using:
- `registerAppTool` from `@modelcontextprotocol/ext-apps/server`
- `registerAppResource` with `RESOURCE_MIME_TYPE`
- `ui://` scheme resource URIs
- `structuredContent` for passing data to the UI
- Action tools (no UI) called from the app via `app.callServerTool()`
## Comparison: Old vs New
### Old Approach (hand-coded HTML)
- 11 separate HTML files, each 400-540 lines
- **~5,000 lines** of bespoke HTML/CSS/JS
- Each app has duplicated styles, markup patterns
- Changes to design require editing all 11 files
- No validation — errors surface at runtime
### New Approach (json-render)
- 1 component catalog: **~270 lines** (Zod schemas)
- 20 React components: **~800 lines** total
- 4 JSON examples: **~400 lines** total (what AI generates)
- **~1,470 lines** total for equivalent coverage
- Changes to design: update one component, all views update
- Catalog-enforced validation — AI can only use defined components
### Line Count Reduction
| | Old | New | Savings |
|---|---|---|---|
| Per app | ~470 lines | ~100 lines JSON | **~79%** |
| Total (4 apps) | ~1,880 lines | ~400 lines JSON + ~1,070 shared | **~22% fewer total, 79% less per-app work** |
| Adding a new app | ~470 lines from scratch | ~100 lines JSON (AI-generated) | **AI does it** |
The real win: **adding new views costs ~0 developer effort** — Claude generates the JSON tree from the catalog.
## Next Steps
1. **Build the single-file HTML app** — Vite + vite-plugin-singlefile bundle containing React, json-render, and all GHL components
2. **Wire up to real MCP server** — Replace the Next.js demo with `registerAppResource` serving the bundle
3. **Add streaming** — Use `useUIStream` from json-render for progressive rendering as Claude generates
4. **Expand the catalog** — Add calendar, workflow, timeline components for remaining 7 GHL apps
5. **AI generation testing** — Test Claude generating JSON trees from the catalog prompt with real GHL data
6. **Action integration** — Wire `app.callServerTool()` to actual GHL API endpoints