feat(ui): support prefersBorder option for MCP Apps (#6465)

This commit is contained in:
Andrew Harvard
2026-01-13 12:17:10 -05:00
committed by GitHub
parent b913ad1628
commit 1a2bc4807a
@@ -32,6 +32,12 @@ interface McpAppRendererProps {
append?: (text: string) => void; append?: (text: string) => void;
} }
interface ResourceData {
html: string | null;
csp: CspMetadata | null;
prefersBorder: boolean;
}
export default function McpAppRenderer({ export default function McpAppRenderer({
resourceUri, resourceUri,
extensionName, extensionName,
@@ -42,8 +48,11 @@ export default function McpAppRenderer({
toolCancelled, toolCancelled,
append, append,
}: McpAppRendererProps) { }: McpAppRendererProps) {
const [resourceHtml, setResourceHtml] = useState<string | null>(null); const [resource, setResource] = useState<ResourceData>({
const [resourceCsp, setResourceCsp] = useState<CspMetadata | null>(null); html: null,
csp: null,
prefersBorder: true,
});
const [error, setError] = useState<string | null>(null); const [error, setError] = useState<string | null>(null);
const [iframeHeight, setIframeHeight] = useState(DEFAULT_IFRAME_HEIGHT); const [iframeHeight, setIframeHeight] = useState(DEFAULT_IFRAME_HEIGHT);
@@ -60,11 +69,15 @@ export default function McpAppRenderer({
if (response.data) { if (response.data) {
const content = response.data; const content = response.data;
const meta = content._meta as
| { ui?: { csp?: CspMetadata; prefersBorder?: boolean } }
| undefined;
setResourceHtml(content.text); setResource({
html: content.text,
const meta = content._meta as { ui?: { csp?: CspMetadata } } | undefined; csp: meta?.ui?.csp || null,
setResourceCsp(meta?.ui?.csp || null); prefersBorder: meta?.ui?.prefersBorder ?? true,
});
} }
} catch (err) { } catch (err) {
setError(err instanceof Error ? err.message : 'Failed to load resource'); setError(err instanceof Error ? err.message : 'Failed to load resource');
@@ -161,8 +174,8 @@ export default function McpAppRenderer({
}, []); }, []);
const { iframeRef, proxyUrl } = useSandboxBridge({ const { iframeRef, proxyUrl } = useSandboxBridge({
resourceHtml: resourceHtml || '', resourceHtml: resource.html || '',
resourceCsp, resourceCsp: resource.csp,
resourceUri, resourceUri,
toolInput, toolInput,
toolInputPartial, toolInputPartial,
@@ -174,25 +187,20 @@ export default function McpAppRenderer({
if (error) { if (error) {
return ( return (
<div className="mt-3 p-4 border border-red-500 rounded-lg bg-red-50 dark:bg-red-900/20"> <div className="p-4 border border-red-500 rounded-lg bg-red-50 dark:bg-red-900/20">
<div className="text-red-700 dark:text-red-300">Failed to load MCP app: {error}</div> <div className="text-red-700 dark:text-red-300">Failed to load MCP app: {error}</div>
</div> </div>
); );
} }
if (!resourceHtml) {
return (
<div className="mt-3 p-4 border border-borderSubtle rounded-lg bg-bgApp">
<div className="flex items-center justify-center" style={{ minHeight: '200px' }}>
Loading MCP app...
</div>
</div>
);
}
return ( return (
<div className={cn('mt-3 bg-bgApp', 'border border-borderSubtle rounded-lg overflow-hidden')}> <div
{proxyUrl ? ( className={cn(
'bg-bgApp overflow-hidden',
resource.prefersBorder ? 'border border-borderSubtle rounded-lg' : 'my-6'
)}
>
{resource.html && proxyUrl ? (
<iframe <iframe
ref={iframeRef} ref={iframeRef}
src={proxyUrl} src={proxyUrl}
@@ -205,16 +213,8 @@ export default function McpAppRenderer({
sandbox="allow-scripts allow-same-origin" sandbox="allow-scripts allow-same-origin"
/> />
) : ( ) : (
<div <div className="flex items-center justify-center p-4" style={{ minHeight: '200px' }}>
style={{ Loading MCP app...
width: '100%',
minHeight: '200px',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
}}
>
Loading...
</div> </div>
)} )}
</div> </div>