Skip to main content
Glama

bbq 🔥🍖

Don’t grill your brain.

A browser UI for Claude Code grilling sessions.

Long grilling sessions are cognitively expensive: code-heavy questions are information-dense, while the terminal mixes questions, options, recommendations, and answers into one scrolling stream.

bbq moves grilling into a dedicated UI. Questions, choices, context, and follow-ups are structured and easy to scan, so you can spend your attention on the decisions—not on parsing the conversation.

It is an MCP server plus a bbq skill that runs the grilling skill (a relentless, multi-round interrogation of a plan) in the browser. Claude pushes rounds of questions into it; you answer with buttons or free text, and on any question you can ask for a re-pitch (Wait what), a visual (Show me, which may come back as a diagram) or a plain-language explainer (ELI5), which open in a contextual panel on the right.

A round in the browser: question, options, recommendation, and a diagram in the side panel

Install

bbq needs Bun 1.4 or newer; the server is built on Bun.serve. The browser UI comes prebuilt inside the bbq-mcp package, so there is nothing to build.

As a Claude Code plugin (server + skills in one go)

/plugin marketplace add adesombergh/bbq
/plugin install bbq-mcp@bbq

That registers the MCP server (pinned to the plugin's version) and installs the bbq skill plus its barbecue and churrasco aliases, namespaced as plugin skills are: /bbq-mcp:bbq, /bbq-mcp:barbecue, /bbq-mcp:churrasco. Then: "grill me on X with the UI", or /bbq-mcp:bbq.

As a plain MCP server

Add to .mcp.json in the project you grill from, or to your user-level config:

{
  "mcpServers": {
    "bbq-mcp": {
      "command": "bunx",
      "args": ["bbq-mcp"]
    }
  }
}

Then copy or symlink skills/bbq from this repo where your skills live (.agents/skills, .claude/skills…), and skills/barbecue and skills/churrasco beside it if you want those words to work too — they are short stubs that invoke bbq, because skill frontmatter has no aliases field (docs/adr/0018-aliases-are-stub-skills.md).

Optional aside skills

Two of the three aside kinds can call an aside skill, both third party and both optional:

npx skills add humanlayer/skills --skill show-me   # Show me
/plugin install eli5@claude-community              # ELI5

Wait, what? has no skill: the one of that name in mattpocock-skills is disable-model-invocation, a slash command for you rather than something Claude can invoke, so its brief is the implementation.

Nothing here is required. Every kind's brief in src/aside-brief.ts is the floor, not a fallback — Claude answers an aside from it alone, and where a skill would have done better it says so at the end of the aside with the command above. The browser never learns what you have installed (docs/adr/0015-skill-availability-is-not-session-state.md).

From a clone

bun install          # one workspace: server + ui deps, installs git hooks
bun run build        # builds ui/dist once; the MCP server only serves it
bun run demo         # plays Claude against the server, opens the browser

See CONTRIBUTING.md for running your working copy from Claude Code and for the release process.

Step 0

Before the browser opens, Claude settles three things — asking only for what you did not already say when you invoked it:

  • Mode — grill-me, or grill-with-docs to also read this project's CONTEXT.md and docs/adr/ and write new terms and decisions as they settle.

  • Question budget — a maximum number of questions, no maximum by default. It counts questions inside rounds; asides and the last round are free. Spent in full, Claude names what it did not get to and what it is assuming.

  • Language — English or French. It covers what Claude writes — questions, notes, asides — and not the browser's own words or this repo's documents.

The last round

When the frontier empties, Claude posts the full shared understanding as a note and asks one short question whose options are the four destinations — where the plan goes next:

Just send to Claude

Claude writes the plan into the terminal and stops.

/implement

…and ends with the line you type to build it.

/to-spec

…and ends with the line you type to publish it as one issue.

/to-tickets

…and ends with the line you type to cut it into tickets.

Three of the four are disable-model-invocation skills, so the answer never starts anything — it says what to set you up for, and all three read the conversation they are typed in. A free-text answer to that question means "not yet": the decision it names reopens and the grilling carries on. Nothing about a destination reaches the Store or a snapshot; it is an ordinary answer to an ordinary round, and the menu never varies with what you have installed.

Related MCP server: A2UI MCP Server

Design

Claude never talks to the browser. Claude talks to state (src/state.ts); state and the browser talk over WebSocket (src/hub.ts). The MCP tools (src/mcp.ts) only read/mutate state and waitFor changes. That seam is the whole design.

Claude ──MCP stdio──▶ src/mcp.ts ──▶ Store (src/state.ts) ◀── WS ── src/hub.ts ◀── browser (ui/)

Tools

tool

blocks?

purpose

open_session({ title })

until a tab connects (≤ waitForBrowserMs, default 30s)

start a session, open the browser

ask_round({ sessionId, intro?, questions })

no

publish one frontier round

wait_for_answers({ sessionId, roundId, timeoutMs? })

≤ timeoutMs

poll; returns answered / aside_requested / pending / closed

post_aside({ sessionId, asideId, format, content })

no

deliver a Wait-what / Show-me / ELI5 result to the panel

post_note({ sessionId, markdown })

no

chat bubble between rounds

close_session({ sessionId })

no

mark the session finished

Polling contract

No tool ever blocks past its timeout. wait_for_answers waits at most timeoutMs (default 55s via BBQ_WAIT_MS, hard cap 280s: under Claude Code's 5-minute MCP idle cutoff) and returns pending on timeout. Claude just calls it again, so a round can sit open for an hour at the cost of one cheap tool call per minute. Asides interrupt the wait immediately and are returned before answers so the user is never left staring at a spinner.

UI rules

  • One question is active at a time; the first unanswered one on a new round.

  • Question N unlocks once N-1 is answered. Any answer can be changed until the round is sent (click an answered question to reopen it).

  • Answering advances to the next question. When all are answered, Send answers to Claude submits the round; the round then freezes.

  • The active card shows three separated blocks: question, options (+ manual answer textarea), recommendation (+ Go with recommendation, and the three aside buttons).

  • The right panel is per question, tabbed by aside kind, closable (Esc). markdown asides render as rich text; html asides render in a sandboxed frame (no scripts). Claude picks the format per aside — the kind does not fix it. A ```mermaid block in an aside is drawn: mermaid arrives through a lazy import(), renders to an SVG string and goes through the same frame, and a block that does not parse falls back to its source.

Hard constraints honored

  1. stdout belongs to MCP; every log is console.error. The server never spawns a build tool or dev server.

  2. The UI is prebuilt; src/mcp.ts serves ui/dist and shows a placeholder page if it is missing. Startup is instant.

  3. idleTimeout: 0 on Bun.serve and on the WS handler.

  4. Polling contract above.

  5. Binds 127.0.0.1:0. Every request needs the per-process token: ?token= on the first navigation, persisted as an HttpOnly cookie for assets and the WS.

Layout

src/mcp.ts          MCP entry (stdio) + tools
src/state.ts        Store: sessions, rounds, answers, asides, notes, waitFor
src/round-rules.ts  pure round rules shared with the UI (canAnswer, isComplete)
src/protocol.ts     Zod validation of browser -> hub messages
src/hub.ts          Bun.serve: static ui/dist, WS relay, token auth
src/browser.ts      open the default browser without touching stdio
src/types.ts        shared types (imported by the UI as @shared/types)
ui/                 Vite 8 + React 19 (React Compiler) + Tailwind v4, builds to ui/dist
  src/router.tsx    TanStack Router: / and /s/$sessionId (typed search params)
  src/lib/          Query options, WebSocket -> cache feeder (Pacer backoff), mutations, derivations
  src/components/   Question card, round, aside panel, TanStack Form manual answer, markdown, diagrams
  src/components/ui shadcn components on Base UI (ours to edit)
scripts/demo.ts     fake Claude for manual testing
skills/             bbq (the protocol Claude follows in the browser), plus the barbecue/churrasco alias stubs
.claude-plugin/     plugin + marketplace manifests; .mcp.json is the plugin's pinned server entry
test/               bun tests for state, protocol, hub and release manifests

Tooling

  • bun run check is the definition of done: oxfmt --check, type-aware oxlint, tsc for both packages, bun test, vite build.

  • Lint and format are ultracite presets on top of oxlint + oxfmt (oxlint.config.ts, oxfmt.config.ts), with the same additions as a sibling internal repo. Zero suppressions.

  • lefthook runs format + lint on staged files before each commit and bun run check before each push.

  • Releases: tag vX.Y.Z, CI publishes bbq-mcp to npm with provenance (CONTRIBUTING.md, docs/adr/0020-distribution-is-npm-wrapped-by-a-plugin.md).

  • .claude/settings.json runs ultracite fix after every file Claude writes. CLAUDE.md holds the rules an agent must know; AGENTS.md is the generated ultracite standard.

  • UI state model: TanStack Query owns the session snapshot (fed by the WebSocket), TanStack Router owns the view state in the URL, there is no useEffect in ui/src (the import is banned by lint) and the React Compiler handles memoisation.

Env: BBQ_WAIT_MS default poll length, BBQ_NO_OPEN=1 to not launch a browser.

Related MCP Connectors

Related MCP Servers

  • A
    license
    A
    quality
    D
    maintenance
    Wraps Claude Code as tools for MCP clients, enabling autonomous coding tasks via a 4-tool lifecycle with session management, async polling, and permission controls.
    4
    41
    20
    MIT
  • F
    license
    A
    quality
    D
    maintenance
    Enables Claude Code to generate and preview rich user interfaces via the A2UI protocol, using declarative JSON components and real-time WebSocket updates.
    7
    -
  • A
    license
    A
    quality
    D
    maintenance
    Enables AI agents to interact programmatically with Claude Code CLI, managing sessions, streaming outputs, and handling permission requests.
    7
    6
    1
    MIT