"""Display an already captured, authenticated PNG in MCP Apps-capable clients.
No HTTP image endpoint, upload, credential, external asset, or new capture is used.
The normal ImageContent remains available to clients that do not render MCP Apps.
"""
from __future__ import annotations
import json
from typing import Annotated, Any
from mcp.types import CallToolResult, TextContent
from pydantic import BaseModel
from .screen_frontdoor import READ
TEMPLATE_URI = "ui://framebridge/screen-v1.html"
TOOL_META = {
"ui": {"resourceUri": TEMPLATE_URI},
"openai/outputTemplate": TEMPLATE_URI,
"openai/toolInvocation/invoking": "Loading original screen capture",
"openai/toolInvocation/invoked": "Original screen capture ready",
}
RESOURCE_META = {
"ui": {"prefersBorder": True, "csp": {"connectDomains": [], "resourceDomains": []}},
"openai/widgetDescription": (
"FrameBridge original screen capture viewer. Shows the full saved PNG, "
"its node, capture identifier, dimensions and verified SHA-256. "
"This is a historical capture, not a live screen."
),
"openai/ui": {"availableDisplayModes": ["inline", "fullscreen"]},
}
class ScreenViewResult(BaseModel):
ok: bool
node: str
requested_node: str | None = None
node_id: str | None = None
hostname: str | None = None
result: dict[str, Any] | None = None
error: str | None = None
VIEWER_HTML = r"""
FrameBridge original screen capture
FrameBridge original screen capture
Saved capture · read only
Waiting for the original PNG.
"""
def presentation_result(response):
"""Preserve the exact image for the model and pass it to the optional UI."""
if not isinstance(response, list) or len(response) != 2:
metadata = response if isinstance(response, dict) else {"ok": False, "node": "", "error": "SCREEN_PRESENTATION_FAILED"}
return CallToolResult(
content=[TextContent(type="text", text=json.dumps(metadata, sort_keys=True))],
structuredContent=metadata, isError=True,
)
metadata, image = response
content = image.to_image_content()
return CallToolResult(
content=[TextContent(type="text", text=json.dumps(metadata, sort_keys=True)), content],
structuredContent=metadata,
_meta={"framebridgeImage": {"mimeType": content.mimeType, "data": content.data}},
)
def register_screen_presentation(mcp, client):
"""Add an explicit render/replay tool without changing observation semantics."""
@mcp.resource(TEMPLATE_URI, name="FrameBridge original screen",
description="Offline viewer for an authenticated saved screen PNG.",
mime_type="text/html;profile=mcp-app", meta=dict(RESOURCE_META))
def framebridge_screen_viewer():
return VIEWER_HTML
@mcp.tool(annotations=dict(READ), meta=dict(TOOL_META))
def framebridge_screen_show(node: str, capture_id: str, authorized: bool) -> Annotated[CallToolResult, ScreenViewResult]:
"""Display or redisplay a saved original screen PNG, without taking a new capture.
First use framebridge_screen_observe, then pass its node and capture_id here.
This returns the same full PNG bytes and SHA-256, and an optional ChatGPT viewer.
It is a historical read only view; use a fresh observation before controlling a screen.
"""
return presentation_result(client.call("read_capture", node, authorized, capture_id=capture_id))
return framebridge_screen_show