# 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
