MCP Protocol Reference
read as.md The 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, nextStep? }
| Field | Type | Description |
|---|---|---|
sessionId | string | Globally-unique id (UUID) for the delivered render. Use for ggui_consume / ggui_update. |
resourceUri | string | Spec-canonical MCP-Apps entry point — ui://ggui/render/<id>, mirrored on the tool result’s _meta.ui.resourceUri. A host mounts the render from this; there is 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. |
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 0.3.0) 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 |
-32004 | PRODUCTION_FAILED | UI production failed |
-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 |
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)
- Ops MCP route — operator agent tools at
/ops - 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