batch_strip_exif
Remove EXIF metadata from images in a folder. Optionally run dry-run, compare results, or generate per-file reports.
Instructions
Remove EXIF metadata from supported images in a folder.
Optional dry-run, comparison, and per-file sidecar-report behavior is available.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| folder_path | Yes | ||
| output_folder | No | ||
| recursive | No | ||
| overwrite | No | ||
| extensions | No | ||
| dry_run | No | ||
| include_comparison | No | ||
| write_report | No |
Output Schema
| Name | Required | Description | Default |
|---|---|---|---|
| folder_path | Yes | ||
| processed_count | Yes | ||
| success_count | Yes | ||
| failed_count | Yes | ||
| skipped_count | Yes | ||
| results | Yes |
Implementation Reference
- src/exif_mcp_server/tools/batch.py:20-47 (handler)MCP tool handler function 'batch_strip_exif' that wraps the core logic with error handling.
def batch_strip_exif( folder_path: str, output_folder: str | None = None, recursive: bool = False, overwrite: bool = False, extensions: list[str] | None = None, dry_run: bool = False, include_comparison: bool = False, write_report: bool = False, ) -> BatchStripExifResult: """Remove EXIF metadata from supported images in a folder. Optional dry-run, comparison, and per-file sidecar-report behavior is available. """ return run_with_mcp_error_handling( "batch_strip_exif", lambda: batch_strip_exif_in_folder( folder_path=folder_path, output_folder=output_folder, recursive=recursive, overwrite=overwrite, extensions=extensions, dry_run=dry_run, include_comparison=include_comparison, write_report=write_report, ), ) - Core implementation 'batch_strip_exif_in_folder' that iterates over files in a folder and strips EXIF metadata.
def batch_strip_exif_in_folder( folder_path: str, output_folder: str | None = None, recursive: bool = False, overwrite: bool = False, extensions: list[str] | None = None, dry_run: bool = False, include_comparison: bool = False, write_report: bool = False, ) -> BatchStripExifResult: """Remove EXIF metadata from supported files in a folder.""" source_folder = validate_folder_path(folder_path) selected_extensions = _normalized_extensions(extensions) output_root = normalize_path(output_folder) if output_folder is not None else None if output_root is not None: if output_root.exists() and not output_root.is_dir(): raise InvalidPathError( f"Expected output_folder to be a directory path: {output_root}" ) output_root.mkdir(parents=True, exist_ok=True) results: list[BatchFileResult] = [] for file_path in _candidate_files(source_folder, recursive): if file_path.suffix.lower() not in selected_extensions: results.append( { "source_path": str(file_path.resolve()), "status": "skipped", "message": ( "Skipped because the file extension is not selected " "for batch processing." ), } ) continue try: per_file_output = None if output_root is not None: per_file_output = str( _batch_output_path(file_path, source_folder, output_root, overwrite).resolve() ) strip_result = strip_exif_from_file( image_path=str(file_path), output_path=per_file_output, overwrite=overwrite, dry_run=dry_run, include_comparison=include_comparison, write_report=write_report, ) batch_item: BatchFileResult = { "source_path": strip_result["source_path"], "output_path": strip_result["output_path"], "status": "success", "message": ( "Dry run completed; no files were written." if strip_result.get("dry_run") else ( "EXIF removed." if strip_result["removed_exif"] else "No EXIF found; wrote clean copy." ) ), "removed_exif": strip_result["removed_exif"], } if strip_result.get("dry_run"): batch_item["dry_run"] = True if "comparison" in strip_result: batch_item["comparison"] = strip_result["comparison"] if "report_path" in strip_result: batch_item["report_path"] = strip_result["report_path"] results.append(batch_item) except (ExifWriteError, InvalidPathError, UnsafeOverwriteError) as exc: results.append( { "source_path": str(file_path.resolve()), "status": "failed", "message": str(exc), } ) success_count = sum(1 for result in results if result["status"] == "success") failed_count = sum(1 for result in results if result["status"] == "failed") skipped_count = sum(1 for result in results if result["status"] == "skipped") return { "folder_path": str(source_folder), "processed_count": len(results), "success_count": success_count, "failed_count": failed_count, "skipped_count": skipped_count, "results": results, } - Return type schema 'BatchStripExifResult' for the batch_strip_exif tool.
class BatchStripExifResult(TypedDict): """Contract for the batch_strip_exif MCP tool.""" folder_path: str processed_count: int success_count: int failed_count: int skipped_count: int results: list[BatchFileResult] - Per-file result schema 'BatchFileResult' used by batch_strip_exif.
class BatchFileResult(TypedDict): """Per-file status entry for batch_strip_exif.""" source_path: str status: BatchFileStatus message: str output_path: NotRequired[str] removed_exif: NotRequired[bool] dry_run: NotRequired[bool] comparison: NotRequired[ExifComparison] report_path: NotRequired[str] - src/exif_mcp_server/tools/batch.py:106-111 (registration)Registration of batch_strip_exif via server.tool() decorator.
def register_batch_tools(server: Any) -> None: """Register batch cleanup tools on the provided MCP server instance.""" server.tool()(batch_strip_exif) server.tool()(batch_strip_gps_exif) server.tool()(batch_strip_selected_exif_fields)