Hosted Quickstart
read as.md In 5 minutes, your agent will negotiate a UI contract, push a generated form to a user, and receive their submission as structured data — no React code, no front-end build, no infrastructure.
Your Agent → mcp.ggui.ai → MCP-Apps render (ui://ggui/render/<id>) → User submits → Agent gets typed dataPrerequisites
Section titled “Prerequisites”- Node.js 20+
- A free
console.ggui.aiaccount. Sign in at console.ggui.ai with email + password. (console.ggui.aiis the end-user dashboard for hosted ggui — see the glossary if theggui/guueysplit confuses you.)
Step 1: Pick an app and mint an SDK API key
Section titled “Step 1: Pick an app and mint an SDK API key”- Sign in at console.ggui.ai. You land on
/apps— new accounts come pre-provisioned with one default app; create another with New App if you want to scope this quickstart to its own surface (e.g.feedback-demo). - Open the app and go to Keys (
/apps/[appId]/keys). Click Mint key, label it (e.g.feedback-demo-sdk), and submit. - The key reveals exactly once — copy both the key (
ggui_user_…) and the App ID (app_…) immediately. Lose the key and you mint a new one; there’s no recovery.
Step 2: Install the dependencies
Section titled “Step 2: Install the dependencies”npm install @anthropic-ai/claude-agent-sdk @ggui-ai/protocol# or: pnpm add @anthropic-ai/claude-agent-sdk @ggui-ai/protocol# or: yarn add @anthropic-ai/claude-agent-sdk @ggui-ai/protocol@anthropic-ai/claude-agent-sdkruns Claude as a tool-using agent and speaks MCP natively — no wrapper needed.@ggui-ai/protocolexportsGGUI_AGENT_SYSTEM_PROMPT, the canonical system prompt that teaches Claude the handshake → render → consume loop.
Step 3: Write your agent
Section titled “Step 3: Write your agent”Create agent.ts. The Claude Agent SDK connects to mcp.ggui.ai directly via its mcpServers config — your code just streams Claude’s messages and lets the model drive the ggui tools.
import { query } from "@anthropic-ai/claude-agent-sdk";import { GGUI_AGENT_SYSTEM_PROMPT } from "@ggui-ai/protocol";
// 1. Point Claude's MCP client at your app's hosted endpoint. Bearer-auth// with the `ggui_user_*` key you minted in Step 1. Note: the per-app// cloud endpoint is the bare `/apps/<appId>` path — NO `/mcp` suffix// (that suffix is local-`ggui serve`-only).const mcpServers = { ggui: { type: "http" as const, url: "https://mcp.ggui.ai/apps/<your appId>", headers: { Authorization: `Bearer ${process.env.GGUI_API_KEY!}` }, },};
// 2. Allow Claude to call every ggui tool. The `mcp__<server>__<tool>`// naming is the SDK's convention — `<server>` = `ggui` (the key above).const allowedTools = [ "mcp__ggui__ggui_handshake", "mcp__ggui__ggui_render", "mcp__ggui__ggui_update", "mcp__ggui__ggui_emit", "mcp__ggui__ggui_consume", "mcp__ggui__ggui_get_session",];
async function main() { const prompt = "Collect product feedback from the user. Show a feedback form with a " + "1-5 star rating and a comments text area, wait for them to submit, " + "then summarize what they said.";
// 3. Stream the conversation. Claude reads GGUI_AGENT_SYSTEM_PROMPT, // decides when to call ggui_handshake / ggui_render, // polls ggui_consume until the user submits, and reports back — // all without any wrapper SDK on your side. for await (const msg of query({ prompt, options: { mcpServers, allowedTools, systemPrompt: GGUI_AGENT_SYSTEM_PROMPT, }, })) { if (msg.type === "assistant") { for (const block of msg.message.content) { if (block.type === "text") console.log(block.text); } } else if (msg.type === "result") { console.log("Done:", msg.subtype); } }}
main().catch(console.error);Step 4: Run it
Section titled “Step 4: Run it”export ANTHROPIC_API_KEY="sk-ant-..." # for the Claude Agent SDKexport GGUI_API_KEY="ggui_user_..." # for mcp.ggui.ainpx tsx agent.tsYou’ll see Claude narrate the handshake, call ggui_render (which returns { sessionId, resourceUri } — the render surfaces as an MCP-Apps resource at ui://ggui/render/<id>, not a clickable link), and then block on ggui_consume, polling for the user’s submission.
That block is the point to notice: run programmatically like this, there is no UI surface for a human to submit through yet — the agent is waiting on a render nobody can see. To actually mount the render and let a user interact, you need an MCP-Apps host. Two ways to get one:
- Embed it in your own app — Step 5 below mounts the render with the React SDK.
- Run the agent inside an MCP-Apps host — claude.ai or Claude Desktop renders ggui resources inline (see Clients).
Step 5 (optional): Embed in React
Section titled “Step 5 (optional): Embed in React”Want the UI inside your own app? Install the React SDK and the MCP-Apps host:
npm install @ggui-ai/react @mcp-ui/clientA ggui render is an MCP-Apps resource. You drive the conversation with the useMcpAppsChat hook and mount each render’s sandboxed iframe with <AppRenderer> (imported directly from @mcp-ui/client — ggui doesn’t re-export it):
import { AppRenderer } from "@mcp-ui/client";import { useMcpAppsChat } from "@ggui-ai/react/chat-helpers";
function Chat({ agentUrl }: { agentUrl: string }) { const { entries, sessions, send, handleAppMessage } = useMcpAppsChat({ chatEndpoint: `${agentUrl}/agent`, });
// - render `entries` as chat bubbles; call `send(prompt)` to talk to the agent // - mount each `sessions` entry with <AppRenderer> — it needs a sandbox-proxy // origin + onReadResource / onCallTool relay + onMessage={handleAppMessage}}useMcpAppsChat talks to your agent backend (the process running the Step-3 query() loop, exposed over HTTP — @ggui-ai/agent-server gives you a brand-neutral POST /agent endpoint for exactly this). <AppRenderer>’s sandbox + resource-read + tool-call relay wiring is non-trivial; the complete runnable reference is the ggui-basic-web sample. Start there.
What just happened
Section titled “What just happened”Under the hood, Claude drove these MCP tool calls against mcp.ggui.ai:
ggui_handshakenegotiated a contract from a natural-language intent + draft, returning ahandshakeId+ a server suggestion (cache / agent / synth).ggui_renderwith{ handshakeId, props }materialized the contract: ggui matched a cached blueprint (or synthesized a fresh React component), minted asessionId, and returned{ sessionId, resourceUri }— the render is an MCP-Apps resource atui://ggui/render/<id>, surfaced on the tool result’s_meta.ui.resourceUri. (There is no clickable URL on the wire.)- A host mounted that resource — your app via
<AppRenderer>(Step 5), or an MCP-Apps host like claude.ai inline — and the user submitted the form. ggui_consumedelivered the user’s submit gesture as aConsumeEventEntry({ intent, actionData, uiContext, ... }).
Renders decay implicitly via TTL — there is no terminal close ceremony.
Everything above the wire is GGUI_AGENT_SYSTEM_PROMPT + the Claude Agent SDK’s tool loop — no ggui-specific client code on your side.
Agent mcp.ggui.ai MCP-Apps host │ │ (your app / claude.ai) │── handshake ───────────→ │ │ │← { handshakeId, ─ │ │ │ suggestion } │ │ │── render(handshakeId, ─ │ │ │ props) ────────────→ │ │ │ │── match/synth blueprint │ │← { sessionId, ─ │ │ │ resourceUri } │ │ │ │── ui://ggui/render/<id> ──→│ (host mounts iframe) │ │ │ │── consume(sessionId) ───→ │ │ │ │←── submit gesture ────────│ │← { events } ──────────── │ │ │ │ │ │ (render decays via TTL — no explicit close) │Next steps
Section titled “Next steps”- Claude agent example — canonical reference implementation for the snippet above
- MCP protocol reference — every
ggui_*tool, request/response, and error code - React SDK — embed ggui renders directly in your own React app with
useMcpAppsChat+<AppRenderer> - Other LLMs — raw
@modelcontextprotocol/sdkrecipe; also OpenAI, Gemini, OpenClaw - Feedback-form cookbook — the recipe above, with variations
- Troubleshooting — common issues and fixes
- Glossary — gadget vs tool vs blueprint, ggui vs guuey, and the rest
- Agentic App Builders — if your goal is to make an existing app agent-drivable rather than building a fresh agent.