@@ -40,17 +40,25 @@ export async function connectToServer(serverUrl: URL): Promise<ServerInfo> {
4040}
4141
4242
43+ interface UiResourceData {
44+ html : string ;
45+ csp ?: {
46+ connectDomains ?: string [ ] ;
47+ resourceDomains ?: string [ ] ;
48+ } ;
49+ }
50+
4351export interface ToolCallInfo {
4452 serverInfo : ServerInfo ;
4553 tool : Tool ;
4654 input : Record < string , unknown > ;
4755 resultPromise : Promise < CallToolResult > ;
48- appHtmlPromise ?: Promise < string > ;
56+ appResourcePromise ?: Promise < UiResourceData > ;
4957}
5058
5159
5260export function hasAppHtml ( toolCallInfo : ToolCallInfo ) : toolCallInfo is Required < ToolCallInfo > {
53- return ! ! toolCallInfo . appHtmlPromise ;
61+ return ! ! toolCallInfo . appResourcePromise ;
5462}
5563
5664
@@ -71,7 +79,7 @@ export function callTool(
7179
7280 const uiResourceUri = getUiResourceUri ( tool ) ;
7381 if ( uiResourceUri ) {
74- toolCallInfo . appHtmlPromise = getUiResourceHtml ( serverInfo , uiResourceUri ) ;
82+ toolCallInfo . appResourcePromise = getUiResource ( serverInfo , uiResourceUri ) ;
7583 }
7684
7785 return toolCallInfo ;
@@ -88,13 +96,7 @@ function getUiResourceUri(tool: Tool): string | undefined {
8896}
8997
9098
91- async function getUiResourceHtml ( serverInfo : ServerInfo , uri : string ) : Promise < string > {
92- let html = serverInfo . appHtmlCache . get ( uri ) ;
93- if ( html ) {
94- log . info ( "Read UI resource from cache:" , uri ) ;
95- return html ;
96- }
97-
99+ async function getUiResource ( serverInfo : ServerInfo , uri : string ) : Promise < UiResourceData > {
98100 log . info ( "Reading UI resource:" , uri ) ;
99101 const resource = await serverInfo . client . readResource ( { uri } ) ;
100102
@@ -114,9 +116,17 @@ async function getUiResourceHtml(serverInfo: ServerInfo, uri: string): Promise<s
114116 throw new Error ( `Unsupported MIME type: ${ content . mimeType } ` ) ;
115117 }
116118
117- html = "blob" in content ? atob ( content . blob ) : content . text ;
118- serverInfo . appHtmlCache . set ( uri , html ) ;
119- return html ;
119+ const html = "blob" in content ? atob ( content . blob ) : content . text ;
120+
121+ // Extract CSP metadata from resource content._meta.ui.csp (or content.meta for Python SDK)
122+ log . info ( "Resource content keys:" , Object . keys ( content ) ) ;
123+ log . info ( "Resource content._meta:" , ( content as any ) . _meta ) ;
124+
125+ // Try both _meta (spec) and meta (Python SDK quirk)
126+ const contentMeta = ( content as any ) . _meta || ( content as any ) . meta ;
127+ const csp = contentMeta ?. ui ?. csp ;
128+
129+ return { html, csp } ;
120130}
121131
122132
@@ -150,7 +160,7 @@ export function loadSandboxProxy(iframe: HTMLIFrameElement): Promise<boolean> {
150160export async function initializeApp (
151161 iframe : HTMLIFrameElement ,
152162 appBridge : AppBridge ,
153- { input, resultPromise, appHtmlPromise } : Required < ToolCallInfo > ,
163+ { input, resultPromise, appResourcePromise } : Required < ToolCallInfo > ,
154164) : Promise < void > {
155165 const appInitializedPromise = hookInitializedCallback ( appBridge ) ;
156166
@@ -162,9 +172,10 @@ export async function initializeApp(
162172 new PostMessageTransport ( iframe . contentWindow ! , iframe . contentWindow ! ) ,
163173 ) ;
164174
165- // Load inner iframe HTML
166- log . info ( "Sending UI resource HTML to MCP App" ) ;
167- await appBridge . sendSandboxResourceReady ( { html : await appHtmlPromise } ) ;
175+ // Load inner iframe HTML with CSP metadata
176+ const { html, csp } = await appResourcePromise ;
177+ log . info ( "Sending UI resource HTML to MCP App" , csp ? `(CSP: ${ JSON . stringify ( csp ) } )` : "" ) ;
178+ await appBridge . sendSandboxResourceReady ( { html, csp } ) ;
168179
169180 // Wait for inner iframe to be ready
170181 log . info ( "Waiting for MCP App to initialize..." ) ;
0 commit comments