feat: enhance sandbox capability negotiation (#158)

* feat: enhance sandbox capability negotiation

* Update specification/draft/apps.mdx

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* add all csp and permissions to host capabilities + update SEP

* feat: add clipboard-write permission support

Adds clipboardWrite to McpUiResourcePermissions for clipboard access.
Maps to Permission Policy 'clipboard-write' feature.

* rm files added by mistake

* refactor: change McpUiResourcePermissions properties to empty objects

* chore: remove scratch files and redundant cross-env dependency

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Olivier Chafik <ochafik@anthropic.com>
This commit is contained in:
Ido Salomon 2026-01-12 17:10:27 +02:00 • committed by GitHub
commit 9acc52cb96
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 496 additions and 16 deletions

View file

@ -1,4 +1,4 @@
import { RESOURCE_MIME_TYPE, getToolUiResourceUri, type McpUiSandboxProxyReadyNotification, AppBridge, PostMessageTransport, type McpUiResourceCsp } from "@modelcontextprotocol/ext-apps/app-bridge";
import { RESOURCE_MIME_TYPE, getToolUiResourceUri, type McpUiSandboxProxyReadyNotification, AppBridge, PostMessageTransport, type McpUiResourceCsp, type McpUiResourcePermissions } from "@modelcontextprotocol/ext-apps/app-bridge";
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
import type { CallToolResult, Tool } from "@modelcontextprotocol/sdk/types.js";
@ -43,6 +43,7 @@ export async function connectToServer(serverUrl: URL): Promise<ServerInfo> {
interface UiResourceData {
html: string;
csp?: McpUiResourceCsp;
permissions?: McpUiResourcePermissions;
}
export interface ToolCallInfo {
@ -105,15 +106,16 @@ async function getUiResource(serverInfo: ServerInfo, uri: string): Promise<UiRes
const html = "blob" in content ? atob(content.blob) : content.text;
// Extract CSP metadata from resource content._meta.ui.csp (or content.meta for Python SDK)
// Extract CSP and permissions metadata from resource content._meta.ui (or content.meta for Python SDK)
log.info("Resource content keys:", Object.keys(content));
log.info("Resource content._meta:", (content as any)._meta);
// Try both _meta (spec) and meta (Python SDK quirk)
const contentMeta = (content as any)._meta || (content as any).meta;
const csp = contentMeta?.ui?.csp;
const permissions = contentMeta?.ui?.permissions;
return { html, csp };
return { html, csp, permissions };
}
@ -168,10 +170,10 @@ export async function initializeApp(
new PostMessageTransport(iframe.contentWindow!, iframe.contentWindow!),
);
// Load inner iframe HTML with CSP metadata
const { html, csp } = await appResourcePromise;
log.info("Sending UI resource HTML to MCP App", csp ? `(CSP: ${JSON.stringify(csp)})` : "");
await appBridge.sendSandboxResourceReady({ html, csp });
// Load inner iframe HTML with CSP and permissions metadata
const { html, csp, permissions } = await appResourcePromise;
log.info("Sending UI resource HTML to MCP App", csp ? `(CSP: ${JSON.stringify(csp)})` : "", permissions ? `(Permissions: ${JSON.stringify(permissions)})` : "");
await appBridge.sendSandboxResourceReady({ html, csp, permissions });
// Wait for inner iframe to be ready
log.info("Waiting for MCP App to initialize...");

View file

@ -66,6 +66,24 @@ const PROXY_READY_NOTIFICATION: McpUiSandboxProxyReadyNotification["method"] =
// Security: CSP is enforced via HTTP headers on sandbox.html (set by serve.ts
// based on ?csp= query param). This is tamper-proof unlike meta tags.
// Build iframe allow attribute from permissions
function buildAllowAttribute(permissions?: {
camera?: boolean;
microphone?: boolean;
geolocation?: boolean;
clipboardWrite?: boolean;
}): string {
if (!permissions) return "";
const allowList: string[] = [];
if (permissions.camera) allowList.push("camera");
if (permissions.microphone) allowList.push("microphone");
if (permissions.geolocation) allowList.push("geolocation");
if (permissions.clipboardWrite) allowList.push("clipboard-write");
return allowList.join("; ");
}
window.addEventListener("message", async (event) => {
if (event.source === window.parent) {
// Validate that messages from parent come from the expected host origin.
@ -81,10 +99,16 @@ window.addEventListener("message", async (event) => {
}
if (event.data && event.data.method === RESOURCE_READY_NOTIFICATION) {
const { html, sandbox } = event.data.params;
const { html, sandbox, permissions } = event.data.params;
if (typeof sandbox === "string") {
inner.setAttribute("sandbox", sandbox);
}
// Set Permission Policy allow attribute if permissions are requested
const allowAttribute = buildAllowAttribute(permissions);
if (allowAttribute) {
console.log("[Sandbox] Setting allow attribute:", allowAttribute);
inner.setAttribute("allow", allowAttribute);
}
if (typeof html === "string") {
// Use document.write instead of srcdoc for WebGL compatibility.
// srcdoc creates an opaque origin which prevents WebGL canvas updates

View file

@ -132,11 +132,23 @@ interface McpUiResourceCsp {
*/
resourceDomains?: string[],
/**
* Origins for nested iframes (frame-src directive).
* Origins for nested iframes
*
* - Empty or omitted = no nested iframes allowed (`frame-src 'none'`)
* - Maps to CSP `frame-src` directive
*
* @example
* ["https://www.youtube.com", "https://player.vimeo.com"]
*/
frameDomains?: string[],
/**
* Allowed base URIs for the document (base-uri directive).
* Allowed base URIs for the document
*
* - Empty or omitted = only same origin allowed (`base-uri 'self'`)
* - Maps to CSP `base-uri` directive
*
* @example
* ["https://cdn.example.com"]
*/
baseUriDomains?: string[],
}
@ -149,6 +161,39 @@ interface UIResourceMeta {
* Hosts use this to enforce appropriate CSP headers.
*/
csp?: McpUiResourceCsp,
/**
* Sandbox permissions requested by the UI
*
* Servers declare which browser capabilities their UI needs.
* Hosts MAY honor these by setting appropriate iframe `allow` attributes.
* Apps SHOULD NOT assume permissions are granted; use JS feature detection as fallback.
*/
permissions?: {
/**
* Request camera access
*
* Maps to Permission Policy `camera` feature
*/
camera?: boolean,
/**
* Request microphone access
*
* Maps to Permission Policy `microphone` feature
*/
microphone?: boolean,
/**
* Request geolocation access
*
* Maps to Permission Policy `geolocation` feature
*/
geolocation?: boolean,
/**
* Request clipboard write access
*
* Maps to Permission Policy `clipboard-write` feature
*/
clipboardWrite?: boolean,
},
/**
* Dedicated origin for widget
*
@ -193,6 +238,12 @@ The resource content is returned via `resources/read`:
frameDomains?: string[]; // Origins for nested iframes (frame-src directive).
baseUriDomains?: string[]; // Allowed base URIs for the document (base-uri directive).
};
permissions?: {
camera?: boolean; // Request camera access
microphone?: boolean; // Request microphone access
geolocation?: boolean; // Request geolocation access
clipboardWrite?: boolean; // Request clipboard write access
};
domain?: string;
prefersBorder?: boolean;
};
@ -416,9 +467,11 @@ If the Host is a web page, it MUST wrap the Guest UI and communicate with it thr
4. Once the Sandbox is ready, the Host MUST send the raw HTML resource to load in a `ui/notifications/sandbox-resource-ready` notification.
5. The Sandbox MUST load the raw HTML of the Guest UI with CSP settings that:
- Enforce the domains declared in `ui.csp` metadata
- Prevent nested iframes (`frame-src 'none'`)
- Block dangerous features (`object-src 'none'`, `base-uri 'self'`)
- If `frameDomains` is provided, allow nested iframes from declared origins; otherwise use `frame-src 'none'`
- If `baseUriDomains` is provided, allow base URIs from declared origins; otherwise use `base-uri 'self'`
- Block dangerous features (`object-src 'none'`)
- Apply restrictive defaults if no CSP metadata is provided
- If `permissions` is declared, the Sandbox MAY set the inner iframe's `allow` attribute accordingly
6. The Sandbox MUST forward messages sent by the Host to the Guest UI, and vice versa, for any method that doesn’t start with `ui/notifications/sandbox-`. This includes lifecycle messages, e.g., `ui/initialize` request & `ui/notifications/initialized` notification both sent by the Guest UI. The Host MUST NOT send any request or notification to the Guest UI before it receives an `initialized` notification.
7. The Sandbox SHOULD NOT create/send any requests to the Host or to the Guest UI (this would require synthesizing new request ids).
8. The Host MAY forward any message from the Guest UI (coming via the Sandbox) to the MCP Apps server, for any method that doesn’t start with `ui/`. While the Host SHOULD ensure the Guest UI’s MCP connection is spec-compliant, it MAY decide to block some messages or subject them to further user approval.
@ -535,6 +588,53 @@ Example:
}
```
### Host Capabilities
`HostCapabilities` are sent to the Guest UI as part of the response to `ui/initialize` (inside `McpUiInitializeResult`).
They describe the features and capabilities that the Host supports.
```typescript
interface HostCapabilities {
/** Experimental features (structure TBD). */
experimental?: {};
/** Host supports opening external URLs. */
openLinks?: {};
/** Host can proxy tool calls to the MCP server. */
serverTools?: {
/** Host supports tools/list_changed notifications. */
listChanged?: boolean;
};
/** Host can proxy resource reads to the MCP server. */
serverResources?: {
/** Host supports resources/list_changed notifications. */
listChanged?: boolean;
};
/** Host accepts log messages. */
logging?: {};
/** Sandbox configuration applied by the host. */
sandbox?: {
/** Permissions granted by the host (camera, microphone, geolocation, clipboard-write). */
permissions?: {
camera?: boolean;
microphone?: boolean;
geolocation?: boolean;
clipboardWrite?: boolean;
};
/** CSP domains approved by the host. */
csp?: {
/** Approved origins for network requests (fetch/XHR/WebSocket). */
connectDomains?: string[];
/** Approved origins for static resources (scripts, images, styles, fonts). */
resourceDomains?: string[];
/** Approved origins for nested iframes (frame-src directive). */
frameDomains?: string[];
/** Approved base URIs for the document (base-uri directive). */
baseUriDomains?: string[];
};
};
}
```
### Container Dimensions
The `HostContext` provides sizing information via `containerDimensions`:
@ -1028,12 +1128,24 @@ These messages are reserved for web-based hosts that implement the recommended d
method: "ui/notifications/sandbox-resource-ready",
params: {
html: string, // HTML content to load
sandbox: string // Optional override for inner iframe `sandbox` attribute
sandbox?: string, // Optional override for inner iframe `sandbox` attribute
csp?: { // CSP configuration from resource metadata
connectDomains?: string[],
resourceDomains?: string[],
frameDomains?: string[],
baseUriDomains?: string[],
},
permissions?: { // Sandbox permissions from resource metadata
camera?: boolean,
microphone?: boolean,
geolocation?: boolean,
clipboardWrite?: boolean,
}
}
}
```
These messages facilitate the communication between the outer sandbox proxy iframe and the host, enabling secure loading of untrusted HTML content.
These messages facilitate the communication between the outer sandbox proxy iframe and the host, enabling secure loading of untrusted HTML content. The `permissions` field maps to the inner iframe's `allow` attribute for Permission Policy features.
### Lifecycle
@ -1489,6 +1601,7 @@ Hosts MUST enforce Content Security Policies based on resource metadata.
```typescript
const csp = resource._meta?.ui?.csp;
const permissions = resource._meta?.ui?.permissions;
const cspValue = `
default-src 'none';
@ -1498,10 +1611,17 @@ const cspValue = `
img-src 'self' data: ${csp?.resourceDomains?.join(' ') || ''};
font-src 'self' ${csp?.resourceDomains?.join(' ') || ''};
media-src 'self' data: ${csp?.resourceDomains?.join(' ') || ''};
frame-src 'none';
frame-src ${csp?.frameDomains?.join(' ') || "'none'"};
object-src 'none';
base-uri 'self';
base-uri ${csp?.baseUriDomains?.join(' ') || "'self'"};
`;
// Permission Policy for iframe allow attribute
const allowList: string[] = [];
if (permissions?.camera) allowList.push('camera');
if (permissions?.microphone) allowList.push('microphone');
if (permissions?.geolocation) allowList.push('geolocation');
const allowAttribute = allowList.join(' ');
```
**Security Requirements:**

View file

@ -89,6 +89,79 @@
"type": "object",
"properties": {},
"additionalProperties": false
},
"sandbox": {
"description": "Sandbox configuration applied by the host.",
"type": "object",
"properties": {
"permissions": {
"description": "Permissions granted by the host (camera, microphone, geolocation).",
"type": "object",
"properties": {
"camera": {
"description": "Request camera access (Permission Policy `camera` feature).",
"type": "object",
"properties": {},
"additionalProperties": false
},
"microphone": {
"description": "Request microphone access (Permission Policy `microphone` feature).",
"type": "object",
"properties": {},
"additionalProperties": false
},
"geolocation": {
"description": "Request geolocation access (Permission Policy `geolocation` feature).",
"type": "object",
"properties": {},
"additionalProperties": false
},
"clipboardWrite": {
"description": "Request clipboard write access (Permission Policy `clipboard-write` feature).",
"type": "object",
"properties": {},
"additionalProperties": false
}
},
"additionalProperties": false
},
"csp": {
"description": "CSP domains approved by the host.",
"type": "object",
"properties": {
"connectDomains": {
"description": "Origins for network requests (fetch/XHR/WebSocket).",
"type": "array",
"items": {
"type": "string"
}
},
"resourceDomains": {
"description": "Origins for static resources (scripts, images, styles, fonts).",
"type": "array",
"items": {
"type": "string"
}
},
"frameDomains": {
"description": "Origins for nested iframes (frame-src directive).",
"type": "array",
"items": {
"type": "string"
}
},
"baseUriDomains": {
"description": "Allowed base URIs for the document (base-uri directive).",
"type": "array",
"items": {
"type": "string"
}
}
},
"additionalProperties": false
}
},
"additionalProperties": false
}
},
"additionalProperties": false
@ -2231,6 +2304,79 @@
"type": "object",
"properties": {},
"additionalProperties": false
},
"sandbox": {
"description": "Sandbox configuration applied by the host.",
"type": "object",
"properties": {
"permissions": {
"description": "Permissions granted by the host (camera, microphone, geolocation).",
"type": "object",
"properties": {
"camera": {
"description": "Request camera access (Permission Policy `camera` feature).",
"type": "object",
"properties": {},
"additionalProperties": false
},
"microphone": {
"description": "Request microphone access (Permission Policy `microphone` feature).",
"type": "object",
"properties": {},
"additionalProperties": false
},
"geolocation": {
"description": "Request geolocation access (Permission Policy `geolocation` feature).",
"type": "object",
"properties": {},
"additionalProperties": false
},
"clipboardWrite": {
"description": "Request clipboard write access (Permission Policy `clipboard-write` feature).",
"type": "object",
"properties": {},
"additionalProperties": false
}
},
"additionalProperties": false
},
"csp": {
"description": "CSP domains approved by the host.",
"type": "object",
"properties": {
"connectDomains": {
"description": "Origins for network requests (fetch/XHR/WebSocket).",
"type": "array",
"items": {
"type": "string"
}
},
"resourceDomains": {
"description": "Origins for static resources (scripts, images, styles, fonts).",
"type": "array",
"items": {
"type": "string"
}
},
"frameDomains": {
"description": "Origins for nested iframes (frame-src directive).",
"type": "array",
"items": {
"type": "string"
}
},
"baseUriDomains": {
"description": "Allowed base URIs for the document (base-uri directive).",
"type": "array",
"items": {
"type": "string"
}
}
},
"additionalProperties": false
}
},
"additionalProperties": false
}
},
"additionalProperties": false,
@ -3532,6 +3678,37 @@
},
"additionalProperties": false
},
"permissions": {
"description": "Sandbox permissions requested by the UI.",
"type": "object",
"properties": {
"camera": {
"description": "Request camera access (Permission Policy `camera` feature).",
"type": "object",
"properties": {},
"additionalProperties": false
},
"microphone": {
"description": "Request microphone access (Permission Policy `microphone` feature).",
"type": "object",
"properties": {},
"additionalProperties": false
},
"geolocation": {
"description": "Request geolocation access (Permission Policy `geolocation` feature).",
"type": "object",
"properties": {},
"additionalProperties": false
},
"clipboardWrite": {
"description": "Request clipboard write access (Permission Policy `clipboard-write` feature).",
"type": "object",
"properties": {},
"additionalProperties": false
}
},
"additionalProperties": false
},
"domain": {
"description": "Dedicated origin for widget sandbox.",
"type": "string"
@ -3543,6 +3720,37 @@
},
"additionalProperties": false
},
"McpUiResourcePermissions": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"camera": {
"description": "Request camera access (Permission Policy `camera` feature).",
"type": "object",
"properties": {},
"additionalProperties": false
},
"microphone": {
"description": "Request microphone access (Permission Policy `microphone` feature).",
"type": "object",
"properties": {},
"additionalProperties": false
},
"geolocation": {
"description": "Request geolocation access (Permission Policy `geolocation` feature).",
"type": "object",
"properties": {},
"additionalProperties": false
},
"clipboardWrite": {
"description": "Request clipboard write access (Permission Policy `clipboard-write` feature).",
"type": "object",
"properties": {},
"additionalProperties": false
}
},
"additionalProperties": false
},
"McpUiResourceTeardownRequest": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
@ -3638,6 +3846,37 @@
}
},
"additionalProperties": false
},
"permissions": {
"description": "Sandbox permissions from resource metadata.",
"type": "object",
"properties": {
"camera": {
"description": "Request camera access (Permission Policy `camera` feature).",
"type": "object",
"properties": {},
"additionalProperties": false
},
"microphone": {
"description": "Request microphone access (Permission Policy `microphone` feature).",
"type": "object",
"properties": {},
"additionalProperties": false
},
"geolocation": {
"description": "Request geolocation access (Permission Policy `geolocation` feature).",
"type": "object",
"properties": {},
"additionalProperties": false
},
"clipboardWrite": {
"description": "Request clipboard write access (Permission Policy `clipboard-write` feature).",
"type": "object",
"properties": {},
"additionalProperties": false
}
},
"additionalProperties": false
}
},
"required": ["html"],

View file

@ -47,6 +47,10 @@ export type McpUiResourceCspSchemaInferredType = z.infer<
typeof generated.McpUiResourceCspSchema
>;
export type McpUiResourcePermissionsSchemaInferredType = z.infer<
typeof generated.McpUiResourcePermissionsSchema
>;
export type McpUiSizeChangedNotificationSchemaInferredType = z.infer<
typeof generated.McpUiSizeChangedNotificationSchema
>;
@ -173,6 +177,12 @@ expectType<McpUiSandboxProxyReadyNotificationSchemaInferredType>(
);
expectType<spec.McpUiResourceCsp>({} as McpUiResourceCspSchemaInferredType);
expectType<McpUiResourceCspSchemaInferredType>({} as spec.McpUiResourceCsp);
expectType<spec.McpUiResourcePermissions>(
{} as McpUiResourcePermissionsSchemaInferredType,
);
expectType<McpUiResourcePermissionsSchemaInferredType>(
{} as spec.McpUiResourcePermissions,
);
expectType<spec.McpUiSizeChangedNotification>(
{} as McpUiSizeChangedNotificationSchemaInferredType,
);

View file

@ -211,6 +211,40 @@ export const McpUiResourceCspSchema = z.object({
.describe("Allowed base URIs for the document (base-uri directive)."),
});
/**
* @description Sandbox permissions requested by the UI resource.
* Hosts MAY honor these by setting appropriate iframe `allow` attributes.
* Apps SHOULD NOT assume permissions are granted; use JS feature detection as fallback.
*/
export const McpUiResourcePermissionsSchema = z.object({
/** @description Request camera access (Permission Policy `camera` feature). */
camera: z
.object({})
.optional()
.describe("Request camera access (Permission Policy `camera` feature)."),
/** @description Request microphone access (Permission Policy `microphone` feature). */
microphone: z
.object({})
.optional()
.describe(
"Request microphone access (Permission Policy `microphone` feature).",
),
/** @description Request geolocation access (Permission Policy `geolocation` feature). */
geolocation: z
.object({})
.optional()
.describe(
"Request geolocation access (Permission Policy `geolocation` feature).",
),
/** @description Request clipboard write access (Permission Policy `clipboard-write` feature). */
clipboardWrite: z
.object({})
.optional()
.describe(
"Request clipboard write access (Permission Policy `clipboard-write` feature).",
),
});
/**
* @description Notification of UI size changes (bidirectional: Guest <-> Host).
* @see {@link app.App.sendSizeChanged} for the method to send this from Guest UI
@ -363,6 +397,20 @@ export const McpUiHostCapabilitiesSchema = z.object({
.describe("Host can proxy resource reads to the MCP server."),
/** @description Host accepts log messages. */
logging: z.object({}).optional().describe("Host accepts log messages."),
/** @description Sandbox configuration applied by the host. */
sandbox: z
.object({
/** @description Permissions granted by the host (camera, microphone, geolocation). */
permissions: McpUiResourcePermissionsSchema.optional().describe(
"Permissions granted by the host (camera, microphone, geolocation).",
),
/** @description CSP domains approved by the host. */
csp: McpUiResourceCspSchema.optional().describe(
"CSP domains approved by the host.",
),
})
.optional()
.describe("Sandbox configuration applied by the host."),
});
/**
@ -405,6 +453,10 @@ export const McpUiResourceMetaSchema = z.object({
csp: McpUiResourceCspSchema.optional().describe(
"Content Security Policy configuration.",
),
/** @description Sandbox permissions requested by the UI. */
permissions: McpUiResourcePermissionsSchema.optional().describe(
"Sandbox permissions requested by the UI.",
),
/** @description Dedicated origin for widget sandbox. */
domain: z
.string()
@ -514,6 +566,10 @@ export const McpUiSandboxResourceReadyNotificationSchema = z.object({
csp: McpUiResourceCspSchema.optional().describe(
"CSP configuration from resource metadata.",
),
/** @description Sandbox permissions from resource metadata. */
permissions: McpUiResourcePermissionsSchema.optional().describe(
"Sandbox permissions from resource metadata.",
),
}),
});

View file

@ -220,6 +220,8 @@ export interface McpUiSandboxResourceReadyNotification {
sandbox?: string;
/** @description CSP configuration from resource metadata. */
csp?: McpUiResourceCsp;
/** @description Sandbox permissions from resource metadata. */
permissions?: McpUiResourcePermissions;
};
}
@ -423,6 +425,13 @@ export interface McpUiHostCapabilities {
};
/** @description Host accepts log messages. */
logging?: {};
/** @description Sandbox configuration applied by the host. */
sandbox?: {
/** @description Permissions granted by the host (camera, microphone, geolocation). */
permissions?: McpUiResourcePermissions;
/** @description CSP domains approved by the host. */
csp?: McpUiResourceCsp;
};
}
/**
@ -498,12 +507,30 @@ export interface McpUiResourceCsp {
baseUriDomains?: string[];
}
/**
* @description Sandbox permissions requested by the UI resource.
* Hosts MAY honor these by setting appropriate iframe `allow` attributes.
* Apps SHOULD NOT assume permissions are granted; use JS feature detection as fallback.
*/
export interface McpUiResourcePermissions {
/** @description Request camera access (Permission Policy `camera` feature). */
camera?: {};
/** @description Request microphone access (Permission Policy `microphone` feature). */
microphone?: {};
/** @description Request geolocation access (Permission Policy `geolocation` feature). */
geolocation?: {};
/** @description Request clipboard write access (Permission Policy `clipboard-write` feature). */
clipboardWrite?: {};
}
/**
* @description UI Resource metadata for security and rendering configuration.
*/
export interface McpUiResourceMeta {
/** @description Content Security Policy configuration. */
csp?: McpUiResourceCsp;
/** @description Sandbox permissions requested by the UI. */
permissions?: McpUiResourcePermissions;
/** @description Dedicated origin for widget sandbox. */
domain?: string;
/** @description Visual boundary preference - true if UI prefers a visible border. */

View file

@ -53,6 +53,7 @@ export {
type McpUiInitializeResult,
type McpUiInitializedNotification,
type McpUiResourceCsp,
type McpUiResourcePermissions,
type McpUiResourceMeta,
type McpUiRequestDisplayModeRequest,
type McpUiRequestDisplayModeResult,
@ -110,6 +111,7 @@ export {
McpUiInitializeResultSchema,
McpUiInitializedNotificationSchema,
McpUiResourceCspSchema,
McpUiResourcePermissionsSchema,
McpUiResourceMetaSchema,
McpUiRequestDisplayModeRequestSchema,
McpUiRequestDisplayModeResultSchema,