Skip to main content
Glama
nextframedev

EXIF MCP Server

by nextframedev

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

TableJSON Schema
NameRequiredDescriptionDefault
folder_pathYes
output_folderNo
recursiveNo
overwriteNo
extensionsNo
dry_runNo
include_comparisonNo
write_reportNo

Output Schema

TableJSON Schema
NameRequiredDescriptionDefault
folder_pathYes
processed_countYes
success_countYes
failed_countYes
skipped_countYes
resultsYes

Implementation Reference

  • 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]
  • 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)
Behavior2/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description must disclose behavioral traits. It mentions destructive removal and optional dry-run/report but fails to clarify default overwriting behavior, whether files are modified in-place, or error handling. Key details are missing.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness3/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two sentences are concise but lack structure. The information is front-loaded but could benefit from bullet points or more organized presentation to improve scannability.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness2/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given 8 parameters, sibling tools, and an output schema, the description is incomplete. It doesn't mention return values, supported file types, default behavior, or error handling. For a batch tool with many options, this is insufficient.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters2/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0%, so the description must explain parameters. It only hints at folder_path, dry_run, include_comparison, and write_report, but omits output_folder, recursive, overwrite, and extensions. These are critical for correct invocation.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the verb 'Remove' and resource 'EXIF metadata from supported images in a folder'. It implicitly distinguishes from sibling tools like batch_strip_gps_exif and batch_strip_selected_exif_fields by focusing on all EXIF data.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description does not provide guidance on when to use this tool versus alternatives like batch_strip_gps_exif or strip_exif for single files. It mentions optional features but lacks explicit usage context or exclusion conditions.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Install Server

Other Tools

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/nextframedev/exif_mcp_server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server