diff --git a/specification/draft/apps.mdx b/specification/draft/apps.mdx index e5ff305..f4aa9d5 100644 --- a/specification/draft/apps.mdx +++ b/specification/draft/apps.mdx @@ -461,18 +461,17 @@ interface HostContext { displayMode?: "inline" | "fullscreen" | "pip"; /** Display modes the host supports */ availableDisplayModes?: string[]; - /** - * Viewport dimensions available to the UI. - * - * The viewport has two independent dimension pairs: - * - Height: Either `height` (fixed) or `maxHeight` (flexible), never both - * - Width: Either `width` (fixed) or `maxWidth` (flexible), never both - * - * Fixed dimensions (height/width): The host controls the size. Set height: 100% (recommended) or use the pixel value directly. - * Flexible dimensions (maxHeight/maxWidth or undefined): The app controls the size, up to the max if specified. - */ - viewport?: ({ height: number } | { maxHeight?: number }) & - ({ width: number } | { maxWidth?: number }); + /** Current and maximum dimensions available to the UI. */ + viewport?: { + /** Current viewport width in pixels. */ + width: number; + /** Current viewport height in pixels. */ + height: number; + /** Maximum available height in pixels (if constrained). */ + maxHeight?: number; + /** Maximum available width in pixels (if constrained). */ + maxWidth?: number; + }; /** User's language/region preference (BCP 47, e.g., "en-US") */ locale?: string; /** User's timezone (IANA, e.g., "America/New_York") */ diff --git a/src/app-bridge.test.ts b/src/app-bridge.test.ts index 3424d69..5a36b47 100644 --- a/src/app-bridge.test.ts +++ b/src/app-bridge.test.ts @@ -113,7 +113,7 @@ describe("App <-> AppBridge integration", () => { const testHostContext = { theme: "dark" as const, locale: "en-US", - viewport: { width: 800, maxHeight: 600 }, + viewport: { width: 800, height: 600, maxHeight: 600 }, }; const newBridge = new AppBridge( createMockClient() as Client, @@ -337,7 +337,7 @@ describe("App <-> AppBridge integration", () => { const initialContext = { theme: "light" as const, locale: "en-US", - viewport: { width: 800, maxHeight: 600 }, + viewport: { width: 800, height: 600, maxHeight: 600 }, }; const newBridge = new AppBridge( createMockClient() as Client, @@ -356,7 +356,7 @@ describe("App <-> AppBridge integration", () => { // Send another partial update: only viewport changes newBridge.sendHostContextChange({ - viewport: { width: 1024, maxHeight: 768 }, + viewport: { width: 1024, height: 768, maxHeight: 768 }, }); await flush(); @@ -367,7 +367,11 @@ describe("App <-> AppBridge integration", () => { const context = newApp.getHostContext(); expect(context?.theme).toBe("dark"); expect(context?.locale).toBe("en-US"); - expect(context?.viewport).toEqual({ width: 1024, maxHeight: 768 }); + expect(context?.viewport).toEqual({ + width: 1024, + height: 768, + maxHeight: 768, + }); await newAppTransport.close(); await newBridgeTransport.close(); diff --git a/src/generated/schema.json b/src/generated/schema.json index f1a458c..e835986 100644 --- a/src/generated/schema.json +++ b/src/generated/schema.json @@ -629,55 +629,28 @@ } }, "viewport": { - "description": "Viewport dimensions available to the UI.\n\nThe viewport has two independent dimension pairs:\n- Height: Either `height` (fixed) or `maxHeight` (flexible), never both\n- Width: Either `width` (fixed) or `maxWidth` (flexible), never both\n\nFixed dimensions (height/width): The host controls the size. Set height: 100% (recommended) or use the pixel value directly.\nFlexible dimensions (maxHeight/maxWidth or undefined): The app controls the size, up to the max if specified. If undefined, there is no limit.", - "allOf": [ - { - "anyOf": [ - { - "type": "object", - "properties": { - "height": { - "type": "number" - } - }, - "required": ["height"], - "additionalProperties": false - }, - { - "type": "object", - "properties": { - "maxHeight": { - "type": "number" - } - }, - "additionalProperties": false - } - ] + "description": "Current and maximum dimensions available to the UI.", + "type": "object", + "properties": { + "width": { + "description": "Current viewport width in pixels.", + "type": "number" }, - { - "anyOf": [ - { - "type": "object", - "properties": { - "width": { - "type": "number" - } - }, - "required": ["width"], - "additionalProperties": false - }, - { - "type": "object", - "properties": { - "maxWidth": { - "type": "number" - } - }, - "additionalProperties": false - } - ] + "height": { + "description": "Current viewport height in pixels.", + "type": "number" + }, + "maxHeight": { + "description": "Maximum available height in pixels (if constrained).", + "type": "number" + }, + "maxWidth": { + "description": "Maximum available width in pixels (if constrained).", + "type": "number" } - ] + }, + "required": ["width", "height"], + "additionalProperties": false }, "locale": { "description": "User's language and region preference in BCP 47 format.", @@ -1282,55 +1255,28 @@ } }, "viewport": { - "description": "Viewport dimensions available to the UI.\n\nThe viewport has two independent dimension pairs:\n- Height: Either `height` (fixed) or `maxHeight` (flexible), never both\n- Width: Either `width` (fixed) or `maxWidth` (flexible), never both\n\nFixed dimensions (height/width): The host controls the size. Set height: 100% (recommended) or use the pixel value directly.\nFlexible dimensions (maxHeight/maxWidth or undefined): The app controls the size, up to the max if specified. If undefined, there is no limit.", - "allOf": [ - { - "anyOf": [ - { - "type": "object", - "properties": { - "height": { - "type": "number" - } - }, - "required": ["height"], - "additionalProperties": false - }, - { - "type": "object", - "properties": { - "maxHeight": { - "type": "number" - } - }, - "additionalProperties": false - } - ] + "description": "Current and maximum dimensions available to the UI.", + "type": "object", + "properties": { + "width": { + "description": "Current viewport width in pixels.", + "type": "number" }, - { - "anyOf": [ - { - "type": "object", - "properties": { - "width": { - "type": "number" - } - }, - "required": ["width"], - "additionalProperties": false - }, - { - "type": "object", - "properties": { - "maxWidth": { - "type": "number" - } - }, - "additionalProperties": false - } - ] + "height": { + "description": "Current viewport height in pixels.", + "type": "number" + }, + "maxHeight": { + "description": "Maximum available height in pixels (if constrained).", + "type": "number" + }, + "maxWidth": { + "description": "Maximum available width in pixels (if constrained).", + "type": "number" } - ] + }, + "required": ["width", "height"], + "additionalProperties": false }, "locale": { "description": "User's language and region preference in BCP 47 format.", @@ -2468,55 +2414,28 @@ } }, "viewport": { - "description": "Viewport dimensions available to the UI.\n\nThe viewport has two independent dimension pairs:\n- Height: Either `height` (fixed) or `maxHeight` (flexible), never both\n- Width: Either `width` (fixed) or `maxWidth` (flexible), never both\n\nFixed dimensions (height/width): The host controls the size. Set height: 100% (recommended) or use the pixel value directly.\nFlexible dimensions (maxHeight/maxWidth or undefined): The app controls the size, up to the max if specified. If undefined, there is no limit.", - "allOf": [ - { - "anyOf": [ - { - "type": "object", - "properties": { - "height": { - "type": "number" - } - }, - "required": ["height"], - "additionalProperties": false - }, - { - "type": "object", - "properties": { - "maxHeight": { - "type": "number" - } - }, - "additionalProperties": false - } - ] + "description": "Current and maximum dimensions available to the UI.", + "type": "object", + "properties": { + "width": { + "description": "Current viewport width in pixels.", + "type": "number" }, - { - "anyOf": [ - { - "type": "object", - "properties": { - "width": { - "type": "number" - } - }, - "required": ["width"], - "additionalProperties": false - }, - { - "type": "object", - "properties": { - "maxWidth": { - "type": "number" - } - }, - "additionalProperties": false - } - ] + "height": { + "description": "Current viewport height in pixels.", + "type": "number" + }, + "maxHeight": { + "description": "Maximum available height in pixels (if constrained).", + "type": "number" + }, + "maxWidth": { + "description": "Maximum available width in pixels (if constrained).", + "type": "number" } - ] + }, + "required": ["width", "height"], + "additionalProperties": false }, "locale": { "description": "User's language and region preference in BCP 47 format.", diff --git a/src/generated/schema.ts b/src/generated/schema.ts index 5558961..5e59edc 100644 --- a/src/generated/schema.ts +++ b/src/generated/schema.ts @@ -562,39 +562,26 @@ export const McpUiHostContextSchema = z.looseObject({ .array(z.string()) .optional() .describe("Display modes the host supports."), - /** - * @description Viewport dimensions available to the UI. - * - * The viewport has two independent dimension pairs: - * - Height: Either `height` (fixed) or `maxHeight` (flexible), never both - * - Width: Either `width` (fixed) or `maxWidth` (flexible), never both - * - * Fixed dimensions (height/width): The host controls the size. Set height: 100% (recommended) or use the pixel value directly. - * Flexible dimensions (maxHeight/maxWidth or undefined): The app controls the size, up to the max if specified. If undefined, there is no limit. - */ + /** @description Current and maximum dimensions available to the UI. */ viewport: z - .union([ - z.object({ - height: z.number(), - }), - z.object({ - maxHeight: z.number().optional(), - }), - ]) - .and( - z.union([ - z.object({ - width: z.number(), - }), - z.object({ - maxWidth: z.number().optional(), - }), - ]), - ) + .object({ + /** @description Current viewport width in pixels. */ + width: z.number().describe("Current viewport width in pixels."), + /** @description Current viewport height in pixels. */ + height: z.number().describe("Current viewport height in pixels."), + /** @description Maximum available height in pixels (if constrained). */ + maxHeight: z + .number() + .optional() + .describe("Maximum available height in pixels (if constrained)."), + /** @description Maximum available width in pixels (if constrained). */ + maxWidth: z + .number() + .optional() + .describe("Maximum available width in pixels (if constrained)."), + }) .optional() - .describe( - "Viewport dimensions available to the UI.\n\nThe viewport has two independent dimension pairs:\n- Height: Either `height` (fixed) or `maxHeight` (flexible), never both\n- Width: Either `width` (fixed) or `maxWidth` (flexible), never both\n\nFixed dimensions (height/width): The host controls the size. Set height: 100% (recommended) or use the pixel value directly.\nFlexible dimensions (maxHeight/maxWidth or undefined): The app controls the size, up to the max if specified. If undefined, there is no limit.", - ), + .describe("Current and maximum dimensions available to the UI."), /** @description User's language and region preference in BCP 47 format. */ locale: z .string() diff --git a/src/spec.types.ts b/src/spec.types.ts index 1a0c8c2..f97a0a6 100644 --- a/src/spec.types.ts +++ b/src/spec.types.ts @@ -324,18 +324,17 @@ export interface McpUiHostContext { displayMode?: McpUiDisplayMode; /** @description Display modes the host supports. */ availableDisplayModes?: string[]; - /** - * @description Viewport dimensions available to the UI. - * - * The viewport has two independent dimension pairs: - * - Height: Either `height` (fixed) or `maxHeight` (flexible), never both - * - Width: Either `width` (fixed) or `maxWidth` (flexible), never both - * - * Fixed dimensions (height/width): The host controls the size. Set height: 100% (recommended) or use the pixel value directly. - * Flexible dimensions (maxHeight/maxWidth or undefined): The app controls the size, up to the max if specified. If undefined, there is no limit. - */ - viewport?: ({ height: number } | { maxHeight?: number }) & - ({ width: number } | { maxWidth?: number }); + /** @description Current and maximum dimensions available to the UI. */ + viewport?: { + /** @description Current viewport width in pixels. */ + width: number; + /** @description Current viewport height in pixels. */ + height: number; + /** @description Maximum available height in pixels (if constrained). */ + maxHeight?: number; + /** @description Maximum available width in pixels (if constrained). */ + maxWidth?: number; + }; /** @description User's language and region preference in BCP 47 format. */ locale?: string; /** @description User's timezone in IANA format. */