MCP Protocol Reference
read as.mdThe ggui MCP API is Model Context Protocol over HTTP with JSON-RPC 2.0. This page is the wire reference: tool names, input shapes, return shapes, error codes, and a complete curl walkthrough.
Three-noun vocab in play on this page: blueprint (a cached recipe routed by BlueprintSearch over the draft’s contract + variance axes), tool (an agent-side MCP method the LLM invokes), gadget (a renderer-side capability the generated component imports). If those terms are new, skim the glossary first.
Endpoint
Section titled “Endpoint”Self-hosted (ggui serve):
POST http://127.0.0.1:6781/mcpAuthentication
Section titled “Authentication”Local dev: start the server with ggui serve --dev-allow-all and any bearer (conventionally dev) authenticates as the builder identity:
Authorization: Bearer devContent-Type: application/jsonDefault ggui serve is strict — only pairing-minted bearers authenticate /mcp. Pair a key via the pair code the server prints at boot, or mint one locally with ggui keys create --keys-file <path> (the same file ggui serve --keys-file reads). The bare createGguiServer factory defaults to dev-allow-all until you pass auth — swap in a real AuthAdapter before exposing the port beyond 127.0.0.1.
Hosted ggui (coming soon) will use OAuth 2.0 with Dynamic Client Registration — Claude Desktop and other MCP-Apps hosts run the ceremony for you; raw-HTTP callers present the issued bearer token on every call. See OAuth on mcp.ggui.ai for the ceremony.
Render Lifecycle
Section titled “Render Lifecycle”1. initialize → MCP handshake (one-shot per connection)2. ggui_list_gadgets → Optional: fetch the gadget catalog3. ggui_handshake → Negotiate the wire (handshakeId + suggestion)4. ggui_render → Materialize the UI (mints sessionId)5. ggui_consume → Long-poll for user events (keyed by sessionId)6. ggui_update → Mutate props in place (never re-render)7. ggui_emit → Optional: push frames on a streamSpec channelRenders decay implicitly via TTL — there is no explicit close ceremony. ggui_update and ggui_consume are keyed by sessionId (globally unique); the server tenancy-checks via the bearer token.
Rendering Pipeline
Section titled “Rendering Pipeline”The rendering decision is made during ggui_handshake. The negotiator runs BlueprintSearch plus contract validation in parallel and returns a routed suggestion whose origin tag tells the agent which branch fired:
origin: 'cache'— exact or semantic match against a registered blueprint. Free, deterministic reuse on the paired render.origin: 'agent'— no cache hit, but the agent’s draft passed validation. Gen runs against the agent’s contract verbatim.origin: 'synth'— no cache hit AND validation surfaced amendments. The server amends the draft and gen runs against the amended contract.
The handshake response carries handshakeId, action, and suggestion (always with a provisional blueprintMeta). The agent then calls ggui_render with the handshakeId plus props: omit override to reuse the suggestion’s provisional blueprintId as-is, or pass override: {contract?, variance?} to mint a fresh blueprintId against a re-aimed draft.
Agent system prompt
Section titled “Agent system prompt”The canonical posture-only system prompt for any agent calling these tools is exported as a string constant:
import { GGUI_AGENT_SYSTEM_PROMPT } from "@ggui-ai/protocol";Use it as-is. It teaches the wire flow (handshake → render → consume), the three rendering origins, and when to call which tool — without baking in any product-specific persona. Per-tool description strings on each ggui_* MCP tool reinforce the same flow at the tool layer, so the agent has two consistent signals during planning.
Roll your own system prompt only when you have a domain-specific persona to layer on top. In that case, concatenate, don’t replace: keep GGUI_AGENT_SYSTEM_PROMPT first, then append your additions. Replacing it removes the wire-flow teaching, and the agent will misuse the toolset.
The prompt source lives at packages/protocol/src/recommended-prompts.ts and ships with @ggui-ai/protocol for every consumer language the protocol package targets.
| Tool | Purpose |
|---|---|
ggui_handshake |
Negotiate the wire surface before rendering — returns handshakeId + a routed suggestion. |
ggui_render |
Materialize the UI; mints sessionId. |
ggui_consume |
Long-poll buffered user events on one GguiSession. |
ggui_update |
Mutate props on a delivered GguiSession in place. |
ggui_emit |
Push a delivery onto a declared streamSpec channel. |
ggui_get_session |
Read GguiSession state + activity timestamps. |
ggui_list_sessions |
Enumerate GguiSessions by host conversation (resume flows). |
ggui_list_gadgets |
Fetch the renderer-side gadget catalog before authoring a contract. |
ggui_list_themes |
List the theme presets usable via ggui_render({themeId}). |
ggui_list_featured_blueprints |
Enumerate builder-curated featured blueprints. |
ggui_search_blueprints |
Semantic search across this app’s blueprints. |
ggui_render_blueprint |
Resolve a registered blueprint id to its compiled bundle. |
ggui_discover |
Platform capability discovery (hosted-only, coming soon). |
ggui_request_credential |
OAuth consent proxy (hosted-only, coming soon). |
ggui_handshake
Section titled “ggui_handshake”Negotiate the wire surface for a UI. Call BEFORE ggui_render. The agent posts a draft; the server runs blueprint-search + contract-validation in parallel and returns a routed suggestion the agent then accepts or overrides on render.
Top-level fields:
| Field | Type | Required | Description |
|---|---|---|---|
intent |
string |
Yes | Concise semantic identity — same intent across calls = same component reused. Example: "Gmail inbox for email triage". |
blueprintDraft |
object |
Yes | Single-field draft wrapping the agent’s contract (required) plus optional variance and optional generator slug hint. The contract drives blueprint-search embed/structural axes; variance feeds the variance axis. |
forceCreate |
boolean |
No | Skip blueprint-search and route straight to validation + agent-mode suggestion against the draft. Use after a prior handshake returned an unwanted cache suggestion. |
Returns: { handshakeId, action, suggestion, nextStep? }
| Field | Type | Description |
|---|---|---|
handshakeId |
string |
Stable id — pass to ggui_render. Records are SINGLE-USE and expire after 10 minutes. |
action |
enum |
One of create / reuse / update / replace / declined. |
suggestion |
object |
Routed suggestion. Carries origin: 'cache' | 'agent' | 'synth', an always-present provisional blueprintMeta (incl. blueprintId), and conditional amendments (synth-only) / validationFindings (soft on cache). |
nextStep |
object |
Wire-shape recovery hint — {tool: 'ggui_render', example} worked-literal of the next call. |
The agent branches the paired ggui_render on suggestion.origin: any origin can be accepted (reuse the provisional blueprintId verbatim) or overridden (mint fresh against a new draft).
ggui_render
Section titled “ggui_render”Materialize the UI. Step 3 of the three-step handshake protocol. handshakeId and props are REQUIRED. Commit relative to the handshake’s suggestion by PRESENCE of override: omit it to ACCEPT the suggestion as-is, or provide override: {contract?, variance?} to re-aim (PATCH semantics).
// ACCEPT the suggestion as-is{ "handshakeId": "h_…", "props": {…} }
// re-draft the contract (cold-gen){ "handshakeId": "h_…", "props": {…}, "override": { "contract": {…} } }
// re-aim the variant axis{ "handshakeId": "h_…", "props": {…}, "override": { "variance": {…} } }| Field | Type | Required | Description |
|---|---|---|---|
handshakeId |
string |
Yes | From a prior ggui_handshake response. |
props |
object |
Yes | Runtime prop values for THIS render. Validated against the effective contract’s propsSpec; failures fail the render with a recoverable ContractViolationError. Pass {} when the contract declares no propsSpec. |
themeId |
string |
No | Per-render theme preset override — wins over App.defaultThemeId for THIS render. Discover ids via ggui_list_themes. Omit to inherit the app theme. |
infra |
object |
No | {model?} — provider-prefixed per-render model override (e.g. anthropic/claude-haiku-4-5). Strict — unknown keys are rejected. |
override |
object |
No | Omit to ACCEPT the suggestion as-is. Provide {contract?, variance?} to re-aim: override.contract re-drafts the contract (STRICT — must already conform) and cold-gens; override.variance re-aims the variant axis. |
Returns: { sessionId, resourceUri?, action, contractHash, blueprintId, variantKey, cache, error?, nextStep? }
| Field | Type | Description |
|---|---|---|
sessionId |
string |
Globally-unique id (UUID) for the delivered render. Use for ggui_consume / ggui_update. |
resourceUri |
string |
Optional — present iff the render is mountable (absent on failure). Spec-canonical MCP-Apps entry point — ui://ggui/render/<id>, mirrored on _meta.ui.resourceUri; no clickable URL on the wire. |
action |
enum |
One of create / reuse / update / replace / declined. |
contractHash |
string |
Canonical hash of the rendered data contract (shape only — fields, types, specs). Same hash ⟺ same data flow. |
blueprintId |
string |
Opaque id of the materialised component. Equal across two renders ⟺ the same cached component was served (a fresh gen mints a new id). |
variantKey |
string |
Canonical hash of the design-time variance. With contractHash it forms the reuse key. |
cache |
object |
Reuse outcome — { hit, similarity?, cachedBlueprintId?, llmCallsAvoided, kind?, reason? }. |
nextStep |
object |
Emitted ONLY when the rendered contract has a non-empty actionSpec. Points at ggui_consume({sessionId}) for the inbound action loop. Pure-display renders get no nextStep. |
error |
object |
Optional — present iff the tool result is isError: true. { code, message } with the closed enum PRODUCTION_FAILED | VALIDATION_ERROR | NO_PLATFORM_KEY | NO_CREDENTIALS. Failed renders carry no resourceUri and no _meta. |
The render consumes the handshake record. Bootstrap credentials (wsUrl, wsToken, expiresAt) reach the iframe via the _meta["ai.ggui/render"] slice, not via this response.
ggui_consume
Section titled “ggui_consume”Long-poll for buffered user events on one render. Events drain on read (consume-once semantics). Call this right after every ggui_render whose response carries nextStep.tool === 'ggui_consume'.
| Field | Type | Required | Description |
|---|---|---|---|
sessionId |
string |
Yes | Render to consume from. Globally unique. |
timeout |
number |
No | Long-poll seconds — integer in [0, 25]; 0 = immediate (default). Values outside [0, 25] reject INVALID_PARAMS. Returns on the first event or at timeout; re-call on empty to keep waiting — a longer wait is your loop, not a bigger timeout (pick 5–15s typical, 25 max). |
Returns: { events: ConsumeEventEntry[], status: "active" | "expired", client? }
Keyed by sessionId. THE LOOP: when events is non-empty, react (commonly via ggui_update to refresh the iframe), then re-call ggui_consume. The render stays active until its TTL elapses — status: "expired" means no more events will arrive, so the long-poll loop terminates. Exit when you have the events you need, or when status is expired. The optional client field echoes mid-render host observations (window resize, fullscreen toggle, etc.) without forcing a fresh handshake.
ggui_update
Section titled “ggui_update”Mutate props on a delivered render in place. Targets sessionId directly. Discriminated on kind.
// FULL replacement{ "sessionId": "…", "kind": "replace", "props": { … } }
// RFC 7396 JSON Merge Patch{ "sessionId": "…", "kind": "merge", "patch": { … } }| Field | Type | Required | Description |
|---|---|---|---|
sessionId |
string |
Yes | The render to mutate (UUID from ggui_render response). |
kind |
enum |
Yes | 'replace' — the props map IS the new state. 'merge' — apply RFC 7396 JSON Merge Patch (null deletes a key; arrays fully replace, NOT element-wise). |
props |
object |
If replace | Full replacement props map. Required when kind: 'replace'. |
patch |
object |
If merge | RFC 7396 patch. Required when kind: 'merge'. |
Returns: { sessionId, updated, resourceUri }
resourceUri is unchanged from the initial render — the same ui://ggui/render/{sessionId} the mount stamped. Both modes validate the final props state (post-merge for merge) against the render’s propsSpec and reject on violation. Use for partial UI state changes; for a structurally different surface, handshake + render a fresh one. Post-update the iframe receives the new props via the live-channel props_update WS frame.
ggui_emit
Section titled “ggui_emit”Emit a new delivery on a declared streamSpec channel of the render. The agent describes WHAT new data exists; the server stamps the canonical StreamEnvelope (mode derived from streamSpec[channel].mode, seq + timestamp server-assigned). Validates payload against the channel’s declared schema and rejects undeclared channels at call time.
| Field | Type | Required | Description |
|---|---|---|---|
sessionId |
string |
Yes | Render to stream to. Server enforces app-ownership. |
channel |
string |
Yes | Channel name declared on the render’s streamSpec. Undeclared channels reject. |
payload |
unknown |
Yes | Delivery payload. Validated against streamSpec[channel].schema. |
complete |
boolean |
No | Terminal-delivery marker. Only valid when the channel was declared with complete: true on the streamSpec; setting it on a non-completable channel rejects. |
Returns: { accepted }
accepted: true means the server validated and enqueued the envelope at the boundary. No-subscriber is NOT an error — buffered retention and live fan-out happen independently. The server-assigned seq is observable on the delivered StreamEnvelope (the live-channel data WS frame), not on this tool result.
ggui_get_session
Section titled “ggui_get_session”Retrieve GguiSession state — id, appId, event sequence, activity timestamps. Bumps the activity heartbeat on every successful read. Omits componentCode + sourceCode (those live on the renderable surface, not the agent-visible one).
| Field | Type | Required | Description |
|---|---|---|---|
sessionId |
string |
Yes | GguiSession to inspect. |
Returns: { id, appId, eventSequence, createdAt, lastActivityAt, expiresAt }
createdAt / lastActivityAt / expiresAt are epoch milliseconds (numbers).
ggui_list_sessions
Section titled “ggui_list_sessions”Enumerate this app’s GguiSessions by host conversation — the lookup behind resume flows. Matches on the _meta["ai.ggui/host-session"] pair (hostName + hostSessionId) captured at render creation; sessions created without that slice never match host-scoped queries.
| Field | Type | Required | Description |
|---|---|---|---|
hostName |
string |
No | Filter by host identifier (claude.ai, sample, …). Pair with hostSessionId to target one conversation. |
hostSessionId |
string |
No | The host’s opaque conversation-grouping key (e.g. a claude.ai thread id). Typically paired with hostName. |
limit |
number |
No | Max rows, 1–200. Default 50. Newest-last ordering matches the conversation timeline. |
Returns: { sessions: [{ sessionId, hostName?, hostSessionId?, createdAt, lastActivityAt, status, wsToken?, wsTokenExpiresAt? }] }
createdAt / lastActivityAt are ISO 8601 strings here. The wsToken pair is present only when the deployment wires a mintWsToken seam — resume flows use it to remount each iframe without a fresh handshake.
ggui_list_gadgets
Section titled “ggui_list_gadgets”Return the catalog of renderer-side gadgets the UI may import via the package-keyed clientCapabilities.gadgets map of a DataContract. Call this BEFORE authoring a contract so the catalog you seed only references gadgets the renderer will actually serve. Returns the per-app catalog: the 7-hook stdlib package (@ggui-ai/gadgets) is the structural floor; gadgets declared in ggui.json#app.gadgets layer on top (declared wins on a package collision).
| Field | Type | Required | Description |
|---|---|---|---|
appId |
string |
No | The app whose catalog to fetch. Defaults to the caller-resolved appId from the auth header. Explicit mismatch surfaces as app_access_denied. |
Returns: { gadgets: GadgetDescriptor[] }
Each GadgetDescriptor is a gadget PACKAGE: { package, version, exports: GadgetExport[], … } — package-level identity plus transport metadata (bundleUrl / bundleHost / bundleSri / styleUrl / connect / requires / typesUrl / typesSri for non-stdlib packages). Each GadgetExport is a field-presence-discriminated union — a hook export { hook, description?, usage?, example?, gotchas?, permission?, required? } or a component export { component, description?, usage?, example?, gotchas?, permission?, required? }. Full entry shape: SDK gadgets guide.
ggui_list_themes
Section titled “ggui_list_themes”Return the theme presets an agent may apply per render via ggui_render({ themeId }). When the app configures an availableThemeIds allowlist, the catalog is filtered to it (catalog order preserved; unregistered ids silently dropped).
| Field | Type | Required | Description |
|---|---|---|---|
appId |
string |
No | The app whose theme catalog to fetch. Defaults to the caller-resolved appId from the auth header. Explicit mismatch surfaces as app_access_denied. |
Returns: { themes: [{ id, name, description, modes }] }
modes lists the variants each preset ships (light / dark).
ggui_list_featured_blueprints
Section titled “ggui_list_featured_blueprints”Enumerate the builder-curated featured blueprints declared via the server’s blueprint catalog (typically ggui.json#blueprints.include for OSS deployments). Returns an empty list when no catalog is wired.
Inputs: none.
Returns: { blueprints: BlueprintEntry[], total }
Pair with ggui_search_blueprints for semantic lookup or ggui_render_blueprint to materialize one directly.
ggui_search_blueprints
Section titled “ggui_search_blueprints”Semantic search across this app’s blueprints — both manifest-declared UIs (ggui.json#blueprints.include) and previously cached generations. Matches by name/description against the manifest source and by cosine similarity against the semantic vector index; results merge + dedupe by id (manifest wins on collision) and sort by score descending.
| Field | Type | Required | Description |
|---|---|---|---|
query |
string |
Yes | Natural-language description of the UI you’re looking for. |
limit |
number |
No | Max results. Default 10. Maximum 100. |
Returns: { results, total, query }
Each result row carries { id, name, description, category, props, callbacks, featured, relevance: 'match', score }. score is 0–1 (cosine similarity for semantic hits; 1.0 for exact manifest-name matches, 0.7 for manifest substring matches). Agents use score to decide whether to reuse a blueprint or generate from scratch.
ggui_discover
Section titled “ggui_discover”Return platform capabilities (protocol version, supported content types, shell types, adapter types, component-capability catalog) and — when the bearer token resolves to a known app — that app’s enabled adapters / granted capabilities / auth mode / rate limit. Call BEFORE the first handshake when you need to branch on what this deployment supports.
Inputs: none.
Returns: { protocolVersion, contentTypes, shellTypes, adapterTypes, componentCapabilities, app? }
| Field | Type | Description |
|---|---|---|
protocolVersion |
string |
ggui protocol revision (prelaunch drafts use draft-YYYY-MM-DD; first frozen release will be 1.0.0). |
contentTypes |
string[] |
Bundle content types this deployment serves (e.g. application/javascript+react). |
shellTypes |
string[] |
Available shell flavors (chat, fullscreen, spatial). |
adapterTypes |
string[] |
Adapter families wired on this deployment (voice, camera, location, bluetooth). |
componentCapabilities |
string[] |
Informational capability vocabulary. The load-bearing per-app grant lives on the operator-registered GadgetExport.permission in App.gadgets (registry side) — not on the contract wire. |
app |
object? |
Present when the bearer token resolves to a known app. { enabledAdapters?, grantedCapabilities?, defaultShellType?, authMode?, rateLimitPerMinute? }. |
ggui_request_credential
Section titled “ggui_request_credential”Request OAuth consent from the end user via the Portal’s consent overlay. Blocks up to 25 seconds polling for the user’s choice (Allow once / Always allow / Deny). Short-circuits with granted: true when a prior grant already exists for this user + app + service.
| Field | Type | Required | Description |
|---|---|---|---|
serviceId |
string |
Yes | OAuth service identifier (matches an McpServiceConfig entry — e.g. "bashdoor", "ubot"). |
reason |
string |
No | One-line rationale shown to the user inside the consent overlay. |
sessionId |
string |
No | Existing render to surface the consent UI into. Required to actually surface the overlay. |
Returns: { granted, mode?, service?, reason? }
| Field | Type | Description |
|---|---|---|
granted |
boolean |
Whether the user (or a prior grant) approved. |
mode |
'once' | 'always' |
Grant mode when granted: true. Absent on denial / timeout. |
service |
{ name, icon } |
Display info pulled from McpServiceConfig. Echoed back for UI parity. |
reason |
string |
Denial / timeout / error rationale when granted: false. |
ggui_render_blueprint
Section titled “ggui_render_blueprint”Resolve a registered blueprint id to its compiled JS bundle, inline. The OSS handler reads the manifest entry via the server’s UiRegistry, compiles on demand from the colocated TSX (@ggui-ai/dev-stack::LocalUiRegistry is the reference impl), and returns the bundle as a single JSON field. Fails with a clear error when the id is unknown or no bundle is available. Only registered when the server boots with a UiRegistry seam — otherwise the tool is omitted from tools/list entirely.
| Field | Type | Required | Description |
|---|---|---|---|
blueprintId |
string |
Yes | Stable blueprint id declared via ggui.ui.json#id. Must match an entry in this server’s UI registry. |
Returns: { blueprintId, blueprintName, code, contentType }
code is the compiled JS bundle as a string (ESM export default producing the component to mount). contentType is typically 'application/javascript+react' — pinned by the server’s compile pipeline. The caller mounts code directly; no second round-trip is required.
Events
Section titled “Events”Two distinct shapes are in play. Don’t conflate them:
ActionEnvelope— live-channel inbound on the WebSocket subscribe seam. Used by browser/SDK consumers that listen to live events (e.g.@ggui-ai/wire’suseRender). See WebSocket Protocol.ConsumeEventEntry— per-gesture row on the render-keyed consume pipe, returned byggui_consume. This is what agents read.
ActionEnvelope (live-channel inbound)
Section titled “ActionEnvelope (live-channel inbound)”interface ActionEnvelope<TPayload = JsonValue> { sessionId: string; type: EventType; payload?: TPayload; // For `data:submit`: { action, data?, tool? } clientSeq?: number; // client-monotonic, for at-least-once dedup}ConsumeEventEntry (consume pipe)
Section titled “ConsumeEventEntry (consume pipe)”interface ConsumeEventEntry { readonly type: "action"; readonly sessionId: string; readonly intent: string; // which actionSpec[*] fired readonly actionData: JsonValue | null; // matches actionSpec[intent].schema readonly uiContext: JsonObject; // contextSpec slot snapshot at gesture time readonly actionId: string; // 8-hex FNV-1a correlation id readonly firedAt: string; // ISO 8601 UTC}Both envelopes are flat — no nested event / context / meta blocks. Diagnostic render metadata (device info, interface context) lives on the render at subscribe time, not per-delivery.
Event Types
Section titled “Event Types”EventType has exactly one member, data:submit.
| Type | Category | Description |
|---|---|---|
data:submit |
Data | User gesture surfaced as a consume event |
The pre-actionSpec multi-event vocabulary (data:change, lifecycle:*, interaction:*, error:*) was deleted in draft-2026-06-12 — it never had a first-party producer. Today’s actionSpec-driven flow surfaces every user gesture as a data:submit ConsumeEventEntry. There is no other event vocabulary — agent code reads from ggui_consume and only needs to recognize the data:submit shape.
Error Codes
Section titled “Error Codes”Names mirror the MCP_ERROR_CODES / PLATFORM_ERROR_CODES constants exported from @ggui-ai/protocol. The -32010 range is the ggui platform-extension block, not part of the core protocol.
| Code | Name | Description |
|---|---|---|
-32700 |
PARSE_ERROR |
Invalid JSON in request |
-32600 |
INVALID_REQUEST |
Not a valid JSON-RPC object |
-32601 |
METHOD_NOT_FOUND |
Unknown method name |
-32602 |
INVALID_PARAMS |
Missing or invalid tool arguments |
-32603 |
INTERNAL_ERROR |
Server-side failure |
-32001 |
UNAUTHORIZED |
Invalid token or app ID |
-32002 |
SESSION_NOT_FOUND |
Session expired or deleted |
-32003 |
APP_NOT_FOUND |
App ID does not exist |
-32005 |
CAPABILITY_DENIED |
Requested capability not granted |
-32010 |
GENERATION_QUOTA_EXCEEDED |
Platform: generation quota exhausted |
-32011 |
APP_LIMIT_EXCEEDED |
Platform: app-count ceiling reached |
-32012 |
CONCURRENT_SESSION_LIMIT |
Platform: too many live sessions |
-32013 |
RATE_LIMIT_EXCEEDED |
Platform: reserved rate-limit code |
-32020 |
CONTRACT_VIOLATION |
Platform: contract validation failed |
-32004 is retired-reserved: PRODUCTION_FAILED is now an in-result
tool-error code. A failed generation returns a tools/call result with
isError: true and schema-conformant structuredContent carrying
error: { code, message } (closed enum PRODUCTION_FAILED | VALIDATION_ERROR | NO_PLATFORM_KEY | NO_CREDENTIALS), with no
resourceUri and no _meta — a failed render is not mountable. The
error session is still committed, so the live channel and render
resource keep the failure message. See SPEC §7.9 Plane 3.
Supported Models
Section titled “Supported Models”There is no fixed model menu. The operator sets the per-app default via ggui.json#generation.model — any provider-prefixed route, written provider:model (canonical) or LiteLLM-style provider/model, e.g. anthropic:claude-haiku-4-5-20251001. Providers on the self-hosted BYOK path: anthropic, openai, google, openrouter (bedrock routes are hosted-runtime-only and rejected by ggui serve).
Agents can override the model per render via ggui_render({ infra: { model } }) with a provider-prefixed id (e.g. anthropic/claude-haiku-4-5).
Example: Full Render Flow (curl)
Section titled “Example: Full Render Flow (curl)”This walkthrough runs against self-hosted ggui serve (started with --dev-allow-all). Hosted mcp.ggui.ai (coming soon) will speak the same wire — only the URL and bearer change.
# 1. Initializecurl -X POST http://127.0.0.1:6781/mcp \ -H "Authorization: Bearer dev" \ -H "Content-Type: application/json" \ -d '{ "jsonrpc": "2.0", "id": 1, "method": "initialize", "params": { "protocolVersion": "2025-06-18", "clientInfo": { "name": "curl", "version": "1.0" }, "capabilities": {} } }'
# 2. Handshake — negotiate the wire surface# (blueprintDraft carries the agent's contract)curl -X POST http://127.0.0.1:6781/mcp \ -H "Authorization: Bearer dev" \ -H "Content-Type: application/json" \ -d '{ "jsonrpc": "2.0", "id": 2, "method": "tools/call", "params": { "name": "ggui_handshake", "arguments": { "intent": "Contact form", "blueprintDraft": { "contract": { "propsSpec": {}, "actionSpec": {} } } } } }'
# 3. Render — accept the handshake suggestion verbatim (mints sessionId)curl -X POST http://127.0.0.1:6781/mcp \ -H "Authorization: Bearer dev" \ -H "Content-Type: application/json" \ -d '{ "jsonrpc": "2.0", "id": 3, "method": "tools/call", "params": { "name": "ggui_render", "arguments": { "handshakeId": "hs_…", "props": {} } } }'
# 4. Poll for events (keyed by sessionId from step 3)curl -X POST http://127.0.0.1:6781/mcp \ -H "Authorization: Bearer dev" \ -H "Content-Type: application/json" \ -d '{ "jsonrpc": "2.0", "id": 4, "method": "tools/call", "params": { "name": "ggui_consume", "arguments": { "sessionId": "<sessionId from step 3>", "timeout": 15 } } }'
# Renders decay implicitly via TTL — no explicit close.See Also
Section titled “See Also”- Protocol overview — the three channels at a glance
- WebSocket Protocol — live-channel live events
- MCP Apps — inline rendering inside MCP hosts
- OAuth on mcp.ggui.ai — hosted auth ceremony (coming soon)
- Control MCP plane — design-time and operator agent tools at
/control - Docs MCP route — anonymous docs search at
/docs - Playground · Todos — hosted demo service at
/playground/todos(coming soon) - Playground · MDH — hosted demo service at
/playground/mdh(coming soon) - MCP services — mounting standalone services on one server
- Glossary — gadget / tool / blueprint, plus every other term