@@ -72,6 +72,7 @@ interface UiResourceData {
7272 html : string ;
7373 csp ?: McpUiResourceCsp ;
7474 permissions ?: McpUiResourcePermissions ;
75+ linkTrustedDomains ?: string [ ] ;
7576}
7677
7778export interface ToolCallInfo {
@@ -151,8 +152,9 @@ async function getUiResource(serverInfo: ServerInfo, uri: string): Promise<UiRes
151152 const uiMeta = contentMeta ?. ui ?? listingMeta ?. ui ;
152153 const csp = uiMeta ?. csp ;
153154 const permissions = uiMeta ?. permissions ;
155+ const linkTrustedDomains = uiMeta ?. linkTrustedDomains ;
154156
155- return { html, csp, permissions } ;
157+ return { html, csp, permissions, linkTrustedDomains } ;
156158}
157159
158160
@@ -271,6 +273,7 @@ export interface AppBridgeCallbacks {
271273export interface AppBridgeOptions {
272274 containerDimensions ?: { maxHeight ?: number ; width ?: number } | { height : number ; width ?: number } ;
273275 displayMode ?: "inline" | "fullscreen" ;
276+ linkTrustedDomains ?: string [ ] ;
274277}
275278
276279export function newAppBridge (
@@ -340,8 +343,24 @@ export function newAppBridge(
340343
341344 appBridge . onopenlink = async ( params , _extra ) => {
342345 log . info ( "Open link request:" , params ) ;
343- window . open ( params . url , "_blank" , "noopener,noreferrer" ) ;
344- return { } ;
346+
347+ const HOST_OPENLINK_DENYLIST = [ new URL ( "https://malicious.com" ) ] ;
348+ if ( HOST_OPENLINK_DENYLIST . some ( ( { origin } ) => origin === new URL ( params . url ) . origin ) ) {
349+ log . info ( "Blocked link by host denylist:" , params . url ) ;
350+ return { isError : true } ;
351+ }
352+
353+ const isTrustedByApp = options ?. linkTrustedDomains ?. some ( ( trustedDomain ) =>
354+ new URLPattern ( trustedDomain ) . test ( params . url )
355+ ) ;
356+ const shouldOpen = isTrustedByApp || window . confirm ( `Open external link?\n${ params . url } ` ) ;
357+ if ( shouldOpen ) {
358+ window . open ( params . url , "_blank" , "noopener,noreferrer" ) ;
359+ return { } ;
360+ }
361+
362+ log . info ( "User declined to open link:" , params . url ) ;
363+ return { isError : true } ;
345364 } ;
346365
347366 appBridge . onloggingmessage = ( params ) => {
0 commit comments