list_report_images
List report images from ORFS runs organized by stage to visualize results for a given platform, design, and run slug.
Instructions
List available report images from ORFS runs organized by stage.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| platform | Yes | ||
| design | Yes | ||
| run_slug | Yes | ||
| stage | No | all |
Output Schema
| Name | Required | Description | Default |
|---|---|---|---|
| result | Yes |
Implementation Reference
- Core implementation of ListReportImagesTool: validates run path, discovers .webp files, classifies by stage, and returns organized list of ImageInfo objects.
class ListReportImagesTool(BaseTool): """Tool for listing available report images from ORFS runs.""" async def execute(self, platform: str, design: str, run_slug: str, stage: str = "all") -> str: """List available report images for a specific run.""" try: reports_base, run_path = _resolve_run_path(platform, design, run_slug) if not run_path.exists(): logger.warning(f"Run slug not found: {run_slug}") available_runs = [d.name for d in reports_base.iterdir() if d.is_dir()] return self._format_result( ListImagesResult( error="RunSlugNotFound", message=f"Run slug '{run_slug}' not found in {reports_base}. " f"Available run slugs: {', '.join(sorted(available_runs)[:5]) if available_runs else 'none'}", ) ) webp_files = list(run_path.rglob("*.webp")) if not webp_files: logger.warning(f"No webp images found in {run_path}") return self._format_result( ListImagesResult( run_path=str(run_path), total_images=0, images_by_stage={}, message=f"No webp images found in {run_path}", ) ) images_by_stage: dict[str, list[ImageInfo]] = {} for webp_file in webp_files: file_stage, file_type = classify_image_type(webp_file.name) if stage != "all" and file_stage != stage: continue stat = webp_file.stat() image_info = ImageInfo( filename=webp_file.name, path=str(webp_file), size_bytes=stat.st_size, modified_time=datetime.fromtimestamp(stat.st_mtime).isoformat(), type=file_type, ) if file_stage not in images_by_stage: images_by_stage[file_stage] = [] images_by_stage[file_stage].append(image_info) for stage_images in images_by_stage.values(): stage_images.sort(key=lambda x: x.filename) total_images = sum(len(images) for images in images_by_stage.values()) result = ListImagesResult( run_path=str(run_path), total_images=total_images, images_by_stage=images_by_stage, ) return self._format_result(result) except ValidationError as e: return self._format_result(ListImagesResult(error=type(e).__name__, message=str(e))) except Exception as e: logger.exception(f"Failed to list report images: {e}") return self._format_result( ListImagesResult( error="UnexpectedError", message=f"Failed to list report images: {str(e)}", ) ) - src/openroad_mcp/server.py:174-185 (registration)MCP tool registration using @mcp.tool decorator: maps the 'list_report_images' tool name to the handler.
# Report image tools @mcp.tool( annotations=ToolAnnotations( readOnlyHint=True, destructiveHint=False, idempotentHint=True, openWorldHint=False, ) ) async def list_report_images(platform: str, design: str, run_slug: str, stage: str = "all") -> str: """List available report images from ORFS runs organized by stage.""" return await list_report_images_tool.execute(platform, design, run_slug, stage) - ImageInfo model: a single image entry (filename, path, size, modified_time, type).
class ImageInfo(BaseModel): """Information about a single report image.""" filename: str path: str size_bytes: int modified_time: str type: str - ListImagesResult model: the top-level result shape with run_path, total_images, and images_by_stage.
class ListImagesResult(BaseResult): """Result from listing report images.""" run_path: str | None = None total_images: int | None = None images_by_stage: dict[str, list[ImageInfo]] | None = None message: str | None = None - Image type mapping and classify_image_type helper: maps filenames to stage/type categories.
IMAGE_TYPE_MAPPING = { "cts_clk": "clock_visualization", "cts_clk_layout": "clock_layout", "cts_core_clock": "core_clock_visualization", "cts_core_clock_layout": "core_clock_layout", "final_all": "complete_design", "final_clocks": "clock_routing", "final_congestion": "congestion_heatmap", "final_ir_drop": "ir_drop_analysis", "final_placement": "cell_placement", "final_resizer": "resizer_results", "final_routing": "routing_visualization", } def classify_image_type(filename: str) -> tuple[str, str]: """Classify image by stage and type based on filename.""" base_name = filename.rsplit(".", 1)[0] stage = base_name.split("_")[0] if "_" in base_name else "unknown" image_type = IMAGE_TYPE_MAPPING.get(base_name, "unknown") return stage, image_type