Skip to content
Sneak peek — you found hosted ggui early · official launch soon

The dev loop

read as .md

Your agent talks to ggui over MCP and nothing else. That single fact is what makes the local-to-hosted move small: the tools, the contracts, and the system prompt are identical on both sides, so promoting an agent from your laptop to mcp.ggui.ai is a change of URL and bearer, not a change of code.

This page is the loop that follows from that: iterate locally where restarts are instant and generation runs on your own provider key, then flip the endpoint when you need a real user in front of the render.

The CLI ships two local runtimes and they are not interchangeable.

ggui dev ggui serve
Default bind 127.0.0.1:6780 127.0.0.1:6781
Serves MCP No Yes, at /mcp
For Iterating on blueprints and gadgets in the dev hub Running the protocol the way production runs it

ggui dev is a blueprint registry plus a dev hub — it indexes the ggui.ui.json manifests declared in ggui.json#blueprints.include, previews them at /hub/preview?ui=<id>, and with --agent <entry> supervises your agent process in the same shell. It deliberately does not run an MCP server.

ggui serve is the one your agent connects to. It boots @ggui-ai/mcp-server and, by default, the agent declared in ggui.json#agent.entry. Both can run side by side; the ports do not collide.

Terminal window
ggui serve --dev-allow-all # MCP at http://127.0.0.1:6781/mcp, any bearer accepted
ggui dev # blueprint hub at http://127.0.0.1:6780/hub

--dev-allow-all accepts any bearer (or none) as a builder identity. It is a loopback convenience and the boot banner says so — never put it on a public URL. Without it, ggui serve requires a pair-minted bearer.

The endpoint shape differs, and it is the first thing that breaks

Section titled “The endpoint shape differs, and it is the first thing that breaks”

The two deployments do not spell their MCP endpoint the same way.

  • Local ggui serve serves MCP at the /mcp sub-path: http://127.0.0.1:6781/mcp.
  • Hosted ggui serves each app at its bare per-app path: https://mcp.ggui.ai/apps/<appId>.

The /mcp suffix is a local-ggui serve convention only. Append it to the hosted URL and the per-app pod returns 404, which surfaces as an agent that discovers zero ggui tools rather than as an obvious connection error. Carry the suffix over from your local config and this is the symptom you will see.

Swap the deployment with environment variables

Section titled “Swap the deployment with environment variables”

The reference samples read the endpoint and the bearer from the environment, so the two deployments are two .env.local files rather than two code paths.

Terminal window
# .env.local — hosted ggui
GGUI_MCP_URL=https://mcp.ggui.ai/apps/aB3kP9xY
GGUI_MCP_BEARER=ggui_user_xxxxxxxx
ANTHROPIC_API_KEY=sk-ant-...

Both values come out of the ggui console or the CLI: ggui login, then ggui keys create --name my-agent mints the ggui_user_* connector key. The app ID is an opaque 8-character string with no prefix — the app’s page shows the whole endpoint, mcp.ggui.ai/apps/<appId>, ready to copy.

Nothing else moves. The agent wiring reads both values and hands them to the MCP client:

const mcpServers = {
ggui: { url: process.env.GGUI_MCP_URL ?? "http://localhost:6781/mcp" },
};

That is the shape used by every sample in samples/agents/ — Claude Agent SDK, OpenAI Agents SDK, and Google ADK all read the same two variables. @ggui-ai/agent-server resolves the bearer as opts.bearer ?? process.env.GGUI_MCP_BEARER ?? "dev" and sends it as Authorization: Bearer <token> on every MCP request. Wiring the SDK by hand instead? The Claude agent example shows the explicit headers form.

ggui deploy does the whole hosted-side provisioning in one idempotent pass: sign in, create the cloud app and write its ID into ggui.json, mint a connector key into .env.local as GGUI_MCP_BEARER, upload your local blueprint pool, push declared gadgets and publicEnv, and finally write GGUI_MCP_URL. Re-run it after a partial failure and it resumes from where it stopped.

Terminal window
ggui deploy

It writes the endpoint in the correct bare /apps/<appId> shape, which is the practical reason to prefer it over hand-editing the URL. An existing GGUI_MCP_URL is never clobbered — the first deploy writes it, later ones leave your override alone. Add --push-keys to also push your provider key to the app so keySource=own renders run on your own model account.

Who pays for generation. Local ggui serve calls your provider directly with the key in your environment — no ggui credits involved. Hosted renders draw down your credit balance unless the app runs on your own provider key, in which case ggui skips the charge entirely. See Credits, billing & BYOK.

Auth posture. --dev-allow-all is a loopback-only shortcut. Hosted requires a real ggui_user_* connector key per app, revocable from the CLI.

The blueprint pool. Blueprints you matured locally are worth carrying over — a hosted app that already has them matches from cache instead of paying to synthesize the same UI again. ggui push compiles and uploads the local pool to a cloud app (ggui deploy runs it as one of its steps); ggui export-pool writes the pool to a directory that another ggui serve can load with --seed-pool <dir>.

Rate limits. Hosted enforces per-key limits that a loopback server does not — see Rate limits.

What does not change: the ggui_* tool surface, the handshake-render-consume sequence, GGUI_AGENT_SYSTEM_PROMPT, your contracts, and your component code. That is the portability claim, and it is testable — see Portability.

  1. ggui serve --dev-allow-all in one shell, your agent pointed at http://127.0.0.1:6781/mcp.
  2. ggui dev in another when you are shaping blueprints; edit ggui.ui.json, reload the hub, the registry re-indexes on every load.
  3. ggui dev --no-serve in CI — it loads and validates every manifest and exits non-zero on a malformed one, without binding a socket.
  4. When you need a real user in front of the render, ggui deploy, then restart the agent with the hosted .env.local.
  5. Need a public URL without leaving the local runtime? Run a tunnel beside it (cloudflared tunnel --url http://localhost:6781) and point the client at https://<tunnel>/mcp.