# SeatMapOS — Complete AI Integration Guide

This document is for AI coding assistants (Claude, Codex, Antigravity, ChatGPT, Copilot, …) helping developers integrate SeatMapOS into a ticketing system, or driving SeatMapOS directly over MCP. Feed this document (or its URL) to your AI assistant before asking it to plan or implement an integration.

Single combined reference: platform overview, the MCP server, the Designs API, the venue generator, the designer's self-serve flow, and the Booking API.

## Quick Facts

| Property | Value |
|----------|-------|
| Platform | SeatMapOS |
| Live deployment | `https://seatmap-layer.shadhinarafat.workers.dev` |
| Designer | `/designer` |
| MCP server (streamable HTTP) | `/api/mcp` |
| Designs API (REST mirror) | `/api/designs` |
| Booking API | `/api/events/{eventId}/…` |
| This document | `/ai-integration.md` |
| Runtime | Next.js 15 on Cloudflare Workers (D1 + R2 + Durable Objects) |
| Renderer | maplibre-gl (WebGL), venue-local projection |

Replace the host with your own deployment when self-hosting; all paths are relative to it.

## Table of Contents

1. [What SeatMapOS Is](#1-what-seatmapos-is)
2. [Integration Architecture](#2-integration-architecture)
3. [Connect over MCP](#3-connect-over-mcp)
4. [MCP Tools Reference](#4-mcp-tools-reference)
5. [Design GeoJSON Format](#5-design-geojson-format)
6. [Designs REST API](#6-designs-rest-api)
7. [The Venue Generator (VenueSpec)](#7-the-venue-generator-venuespec)
8. [Designer Self-Serve Flow](#8-designer-self-serve-flow)
9. [Booking API](#9-booking-api)
10. [Guidelines for AI Assistants](#10-guidelines-for-ai-assistants)
11. [End-to-End Example Session](#11-end-to-end-example-session)

---

## 1. What SeatMapOS Is

SeatMapOS is a seat-map design and booking platform: it creates, edits and renders interactive venue seating charts and manages live seat state for events.

### Components

| Component | What It Does | Who Uses It |
|-----------|-------------|-------------|
| **Designer** (`/designer`) | Web editor: parametric bowl generation, drawing tools, row slicing, numbering, zones, pricing, validation | Venue managers, organizers |
| **MCP server** (`/api/mcp`) | Lets AI tools generate venue geojson and push designs straight into the designer | AI assistants (Claude Code, Codex, Antigravity) |
| **Designs API** (`/api/designs`) | REST mirror of the MCP push/list/get tools | Backend developers, CI |
| **Copilot** (in-designer, `/api/designer/generate`) | Natural language → venue spec → generated chart | Designer users |
| **Booking API** (`/api/events/…`) | Live availability, all-or-nothing holds, confirm/release, WebSocket push | Ticketing backends |
| **Book flow** (`/book/{eventId}`) | Reference customer-facing seat picker wired to the Booking API | End users |

### The Core Idea for AI Tools

An AI assistant connected over MCP can **create a complete venue** two ways:

1. **Generate** — call `generate_venue_geojson` with a high-level spec (stadium/arena/theater, tiers, oval/octagon). SeatMapOS's parametric bowl engine produces the full chart: sections, aisles, rows, seats.
2. **Push** — build your own GeoJSON FeatureCollection of section polygons (from any source: your CAD export, another platform, hand-written) and call `push_design`. The designer loads it, and the user slices sections into equally spaced seat rows with one action.

Either way the tool returns an `open` URL that deep-links into the designer.

---

## 2. Integration Architecture

```
Step 1               Step 2                Step 3                Step 4
Create design   -->  Slice rows       -->  Number/Zones/    --> Sell seats
(MCP tool or         (designer:            Pricing              (Booking API:
 designer)            select + slice)      (designer)            hold/confirm)
```

- Steps 1–3 are setup, done once per venue.
- Step 4 happens on every purchase, against `/api/events/{eventId}/…`.

Storage: designs live in D1 (`designs` table, auto-created). Seat state lives in a per-event Durable Object with durable sales records in D1.

---

## 3. Connect over MCP

The MCP server is **stateless streamable HTTP** — every JSON-RPC request is self-contained (no SSE session), which works from CLIs, CI, and serverless clients.

**Claude Code**
```bash
claude mcp add --transport http seatmapos https://seatmap-layer.shadhinarafat.workers.dev/api/mcp
```

**Codex** (recent versions)
```bash
codex mcp add seatmapos --url https://seatmap-layer.shadhinarafat.workers.dev/api/mcp
```
or `~/.codex/config.toml`:
```toml
experimental_use_rmcp_client = true

[mcp_servers.seatmapos]
url = "https://seatmap-layer.shadhinarafat.workers.dev/api/mcp"
```

**Gemini CLI** — the key must be `httpUrl` (streamable HTTP). `url` means the legacy SSE transport and will fail with `405`. In `~/.gemini/settings.json`:
```json
{
  "mcpServers": {
    "seatmapos": { "httpUrl": "https://seatmap-layer.shadhinarafat.workers.dev/api/mcp" }
  }
}
```

**Any stdio-only or SSE-only MCP client** (bridge via mcp-remote) — this includes clients that only speak the legacy HTTP+SSE transport (older Windsurf builds, Cursor < 0.48): they open `GET` expecting an event stream, get `405`, and give up. Bridge them instead:
```bash
npx -y mcp-remote https://seatmap-layer.shadhinarafat.workers.dev/api/mcp
```

**Antigravity** — Settings → MCP servers → add HTTP server with the same URL.

**Raw JSON-RPC (no MCP client needed)**
```bash
curl -X POST https://seatmap-layer.shadhinarafat.workers.dev/api/mcp \
  -H 'content-type: application/json' \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'
```

### Authentication

Open by default. When the deployment sets the `MCP_TOKEN` secret, every request must carry:
```
Authorization: Bearer <token>
```
Add `--header "Authorization: Bearer …"` (Claude Code / mcp-remote) or `bearer_token_env_var` (Codex) accordingly.

With `MCP_TOKEN` set, **only header-capable clients can connect** — the server does static bearer auth, not OAuth. Connector UIs that offer no custom-header field (e.g. the Claude.ai / ChatGPT web connector dialogs) cannot authenticate; route them through `mcp-remote --header "Authorization: Bearer …"` or leave the deployment open.

### Protocol Notes

- Supported protocol versions: `2025-06-18`, `2025-03-26`, `2024-11-05`. A client asking for an unlisted version is answered with `2025-06-18` (the newest supported).
- `POST /api/mcp` with a single JSON-RPC message (batches accepted); responses are plain JSON. CORS is open (`Access-Control-Allow-Origin: *`), so browser-based clients work too.
- Notifications get HTTP `202` with an empty body. `GET` returns `405` (no server-initiated stream) — SSE-only clients must bridge via `mcp-remote` (above).
- Tool errors come back as `result.isError: true` with a text explanation — read the text.

---

## 4. MCP Tools Reference

### `generate_venue_geojson`

Generate a full venue with the parametric bowl engine (the same generator the designer uses).

| Argument | Type | Required | Description |
|----------|------|----------|-------------|
| `kind` | `"stadium" \| "arena" \| "theater"` | yes | Venue archetype (field/court/stage + matching bowl) |
| `name` | string | no | Venue display name |
| `capacity` | number | no | Advisory target; tightens seat pitch above 30k |
| `sectionsPerSide` | number | no | Sections along each long side (3–8) |
| `tiers` | `{ rows: number; sections?: number }[]` | no | Up to 6 tiers, inner→outer; `sections` gives a tier its own aisle count |
| `oval` | boolean | no | Continuous oval bowl (Parc-des-Princes style) |
| `octagon` | boolean | no | Octagonal bowl, 45° chamfer corners (Hard-Rock style) |
| `push` | boolean | no | Also store as a design (requires `slug`) |
| `slug` | string | no | `[a-z0-9-]`, max 64 — the design identity |
| `includeGeojson` | boolean | no | Return the full geojson (default: true unless `push`) |

Returns `{ sections, seatTotal, kind, design?, open?, geojson? }` as JSON text. `open` is the designer deep link.

```bash
curl -X POST …/api/mcp -H 'content-type: application/json' -d '{
  "jsonrpc":"2.0","id":1,"method":"tools/call",
  "params":{"name":"generate_venue_geojson","arguments":{
    "kind":"arena","name":"Club Arena","capacity":12000,
    "tiers":[{"rows":14},{"rows":10}],"sectionsPerSide":6,
    "push":true,"slug":"club-arena"}}}'
```

### `push_design`

Push any FeatureCollection of section polygons (see [format](#5-design-geojson-format)). Same `slug` overwrites — safe to iterate.

| Argument | Type | Required | Description |
|----------|------|----------|-------------|
| `slug` | string | yes | `[a-z0-9-]`, max 64 |
| `name` | string | no | Display name (defaults to slug) |
| `geojson` | object | yes | FeatureCollection with Polygon features |

Returns `{ design, open }`.

### `list_designs`

No arguments. Returns `{ designs: [{ id, slug, name, source, sections, created_at }] }` (newest first, max 100).

### `get_design`

| Argument | Type | Required |
|----------|------|----------|
| `slug` | string | yes |

Returns `{ design, geojson }`.

---

## 5. Design GeoJSON Format

A design is a standard GeoJSON FeatureCollection. Rules:

- Each **`Polygon`** (or `MultiPolygon`) feature is one **section**. Non-polygon features are ignored.
- Feature property **`label`** (or `name`, or `ref`) becomes the section label; otherwise sections are numbered 1..N.
- **Any coordinate system works** — venue units, meters, pixels, even real lng/lat. The designer normalizes on load: uniform fit into the canvas with a y-flip (GeoJSON y-up → screen y-down). Aspect ratio is preserved.
- Limits: ≥1 and ≤2000 sections, ≤8 MB of JSON.
- Sections arrive **without seats** — rows/seats are added by slicing in the designer (or pre-generate with `generate_venue_geojson` instead).

Minimal example:

```json
{
  "type": "FeatureCollection",
  "features": [
    { "type": "Feature", "properties": { "label": "101" },
      "geometry": { "type": "Polygon",
        "coordinates": [[[0,0],[40,0],[40,30],[0,30],[0,0]]] } },
    { "type": "Feature", "properties": { "label": "102" },
      "geometry": { "type": "Polygon",
        "coordinates": [[[45,0],[85,0],[85,30],[45,30],[45,0]]] } }
  ]
}
```

---

## 6. Designs REST API

Same contract as the MCP tools, for plain HTTP callers:

| Method | Path | Body | Response |
|--------|------|------|----------|
| GET | `/api/designs` | — | `{ designs: DesignRow[] }` |
| GET | `/api/designs/{slug}` | — | `{ design, geojson }` or 404 |
| POST | `/api/designs` | `{ slug, name?, geojson }` | `{ design, open }` (upsert by slug) |

```bash
curl -X POST https://seatmap-layer.shadhinarafat.workers.dev/api/designs \
  -H 'content-type: application/json' \
  -d '{"slug":"my-hall","name":"My Hall","geojson":{"type":"FeatureCollection","features":[…]}}'
```

---

## 7. The Venue Generator (VenueSpec)

What the bowl engine does with a spec (used by `generate_venue_geojson` and the in-designer Copilot):

- **Global ring model**: concentric rounded-rect / oval / chamfered-octagon rings around the playing surface. Row *i* of every section in a tier lies on the same global ring, so rows align across section borders.
- **Radial aisles** cut the rings into sections; per-tier `sections` counts give upper decks more, narrower sections.
- **Seats** sit exactly on their row's ring at uniform arc pitch, with a one-seat-pitch safe area from every section wall.
- `theater` produces a horseshoe (no sections behind the stage); `arena` a tighter bowl around a court; `stadium` a full bowl around a football pitch with field art.

Typical shapes: `{kind:"stadium", tiers:[{rows:11},{rows:9},{rows:17}], sectionsPerSide:8}` (three-tier stadium) · `{kind:"stadium", oval:true, tiers:[{rows:26},{rows:15},{rows:13}]}` (continuous oval) · `{kind:"stadium", octagon:true, tiers:[{rows:32,sections:41},{rows:10,sections:60},{rows:30,sections:82}]}` (octagonal, per-tier section counts).

---

## 8. Designer Self-Serve Flow

What the human (or a browser-driving agent) does after a design is pushed:

1. Open the `open` URL — `/designer?design={slug}` — or pick the design from **Venues → Pushed designs**.
2. **Select sections**: tap to select; multi-select in Number/Zones/Pricing modes; or *Select all sections*.
3. **Row slicing** (Draw panel): set **Slices** = N, press **Slice selected into rows**. Every selected section is partitioned into N row bands of **equal depth** — straight parallel cuts perpendicular to the section's depth axis, band edges exactly on the section border — each band seated with a straight centred row (seat spacing configurable). Different selections may use different N. Undo works; GA sections are skipped.
4. **Number** rows/seats, group **Zones**, set **Pricing**, run **Validate**, **Publish**.

Row slicing internals (for agents reasoning about output): the section polygon is boolean-partitioned (polygon-clipping) into N equal slabs along its depth axis — slices are always equal, parallel, and border-exact.

---

## 9. Booking API

Live seat state per event, backed by a Durable Object (strong consistency) with durable sales in D1.

| Method | Path | Body | Description |
|--------|------|------|-------------|
| GET | `/api/events/{eventId}/availability` | — | Live seat states snapshot |
| POST | `/api/events/{eventId}/hold` | `{ owner, seatIds: string[], holdId?, ttlMs? }` | **All-or-nothing** hold: reserves every seat or none (409 with conflicts) |
| POST | `/api/events/{eventId}/confirm` | `{ holdId, owner, externalOrderRef }` | Converts a hold to a confirmed sale (recorded in D1) |
| POST | `/api/events/{eventId}/release` | `{ holdId }` | Releases a hold |
| GET | `/api/events/{eventId}/ws` | — | WebSocket: snapshot on connect, then `held/confirmed/released/expired` broadcasts |
| GET | `/api/venues` | — | Venue metadata list (D1) |
| POST | `/api/venues` | `{ slug, name, city?, seat_count? }` | Create venue metadata |
| GET | `/api/seatmap/{slug}` | — | Streams the venue's binary seat blob from R2 |

### Booking Flow

```
AVAILABLE --hold--> HELD --confirm--> SOLD
AVAILABLE <--release/expire-- HELD
```

1. Customer picks seats (renderer / book flow).
2. Backend `POST /hold` with the seat IDs — all-or-nothing prevents partial carts.
3. Payment in **your** system (SeatMapOS never touches payment).
4. `POST /confirm` on success, `POST /release` on failure; holds auto-expire after `ttlMs`.
5. Every connected client sees state changes over the WebSocket.

---

## 10. Guidelines for AI Assistants

**Configuration hygiene**
- Put the host and any `MCP_TOKEN` in environment variables — never inline secrets in code:
  ```
  SEATMAPOS_URL=https://seatmap-layer.shadhinarafat.workers.dev
  SEATMAPOS_MCP_TOKEN=…        # only if the deployment enables auth
  ```
- Slugs are identities: reuse a slug to iterate on a design, mint a new slug for a new venue.

**Reading tool results**
- Tool results are JSON **as text** in `result.content[0].text` — parse it.
- `result.isError: true` carries a human-readable reason (bad slug, invalid geojson, unknown design).

**Common mistakes**
1. Pushing geometry other than polygons — only `Polygon`/`MultiPolygon` features become sections.
2. Expecting seats in a pushed design — pushed sections are seatless by design; slicing adds rows, or use the generator.
3. Forgetting `slug` with `push: true` — generation succeeds but nothing is stored.
4. Assuming a session — the MCP server is stateless; do not send `Mcp-Session-Id` expectations or rely on server-initiated streams.
5. Hammering `list_designs` for change detection — designs change only when someone pushes.

---

## 11. End-to-End Example Session

A realistic assistant transcript:

> **User:** Create a 15k two-tier arena for our club and give me a link to finish it.
>
> **Assistant** calls `generate_venue_geojson` with
> `{"kind":"arena","name":"Club Arena","capacity":15000,"tiers":[{"rows":16},{"rows":11}],"sectionsPerSide":6,"push":true,"slug":"club-arena"}`
>
> **Tool returns** `{"sections":48,"seatTotal":11832,"open":"https://…/designer?design=club-arena","design":{…}}`
>
> **Assistant:** Done — 48 sections, ~11.8k seats. Open https://…/designer?design=club-arena, press *Select all sections*, set Slices, and hit *Slice selected into rows* if you want to re-row any tier; then Number → Zones → Pricing → Publish.

Custom-geometry variant: build the FeatureCollection yourself (Section 5), `push_design` it, hand back the `open` link — the user slices rows in the designer.
