read_report_image
Retrieve a report image from an OpenROAD flow run, returning base64-encoded data and metadata. Specify platform, design, run slug, and image name to get the image content.
Instructions
Read a report image and return base64-encoded data with metadata.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| platform | Yes | ||
| design | Yes | ||
| run_slug | Yes | ||
| image_name | Yes |
Output Schema
| Name | Required | Description | Default |
|---|---|---|---|
| result | Yes |
Implementation Reference
- Main handler: ReadReportImageTool.execute() reads a .webp report image from disk, validates paths for security, checks file existence/size, loads/compresses the image, base64-encodes it, and returns a ReadImageResult with metadata.
class ReadReportImageTool(BaseTool): """Tool for reading report images and returning base64-encoded data with metadata.""" async def execute(self, platform: str, design: str, run_slug: str, image_name: str) -> str: """Read a specific report image and return base64-encoded data.""" try: reports_base, run_path = _resolve_run_path(platform, design, run_slug) validate_path_segment(image_name, "image_name") if not image_name.endswith(".webp"): raise ValidationError(f"Image filename must have .webp extension: {image_name}") if not run_path.exists(): logger.warning(f"Run slug not found: {run_slug}") return self._format_result( ReadImageResult( error="RunSlugNotFound", message=f"Run slug '{run_slug}' not found in {reports_base}. " "Use list_report_images to see available runs.", ) ) image_path = run_path / image_name validate_safe_path_containment(image_path, run_path, "image file") if not image_path.exists(): logger.warning(f"Image not found: {image_name}") available_images = [f.name for f in run_path.rglob("*.webp")] return self._format_result( ReadImageResult( error="ImageNotFound", message=f"Image '{image_name}' not found in {run_path}. " f"Available images: {', '.join(available_images) if available_images else 'none'}. " "Use list_report_images to see all available images.", ) ) if not image_path.is_file(): logger.warning(f"Image path is not a file: {image_path}") return self._format_result( ReadImageResult( error="InvalidImagePath", message=f"Image path {image_name} is not a regular file.", ) ) file_size_mb = image_path.stat().st_size / (1024 * 1024) if file_size_mb > MAX_IMAGE_SIZE_MB: logger.warning(f"Image too large: {file_size_mb:.2f}MB > {MAX_IMAGE_SIZE_MB}MB") return self._format_result( ReadImageResult( error="FileTooLarge", message=f"Image size ({file_size_mb:.2f}MB) exceeds maximum " f"allowed size ({MAX_IMAGE_SIZE_MB}MB).", ) ) ( image_bytes, compression_applied, original_size, compressed_size, original_width, original_height, width, height, ) = load_and_compress_image(image_path) image_data_b64 = base64.b64encode(image_bytes).decode("utf-8") stat = image_path.stat() file_stage, file_type = classify_image_type(image_name) compression_ratio = compressed_size / original_size if compression_applied else None metadata = ImageMetadata( filename=image_name, format="webp", size_bytes=compressed_size, width=width, height=height, modified_time=datetime.fromtimestamp(stat.st_mtime).isoformat(), stage=file_stage, type=file_type, compression_applied=compression_applied, original_size_bytes=original_size if compression_applied else None, original_width=original_width if compression_applied else None, original_height=original_height if compression_applied else None, compression_ratio=compression_ratio, ) result = ReadImageResult( image_data=image_data_b64, metadata=metadata, ) return self._format_result(result) except ValidationError as e: return self._format_result(ReadImageResult(error=type(e).__name__, message=str(e))) except Exception as e: logger.exception(f"Failed to read report image: {e}") return self._format_result( ReadImageResult( error="UnexpectedError", message=f"Failed to read report image: {str(e)}", ) ) - src/openroad_mcp/server.py:43-45 (registration)Tool instance creation: read_report_image_tool = ReadReportImageTool(manager) instantiates the tool with the shared OpenROADManager.
# Initialize report image tool instances list_report_images_tool = ListReportImagesTool(manager) read_report_image_tool = ReadReportImageTool(manager) - src/openroad_mcp/server.py:186-198 (registration)Registration: The read_report_image function is decorated with @mcp.tool(...) and delegates to read_report_image_tool.execute(). This is the FastMCP tool registration entry point.
@mcp.tool( annotations=ToolAnnotations( readOnlyHint=True, destructiveHint=False, idempotentHint=True, openWorldHint=False, ) ) async def read_report_image(platform: str, design: str, run_slug: str, image_name: str) -> str: """Read a report image and return base64-encoded data with metadata.""" return await read_report_image_tool.execute(platform, design, run_slug, image_name) - Schema: ReadImageResult model defines the output shape with fields image_data (base64 str or None), metadata (ImageMetadata or None), and message (str or None).
class ReadImageResult(BaseResult): """Result from reading a report image.""" image_data: str | None = None metadata: ImageMetadata | None = None message: str | None = None - Schema: ImageMetadata model defines the metadata fields returned alongside the image data, including filename, dimensions, compression info, and stage/type classification.
class ImageMetadata(BaseModel): """Metadata for a report image.""" filename: str format: str size_bytes: int width: int | None = None height: int | None = None modified_time: str stage: str type: str compression_applied: bool = False original_size_bytes: int | None = None original_width: int | None = None original_height: int | None = None compression_ratio: float | None = None