---
title: Million-Dollar Homepage playground
description: Hosted MCP service at mcp.ggui.ai/playground/mdh — shared 1000×1000 pixel grid. Demo of generative UI rendering globally shared mutable state.
---

:::caution[Coming soon]
This page describes the **managed hosted path** (`mcp.ggui.ai` / `console.ggui.ai`), which is **not yet live** — it is not part of GGUI Preview 0.1.0. The self-hosted path is available today — start with the [Quickstart](/oss-quickstart/). This page is kept as a preview of the managed path and goes live when hosted ggui ships.
:::

`mcp.ggui.ai/playground/mdh` is a first-party hosted MCP service that exposes a globally shared 1000×1000 pixel grid — a tribute to [Alex Tew's 2005 Million-Dollar Homepage](https://en.wikipedia.org/wiki/The_Million_Dollar_Homepage). Every signed-in user reads and writes the same canvas. Where the [Todos playground](/clients/playground-todos/) demonstrates per-user persistent state, MDH demonstrates **globally shared mutable state** rendered as generative UI.

Three tools, one canvas, last-write-wins semantics. The agent claims a pixel, the host renders the new region inline, the next user can claim right over the top.

## What it demonstrates

- **Shared mutable state across users**: every claim is visible to every other signed-in caller immediately.
- **Generative UI over a sparse data shape**: `mdh_read_region` returns only claimed cells, so the host renders a sparse grid — pure data, no markup decisions baked into the tool.
- **Attribution without ownership**: each claimed cell records the writer's `userId`, but anyone can overwrite. Identity is recorded; tenure is not.

If you want the deep "why" of the service model behind this, see [MCP services](/architecture/mcp-services/) — `playground-mdh` is one of three reference services in `cloud/mcp-services/`.

## The three tools

All three are Cognito-gated. Anonymous calls (no bearer token) return an explicit `authentication required` error.

| Tool              | Input                     | Output                                                 | Notes                                                                             |
| ----------------- | ------------------------- | ------------------------------------------------------ | --------------------------------------------------------------------------------- |
| `mdh_read_region` | `{ x, y, width, height }` | `{ pixels: Pixel[] }`                                  | Sparse — unclaimed cells omitted. Region capped at 100×100 cells per call.        |
| `mdh_claim_pixel` | `{ x, y, color }`         | `{ pixel }`                                            | Single-cell write. `color` is `#RRGGBB` hex. Last-write-wins (see warning below). |
| `mdh_get_stats`   | `{}`                      | `{ totalClaimed, uniqueUsers, gridWidth, gridHeight }` | Cheap aggregate read. `gridWidth` and `gridHeight` are always 1000.               |

A `Pixel` row is `{ x, y, color, userId, claimedAt }`. Coordinates are integers, `0 <= x < 1000` and `0 <= y < 1000`. `claimedAt` is ISO-8601.

The region cap exists to bound payload size: a 100×100 fully-claimed read returns 10,000 entries. Agents wanting a wider view issue multiple reads — the sparse response means the cost scales with claimed density, not region size.

:::caution[Last-write-wins, no permanent ownership]
`mdh_claim_pixel` REPLACES any existing claim on that cell. The most recent writer's `userId` overwrites the previous one. There is no "owned forever", no allow-list, no first-claim precedence. Anyone signed in can paint over anything. The wire description on the tool itself spells this out so agents surface it before the user buys in.
:::

## Auth model

Cognito-gated. Every handler resolves `ctx.userId` from the bearer key minted by [console.ggui.ai](/clients/console/) and rejects calls without one. On `mdh_claim_pixel`, that same `userId` is stamped on the resulting pixel row — that's the attribution surface visible to subsequent `mdh_read_region` callers.

Reads are gated too. The canvas is end-user surface, not a public dataset; anonymous reads are deliberately not exposed at this slice.

## Try it

The wiring is identical to the [Todos playground](/clients/playground-todos/) — same auth header, same OAuth ceremony from MCP-Apps-aware hosts, different URL.

**Claude Desktop**: Settings → Connectors → Add custom connector, paste:

```
https://mcp.ggui.ai/playground/mdh
```

Approve the OAuth ceremony at `console.ggui.ai`; a new `ggui_user_*` row appears in [`/keys/connector`](https://console.ggui.ai/keys/connector). Then try prompts like:

> Show me a 50×50 region around the center of the canvas.

> Claim the pixel at (500, 500) in red.

> How many pixels have been claimed total, and by how many users?

**Claude Agent SDK** (or any host that takes a remote MCP URL):

```typescript
import { query } from "@anthropic-ai/claude-agent-sdk";

const apiKey = process.env.GGUI_USER_KEY!; // ggui_user_*

const mcpServers = {
  mdh: {
    type: "http" as const,
    url: "https://mcp.ggui.ai/playground/mdh",
    headers: { Authorization: `Bearer ${apiKey}` },
  },
};

for await (const msg of query({
  prompt: "Claim (500, 500) in #ff0044, then show me a 20×20 region around it.",
  options: {
    model: "claude-sonnet-4-6",
    mcpServers,
    allowedTools: [
      "mcp__mdh__mdh_read_region",
      "mcp__mdh__mdh_claim_pixel",
      "mcp__mdh__mdh_get_stats",
    ],
  },
})) {
  // consume the SDK message stream
}
```

The Claude Agent SDK namespaces every MCP tool as `mcp__<serverName>__<toolName>` — with `serverName: "mdh"` here, the tool ids land as `mcp__mdh__mdh_read_region`, `mcp__mdh__mdh_claim_pixel`, and `mcp__mdh__mdh_get_stats`. For the full agent-side wiring (system prompt, error handling, streaming), see the [Claude Agent example](/examples/claude-agent/) and swap the URL + tool whitelist.

## How it's built

The service is closed-source (`@ggui-private/mcp-playground-mdh` — not published or mirrored), but its shape is simple enough to describe:

- a package entry exporting a `createPlaygroundMdhHandlers({ grid })` convenience factory,
- one file per tool (read-region / claim-pixel / get-stats),
- a `PixelGrid` interface with an in-memory implementation. The grid is the single source of truth for its own dimensions and surfaces them on the wire via `mdh_get_stats`.

To build your own globally shared state surface in this shape, use the OSS **`McpService` mount primitive** in [`@ggui-ai/mcp-server`](/architecture/mcp-services/) — the same seam this service mounts through. The [MCP services architecture](/architecture/mcp-services/) page covers mount points, the `AuthAdapter` contract, and how `ctx.userId` flows from the bearer header to the handler.