/** * Host Context — projected subset of `McpUiHostContext` ggui captures from * the MCP Apps `ui/initialize` response and echoes back to render state so * the agent can reason about device/host capabilities on subsequent turns. * * The MCP Apps spec (`@modelcontextprotocol/ext-apps`) defines a rich * {@link McpUiHostContext} (theme, styles, displayMode, availableDisplayModes, * containerDimensions, locale, timeZone, userAgent, platform, * deviceCapabilities). ggui captures it iframe-side at `ui/initialize` and * echoes a TRIMMED projection back over the live channel (via the * `host_context_observed` outbound message) so the server can persist it on * `GguiSession.hostContext` and surface it on `ggui_handshake` / * `ggui_consume` output for the agent. * * Why a projection rather than passthrough: * * - `theme` / `styles` already flow through ggui's separate theming * pipeline (`InterfaceContext` + the theme registry). Duplicating * creates two sources of truth. * - `toolInfo` is host-loop-internal — the agent has its own toolInfo via * MCP framing. * - `userAgent` is rarely actionable; skip until a concrete use case * appears. Easy to add later (additive optional field). * * What the projection KEEPS: * * - `availableDisplayModes` / `currentDisplayMode` — drives the * display-mode escalation policy (MCP Apps inline/fullscreen/pip). * - `containerDimensions` — lets the agent reason about layout density * and lets the rendered UI reflow on resize. * - `platform` / `deviceCapabilities` — feeds the generator's * responsive-UI prompts. * - `locale` / `timeZone` — useful for the agent's date/number rendering. * * Compatibility posture: every field is optional. Hosts that emit a * minimal `McpUiHostContext` (spec-permissible) project to an empty * object; consumers MUST handle every field as possibly absent. * * Versioning: the wire wrapper carries `schemaVersion` so future * projection widenings can be detected; this module is the canonical * shape for the current schema major. */ import type { JsonValue } from './data-contract.js'; export type { McpUiDisplayMode, McpUiHostContext, McpUiHostCapabilities, } from '@modelcontextprotocol/ext-apps'; import type { McpUiDisplayMode } from '@modelcontextprotocol/ext-apps'; import type { z } from 'zod'; import type { hostContextProjectionSchema, mcpUiDisplayModeSchema } from '../schemas/mcp.js'; import type { DeepReadonly } from './readonly.js'; /** * The wire enum is assignable to ext-apps' `McpUiDisplayMode` — checked at * compile time, so a display mode the host SDK adds is a protocol change * here, never a silent widening. */ export type HostContextDisplayModeIsExtApps = z.infer extends McpUiDisplayMode ? true : never; /** * Forces the check above to be EVALUATED: a conditional type nobody consumes * is never checked, but a value annotated with it is — if the wire enum ever * drifts from ext-apps' `McpUiDisplayMode`, `true` stops being assignable to * `never` and this line is the compile error. */ export declare const HOST_CONTEXT_DISPLAY_MODE_IS_EXT_APPS: HostContextDisplayModeIsExtApps; /** * Width specification — either fixed `width` or `maxWidth`, never both. * Matches the spec's discriminated container-dimension shape. */ export interface HostContextWidth { readonly width?: number; readonly maxWidth?: number; } /** * Height specification — either fixed `height` or `maxHeight`, never both. */ export interface HostContextHeight { readonly height?: number; readonly maxHeight?: number; } /** * Iframe / container dimensions reported by the host. Width and height * are independently spec'd (one may be fixed, the other max-bounded). */ export type HostContextContainerDimensions = HostContextWidth & HostContextHeight; /** * Input capabilities reported by the host. Both `touch` and `hover` may * be true (hybrid devices); both may be false (rare, e.g., voice-only * hosts). */ export interface HostContextDeviceCapabilities { readonly touch?: boolean; readonly hover?: boolean; } /** * Trimmed projection of `McpUiHostContext` that ggui captures iframe-side * and echoes to render state for agent visibility. * * Every field is optional. Hosts that emit minimal context project to * mostly-empty objects; consumers MUST treat every field as possibly * absent and degrade gracefully. * * Theme + styles intentionally EXCLUDED — they flow through ggui's own * theming pipeline (`InterfaceContext`, theme registry). Duplicating * here would create two sources of truth. * * `userAgent` + `toolInfo` intentionally EXCLUDED for v1 — easy to add * later if a concrete use case appears. */ export type HostContextProjection = DeepReadonly>; /** * Live-channel inbound (client → server) payload that delivers the * iframe-captured `HostContextProjection` to the server. Server-side * handler writes to `GguiSession.hostContext`; subsequent * `ggui_handshake` / `ggui_consume` responses surface the value to the * agent via the optional `client.hostContext` field. * * Emission cadence: * - Once after the iframe-runtime's `ui/initialize` resolves (initial * capture). * - Once per `ui/notifications/host-context-changed` notification * received from the host. * * Idempotent — re-delivery (e.g., after a reconnect) overwrites the * stored value; no merge logic. */ export interface HostContextObservedPayload { readonly sessionId: string; readonly hostContext: HostContextProjection; } /** * Project a raw `McpUiHostContext` (from the spec SDK or any equivalent * shape — accepts `unknown` so callers don't need to drag in the SDK * just to call this) into `HostContextProjection`. * * Defensive: every field crosses a trust boundary. Malformed inputs * (wrong types, weird shapes) drop silently to undefined for that * field rather than failing the whole projection. The whole capture * path is best-effort — never blocks the bootstrap. * * Returns `undefined` when the input is not an object at all (caller * received null / array / primitive from the host). Returns an empty * object when the input is an object but no recognized fields are * present — the distinction lets callers tell "host emitted context * with no recognized fields" from "host emitted no context." */ export declare function projectHostContext(raw: unknown): HostContextProjection | undefined; /** * Deep equality check for two projections. Used by the iframe-runtime * to suppress no-op re-emissions when a `host-context-changed` * notification arrives but no projection-visible field actually changed. * * JSON-stringify is sufficient because the projection contains only * primitives, arrays of primitives, and plain objects of primitives. */ export declare function hostContextProjectionsEqual(a: HostContextProjection | undefined, b: HostContextProjection | undefined): boolean; export type { JsonValue }; //# sourceMappingURL=host-context.d.ts.map