diff --git a/src/app-bridge.ts b/src/app-bridge.ts index 079dc705..fb88efa3 100644 --- a/src/app-bridge.ts +++ b/src/app-bridge.ts @@ -70,6 +70,8 @@ import { McpUiOpenLinkResult, McpUiResourceTeardownRequest, McpUiResourceTeardownResultSchema, + McpUiCloseResourceRequest, + McpUiCloseResourceRequestSchema, McpUiSandboxProxyReadyNotification, McpUiSandboxProxyReadyNotificationSchema, McpUiSizeChangedNotificationSchema, @@ -586,6 +588,40 @@ export class AppBridge extends Protocol< ); } + /** + * Register a handler for app-initiated close notifications from the Guest UI. + * + * The Guest UI sends `ui/close-resource` when it wants to close itself. + * This is the app-initiated counterpart to the host-initiated `ui/resource-teardown`. + * Since the app initiates this, it has already performed any necessary cleanup + * before sending the message. + * + * This is a fire-and-forget event - the host should immediately unmount + * the app iframe upon receiving this request. No response are expected since the + * host will immediately unmount the app upon receiving this request. + * + * @param callback - Handler that receives close params + * - params - Empty object (reserved for future use) + * + * @example + * ```typescript + * bridge.oncloseresource = (params) => { + * console.log("App requested close"); + * // Unmount the iframe + * iframe.remove(); + * }; + * ``` + * + * @see {@link McpUiCloseResourceRequest} for the notification type + */ + set oncloseresource( + callback: (params: McpUiCloseResourceRequest["params"]) => void, + ) { + this.setNotificationHandler(McpUiCloseResourceRequestSchema, (request) => + callback(request.params), + ); + } + /** * Register a handler for display mode change requests from the Guest UI. * diff --git a/src/app.ts b/src/app.ts index e24913e3..d20ee491 100644 --- a/src/app.ts +++ b/src/app.ts @@ -36,6 +36,7 @@ import { McpUiResourceTeardownRequest, McpUiResourceTeardownRequestSchema, McpUiResourceTeardownResult, + McpUiCloseResourceRequest, McpUiSizeChangedNotification, McpUiToolCancelledNotification, McpUiToolCancelledNotificationSchema, @@ -906,6 +907,43 @@ export class App extends Protocol { /** @deprecated Use {@link openLink} instead */ sendOpenLink: App["openLink"] = this.openLink; + /** + * Signal the host to close this app. + * + * Apps call this method to initiate their own termination. The host will + * close/unmount the app iframe upon receiving this notification. Since the app + * is initiating the close, it should perform any necessary cleanup (save state, + * close connections, etc.) before calling this method. + * + * This is a fire-and-forget request - no response is expected since the + * host will immediately unmount the app upon receiving this payload. + * + * Unlike host-initiated teardown (`ui/resource-teardown`), the app has already + * done its cleanup when it calls this method, so no teardown request is + * sent back to the app. + * + * @param params - Empty params object (reserved for future use) + * @returns Promise that resolves when the message is sent + * + * @example App-initiated close after user action + * ```typescript + * // User clicks "Done" button in the app + * async function handleDoneClick() { + * await saveState(); + * await app.closeResource(); + * // App will be unmounted by host + * } + * ``` + * + * @see {@link McpUiCloseResourceRequest} for notification structure + */ + closeResource(params: McpUiCloseResourceRequest["params"] = {}) { + return this.notification({ + method: "ui/close-resource", + params, + }); + } + /** * Request a change to the display mode. * diff --git a/src/generated/schema.json b/src/generated/schema.json index e17767d9..3c2876ef 100644 --- a/src/generated/schema.json +++ b/src/generated/schema.json @@ -28,6 +28,23 @@ }, "additionalProperties": false }, + "McpUiCloseResourceRequest": { + "$schema": "https://json-schema.org/draft/2020-12/schema", + "type": "object", + "properties": { + "method": { + "type": "string", + "const": "ui/close-resource" + }, + "params": { + "type": "object", + "properties": {}, + "additionalProperties": false + } + }, + "required": ["method", "params"], + "additionalProperties": false + }, "McpUiDisplayMode": { "$schema": "https://json-schema.org/draft/2020-12/schema", "anyOf": [ diff --git a/src/generated/schema.test.ts b/src/generated/schema.test.ts index 727c28d2..e246dcd1 100644 --- a/src/generated/schema.test.ts +++ b/src/generated/schema.test.ts @@ -87,6 +87,10 @@ export type McpUiSupportedContentBlockModalitiesSchemaInferredType = z.infer< typeof generated.McpUiSupportedContentBlockModalitiesSchema >; +export type McpUiCloseResourceRequestSchemaInferredType = z.infer< + typeof generated.McpUiCloseResourceRequestSchema +>; + export type McpUiHostCapabilitiesSchemaInferredType = z.infer< typeof generated.McpUiHostCapabilitiesSchema >; @@ -237,6 +241,12 @@ expectType( expectType( {} as spec.McpUiSupportedContentBlockModalities, ); +expectType( + {} as McpUiCloseResourceRequestSchemaInferredType, +); +expectType( + {} as spec.McpUiCloseResourceRequest, +); expectType( {} as McpUiHostCapabilitiesSchemaInferredType, ); diff --git a/src/generated/schema.ts b/src/generated/schema.ts index 32277d23..c1f8d713 100644 --- a/src/generated/schema.ts +++ b/src/generated/schema.ts @@ -385,6 +385,19 @@ export const McpUiSupportedContentBlockModalitiesSchema = z.object({ .describe("Host supports structured content."), }); +/** + * @description Request for app-initiated termination (Guest UI -> Host). + * Apps send this to signal the host to close/unmount them. + * Since the app initiates this, it has already performed any necessary cleanup. + * This is fire-and-forget - no response is expected since the host will + * immediately unmount the app upon receiving this notification. + * @see {@link app.App.closeResource} for the app method that sends this + */ +export const McpUiCloseResourceRequestSchema = z.object({ + method: z.literal("ui/close-resource"), + params: z.object({}), +}); + /** * @description Capabilities supported by the host application. * @see {@link McpUiInitializeResult} for the initialization result that includes these capabilities diff --git a/src/spec.types.ts b/src/spec.types.ts index cb6af1f7..8246f66a 100644 --- a/src/spec.types.ts +++ b/src/spec.types.ts @@ -441,6 +441,19 @@ export interface McpUiSupportedContentBlockModalities { structuredContent?: {}; } +/** + * @description Request for app-initiated termination (Guest UI -> Host). + * Apps send this to signal the host to close/unmount them. + * Since the app initiates this, it has already performed any necessary cleanup. + * This is fire-and-forget - no response is expected since the host will + * immediately unmount the app upon receiving this notification. + * @see {@link app.App.closeResource} for the app method that sends this + */ +export interface McpUiCloseResourceRequest { + method: "ui/close-resource"; + params: {}; +} + /** * @description Capabilities supported by the host application. * @see {@link McpUiInitializeResult} for the initialization result that includes these capabilities diff --git a/src/types.ts b/src/types.ts index 77563dc8..8271ee01 100644 --- a/src/types.ts +++ b/src/types.ts @@ -49,6 +49,7 @@ export { type McpUiHostContextChangedNotification, type McpUiResourceTeardownRequest, type McpUiResourceTeardownResult, + type McpUiCloseResourceRequest, type McpUiHostCapabilities, type McpUiAppCapabilities, type McpUiInitializeRequest, @@ -80,6 +81,7 @@ import type { McpUiInitializedNotification, McpUiSizeChangedNotification, McpUiSandboxProxyReadyNotification, + McpUiCloseResourceRequest, McpUiInitializeResult, McpUiOpenLinkResult, McpUiMessageResult, @@ -110,6 +112,7 @@ export { McpUiHostContextChangedNotificationSchema, McpUiResourceTeardownRequestSchema, McpUiResourceTeardownResultSchema, + McpUiCloseResourceRequestSchema, McpUiHostCapabilitiesSchema, McpUiAppCapabilitiesSchema, McpUiInitializeRequestSchema, @@ -180,7 +183,7 @@ export type AppRequest = * - Sandbox resource ready * * App to host: - * - Initialized, size-changed, sandbox-proxy-ready + * - Initialized, size-changed, sandbox-proxy-ready, close-resource * - Logging messages */ export type AppNotification = @@ -198,6 +201,7 @@ export type AppNotification = | McpUiInitializedNotification | McpUiSizeChangedNotification | McpUiSandboxProxyReadyNotification + | McpUiCloseResourceRequest | LoggingMessageNotification; /**